def add_topic_and_lambda(self, topic_name, lambda_meta_data): """Adds a SNS topic and SAM Function to the CloudFormation template :param topic_name: (str) the SNS topic name :param lambda_meta_data: (LambdaMetaData) an object specifying info about the lambda to create :return: a sns.Topic object """ topic = sns.Topic( "{}Topic".format(topic_name), TopicName=topic_name, ) self.add_resource(topic) self.add_resource( serverless.Function( "{}Lambda".format(lambda_meta_data.name), Description=lambda_meta_data.description, MemorySize=128, FunctionName=lambda_meta_data.name, Runtime=LAMBDA_RUNTIME, Handler='index.handler', CodeUri='lambda-src/', Timeout=10, Environment=awslambda.Environment( Variables={ 'WEBHOOK_URL': lambda_meta_data.webhook_url, 'MESSAGE_PREFIX': lambda_meta_data.message_prefix, }), Events={ 'SNS': serverless.SNSEvent('sns', Topic=Ref(topic)), }, )) return topic
def build_template(self, template=None): if not template: t = self._init_template() else: t = template topic = t.add_resource( sns.Topic( "{}SNSTopic".format(self.stack_name), TopicName=self.stack_name, DisplayName=self.stack_name, )) for s in self.subscriptions: sub = s.build_subscription(t, topic) subr = t.add_resource( sns.SubscriptionResource('{}SNSSubscription'.format(s.name), Protocol=sub[0], Endpoint=sub[1], TopicArn=Ref(topic))) t.add_output( [Output('{}SNSSubscription'.format(s.name), Value=Ref(subr))]) t.add_output( [Output("{}SNSTopic".format(self.stack_name), Value=Ref(topic))]) return t
def render_sns(context, template): for topic_name in context['sns']: topic = template.add_resource( sns.Topic(_sanitize_title(topic_name) + "Topic", TopicName=topic_name)) template.add_output( Output(_sanitize_title(topic_name) + "TopicArn", Value=Ref(topic)))
def resources(self, stack: Stack) -> list[AWSObject]: """Compute AWS resources for the construct.""" return [ sns.Topic( name_to_id(self.name), TopicName=self.name, Subscription=self.subscriptions, ), *self.optional_resources, ]
def public_topic(base_name: str) -> (sns.Topic, sns.TopicPolicy): topic = sns.Topic(f"{base_name}Topic") topic_policy = sns.TopicPolicy( f"{base_name}TopicPolicy", Topics=[topic.ref()], PolicyDocument=dict( Version="2008-10-17", Statement=[_owner_policy(topic), _public_broadcast(topic)]), ) return topic, topic_policy
def _add_pipeline_notifications(self, pipeline): subscriptions = self._create_sns_subscriptions() topic = sns.Topic( 'AppPipelineDeployments', DisplayName='AppPipelineDeployments', Subscription=subscriptions, ) self._t.add_resource(topic) topic_policy = sns.TopicPolicy( 'AllowCloudWatchEventsPublish', PolicyDocument=Policy(Version='2012-10-17', Statement=[ Statement( Sid='AllowCloudWatchEventsToPublish', Effect=Allow, Action=[_sns.Publish], Principal=Principal( 'Service', 'events.amazonaws.com'), Resource=[topic.Ref()], ) ]), Topics=[topic.Ref()], ) self._t.add_resource(topic_policy) sns_target = [events.Target(Id='1', Arn=topic.Ref())] cw_event = events.Rule( 'PipelineEvents', Description='CloudWatch Events Rule for app pipeline.', EventPattern={ "source": ["aws.codepipeline"], "detail-type": [ 'CodePipeline Action Execution State Change', ], 'detail': { 'type': { # Notify when a deploy fails/succeeds # or when an approval is needed. # We could also add something when any # part of the pipeline fails. 'category': ['Deploy', 'Approval'], }, 'pipeline': [pipeline.Ref()], } }, Targets=sns_target, ) self._t.add_resource(cw_event) return topic
def add_resources(self): """Add resources to template.""" template = self.template pagerdutyalert = template.add_resource( sns.Topic( 'Topic' ) ) template.add_output( Output( "%sARN" % pagerdutyalert.title, Description='SNS topic', Value=Ref(pagerdutyalert) ) )
def create_custom_cloudformation_resources(self): t = self.template queue = sqs.Queue("CustomResourcesQueue") topic = sns.Topic( "CustomResourcesTopic", Subscription=[sns.Subscription( Protocol="sqs", Endpoint=GetAtt("CustomResourcesQueue", "Arn"))]) queue_policy = sqs.QueuePolicy( "CustomResourcesQueuePolicy", Queues=[Ref(queue)], PolicyDocument=sns_to_sqs_policy(Ref(topic))) t.add_resource(queue) t.add_resource(topic) t.add_resource(queue_policy)
def get_template( puppet_version, all_regions, source, is_caching_enabled, is_manual_approvals: bool, scm_skip_creation_of_repo: bool, should_validate: bool, ) -> t.Template: is_codecommit = source.get("Provider", "").lower() == "codecommit" is_github = source.get("Provider", "").lower() == "github" is_codestarsourceconnection = (source.get( "Provider", "").lower() == "codestarsourceconnection") is_custom = (source.get("Provider", "").lower() == "custom") is_s3 = source.get("Provider", "").lower() == "s3" description = f"""Bootstrap template used to bring up the main ServiceCatalog-Puppet AWS CodePipeline with dependencies {{"version": "{puppet_version}", "framework": "servicecatalog-puppet", "role": "bootstrap-master"}}""" template = t.Template(Description=description) version_parameter = template.add_parameter( t.Parameter("Version", Default=puppet_version, Type="String")) org_iam_role_arn_parameter = template.add_parameter( t.Parameter("OrgIamRoleArn", Default="None", Type="String")) with_manual_approvals_parameter = template.add_parameter( t.Parameter( "WithManualApprovals", Type="String", AllowedValues=["Yes", "No"], Default="No", )) puppet_code_pipeline_role_permission_boundary_parameter = template.add_parameter( t.Parameter( "PuppetCodePipelineRolePermissionBoundary", Type="String", Description= "IAM Permission Boundary to apply to the PuppetCodePipelineRole", Default=awscs_iam.ARN(resource="policy/AdministratorAccess").data, )) source_role_permissions_boundary_parameter = template.add_parameter( t.Parameter( "SourceRolePermissionsBoundary", Type="String", Description="IAM Permission Boundary to apply to the SourceRole", Default=awscs_iam.ARN(resource="policy/AdministratorAccess").data, )) puppet_generate_role_permission_boundary_parameter = template.add_parameter( t.Parameter( "PuppetGenerateRolePermissionBoundary", Type="String", Description= "IAM Permission Boundary to apply to the PuppetGenerateRole", Default=awscs_iam.ARN(resource="policy/AdministratorAccess").data, )) puppet_deploy_role_permission_boundary_parameter = template.add_parameter( t.Parameter( "PuppetDeployRolePermissionBoundary", Type="String", Description= "IAM Permission Boundary to apply to the PuppetDeployRole", Default=awscs_iam.ARN(resource="policy/AdministratorAccess").data, )) puppet_provisioning_role_permissions_boundary_parameter = template.add_parameter( t.Parameter( "PuppetProvisioningRolePermissionsBoundary", Type="String", Description= "IAM Permission Boundary to apply to the PuppetProvisioningRole", Default=awscs_iam.ARN(resource="policy/AdministratorAccess").data, )) cloud_formation_deploy_role_permissions_boundary_parameter = template.add_parameter( t.Parameter( "CloudFormationDeployRolePermissionsBoundary", Type="String", Description= "IAM Permission Boundary to apply to the CloudFormationDeployRole", Default=awscs_iam.ARN(resource="policy/AdministratorAccess").data, )) deploy_environment_compute_type_parameter = template.add_parameter( t.Parameter( "DeployEnvironmentComputeType", Type="String", Description="The AWS CodeBuild Environment Compute Type", Default="BUILD_GENERAL1_SMALL", )) spoke_deploy_environment_compute_type_parameter = template.add_parameter( t.Parameter( "SpokeDeployEnvironmentComputeType", Type="String", Description= "The AWS CodeBuild Environment Compute Type for spoke execution mode", Default="BUILD_GENERAL1_SMALL", )) deploy_num_workers_parameter = template.add_parameter( t.Parameter( "DeployNumWorkers", Type="Number", Description= "Number of workers that should be used when running a deploy", Default=10, )) puppet_role_name_parameter = template.add_parameter( t.Parameter("PuppetRoleName", Type="String", Default="PuppetRole")) puppet_role_path_template_parameter = template.add_parameter( t.Parameter("PuppetRolePath", Type="String", Default="/servicecatalog-puppet/")) template.add_condition( "ShouldUseOrgs", t.Not(t.Equals(t.Ref(org_iam_role_arn_parameter), "None"))) template.add_condition( "HasManualApprovals", t.Equals(t.Ref(with_manual_approvals_parameter), "Yes")) template.add_resource( s3.Bucket( "StacksRepository", BucketName=t.Sub("sc-puppet-stacks-repository-${AWS::AccountId}"), VersioningConfiguration=s3.VersioningConfiguration( Status="Enabled"), BucketEncryption=s3.BucketEncryption( ServerSideEncryptionConfiguration=[ s3.ServerSideEncryptionRule( ServerSideEncryptionByDefault=s3. ServerSideEncryptionByDefault(SSEAlgorithm="AES256")) ]), PublicAccessBlockConfiguration=s3.PublicAccessBlockConfiguration( BlockPublicAcls=True, BlockPublicPolicy=True, IgnorePublicAcls=True, RestrictPublicBuckets=True, ), Tags=t.Tags({"ServiceCatalogPuppet:Actor": "Framework"}), )) manual_approvals_param = template.add_resource( ssm.Parameter( "ManualApprovalsParam", Type="String", Name="/servicecatalog-puppet/manual-approvals", Value=t.Ref(with_manual_approvals_parameter), )) template.add_resource( ssm.Parameter( "SpokeDeployEnvParameter", Type="String", Name=constants.SPOKE_EXECUTION_MODE_DEPLOY_ENV_PARAMETER_NAME, Value=t.Ref(spoke_deploy_environment_compute_type_parameter), )) param = template.add_resource( ssm.Parameter( "Param", Type="String", Name="service-catalog-puppet-version", Value=t.Ref(version_parameter), )) partition_parameter = template.add_resource( ssm.Parameter( "PartitionParameter", Type="String", Name="/servicecatalog-puppet/partition", Value=t.Ref("AWS::Partition"), )) puppet_role_name_parameter = template.add_resource( ssm.Parameter( "PuppetRoleNameParameter", Type="String", Name="/servicecatalog-puppet/puppet-role/name", Value=t.Ref(puppet_role_name_parameter), )) puppet_role_path_parameter = template.add_resource( ssm.Parameter( "PuppetRolePathParameter", Type="String", Name="/servicecatalog-puppet/puppet-role/path", Value=t.Ref(puppet_role_path_template_parameter), )) share_accept_function_role = template.add_resource( iam.Role( "ShareAcceptFunctionRole", RoleName="ShareAcceptFunctionRole", ManagedPolicyArns=[ t.Sub( "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" ) ], Path=t.Ref(puppet_role_path_template_parameter), Policies=[ iam.Policy( PolicyName="ServiceCatalogActions", PolicyDocument={ "Version": "2012-10-17", "Statement": [{ "Action": ["sts:AssumeRole"], "Resource": { "Fn::Sub": "arn:${AWS::Partition}:iam::*:role${PuppetRolePath}${PuppetRoleName}" }, "Effect": "Allow", }], }, ) ], AssumeRolePolicyDocument={ "Version": "2012-10-17", "Statement": [{ "Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": { "Service": ["lambda.amazonaws.com"] }, }], }, )) provisioning_role = template.add_resource( iam.Role( "ProvisioningRole", RoleName="PuppetProvisioningRole", AssumeRolePolicyDocument={ "Version": "2012-10-17", "Statement": [ { "Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": { "Service": ["codebuild.amazonaws.com"] }, }, { "Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": { "AWS": { "Fn::Sub": "${AWS::AccountId}" } }, }, ], }, ManagedPolicyArns=[ t.Sub( "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess" ) ], PermissionsBoundary=t.Ref( puppet_provisioning_role_permissions_boundary_parameter), Path=t.Ref(puppet_role_path_template_parameter), )) cloud_formation_deploy_role = template.add_resource( iam.Role( "CloudFormationDeployRole", RoleName="CloudFormationDeployRole", AssumeRolePolicyDocument={ "Version": "2012-10-17", "Statement": [ { "Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": { "Service": ["cloudformation.amazonaws.com"] }, }, { "Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": { "AWS": { "Fn::Sub": "${AWS::AccountId}" } }, }, ], }, ManagedPolicyArns=[ t.Sub( "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess" ) ], PermissionsBoundary=t.Ref( cloud_formation_deploy_role_permissions_boundary_parameter), Path=t.Ref(puppet_role_path_template_parameter), )) pipeline_role = template.add_resource( iam.Role( "PipelineRole", RoleName="PuppetCodePipelineRole", AssumeRolePolicyDocument={ "Version": "2012-10-17", "Statement": [{ "Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": { "Service": ["codepipeline.amazonaws.com"] }, }], }, ManagedPolicyArns=[ t.Sub( "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess" ) ], PermissionsBoundary=t.Ref( puppet_code_pipeline_role_permission_boundary_parameter), Path=t.Ref(puppet_role_path_template_parameter), )) source_role = template.add_resource( iam.Role( "SourceRole", RoleName="PuppetSourceRole", AssumeRolePolicyDocument={ "Version": "2012-10-17", "Statement": [ { "Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": { "Service": ["codepipeline.amazonaws.com"] }, }, { "Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": { "AWS": { "Fn::Sub": "arn:${AWS::Partition}:iam::${AWS::AccountId}:root" } }, }, ], }, ManagedPolicyArns=[ t.Sub( "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess" ) ], PermissionsBoundary=t.Ref( source_role_permissions_boundary_parameter), Path=t.Ref(puppet_role_path_template_parameter), )) dry_run_notification_topic = template.add_resource( sns.Topic( "DryRunNotificationTopic", DisplayName="service-catalog-puppet-dry-run-approvals", TopicName="service-catalog-puppet-dry-run-approvals", Condition="HasManualApprovals", )) deploy_role = template.add_resource( iam.Role( "DeployRole", RoleName="PuppetDeployRole", AssumeRolePolicyDocument={ "Version": "2012-10-17", "Statement": [{ "Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": { "Service": ["codebuild.amazonaws.com"] }, }], }, ManagedPolicyArns=[ t.Sub( "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess" ) ], PermissionsBoundary=t.Ref( puppet_deploy_role_permission_boundary_parameter), Path=t.Ref(puppet_role_path_template_parameter), )) num_workers_ssm_parameter = template.add_resource( ssm.Parameter( "NumWorkersSSMParameter", Type="String", Name="/servicecatalog-puppet/deploy/num-workers", Value=t.Sub("${DeployNumWorkers}"), )) parameterised_source_bucket = template.add_resource( s3.Bucket( "ParameterisedSourceBucket", PublicAccessBlockConfiguration=s3.PublicAccessBlockConfiguration( IgnorePublicAcls=True, BlockPublicPolicy=True, BlockPublicAcls=True, RestrictPublicBuckets=True, ), BucketEncryption=s3.BucketEncryption( ServerSideEncryptionConfiguration=[ s3.ServerSideEncryptionRule( ServerSideEncryptionByDefault=s3. ServerSideEncryptionByDefault(SSEAlgorithm="AES256")) ]), Tags=t.Tags.from_dict( **{"ServiceCatalogPuppet:Actor": "Framework"}), BucketName=t.Sub("sc-puppet-parameterised-runs-${AWS::AccountId}"), VersioningConfiguration=s3.VersioningConfiguration( Status="Enabled"), )) source_stage = codepipeline.Stages( Name="Source", Actions=[ codepipeline.Actions( RunOrder=1, RoleArn=t.GetAtt("SourceRole", "Arn"), ActionTypeId=codepipeline.ActionTypeId( Category="Source", Owner="AWS", Version="1", Provider="S3", ), OutputArtifacts=[ codepipeline.OutputArtifacts(Name="ParameterisedSource") ], Configuration={ "S3Bucket": t.Ref(parameterised_source_bucket), "S3ObjectKey": "parameters.zip", "PollForSourceChanges": True, }, Name="ParameterisedSource", ) ], ) install_spec = { "runtime-versions": dict(python="3.7"), "commands": [ f"pip install {puppet_version}" if "http" in puppet_version else f"pip install aws-service-catalog-puppet=={puppet_version}", ], } deploy_env_vars = [ { "Type": "PLAINTEXT", "Name": "PUPPET_ACCOUNT_ID", "Value": t.Ref("AWS::AccountId"), }, { "Type": "PLAINTEXT", "Name": "PUPPET_REGION", "Value": t.Ref("AWS::Region"), }, { "Type": "PARAMETER_STORE", "Name": "PARTITION", "Value": t.Ref(partition_parameter), }, { "Type": "PARAMETER_STORE", "Name": "PUPPET_ROLE_NAME", "Value": t.Ref(puppet_role_name_parameter), }, { "Type": "PARAMETER_STORE", "Name": "PUPPET_ROLE_PATH", "Value": t.Ref(puppet_role_path_parameter), }, ] if is_codecommit: template.add_resource( codecommit.Repository( "CodeRepo", RepositoryName=source.get("Configuration").get( "RepositoryName"), RepositoryDescription= "Repo to store the servicecatalog puppet solution", DeletionPolicy="Retain", )) source_stage.Actions.append( codepipeline.Actions( RunOrder=1, RoleArn=t.GetAtt("SourceRole", "Arn"), ActionTypeId=codepipeline.ActionTypeId( Category="Source", Owner="AWS", Version="1", Provider="CodeCommit", ), OutputArtifacts=[codepipeline.OutputArtifacts(Name="Source")], Configuration={ "RepositoryName": source.get("Configuration").get("RepositoryName"), "BranchName": source.get("Configuration").get("BranchName"), "PollForSourceChanges": source.get("Configuration").get("PollForSourceChanges", True), }, Name="Source", )) if is_github: source_stage.Actions.append( codepipeline.Actions( RunOrder=1, ActionTypeId=codepipeline.ActionTypeId( Category="Source", Owner="ThirdParty", Version="1", Provider="GitHub", ), OutputArtifacts=[codepipeline.OutputArtifacts(Name="Source")], Configuration={ "Owner": source.get("Configuration").get("Owner"), "Repo": source.get("Configuration").get("Repo"), "Branch": source.get("Configuration").get("Branch"), "OAuthToken": t.Join( "", [ "{{resolve:secretsmanager:", source.get("Configuration").get( "SecretsManagerSecret"), ":SecretString:OAuthToken}}", ], ), "PollForSourceChanges": source.get("Configuration").get("PollForSourceChanges"), }, Name="Source", )) if is_custom: source_stage.Actions.append( codepipeline.Actions( RunOrder=1, ActionTypeId=codepipeline.ActionTypeId( Category="Source", Owner="Custom", Version=source.get("Configuration").get( "CustomActionTypeVersion"), Provider=source.get("Configuration").get( "CustomActionTypeProvider"), ), OutputArtifacts=[codepipeline.OutputArtifacts(Name="Source")], Configuration={ "GitUrl": source.get("Configuration").get("GitUrl"), "Branch": source.get("Configuration").get("Branch"), "PipelineName": t.Sub("${AWS::StackName}-pipeline"), }, Name="Source", )) webhook = codepipeline.Webhook( "Webhook", Authentication="IP", TargetAction="Source", AuthenticationConfiguration=codepipeline.WebhookAuthConfiguration( AllowedIPRange=source.get("Configuration").get( "GitWebHookIpAddress")), Filters=[ codepipeline.WebhookFilterRule( JsonPath="$.changes[0].ref.id", MatchEquals="refs/heads/{Branch}") ], TargetPipelineVersion=1, TargetPipeline=t.Sub("${AWS::StackName}-pipeline"), ) template.add_resource(webhook) values_for_sub = { "GitUrl": source.get("Configuration").get("GitUrl"), "WebhookUrl": t.GetAtt(webhook, "Url"), } output_to_add = t.Output("WebhookUrl") output_to_add.Value = t.Sub("${GitUrl}||${WebhookUrl}", **values_for_sub) output_to_add.Export = t.Export(t.Sub("${AWS::StackName}-pipeline")) template.add_output(output_to_add) if is_codestarsourceconnection: source_stage.Actions.append( codepipeline.Actions( RunOrder=1, RoleArn=t.GetAtt("SourceRole", "Arn"), ActionTypeId=codepipeline.ActionTypeId( Category="Source", Owner="AWS", Version="1", Provider="CodeStarSourceConnection", ), OutputArtifacts=[codepipeline.OutputArtifacts(Name="Source")], Configuration={ "ConnectionArn": source.get("Configuration").get("ConnectionArn"), "FullRepositoryId": source.get("Configuration").get("FullRepositoryId"), "BranchName": source.get("Configuration").get("BranchName"), "OutputArtifactFormat": source.get("Configuration").get("OutputArtifactFormat"), }, Name="Source", )) if is_s3: bucket_name = source.get("Configuration").get("S3Bucket") if not scm_skip_creation_of_repo: template.add_resource( s3.Bucket( bucket_name, PublicAccessBlockConfiguration=s3. PublicAccessBlockConfiguration( IgnorePublicAcls=True, BlockPublicPolicy=True, BlockPublicAcls=True, RestrictPublicBuckets=True, ), BucketEncryption=s3.BucketEncryption( ServerSideEncryptionConfiguration=[ s3.ServerSideEncryptionRule( ServerSideEncryptionByDefault=s3. ServerSideEncryptionByDefault( SSEAlgorithm="AES256")) ]), Tags=t.Tags.from_dict( **{"ServiceCatalogPuppet:Actor": "Framework"}), BucketName=bucket_name, VersioningConfiguration=s3.VersioningConfiguration( Status="Enabled"), )) source_stage.Actions.append( codepipeline.Actions( RunOrder=1, ActionTypeId=codepipeline.ActionTypeId( Category="Source", Owner="AWS", Version="1", Provider="S3", ), OutputArtifacts=[codepipeline.OutputArtifacts(Name="Source")], Configuration={ "S3Bucket": bucket_name, "S3ObjectKey": source.get("Configuration").get("S3ObjectKey"), "PollForSourceChanges": source.get("Configuration").get("PollForSourceChanges"), }, Name="Source", )) single_account_run_project_build_spec = dict( version=0.2, phases=dict( install=install_spec, build={ "commands": [ 'echo "single_account: \\"${SINGLE_ACCOUNT_ID}\\"" > parameters.yaml', "cat parameters.yaml", "zip parameters.zip parameters.yaml", "aws s3 cp parameters.zip s3://sc-puppet-parameterised-runs-${PUPPET_ACCOUNT_ID}/parameters.zip", ] }, post_build={ "commands": [ "servicecatalog-puppet wait-for-parameterised-run-to-complete", ] }, ), artifacts=dict( name="DeployProject", files=[ "ServiceCatalogPuppet/manifest.yaml", "ServiceCatalogPuppet/manifest-expanded.yaml", "results/*/*", "output/*/*", "exploded_results/*/*", "tasks.log", ], ), ) single_account_run_project_args = dict( Name="servicecatalog-puppet-single-account-run", Description="Runs puppet for a single account - SINGLE_ACCOUNT_ID", ServiceRole=t.GetAtt(deploy_role, "Arn"), Tags=t.Tags.from_dict(**{"ServiceCatalogPuppet:Actor": "Framework"}), Artifacts=codebuild.Artifacts(Type="NO_ARTIFACTS", ), TimeoutInMinutes=480, Environment=codebuild.Environment( ComputeType=t.Ref(deploy_environment_compute_type_parameter), Image="aws/codebuild/standard:4.0", Type="LINUX_CONTAINER", EnvironmentVariables=[ { "Type": "PLAINTEXT", "Name": "SINGLE_ACCOUNT_ID", "Value": "CHANGE_ME", }, ] + deploy_env_vars, ), Source=codebuild.Source( Type="NO_SOURCE", BuildSpec=yaml.safe_dump(single_account_run_project_build_spec), ), ) single_account_run_project = template.add_resource( codebuild.Project("SingleAccountRunProject", **single_account_run_project_args)) single_account_run_project_build_spec["phases"]["post_build"]["commands"] = [ "servicecatalog-puppet wait-for-parameterised-run-to-complete --on-complete-url $CALLBACK_URL" ] single_account_run_project_args[ "Name"] = "servicecatalog-puppet-single-account-run-with-callback" single_account_run_project_args[ "Description"] = "Runs puppet for a single account - SINGLE_ACCOUNT_ID and then does a http put" single_account_run_project_args.get( "Environment").EnvironmentVariables.append({ "Type": "PLAINTEXT", "Name": "CALLBACK_URL", "Value": "CHANGE_ME", }) single_account_run_project_args["Source"] = codebuild.Source( Type="NO_SOURCE", BuildSpec=yaml.safe_dump(single_account_run_project_build_spec), ) single_account_run_project_with_callback = template.add_resource( codebuild.Project("SingleAccountRunWithCallbackProject", **single_account_run_project_args)) stages = [source_stage] if should_validate: template.add_resource( codebuild.Project( "ValidateProject", Name="servicecatalog-puppet-validate", ServiceRole=t.GetAtt("DeployRole", "Arn"), Tags=t.Tags.from_dict( **{"ServiceCatalogPuppet:Actor": "Framework"}), Artifacts=codebuild.Artifacts(Type="CODEPIPELINE"), TimeoutInMinutes=60, Environment=codebuild.Environment( ComputeType="BUILD_GENERAL1_SMALL", Image="aws/codebuild/standard:4.0", Type="LINUX_CONTAINER", ), Source=codebuild.Source( BuildSpec=yaml.safe_dump( dict( version="0.2", phases={ "install": { "runtime-versions": { "python": "3.7", }, "commands": [ f"pip install {puppet_version}" if "http" in puppet_version else f"pip install aws-service-catalog-puppet=={puppet_version}", ], }, "build": { "commands": [ "servicecatalog-puppet validate manifest.yaml" ] }, }, )), Type="CODEPIPELINE", ), Description="Validate the manifest.yaml file", )) stages.append( codepipeline.Stages( Name="Validate", Actions=[ codepipeline.Actions( InputArtifacts=[ codepipeline.InputArtifacts(Name="Source"), ], Name="Validate", ActionTypeId=codepipeline.ActionTypeId( Category="Build", Owner="AWS", Version="1", Provider="CodeBuild", ), OutputArtifacts=[ codepipeline.OutputArtifacts( Name="ValidateProject") ], Configuration={ "ProjectName": t.Ref("ValidateProject"), "PrimarySource": "Source", }, RunOrder=1, ), ], )) if is_manual_approvals: deploy_stage = codepipeline.Stages( Name="Deploy", Actions=[ codepipeline.Actions( InputArtifacts=[ codepipeline.InputArtifacts(Name="Source"), codepipeline.InputArtifacts( Name="ParameterisedSource"), ], Name="DryRun", ActionTypeId=codepipeline.ActionTypeId( Category="Build", Owner="AWS", Version="1", Provider="CodeBuild", ), OutputArtifacts=[ codepipeline.OutputArtifacts(Name="DryRunProject") ], Configuration={ "ProjectName": t.Ref("DryRunProject"), "PrimarySource": "Source", }, RunOrder=1, ), codepipeline.Actions( ActionTypeId=codepipeline.ActionTypeId( Category="Approval", Owner="AWS", Version="1", Provider="Manual", ), Configuration={ "NotificationArn": t.Ref("DryRunNotificationTopic"), "CustomData": "Approve when you are happy with the dry run.", }, Name="DryRunApproval", RunOrder=2, ), codepipeline.Actions( InputArtifacts=[ codepipeline.InputArtifacts(Name="Source"), codepipeline.InputArtifacts( Name="ParameterisedSource"), ], Name="Deploy", ActionTypeId=codepipeline.ActionTypeId( Category="Build", Owner="AWS", Version="1", Provider="CodeBuild", ), OutputArtifacts=[ codepipeline.OutputArtifacts(Name="DeployProject") ], Configuration={ "ProjectName": t.Ref("DeployProject"), "PrimarySource": "Source", }, RunOrder=3, ), ], ) else: deploy_stage = codepipeline.Stages( Name="Deploy", Actions=[ codepipeline.Actions( InputArtifacts=[ codepipeline.InputArtifacts(Name="Source"), codepipeline.InputArtifacts( Name="ParameterisedSource"), ], Name="Deploy", ActionTypeId=codepipeline.ActionTypeId( Category="Build", Owner="AWS", Version="1", Provider="CodeBuild", ), OutputArtifacts=[ codepipeline.OutputArtifacts(Name="DeployProject") ], Configuration={ "ProjectName": t.Ref("DeployProject"), "PrimarySource": "Source", "EnvironmentVariables": '[{"name":"EXECUTION_ID","value":"#{codepipeline.PipelineExecutionId}","type":"PLAINTEXT"}]', }, RunOrder=1, ), ], ) stages.append(deploy_stage) pipeline = template.add_resource( codepipeline.Pipeline( "Pipeline", RoleArn=t.GetAtt("PipelineRole", "Arn"), Stages=stages, Name=t.Sub("${AWS::StackName}-pipeline"), ArtifactStore=codepipeline.ArtifactStore( Type="S3", Location=t.Sub( "sc-puppet-pipeline-artifacts-${AWS::AccountId}-${AWS::Region}" ), ), RestartExecutionOnUpdate=True, )) if is_github: template.add_resource( codepipeline.Webhook( "Webhook", AuthenticationConfiguration=codepipeline. WebhookAuthConfiguration(SecretToken=t.Join( "", [ "{{resolve:secretsmanager:", source.get("Configuration").get( "SecretsManagerSecret"), ":SecretString:SecretToken}}", ], )), Filters=[ codepipeline.WebhookFilterRule( JsonPath="$.ref", MatchEquals="refs/heads/" + source.get("Configuration").get("Branch"), ) ], Authentication="GITHUB_HMAC", TargetPipeline=t.Ref(pipeline), TargetAction="Source", Name=t.Sub("${AWS::StackName}-webhook"), TargetPipelineVersion=t.GetAtt(pipeline, "Version"), RegisterWithThirdParty="true", )) deploy_project_build_spec = dict( version=0.2, phases=dict( install={ "runtime-versions": dict(python="3.7"), "commands": [ f"pip install {puppet_version}" if "http" in puppet_version else f"pip install aws-service-catalog-puppet=={puppet_version}", ], }, pre_build={ "commands": [ "servicecatalog-puppet --info expand --parameter-override-file $CODEBUILD_SRC_DIR_ParameterisedSource/parameters.yaml manifest.yaml", ] }, build={ "commands": [ "servicecatalog-puppet --info deploy --num-workers ${NUM_WORKERS} manifest-expanded.yaml", ] }, ), artifacts=dict( name="DeployProject", files=[ "manifest-expanded.yaml", "results/*/*", "output/*/*", "exploded_results/*/*", "tasks.log", ], ), ) deploy_project_args = dict( Name="servicecatalog-puppet-deploy", ServiceRole=t.GetAtt(deploy_role, "Arn"), Tags=t.Tags.from_dict(**{"ServiceCatalogPuppet:Actor": "Framework"}), Artifacts=codebuild.Artifacts(Type="CODEPIPELINE", ), TimeoutInMinutes=480, Environment=codebuild.Environment( ComputeType=t.Ref(deploy_environment_compute_type_parameter), Image="aws/codebuild/standard:4.0", Type="LINUX_CONTAINER", EnvironmentVariables=[ { "Type": "PARAMETER_STORE", "Name": "NUM_WORKERS", "Value": t.Ref(num_workers_ssm_parameter), }, { "Type": "PARAMETER_STORE", "Name": "SPOKE_EXECUTION_MODE_DEPLOY_ENV", "Value": constants.SPOKE_EXECUTION_MODE_DEPLOY_ENV_PARAMETER_NAME, }, ] + deploy_env_vars, ), Source=codebuild.Source( Type="CODEPIPELINE", BuildSpec=yaml.safe_dump(deploy_project_build_spec), ), Description="deploys out the products to be deployed", ) deploy_project = template.add_resource( codebuild.Project("DeployProject", **deploy_project_args)) deploy_project_build_spec["phases"]["build"]["commands"] = [ "servicecatalog-puppet --info dry-run manifest-expanded.yaml" ] deploy_project_build_spec["artifacts"]["name"] = "DryRunProject" deploy_project_args["Name"] = "servicecatalog-puppet-dryrun" deploy_project_args[ "Description"] = "dry run of servicecatalog-puppet-dryrun" deploy_project_args["Source"] = codebuild.Source( Type="CODEPIPELINE", BuildSpec=yaml.safe_dump(deploy_project_build_spec), ) dry_run_project = template.add_resource( codebuild.Project("DryRunProject", **deploy_project_args)) bootstrap_project = template.add_resource( codebuild.Project( "BootstrapProject", Name="servicecatalog-puppet-bootstrap-spokes-in-ou", ServiceRole=t.GetAtt("DeployRole", "Arn"), Tags=t.Tags.from_dict( **{"ServiceCatalogPuppet:Actor": "Framework"}), Artifacts=codebuild.Artifacts(Type="NO_ARTIFACTS"), TimeoutInMinutes=60, Environment=codebuild.Environment( ComputeType="BUILD_GENERAL1_SMALL", Image="aws/codebuild/standard:4.0", Type="LINUX_CONTAINER", EnvironmentVariables=[ { "Type": "PLAINTEXT", "Name": "OU_OR_PATH", "Value": "CHANGE_ME" }, { "Type": "PLAINTEXT", "Name": "IAM_ROLE_NAME", "Value": "OrganizationAccountAccessRole", }, { "Type": "PLAINTEXT", "Name": "IAM_ROLE_ARNS", "Value": "" }, ], ), Source=codebuild.Source( BuildSpec= "version: 0.2\nphases:\n install:\n runtime-versions:\n python: 3.7\n commands:\n - pip install aws-service-catalog-puppet\n build:\n commands:\n - servicecatalog-puppet bootstrap-spokes-in-ou $OU_OR_PATH $IAM_ROLE_NAME $IAM_ROLE_ARNS\nartifacts:\n files:\n - results/*/*\n - output/*/*\n name: BootstrapProject\n", Type="NO_SOURCE", ), Description="Bootstrap all the accounts in an OU", )) template.add_resource( codebuild.Project( "BootstrapASpokeProject", Name="servicecatalog-puppet-bootstrap-spoke", ServiceRole=t.GetAtt("DeployRole", "Arn"), Tags=t.Tags.from_dict( **{"ServiceCatalogPuppet:Actor": "Framework"}), Artifacts=codebuild.Artifacts(Type="NO_ARTIFACTS"), TimeoutInMinutes=60, Environment=codebuild.Environment( ComputeType="BUILD_GENERAL1_SMALL", Image="aws/codebuild/standard:4.0", Type="LINUX_CONTAINER", EnvironmentVariables=[ { "Type": "PLAINTEXT", "Name": "PUPPET_ACCOUNT_ID", "Value": t.Sub("${AWS::AccountId}"), }, { "Type": "PLAINTEXT", "Name": "ORGANIZATION_ACCOUNT_ACCESS_ROLE_ARN", "Value": "CHANGE_ME", }, { "Type": "PLAINTEXT", "Name": "ASSUMABLE_ROLE_IN_ROOT_ACCOUNT", "Value": "CHANGE_ME", }, ], ), Source=codebuild.Source( BuildSpec=yaml.safe_dump( dict( version=0.2, phases=dict( install=install_spec, build={ "commands": [ "servicecatalog-puppet bootstrap-spoke-as ${PUPPET_ACCOUNT_ID} ${ASSUMABLE_ROLE_IN_ROOT_ACCOUNT} ${ORGANIZATION_ACCOUNT_ACCESS_ROLE_ARN}" ] }, ), )), Type="NO_SOURCE", ), Description="Bootstrap given account as a spoke", )) cloud_formation_events_queue = template.add_resource( sqs.Queue( "CloudFormationEventsQueue", QueueName="servicecatalog-puppet-cloudformation-events", Tags=t.Tags.from_dict( **{"ServiceCatalogPuppet:Actor": "Framework"}), )) cloud_formation_events_queue_policy = template.add_resource( sqs.QueuePolicy( "CloudFormationEventsQueuePolicy", Queues=[t.Ref(cloud_formation_events_queue)], PolicyDocument={ "Id": "AllowSNS", "Version": "2012-10-17", "Statement": [{ "Sid": "allow-send-message", "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": ["sqs:SendMessage"], "Resource": "*", "Condition": { "ArnEquals": { "aws:SourceArn": t.Sub( "arn:${AWS::Partition}:sns:*:${AWS::AccountId}:servicecatalog-puppet-cloudformation-regional-events" ) } }, }], }, )) spoke_deploy_bucket = template.add_resource( s3.Bucket( "SpokeDeployBucket", PublicAccessBlockConfiguration=s3.PublicAccessBlockConfiguration( IgnorePublicAcls=True, BlockPublicPolicy=True, BlockPublicAcls=True, RestrictPublicBuckets=True, ), BucketEncryption=s3.BucketEncryption( ServerSideEncryptionConfiguration=[ s3.ServerSideEncryptionRule( ServerSideEncryptionByDefault=s3. ServerSideEncryptionByDefault(SSEAlgorithm="AES256")) ]), Tags=t.Tags.from_dict( **{"ServiceCatalogPuppet:Actor": "Framework"}), BucketName=t.Sub("sc-puppet-spoke-deploy-${AWS::AccountId}"), VersioningConfiguration=s3.VersioningConfiguration( Status="Enabled"), )) caching_bucket = template.add_resource( s3.Bucket( "CachingBucket", PublicAccessBlockConfiguration=s3.PublicAccessBlockConfiguration( BlockPublicAcls=True, BlockPublicPolicy=True, IgnorePublicAcls=True, RestrictPublicBuckets=True, ), BucketEncryption=s3.BucketEncryption( ServerSideEncryptionConfiguration=[ s3.ServerSideEncryptionRule( ServerSideEncryptionByDefault=s3. ServerSideEncryptionByDefault(SSEAlgorithm="AES256")) ]), Tags=t.Tags.from_dict( **{"ServiceCatalogPuppet:Actor": "Framework"}), BucketName=t.Sub( "sc-puppet-caching-bucket-${AWS::AccountId}-${AWS::Region}"), VersioningConfiguration=s3.VersioningConfiguration( Status="Enabled"), )) template.add_output( t.Output( "CloudFormationEventsQueueArn", Value=t.GetAtt(cloud_formation_events_queue, "Arn"), )) template.add_output(t.Output("Version", Value=t.GetAtt(param, "Value"))) template.add_output( t.Output("ManualApprovalsParam", Value=t.GetAtt(manual_approvals_param, "Value"))) template.add_resource( ssm.Parameter( "DefaultTerraformVersion", Type="String", Name=constants.DEFAULT_TERRAFORM_VERSION_PARAMETER_NAME, Value=constants.DEFAULT_TERRAFORM_VERSION_VALUE, )) return template
InvitationId=Ref(member_invitation), )) # You can create multiple members if you have multiple members accounts member = t.add_resource( guardduty.Member("Member", Condition="IsMaster", Status="Invited", MemberId=MEMBER_ACCOUNT_ID, Email=MEMBER_ACCOUNT_EMAIL, DetectorId=Ref(detector))) snstopic = t.add_resource( sns.Topic( "SNSTopic", Condition="IsMaster", Subscription=[ # put any subscriptions here ])) event = t.add_resource( events.Rule("EventsRule", Condition="IsMaster", EventPattern={"source": ["aws.guardduty"]}, State="ENABLED", Targets=[events.Target( Arn=Ref(snstopic), Id="sns", )])) # Allow events to send notifications to SNS t.add_resource(
Runtime='python3.6', Timeout=30, Environment=awslambda.Environment( Variables={ 'SOURCE_REGION': Ref(AWS_REGION), 'TARGET_REGION': Ref(target_region_parameter), 'KMS_KEY_ID': Ref(kms_key_parameter), 'CLUSTERS_TO_USE': Ref(clusters_to_use_parameter) }))) # SNS topic for event subscriptions rds_topic = template.add_resource( sns.Topic('RDSBackupTopic', Subscription=[ sns.Subscription( Protocol="lambda", Endpoint=GetAtt(backup_rds_function, 'Arn'), ) ])) # Event subscription - RDS will notify SNS when backup is started and finished template.add_resource( rds.EventSubscription("RDSBackupEvent", Enabled=True, EventCategories=["backup"], SourceType="db-instance", SnsTopicArn=Ref(rds_topic), SourceIds=If("UseAllDatabases", Ref(AWS_NO_VALUE), Ref(databases_to_use_parameter)))) # Permission for SNS to trigger the Lambda
def construct_basics(self): my_topic1 = sns.Topic("SNSTopic1", TopicName=self.topic_name) self.t.add_resource(my_topic1)
alarm_topic = t.add_resource(sns.Topic( 'AlarmTopic', DisplayName=If('HasDisplayNameCondition', Ref(param_display_name), Ref(AWS_NO_VALUE)), Subscription=[ sns.Subscription( Endpoint=Select(0, Ref(param_alarm_emails)), Protocol='email'), If('TwoEmailsCondition', sns.Subscription( Endpoint=Select(1, Ref(param_alarm_emails)), Protocol='email' ), Ref(AWS_NO_VALUE)), If('ThreeEmailsCondition', sns.Subscription( Endpoint=Select(2, Ref(param_alarm_emails)), Protocol='email' ), Ref(AWS_NO_VALUE)), If('FourEmailsCondition', sns.Subscription( Endpoint=Select(3, Ref(param_alarm_emails)), Protocol='email' ), Ref(AWS_NO_VALUE)), If('FiveEmailsCondition', sns.Subscription( Endpoint=Select(4, Ref(param_alarm_emails)), Protocol='email' ), Ref(AWS_NO_VALUE)), If('SixEmailsCondition', sns.Subscription( Endpoint=Select(5, Ref(param_alarm_emails)), Protocol='email' ), Ref(AWS_NO_VALUE)), ], ))
# Add Security Group for Bastion Hosts resource_tags.update({"Name": "ApiDev-Dev-VPC-Bastion-SG"}) BastionSGProperties = { "GroupDescription": "Used for source/dest rules", "Tags": Tags(resource_tags), "VpcId": Ref("VPC") } BastionSG = ec2.SecurityGroup( "BastionSG", GroupDescription=BastionSGProperties.get("GroupDescription"), Tags=BastionSGProperties.get("Tags"), VpcId=BastionSGProperties.get("VpcId")) template.add_resource(BastionSG) # Add CloudWatch Alarm Topic from SNS CloudWatchAlarmTopic = sns.Topic("CloudWatchAlarmTopic", TopicName="ApiDev-Dev-CloudWatchAlarms") template.add_resource(CloudWatchAlarmTopic) # Add DHCP Options resource_tags.update({"Name": "ApiDev-Dev-DhcpOptions"}) DhcpOptions = ec2.DHCPOptions( "DhcpOptions", DomainName=Join("", (Ref("AWS::Region"), ".compute.internal")), DomainNameServers=["AmazonProvidedDNS"], Tags=Tags(resource_tags)) template.add_resource(DhcpOptions) # Add Internet Gateway resource_tags.update({"Name": "ApiDev-Dev-InternetGateway"}) InternetGateway = ec2.InternetGateway("InternetGateway", Tags=Tags(resource_tags))
from troposphere import sns, Ref from stacks.parameters import email_address emails_sns_topic = sns.Topic( "EmailsSNSTopic", Subscription=[ sns.Subscription(Endpoint=Ref(email_address), Protocol="email") ], )
def build_template(self): t = self._init_template() # make iam policy policy = t.add_resource( iam.Role( "{}SlackSNSRole".format(self.stack_name), AssumeRolePolicyDocument=aws.Policy(Statement=[ aws.Statement(Action=[awacs.sts.AssumeRole], Effect=aws.Allow, Principal=aws.Principal( "Service", ["lambda.amazonaws.com"])) ]), Path="/", Policies=[ iam.Policy( PolicyName='snspublic', PolicyDocument=aws.PolicyDocument(Statement=[ aws.Statement(Effect=aws.Allow, Action=[ awacs.sns.Publish, awacs.logs.PutLogEvents, awacs.logs.CreateLogGroup, awacs.logs.CreateLogStream, ], Resource=["*"]) ])) ], ManagedPolicyArns=[ # "arn:aws:iam::aws:policy/AdministratorAccess" ])) code = ["import sys"] # make lambda function fn = t.add_resource( awslambda.Function('{}SlackTopicFN'.format(self.stack_name), Handler='index.handle', Runtime='python3.6', Role=GetAtt(policy, "Arn"), Code=awslambda.Code(ZipFile=Join("", code)))) topic = t.add_resource( sns.Topic( "{}SNSTopic".format(self.stack_name), TopicName=self.stack_name, DisplayName=self.stack_name, Subscription=[ sns.Subscription(Protocol='lambda', Endpoint=GetAtt(fn, "Arn")) ], )) t.add_resource( awslambda.Permission('{}LambdaPerm'.format(self.stack_name), Action='lambda:InvokeFunction', FunctionName=GetAtt(fn, "Arn"), SourceArn=Ref(topic), Principal="sns.amazonaws.com")) t.add_output( [Output("{}SNSTopic".format(self.stack_name), Value=Ref(topic))]) return t
def create_ecs_resources(self): t = self.template # Give the instances access that the Empire daemon needs. t.add_resource( PolicyType( "AccessPolicy", PolicyName="empire", PolicyDocument=empire_policy({ "Environment": Ref("Environment"), "CustomResourcesTopic": Ref("CustomResourcesTopic"), "CustomResourcesQueue": (GetAtt("CustomResourcesQueue", "Arn")), "TemplateBucket": (Join("", ["arn:aws:s3:::", Ref("TemplateBucket"), "/*"])) }), Roles=[Ref("InstanceRole")])) t.add_resource( sns.Topic( EVENTS_TOPIC, DisplayName="Empire events", Condition="CreateSNSTopic", )) t.add_output( Output("EventsSNSTopic", Value=Ref(EVENTS_TOPIC), Condition="CreateSNSTopic")) # Add SNS Events policy if Events are enabled t.add_resource( PolicyType("SNSEventsPolicy", PolicyName="EmpireSNSEventsPolicy", Condition="EnableSNSEvents", PolicyDocument=sns_events_policy( If("CreateSNSTopic", Ref(EVENTS_TOPIC), Ref("EventsSNSTopicName"))), Roles=[Ref("InstanceRole")])) # Add run logs policy if run logs are enabled t.add_resource( PolicyType("RunLogsPolicy", PolicyName="EmpireRunLogsPolicy", Condition="EnableCloudwatchLogs", PolicyDocument=runlogs_policy( If("CreateRunLogsGroup", Ref(RUN_LOGS), Ref("RunLogsCloudwatchGroup"))), Roles=[Ref("InstanceRole")])) # Allow the controller to write empire events to kinesis if kinesis is # enabled. t.add_resource( PolicyType("AppEventStreamPolicy", PolicyName="EmpireAppEventStreamPolicy", Condition="EnableAppEventStream", PolicyDocument=logstream_policy(), Roles=[Ref("InstanceRole")])) t.add_resource( ecs.TaskDefinition( "TaskDefinition", Volumes=[ ecs.Volume( Name="dockerSocket", Host=ecs.Host(SourcePath="/var/run/docker.sock")), ecs.Volume(Name="dockerCfg", Host=ecs.Host(SourcePath="/root/.dockercfg")) ], ContainerDefinitions=[ ecs.ContainerDefinition( Command=["server", "-automigrate=true"], Name="empire", Environment=self.get_empire_environment(), Essential=True, Image=Ref("DockerImage"), MountPoints=[ ecs.MountPoint( SourceVolume="dockerSocket", ContainerPath="/var/run/docker.sock", ReadOnly=False), ecs.MountPoint(SourceVolume="dockerCfg", ContainerPath="/root/.dockercfg", ReadOnly=False) ], PortMappings=[ ecs.PortMapping(HostPort=8081, ContainerPort=8081) ], Cpu=Ref("TaskCPU"), Memory=Ref("TaskMemory")) ])) t.add_resource( Role("ServiceRole", AssumeRolePolicyDocument=get_ecs_assumerole_policy(), Path="/", Policies=[ Policy(PolicyName="ecs-service-role", PolicyDocument=service_role_policy()) ])) t.add_resource( ecs.Service( "Service", Cluster=Ref("ControllerCluster"), DeploymentConfiguration=ecs.DeploymentConfiguration( MaximumPercent=Ref("ServiceMaximumPercent"), MinimumHealthyPercent=Ref("ServiceMinimumHealthyPercent"), ), DesiredCount=Ref("DesiredCount"), LoadBalancers=[ ecs.LoadBalancer(ContainerName="empire", ContainerPort=8081, LoadBalancerName=Ref("LoadBalancer")) ], Role=Ref("ServiceRole"), TaskDefinition=Ref("TaskDefinition")))
domain = os.environ.get("EMAIL_CONTACT_DOMAIN", "http://test") parsed_domain = urlparse(domain) if not parsed_domain.scheme: raise ValueError( f"Domain '{domain}' requires a scheme. This is needed for CORS headers." ) email_target = os.environ.get("EMAIL_TARGET", "*****@*****.**") recaptcha_secret = os.environ.get("RECAPTCHA_SECRET") cdomain = parsed_domain.netloc.replace( ".", "-")[:24] # AWS names have a character limit ### SNS send_to = sns.Subscription(Protocol="email", Endpoint=email_target) topic = sns.Topic("EmailContactForm", Subscription=[send_to]) ### Lambda environment = {"ORIGIN_DOMAIN": domain, "TOPIC_ARN": Ref(topic)} if recaptcha_secret: environment["RECAPTCHA_SECRET"] = recaptcha_secret lambda_environment = awslambda.Environment(Variables=environment) sns_publish_policy = iam.Policy( PolicyName=f"lambda-sns-publish-policy-contact-form-{cdomain}", PolicyDocument={ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow",
'([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|' '([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\.' '([a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,3})$'), Default='example.com', Description= 'FQDN of the main domain or subdomain used to access the site.', MaxLength=100, MinLength=4, Type='String')) # endregion # region Resources notifications = template.add_resource( sns.Topic('Notifications', Subscription=[ sns.Subscription(Endpoint=Ref(email), Protocol='email'), sns.Subscription(Endpoint=Ref(phone), Protocol='sms') ])) main_domain_check = template.add_resource( route53.HealthCheck('MainDomainCheck', HealthCheckConfig=route53.HealthCheckConfiguration( EnableSNI=True, FullyQualifiedDomainName=Ref(main_domain), Port='443', Type='HTTPS'))) main_domain_alarm = template.add_resource( cloudwatch.Alarm( 'MainDomainAlarm', AlarmActions=[Ref(notifications)],
def GenerateStepPublisherLayer(): t = Template() t.add_description("""\ StepScheduler Layer """) stackname_param = t.add_parameter( Parameter( "StackName", Description="Environment Name (default: hackathon)", Type="String", Default="hackathon", )) vpcid_param = t.add_parameter( Parameter( "VpcId", Type="String", Description="VpcId of your existing Virtual Private Cloud (VPC)", Default="vpc-fab00e9f")) subnets = t.add_parameter( Parameter( "Subnets", Type="CommaDelimitedList", Description=( "The list SubnetIds, for public subnets in the " "region and in your Virtual Private Cloud (VPC) - minimum one" ), Default="subnet-b68f3bef,subnet-9a6208ff,subnet-bfdd4fc8")) keypair_param = t.add_parameter( Parameter("KeyPair", Description="Name of an existing EC2 KeyPair to enable SSH " "access to the instance", Type="String", Default="glueteam")) scheduler_ami_id_param = t.add_parameter( Parameter( "SchedulerAmiId", Description="Scheduler server AMI ID (default: ami-a10897d6)", Type="String", Default="ami-a10897d6")) cluster_ami_id_param = t.add_parameter( Parameter("ClusterAmiId", Description="Cluster server AMI ID (default: ami-3db4ca4a)", Type="String", Default="ami-3db4ca4a")) iam_role_param = t.add_parameter( Parameter( "IamRole", Description="IAM Role name", Type="String", )) hashkeyname_param = t.add_parameter( Parameter( "HaskKeyElementName", Description="HashType PrimaryKey Name (default: id)", Type="String", AllowedPattern="[a-zA-Z0-9]*", MinLength="1", MaxLength="2048", ConstraintDescription="must contain only alphanumberic characters", Default="id")) crontab_tablename_param = t.add_parameter( Parameter( "CrontabTablename", Description="Crontab Table Name", Type="String", )) containerlauncher_param = t.add_parameter( Parameter( "Containerlauncher", Description= "Container Launcher zip file (default: containerLauncher-1.0.zip)", Type="String", Default="containerLauncher-1.0.zip")) zipfileversion_param = t.add_parameter( Parameter( "ZipfileVersion", Description="Container Launcher zip file version", Type="String", )) # --------- Lambda Container Launcher lambda_function = t.add_resource( Function( "containerLauncher", Code=Code( S3Bucket="hackathon-glueteam-lambda", S3Key=Ref(containerlauncher_param), S3ObjectVersion=Ref(zipfileversion_param), ), Description=Join('', [Ref(stackname_param), " container Launcher"]), MemorySize=256, Handler="com.philips.glueteam.DockerLauncher::myHandler", Runtime="java8", Timeout=60, Role=Join('', [ "arn:aws:iam::", Ref("AWS::AccountId"), ":role/", Ref(iam_role_param) ]), )) townclock_topic = t.add_resource( sns.Topic( "TownClock", Subscription=[ sns.Subscription(Endpoint=GetAtt("containerLauncher", "Arn"), Protocol="lambda"), ], )) # --------- Scheduler instance scheduler_sg = t.add_resource( ec2.SecurityGroup( 'SchedulerSG', GroupDescription='Security group for Scheduler host', VpcId=Ref(vpcid_param), Tags=Tags(Name=Join("", [Ref(stackname_param), "SchedulerSG"])), SecurityGroupIngress=[ ec2.SecurityGroupRule( IpProtocol="tcp", FromPort="22", ToPort="22", CidrIp="0.0.0.0/0", ), ec2.SecurityGroupRule( IpProtocol="tcp", FromPort="8080", ToPort="8080", CidrIp="0.0.0.0/0", ), ])) cluster = t.add_resource(ecs.Cluster("ECSCluster", )) scheduler_host = t.add_resource( ec2.Instance( 'SchedulerHost', ImageId=Ref(scheduler_ami_id_param), InstanceType='t2.micro', KeyName=Ref(keypair_param), IamInstanceProfile=Ref(iam_role_param), NetworkInterfaces=[ ec2.NetworkInterfaceProperty( AssociatePublicIpAddress=True, SubnetId=Select(0, Ref(subnets)), DeleteOnTermination=True, GroupSet=[ Ref(scheduler_sg), ], DeviceIndex=0, ), ], Tags=Tags(Name=Join("", [Ref(stackname_param), "Scheduler"]), Id=Join("", [Ref(stackname_param), "Scheduler"])), UserData=Base64( Join('', [ '#!/bin/bash\n', 'yum update -y aws-cfn-bootstrap\n', 'sns_topic_arn="', Ref(townclock_topic), '"\n', 'region="', Ref("AWS::Region"), '"\n', 'crontab_tablename="', Ref(crontab_tablename_param), '"\n', 'ecs_clustername="', Ref(cluster), '"\n', 'publish_source=https://raw.githubusercontent.com/hngkr/hackathon/master/ansible/files/unreliable-town-clock-publish\n', 'publish=/usr/local/bin/unreliable-town-clock-publish\n', 'curl -s --location --retry 10 -o $publish $publish_source\n', 'chmod +x $publish\n', 'cat <<EOF >/etc/cron.d/unreliable-town-clock\n', '*/2 * * * * ec2-user $publish "$sns_topic_arn" "$region" "$crontab_tablename" "$ecs_clustername"\n', 'EOF\n', ])), )) cluster_sg = t.add_resource( ec2.SecurityGroup( 'ClusterSG', GroupDescription='Security group for Cluster host', VpcId=Ref(vpcid_param), Tags=Tags(Name=Join("", [Ref(stackname_param), "ClusterSG"])), SecurityGroupIngress=[ ec2.SecurityGroupRule( IpProtocol="tcp", FromPort="22", ToPort="22", CidrIp="0.0.0.0/0", ), ])) cluster_host = t.add_resource( ec2.Instance( 'ClusterHost', ImageId=Ref(cluster_ami_id_param), InstanceType='t2.micro', KeyName=Ref(keypair_param), # TODO: Should have multiple separate iam roles for townclock / clusterhost IamInstanceProfile=Ref(iam_role_param), NetworkInterfaces=[ ec2.NetworkInterfaceProperty( AssociatePublicIpAddress=True, SubnetId=Select(0, Ref(subnets)), DeleteOnTermination=True, GroupSet=[ Ref(cluster_sg), ], DeviceIndex=0, ), ], Tags=Tags(Name=Join("", [Ref(stackname_param), "ClusterNode"]), Id=Join("", [Ref(stackname_param), "ClusterNode"])), UserData=Base64( Join('', [ '#!/bin/bash\n', 'mkdir /etc/ecs\n', 'cat <<EOF >/etc/ecs/ecs.config\n', 'ECS_CLUSTER=', Ref(cluster), '\n', 'EOF\n', ])), )) # --------- Expected DynamoDB Tables dirtylist_table = t.add_resource( dynamodb.Table( "DirtyList", AttributeDefinitions=[ dynamodb.AttributeDefinition(Ref(hashkeyname_param), "S"), ], KeySchema=[dynamodb.Key(Ref(hashkeyname_param), "HASH")], ProvisionedThroughput=dynamodb.ProvisionedThroughput(1, 1))) runlog_table = t.add_resource( dynamodb.Table( "RunLog", AttributeDefinitions=[ dynamodb.AttributeDefinition(Ref(hashkeyname_param), "S"), ], KeySchema=[dynamodb.Key(Ref(hashkeyname_param), "HASH")], ProvisionedThroughput=dynamodb.ProvisionedThroughput(1, 1))) # --------- Outputs t.add_output( [Output( "clusterid", Description="Cluster Id", Value=Ref(cluster), )]) t.add_output([ Output( "dirtylist", Description="DirtyList Tablename", Value=Ref(dirtylist_table), ) ]) t.add_output([ Output( "runlog", Description="Runlog Tablename", Value=Ref(runlog_table), ) ]) t.add_output([ Output( "lambdafunctionname", Description="Lambda Function Name", Value=Ref(lambda_function), ) ]) t.add_output([ Output( "clocktowertopicarn", Description="Clock Tower Topic Arn", Value=Ref(townclock_topic), ) ]) return t
from troposphere import ( Ref, sns, ) sns_topic = sns.Topic("SNSTopic", ) subscription = sns.SubscriptionResource('SNSTopicSubscription', Protocol='email', Endpoint='*****@*****.**', TopicArn=Ref(sns_topic))
def generate_cf_template(): """ Returns an entire CloudFormation stack by using troposphere to construct each piece """ # Header of CloudFormation template t = Template() t.add_version("2010-09-09") t.add_description("Lambda Chat AWS Resources") # Paramters description = "should match [0-9]+-[a-z0-9]+.apps.googleusercontent.com" google_oauth_client_id = t.add_parameter(Parameter( "GoogleOAuthClientID", AllowedPattern="[0-9]+-[a-z0-9]+.apps.googleusercontent.com", Type="String", Description="The Client ID of your Google project", ConstraintDescription=description )) website_s3_bucket_name = t.add_parameter(Parameter( "WebsiteS3BucketName", AllowedPattern="[a-zA-Z0-9\-]*", Type="String", Description="Name of S3 bucket to store the website in", ConstraintDescription="can contain only alphanumeric characters and dashes.", )) # The SNS topic the website will publish chat messages to website_sns_topic = t.add_resource(sns.Topic( 'WebsiteSnsTopic', TopicName='lambda-chat', DisplayName='Lambda Chat' )) t.add_output(Output( "WebsiteSnsTopic", Description="sns_topic_arn", Value=Ref(website_sns_topic), )) # The IAM Role and Policy the website will assume to publish to SNS website_role = t.add_resource(iam.Role( "WebsiteRole", Path="/", AssumeRolePolicyDocument=Policy( Statement=[ Statement( Effect=Allow, Action=[Action("sts", "AssumeRoleWithWebIdentity")], Principal=Principal("Federated", "accounts.google.com"), Condition=Condition( StringEquals( "accounts.google.com:aud", Ref(google_oauth_client_id) ) ), ), ], ), )) t.add_resource(iam.PolicyType( "WebsitePolicy", PolicyName="lambda-chat-website-policy", Roles=[Ref(website_role)], PolicyDocument=Policy( Version="2012-10-17", Statement=[ Statement( Effect=Allow, Action=[Action("sns", "Publish")], Resource=[ Ref(website_sns_topic) ], ), ], ) )) t.add_output(Output( "WebsiteRole", Description="website_iam_role_arn", Value=GetAtt(website_role, "Arn"), )) website_bucket = t.add_resource(s3.Bucket( 'WebsiteS3Bucket', BucketName=Ref(website_s3_bucket_name), WebsiteConfiguration=s3.WebsiteConfiguration( ErrorDocument="error.html", IndexDocument="index.html" ) )) t.add_output(Output( "S3Bucket", Description="s3_bucket", Value=Ref(website_bucket), )) t.add_resource(s3.BucketPolicy( 'WebsiteS3BucketPolicy', Bucket=Ref(website_bucket), PolicyDocument={ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicAccess", "Effect": "Allow", "Principal": "*", "Action": ["s3:GetObject"], "Resource": [{ "Fn::Join": [ "", [ "arn:aws:s3:::", { "Ref": "WebsiteS3Bucket", }, "/*" ] ] }] } ] } )) return t
Action=[awacs.s3.GetBucketAcl], Resource=[Sub('arn:${AWS::Partition}:s3:::${ConfigBucket}')]), Statement( Sid='AWSConfigBucketDelivery', Effect=Allow, Principal=config_service_principal, Action=[awacs.s3.PutObject], Resource=[ Sub('arn:${AWS::Partition}:s3:::${ConfigBucket}/AWSLogs/${AWS::AccountId}/*' ) ]) ]))) config_topic = template.add_resource( sns.Topic('ConfigTopic', TopicName=Sub('config-topic-${AWS::AccountId}'), DisplayName='AWS Config Notification Topic')) config_topic_policy = template.add_resource( sns.TopicPolicy("ConfigTopicPolicy", Topics=[Ref(config_topic)], PolicyDocument=PolicyDocument(Statement=[ Statement(Sid='AWSConfigSNSPolicy', Action=[awacs.sns.Publish], Effect=Allow, Resource=Ref(config_topic), Principal=config_service_principal) ]))) email_notification = template.add_resource( sns.SubscriptionResource('EmailNotification',
from troposphere import Template from troposphere import sns template = Template() android = template.add_resource( sns.Topic('AndroidDevicesTopic', TopicName='devices-android')) android_created = template.add_resource( sns.Topic('AndroidDevicesEndpointCreatedTopic', TopicName='devices-android-created')) android_deleted = template.add_resource( sns.Topic('AndroidDevicesEndpointDeletedTopic', TopicName='devices-android-deleted')) android_updated = template.add_resource( sns.Topic('AndroidDevicesEndpointUpdatedTopic', TopicName='devices-android-updated')) android_delivery_failed = template.add_resource( sns.Topic('AndroidDevicesDeliveryFailedTopic', TopicName='devices-android-delivery-failed'))
def main(): # TODO: # - SNS topic is probably not complete # - BuildCopyCFNProject Encryption keys # - build_copy_cfn_project, validate_resource_project TimeoutInMinutes property # - Lambda function execute permissions # - Build role GetBucketTagging permissions. Only used in build-env step, may be obsolete in other scenarios # - Add customer name to CodeCommit repositories # - What to do with the password of the codecommit user? # - InputArtifact is not used in pipeline # - buildspec.env in project definition itself # - CodeCommitUser Permissions on troporepo and CFNvalidaterepo only! # INIT section template = Template() projectName = "dummy-mytestrepo" # PARAMETERS section github_oauth_token_parameter = template.add_parameter( Parameter( "GithubOauthToken", Type="String", Description="Github OAuthToken", NoEcho=True, )) github_owner_parameter = template.add_parameter( Parameter( "GithubOwner", Type="String", Description="Github owner", Default="cta-int", )) github_branch_parameter = template.add_parameter( Parameter( "GithubBranch", Type="String", Description="Github branch", Default="master", )) github_repository_parameter = template.add_parameter( Parameter( "GithubRepository", Type="String", Description="Github repository", Default="aws-bootstrap", )) # RESOURCES section approve_topic = template.add_resource( sns.Topic("ApproveTopic", Subscription=[ sns.Subscription( Endpoint="*****@*****.**", Protocol="email", ) ])) artifact_store_s3_bucket = template.add_resource( s3.Bucket( "ArtifactStoreS3Bucket", AccessControl=s3.Private, )) # ROLES section cloud_formation_role = template.add_resource( iam.Role( "CloudFormationRole", AssumeRolePolicyDocument=Policy( Version="2012-10-17", Statement=[ Statement(Effect=Allow, Principal=Principal( "Service", "cloudformation.amazonaws.com"), Action=[Action("sts", "AssumeRole")]) ]), Path="/", Policies=[ iam.Policy(PolicyName="CloudFormationNestedCFNAccessPolicy", PolicyDocument=Policy(Version="2012-10-17", Statement=[ Statement( Effect=Allow, Action=[Action("*")], Resource=["*"]) ])) ])) code_build_role = template.add_resource( iam.Role( "CodeBuildRole", AssumeRolePolicyDocument=Policy( Version="2012-10-17", Statement=[ Statement( Effect=Allow, Principal=Principal("Service", "codebuild.amazonaws.com"), Action=[Action("sts", "AssumeRole")], ) ]), Path="/", Policies=[ iam.Policy( PolicyName="CodeBuildAccessPolicy", PolicyDocument=Policy( Version="2012-10-17", Statement=[ Statement( Effect=Allow, Action=[ Action("cloudformation", "Get*"), Action("cloudformation", "Describe*"), Action("cloudformation", "List*"), ], Resource=[ Sub("arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}*" ), ]), Statement(Effect=Allow, Action=[ Action("ec2", "Describe*"), Action("cloudformation", "ValidateTemplate"), Action("elasticloadbalancing", "Describe*"), Action("autoscaling", "Describe*"), Action("iam", "Get*"), Action("iam", "List*"), Action("logs", "Describe*"), Action("logs", "Get*"), Action("tag", "Get*"), ], Resource=["*"]), Statement( Effect=Allow, Action=[ Action("logs", "CreateLogGroup"), Action("logs", "CreateLogStream"), Action("logs", "PutLogEvents"), ], Resource=[ Sub("arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*" ), ]), Statement(Effect=Allow, Action=[ Action("lambda", "ListFunctions"), Action("lambda", "InvokeFunction"), ], Resource=[ "*", ]), Statement( Effect=Allow, Action=[ Action("s3", "PutObject"), Action("s3", "GetObject"), Action("s3", "GetObjectVersion"), Action("s3", "ListBucket"), ], Resource=[ Sub("arn:aws:s3:::codepipeline-${AWS::Region}-*" ), GetAtt(artifact_store_s3_bucket, "Arn"), Join("", [ GetAtt(artifact_store_s3_bucket, "Arn"), "/*" ]), ]), Statement(Effect=Allow, Action=[ Action("s3", "GetBucketTagging"), ], Resource=[ Sub("arn:aws:s3:::*"), ]) ])) ])) code_pipeline_role = template.add_resource( iam.Role( "CodePipelineRole", AssumeRolePolicyDocument=Policy( Version="2012-10-17", Statement=[ Statement( Effect=Allow, Principal=Principal("Service", "codepipeline.amazonaws.com"), Action=[Action("sts", "AssumeRole")], ) ]), Path="/", Policies=[ iam.Policy( PolicyName="CodePipelineAccessPolicy", PolicyDocument=Policy( Version="2012-10-17", Statement=[ Statement(Effect=Allow, Action=[Action("s3", "*")], Resource=[ GetAtt(artifact_store_s3_bucket, "Arn"), Join("", [ GetAtt(artifact_store_s3_bucket, "Arn"), "/*" ]), ]), Statement(Effect=Allow, Action=[ Action("sns", "Publish"), ], Resource=[ Ref(approve_topic), ]), Statement( Effect=Allow, Action=[ Action("codebuild", "StartBuild"), Action("codebuild", "BatchGetBuilds"), ], Resource=[ Sub("arn:aws:codebuild:${AWS::Region}:${AWS::AccountId}:project/codebuild-" + projectName), ]), Statement(Effect=Allow, Action=[ Action("lambda", "ListFunctions"), Action("lambda", "InvokeFunction"), ], Resource=[ "*", ]), Statement( Effect=Allow, Action=[ Action("iam", "PassRole"), ], Resource=[ GetAtt(cloud_formation_role, "Arn"), ], ) ])) ])) code_build_dummy = template.add_resource( codebuild.Project( "CodeBuildDummy", Source=codebuild.Source(Type="CODEPIPELINE"), Artifacts=codebuild.Artifacts(Type="CODEPIPELINE"), Description="Generate cloudformation templates", Environment=codebuild.Environment( ComputeType='BUILD_GENERAL1_SMALL', Image='aws/codebuild/python:3.3.6', Type='LINUX_CONTAINER', ), Name="codebuild-" + projectName, ServiceRole=GetAtt(code_build_role, "Arn"), )) code_pipeline_dummy = template.add_resource( codepipeline.Pipeline( "CodePipelineDummy", Name="pipeline-" + projectName, RoleArn=GetAtt(code_pipeline_role, "Arn"), ArtifactStore=codepipeline.ArtifactStore( Type="S3", Location=Ref(artifact_store_s3_bucket), ), Stages=[ codepipeline.Stages( Name="Source", Actions=[ codepipeline.Actions( Name="Source", ActionTypeId=codepipeline.ActionTypeID( Category="Source", Owner="ThirdParty", Provider="GitHub", Version="1", ), OutputArtifacts=[ codepipeline.OutputArtifacts(Name="Source", ) ], Configuration={ "Branch": Ref(github_branch_parameter), "Repo": Ref(github_repository_parameter), "PollForSourceChanges": True, "Owner": Ref(github_owner_parameter), "OAuthToken": Ref(github_oauth_token_parameter), }, RunOrder="1", ), ]), codepipeline.Stages( Name="Build", Actions=[ codepipeline.Actions( Name="Build", ActionTypeId=codepipeline.ActionTypeID( Category="Build", Owner="AWS", Provider="CodeBuild", Version="1", ), InputArtifacts=[ codepipeline.InputArtifacts(Name="Source", ) ], OutputArtifacts=[ codepipeline.OutputArtifacts(Name="Build", ) ], Configuration={ "ProjectName": Ref(code_build_dummy), }, RunOrder="1", ), ]), codepipeline.Stages( Name="UAT", Actions=[ codepipeline.Actions( Name="CreateUATStack", InputArtifacts=[ codepipeline.InputArtifacts(Name="Build", ) ], ActionTypeId=codepipeline.ActionTypeID( Category="Invoke", Owner="AWS", Version="1", Provider="Lambda", ), Configuration={ "FunctionName": "lambda-cfn-provider", "UserParameters": Sub( json.dumps({ "ActionMode": "CREATE_UPDATE", "ConfigPath": "Build::config.json", "StackName": projectName + "-UAT", "TemplatePath": "Build::dummy.json", })) }, OutputArtifacts=[ codepipeline.OutputArtifacts( Name="CreateUATStack", ) ], RunOrder="1", ), codepipeline.Actions( Name="CreatePRODChangeSet", InputArtifacts=[ codepipeline.InputArtifacts(Name="Build", ) ], ActionTypeId=codepipeline.ActionTypeID( Category="Invoke", Owner="AWS", Version="1", Provider="Lambda", ), Configuration={ "FunctionName": "lambda-cfn-provider", "UserParameters": Sub( json.dumps({ "ActionMode": "CHANGE_SET_REPLACE", "ChangeSetName": projectName + "-PROD-CHANGE-SET", "StackName": projectName + "-PROD", "TemplateConfiguration": "Build::config.json", "TemplatePath": "Build::dummy.json", })) }, OutputArtifacts=[ codepipeline.OutputArtifacts( Name="CreatePRODChangeSet", ) ], RunOrder="2", ), ]), codepipeline.Stages( Name="PROD-ApproveChangeSet", Actions=[ codepipeline.Actions( Name="ApprovePRODChangeSet", ActionTypeId=codepipeline.ActionTypeID( Category="Approval", Owner="AWS", Version="1", Provider="Manual", ), Configuration={ "NotificationArn": Ref(approve_topic), "CustomData": "Approve deployment in production.", }, RunOrder="1", ), ]), codepipeline.Stages( Name="PROD-ExecuteChangeSet", Actions=[ codepipeline.Actions( Name="ExecutePRODChangeSet", InputArtifacts=[ codepipeline.InputArtifacts(Name="Build", ) ], ActionTypeId=codepipeline.ActionTypeID( Category="Invoke", Owner="AWS", Version="1", Provider="Lambda", ), Configuration={ "FunctionName": "lambda-cfn-provider", "UserParameters": Sub( json.dumps({ "ActionMode": "CHANGE_SET_EXECUTE", "ChangeSetName": projectName + "-PROD-CHANGE-SET", "StackName": projectName + "-PROD", })) }, OutputArtifacts=[ codepipeline.OutputArtifacts( Name="ExecutePRODChangeSet", ) ], RunOrder="1", ), ]), ])) # OUTPUT section template.add_output([ Output( "ArtifactStoreS3Bucket", Description= "ResourceName of the S3 bucket containg the artifacts of the pipeline(s)", Value=Ref(artifact_store_s3_bucket), Export=Export(projectName + "-ArtifactS3Bucket"), ), Output( "ArtifactStoreS3BucketArn", Description= "Arn of the S3 bucket containg the artifacts of the pipeline(s)", Value=GetAtt(artifact_store_s3_bucket, "Arn"), Export=Export(projectName + "-ArtifactS3BucketArn"), ), Output( "CodeBuildRole", Description= "Logical name of the role that is used by the CodeBuild projects in the CodePipeline", Value=Ref(code_build_role), Export=Export(projectName + "-CodeBuildRole"), ), Output( "CloudFormationRoleArn", Description= "Arn of the S3 bucket containing the artifacts of the pipeline(s)", Value=GetAtt(cloud_formation_role, "Arn"), Export=Export(projectName + "-CloudFormationRoleArn"), ), Output( "CodePipelineRoleArn", Description= "Logical name of the role that is used by the CodePipeline", Value=GetAtt(code_pipeline_role, "Arn"), Export=Export(projectName + "-CodePipelineRoleArn"), ) ]) print(template.to_json())
from troposphere import sns bucket_updates_topic = sns.Topic("BucketUpdatesTopic")
def get_template(version: str, default_region_value) -> t.Template: description = f"""Bootstrap template used to bootstrap a region of ServiceCatalog-Puppet master {{"version": "{version}", "framework": "servicecatalog-puppet", "role": "bootstrap-master-region"}}""" template = t.Template(Description=description) version_parameter = template.add_parameter( t.Parameter("Version", Default=version, Type="String") ) default_region_value_parameter = template.add_parameter( t.Parameter("DefaultRegionValue", Default=default_region_value, Type="String") ) template.add_resource( ssm.Parameter( "DefaultRegionParam", Name="/servicecatalog-puppet/home-region", Type="String", Value=t.Ref(default_region_value_parameter), Tags={"ServiceCatalogPuppet:Actor": "Framework"}, ) ) version_ssm_parameter = template.add_resource( ssm.Parameter( "Param", Name="service-catalog-puppet-regional-version", Type="String", Value=t.Ref(version_parameter), Tags={"ServiceCatalogPuppet:Actor": "Framework"}, ) ) template.add_resource( s3.Bucket( "PipelineArtifactBucket", BucketName=t.Sub( "sc-puppet-pipeline-artifacts-${AWS::AccountId}-${AWS::Region}" ), VersioningConfiguration=s3.VersioningConfiguration(Status="Enabled"), BucketEncryption=s3.BucketEncryption( ServerSideEncryptionConfiguration=[ s3.ServerSideEncryptionRule( ServerSideEncryptionByDefault=s3.ServerSideEncryptionByDefault( SSEAlgorithm="AES256" ) ) ] ), PublicAccessBlockConfiguration=s3.PublicAccessBlockConfiguration( BlockPublicAcls=True, BlockPublicPolicy=True, IgnorePublicAcls=True, RestrictPublicBuckets=True, ), Tags=t.Tags({"ServiceCatalogPuppet:Actor": "Framework"}), ) ) regional_product_topic = template.add_resource( sns.Topic( "RegionalProductTopic", DisplayName="servicecatalog-puppet-cloudformation-regional-events", TopicName="servicecatalog-puppet-cloudformation-regional-events", Subscription=[ sns.Subscription( Endpoint=t.Sub( "arn:${AWS::Partition}:sqs:${DefaultRegionValue}:${AWS::AccountId}:servicecatalog-puppet-cloudformation-events" ), Protocol="sqs", ) ], ), ) template.add_output( t.Output("Version", Value=t.GetAtt(version_ssm_parameter, "Value")) ) template.add_output( t.Output("RegionalProductTopic", Value=t.Ref(regional_product_topic)) ) return template