def create_deploy_stage(self, source_build_output): deploy_stage=_cp.StageProps( stage_name='Deploy', actions=[ _cpa.CodeDeployEcsDeployAction( action_name='Deploy', container_image_inputs=[ _cpa.CodeDeployEcsContainerImageInput( input=source_build_output, task_definition_placeholder='IMAGE_NAME' ) ], run_order=1, deployment_group=_cd.EcsDeploymentGroup.from_ecs_deployment_group_attributes( self, 'DeploymentGroupAttributes', application=_cd.EcsApplication.from_ecs_application_name( self, 'ApplicationName', 'AppECS-DEMO-CLUSTER-DEMO-SERVICE' ), deployment_group_name='DgpECS-DEMO-CLUSTER-DEMO-SERVICE' ), app_spec_template_file=_cp.ArtifactPath( source_build_output, 'appspec.yml' ), task_definition_template_file=_cp.ArtifactPath( source_build_output, 'taskdef.json' ) ) ] ) return deploy_stage
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) pipeline = codepipeline.Pipeline( self, "CodePipeline", pipeline_name="CDKPipelineTest" ) token = core.SecretValue.secrets_manager("arn:aws:secretsmanager:ap-northeast-1:821383200340:secret:GitHubKey-LPPj2Z") sourceoutput = codepipeline.Artifact() sourceaction = actions.GitHubSourceAction( oauth_token=token, output=sourceoutput, owner="haimila", branch="master", repo="cfpipeline", action_name="SourceAction" ) iam_capabilities = core.CfnCapabilities.NAMED_IAM templatepath = codepipeline.ArtifactPath(sourceoutput, "cfpipeline.yml") deployaction = actions.CloudFormationCreateUpdateStackAction( admin_permissions=True, stack_name="CdkPipelineStack", template_path=templatepath, replace_on_failure=True, action_name="DeployAction", capabilities=[iam_capabilities] ) sourcestage = pipeline.add_stage( stage_name="Source", actions=[sourceaction] ) pipeline.add_stage( stage_name="Deploy", actions=[deployaction], placement={"just_after": sourcestage} )
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) notification_email = ssm.StringParameter.value_from_lookup( self, parameter_name='/serverless-pipeline/sns/notifications/primary-email' ) github_user = ssm.StringParameter.value_from_lookup( self, parameter_name='/serverless-pipeline/codepipeline/github/user' ) github_repo = ssm.StringParameter.value_from_lookup( self, parameter_name='/serverless-pipeline/codepipeline/github/repo' ) github_token = core.SecretValue.secrets_manager( '/serverless-pipeline/secrets/github/token', json_field='github-token', ) artifact_bucket = s3.Bucket( self, 'BuildArtifactsBucket', removal_policy=core.RemovalPolicy.RETAIN, encryption=s3.BucketEncryption.KMS_MANAGED, versioned=True, ) build_project = build.PipelineProject( self, 'BuildProject', project_name='serveless-pipeline', description='Build project for the serverless-pipeline', environment=build.LinuxBuildImage.STANDARD_2_0, environment_variables={ 'BUILD_ARTIFACT_BUCKET': build.BuildEnvironmentVariable(value=artifact_bucket.bucket_name), }, cache=build.Cache.bucket(artifact_bucket, prefix='codebuild-cache'), build_spec=build.BuildSpec.from_object({ 'version': '0.2', 'phases': { 'install': { 'runtime-versions': { 'nodejs': 10, }, 'commands': [ 'echo "--------INSTALL PHASE--------"', 'pip3 install aws-sam-cli', ] }, 'pre_build': { 'commands': [ 'echo "--------PREBUILD PHASE--------"', '# Example shows installation of NPM dependencies for shared deps (layers) in a SAM App', '# cd functions/dependencies/shared_deps_one/nodejs', '# npm install && cd', '# cd functions/dependencies/shared_deps_two/nodejs', '# npm install && cd', ] }, 'build': { 'commands': [ 'echo "--------BUILD PHASE--------"', 'echo "Starting SAM packaging `date` in `pwd`"', 'sam package --template-file template.yaml --s3-bucket $BUILD_ARTIFACT_BUCKET --output-template-file packaged.yaml', ] }, 'post_build': { 'commands': [ 'echo "--------POST-BUILD PHASE--------"', 'echo "SAM packaging completed on `date`"', ] } }, 'artifacts': { 'files': ['packaged.yaml'], 'discard-paths': 'yes', }, 'cache': { 'paths': ['/root/.cache/pip'], } }) ) serverless_pipeline = pipeline.Pipeline( self, 'ServerlessPipeline', artifact_bucket=artifact_bucket, pipeline_name='serverless-pipeline', restart_execution_on_update=True, ) source_output = pipeline.Artifact() build_output = pipeline.Artifact() cfn_output = pipeline.Artifact() # NOTE: This Stage/Action requires a manual OAuth handshake in the browser be complete before automated deployment can occur # Create a new Pipeline in the console, manually authorize GitHub as a source, and then cancel the pipeline wizard. serverless_pipeline.add_stage(stage_name='Source', actions=[ actions.GitHubSourceAction( action_name='SourceCodeRepo', owner=github_user, oauth_token=github_token, repo=github_repo, branch='master', output=source_output, ) ]) serverless_pipeline.add_stage(stage_name='Build', actions=[ actions.CodeBuildAction( action_name='CodeBuildProject', input=source_output, outputs=[build_output], project=build_project, type=actions.CodeBuildActionType.BUILD, ) ]) serverless_pipeline.add_stage(stage_name='Staging', actions=[ actions.CloudFormationCreateReplaceChangeSetAction( action_name='CreateChangeSet', admin_permissions=True, change_set_name='serverless-pipeline-changeset-Staging', stack_name='ServerlessPipelineStaging', template_path=pipeline.ArtifactPath( build_output, file_name='packaged.yaml' ), capabilities=[cfn.CloudFormationCapabilities.ANONYMOUS_IAM], run_order=1, ), actions.CloudFormationExecuteChangeSetAction( action_name='ExecuteChangeSet', change_set_name='serverless-pipeline-changeset-Staging', stack_name='ServerlessPipelineStaging', output=cfn_output, run_order=2, ), ]) serverless_pipeline.add_stage(stage_name='Production', actions=[ actions.CloudFormationCreateReplaceChangeSetAction( action_name='CreateChangeSet', admin_permissions=True, change_set_name='serverless-pipeline-changeset-Production', stack_name='ServerlessPipelineProduction', template_path=pipeline.ArtifactPath( build_output, file_name='packaged.yaml' ), capabilities=[cfn.CloudFormationCapabilities.ANONYMOUS_IAM], run_order=1, ), actions.ManualApprovalAction( action_name='DeploymentApproval', notify_emails=[notification_email], run_order=2, ), actions.CloudFormationExecuteChangeSetAction( action_name='ExecuteChangeSet', change_set_name='serverless-pipeline-changeset-Production', stack_name='ServerlessPipelineProduction', output=cfn_output, run_order=3, ), ]) core.CfnOutput( self, 'BuildArtifactsBucketOutput', value=artifact_bucket.bucket_name, description='Amazon S3 Bucket for Pipeline and Build artifacts', ) core.CfnOutput( self, 'CodeBuildProjectOutput', value=build_project.project_arn, description='CodeBuild Project name', ) core.CfnOutput( self, 'CodePipelineOutput', value=serverless_pipeline.pipeline_arn, description='AWS CodePipeline pipeline name', )
def __init__(self, scope: cdk.Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) # The code that defines your stack goes here vpc = _ec2.Vpc(self, "ecs-vpc", cidr="10.0.0.0/16", nat_gateways=1, max_azs=3) clusterAdmin = _iam.Role(self, "AdminRole", assumed_by=_iam.AccountRootPrincipal()) cluster = _ecs.Cluster(self, "ecs-cluster", vpc=vpc) logging = _ecs.AwsLogDriver(stream_prefix="ecs-logs") taskRole = _iam.Role( self, f"ecs-taskRole-{cdk.Stack.stack_name}", role_name=f"ecs-taskRole-{cdk.Stack.stack_name}", assumed_by=_iam.ServicePrincipal("ecs-tasks.amazonaws.com")) # ECS Contructs executionRolePolicy = _iam.PolicyStatement( effect=_iam.Effect.ALLOW, resources=['*'], actions=[ "ecr:GetAuthorizationToken", "ecr:BatchCheckLayerAvailability", "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage", "logs:CreateLogStream", "logs:PutLogEvents" ]) taskDef = _ecs.FargateTaskDefinition(self, "ecs-taskdef", task_role=taskRole) taskDef.add_to_execution_role_policy(executionRolePolicy) container = taskDef.add_container( 'flask-app', image=_ecs.ContainerImage.from_registry( "nikunjv/flask-image:blue"), memory_limit_mib=256, cpu=256, logging=logging) container.add_port_mappings( _ecs.PortMapping(container_port=5000, protocol=_ecs.Protocol.TCP)) fargateService = ecs_patterns.ApplicationLoadBalancedFargateService( self, "ecs-service", cluster=cluster, task_definition=taskDef, public_load_balancer=True, desired_count=3, listener_port=80) scaling = fargateService.service.auto_scale_task_count(max_capacity=6) scaling.scale_on_cpu_utilization( "CpuScaling", target_utilization_percent=10, scale_in_cooldown=cdk.Duration.seconds(300), scale_out_cooldown=cdk.Duration.seconds(300)) # PIPELINE CONSTRUCTS # ECR Repo ecrRepo = ecr.Repository(self, "EcrRepo") gitHubSource = codebuild.Source.git_hub( owner='samuelhailemariam', repo='aws-ecs-fargate-cicd-cdk', webhook=True, webhook_filters=[ codebuild.FilterGroup.in_event_of( codebuild.EventAction.PUSH).and_branch_is('main'), ]) # CODEBUILD - project project = codebuild.Project( self, "ECSProject", project_name=cdk.Aws.STACK_NAME, source=gitHubSource, environment=codebuild.BuildEnvironment( build_image=codebuild.LinuxBuildImage.AMAZON_LINUX_2_2, privileged=True), environment_variables={ "CLUSTER_NAME": { 'value': cluster.cluster_name }, "ECR_REPO_URI": { 'value': ecrRepo.repository_uri } }, build_spec=codebuild.BuildSpec.from_object({ 'version': "0.2", 'phases': { 'pre_build': { 'commands': [ 'env', 'export TAG=${CODEBUILD_RESOLVED_SOURCE_VERSION}' ] }, 'build': { 'commands': [ 'cd docker-app', 'docker build -t $ECR_REPO_URI:$TAG .', '$(aws ecr get-login --no-include-email)', 'docker push $ECR_REPO_URI:$TAG' ] }, 'post_build': { 'commands': [ 'echo "In Post-Build Stage"', 'cd ..', "printf '[{\"name\":\"flask-app\",\"imageUri\":\"%s\"}]' $ECR_REPO_URI:$TAG > imagedefinitions.json", "pwd; ls -al; cat imagedefinitions.json" ] } }, 'artifacts': { 'files': ['imagedefinitions.json'] } })) # PIPELINE ACTIONS sourceOutput = codepipeline.Artifact() buildOutput = codepipeline.Artifact() sourceAction = codepipeline_actions.GitHubSourceAction( action_name='GitHub_Source', owner='samuelhailemariam', repo='aws-ecs-fargate-cicd-cdk', branch='master', oauth_token=cdk.SecretValue.secrets_manager("/my/github/token"), output=sourceOutput) buildAction = codepipeline_actions.CodeBuildAction( action_name='codeBuild', project=project, input=sourceOutput, outputs=[buildOutput]) manualApprovalAction = codepipeline_actions.ManualApprovalAction( action_name='Approve') deployAction = codepipeline_actions.EcsDeployAction( action_name='DeployAction', service=fargateService.service, image_file=codepipeline.ArtifactPath(buildOutput, 'imagedefinitions.json')) pipeline = codepipeline.Pipeline(self, "ECSPipeline") source_stage = pipeline.add_stage(stage_name="Source", actions=[sourceAction]) build_stage = pipeline.add_stage(stage_name="Build", actions=[buildAction]) approve_stage = pipeline.add_stage(stage_name="Approve", actions=[manualApprovalAction]) deploy_stage = pipeline.add_stage(stage_name="Deploy-to-ECS", actions=[deployAction]) ecrRepo.grant_pull_push(project.role) project.add_to_role_policy( _iam.PolicyStatement(resources=['cluster.cluster_arn'], actions=[ "ecs:DescribeCluster", "ecr:GetAuthorizationToken", "ecr:BatchCheckLayerAvailability", "ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer" ])) # OUTPUT cdk.CfnOutput( self, "LoadBlancer-DNS", value=fargateService.load_balancer.load_balancer_dns_name)
def create_action( scope: core.Construct, id: str, action_def: Union[CodeCommitAction, CodeBuildAction, CloudFormationCreateUpdateStackAction, ApprovalAction, LambdaInvokeAction, S3SourceAction, ], ): action_name = action_def.pop("name") run_order = action_def.get("run_order", 1) variables_namespace = action_def.get("variables_namespace") role = (aws_iam.Role.from_role_arn(scope, f"{id}RoleRef", action_def["role_arn"]) if "role_arn" in action_def else None) if action_def["type"] == "CODECOMMIT": action_def = cast(CodeCommitAction, action_def) repository = aws_codecommit.Repository.from_repository_name( scope, f"{id}Repo", action_def["repository"]) output = aws_codepipeline.Artifact(action_def["output"]) return aws_codepipeline_actions.CodeCommitSourceAction( action_name=action_name, output=output, repository=repository, branch=action_def.get("branch", "master"), run_order=run_order, role=role, variables_namespace=variables_namespace, ) elif action_def["type"] == "S3_SOURCE": action_def = cast(S3SourceAction, action_def) output = aws_codepipeline.Artifact(action_def["output"]) if "kms_key_arn" in action_def: role = aws_iam.Role( scope, f"{id}Role", assumed_by=aws_iam.AccountRootPrincipal(), ) aws_kms.Key.from_key_arn( scope, f"{id}KeyRef", key_arn=action_def["kms_key_arn"]).grant_decrypt(role) if "bucket" in action_def: bucket = aws_s3.Bucket.from_bucket_name(scope, f"{id}SourceBucketRef", action_def["bucket"]) else: bucket = aws_s3.Bucket( scope, f"{id}SourceBucket", block_public_access=aws_s3.BlockPublicAccess.BLOCK_ALL, removal_policy=core.RemovalPolicy.DESTROY, ) core.CfnOutput(scope, f"{id}SourceBucketName", value=bucket.bucket_name) return aws_codepipeline_actions.S3SourceAction( action_name=action_name, output=output, run_order=run_order, role=role, bucket=bucket, bucket_key=action_def["key"], ) elif action_def["type"] == "CODEBUILD": action_def = cast(CodeBuildAction, action_def) # Set up CodeBuild project project_params = { "build_spec": aws_codebuild.BuildSpec.from_source_filename( action_def.get("build_spec", "buildspec.yaml")), "timeout": core.Duration.minutes(int(action_def.get("timeout_minutes", 60))), } project_params["environment"] = { "build_image": aws_codebuild.LinuxBuildImage.AMAZON_LINUX_2_3 } if "environment" in action_def: if "build_image" in action_def["environment"]: project_params["environment"]["build_image"] = getattr( aws_codebuild.LinuxBuildImage, action_def["environment"].pop("build_image"), ) if "compute_type" in action_def["environment"]: project_params["environment"]["compute_type"] = getattr( aws_codebuild.ComputeType, action_def["environment"].pop("compute_type"), ) project_params["environment"].update(**action_def["environment"]) project_role = aws_iam.Role( scope, f"{id}CodeBuildRole", path="/codebuild/", assumed_by=aws_iam.ServicePrincipal( service="codebuild.amazonaws.com"), ) project_role.add_to_policy( aws_iam.PolicyStatement(actions=["*"], resources=["*"], effect=aws_iam.Effect.ALLOW)) project_environment_variables = ({ var_key: aws_codebuild.BuildEnvironmentVariable( value=str(var_value), type=aws_codebuild.BuildEnvironmentVariableType.PLAINTEXT, ) for var_key, var_value in action_def["environment_variables"].items() if "#" not in str(var_value) } if "environment_variables" in action_def else None) project = aws_codebuild.PipelineProject( scope, f"{id}Project", project_name=id, role=project_role, environment_variables=project_environment_variables, **project_params, ) pipeline_environment_variables = ({ var_key: aws_codebuild.BuildEnvironmentVariable( value=str(var_value), type=aws_codebuild.BuildEnvironmentVariableType.PLAINTEXT, ) for var_key, var_value in action_def["environment_variables"].items() if "#" in str(var_value) } if "environment_variables" in action_def else None) extra_inputs = ([ aws_codepipeline.Artifact(input_) for input_ in action_def["extra_inputs"] ] if "extra_inputs" in action_def else None) outputs = ([ aws_codepipeline.Artifact(output) for output in action_def["outputs"] ] if "outputs" in action_def else None) return aws_codepipeline_actions.CodeBuildAction( action_name=action_name, input=aws_codepipeline.Artifact(action_def["input"]), project=project, run_order=run_order, role=role, variables_namespace=variables_namespace, environment_variables=pipeline_environment_variables, extra_inputs=extra_inputs, outputs=outputs, ) elif action_def["type"] == "CLOUDFORMATION": action_def = cast(CloudFormationCreateUpdateStackAction, action_def) return aws_codepipeline_actions.CloudFormationCreateUpdateStackAction( action_name=action_name, admin_permissions=False, stack_name=action_def["stack_name"], template_path=aws_codepipeline.ArtifactPath( aws_codepipeline.Artifact(action_def["input"]), action_def.get("template_path", "template.yaml"), ), capabilities=[ # This lstrip does not support all possibilties, but is good enough for now aws_cloudformation.CloudFormationCapabilities[ capability.lstrip("CAPABILITY_")] for capability in action_def["capabilities"] ] if "capabilities" in action_def else None, deployment_role=role, role=role, parameter_overrides=action_def.get("parameter_overrides"), run_order=run_order, variables_namespace=variables_namespace, ) elif action_def["type"] == "APPROVAL": action_def = cast(ApprovalAction, action_def) return aws_codepipeline_actions.ManualApprovalAction( action_name=action_name, run_order=run_order, role=role, additional_information=action_def.get("additional_information"), external_entity_link=action_def.get("external_entity_link"), notification_topic=action_def.get("notification_topic"), variables_namespace=variables_namespace, ) elif action_def["type"] == "LAMBDA": action_def = cast(LambdaInvokeAction, action_def) user_parameters = action_def.get("user_parameters") return aws_codepipeline_actions.LambdaInvokeAction( action_name=action_name, run_order=run_order, lambda_=aws_lambda.Function.from_function_arn( scope, f"{id}Lambda", action_def["function_arn"]), user_parameters=user_parameters, role=role, variables_namespace=variables_namespace, )
def __init__(self, app: core.App, id: str, props, **kwargs) -> None: super().__init__(app, id, **kwargs) # variables # Context Variables namespace = self.node.try_get_context('namespace') application = self.node.try_get_context('application') image_name_context = application['image-name'] code_branch = application['branch'] # Services from Infra stack fargateService = props['container-infra']['fargateService'] bucket = props['container-infra']['pipeline-bucket'] # Services from PipelineBase stack codecommit = props['pipeline-base']['codecommit'] codebuild = props['pipeline-base']['codebuild'] # define the s3 artifact for stages source_output = _codepipeline.Artifact() build_output = _codepipeline.Artifact() ### defining the pipeline stages ### # code commit (source) stage code_commit_source_action = _codepipeline_actions.CodeCommitSourceAction( repository=codecommit, branch=code_branch, output=source_output, trigger=_codepipeline_actions.CodeCommitTrigger.POLL, action_name="CodeCommitSource", run_order=1, variables_namespace=f"{namespace}") source_stage = _codepipeline.StageProps( stage_name="Source", actions=[code_commit_source_action]) # code build (build) stage code_build_action = _codepipeline_actions.CodeBuildAction( action_name='DockerBuildImages', input=source_output, project=codebuild, run_order=1, outputs=[build_output]) build_stage = _codepipeline.StageProps(stage_name="Build", actions=[code_build_action]) # code deploy (deploy) stage deploy_action = _codepipeline_actions.EcsDeployAction( action_name="DeployAction", service=fargateService.service, image_file=_codepipeline.ArtifactPath(build_output, "imagedefinitions.json")) deploy_stage = _codepipeline.StageProps(stage_name="Deploy", actions=[deploy_action]) pipeline = _codepipeline.Pipeline( self, "Pipeline", pipeline_name=f"{namespace}-{image_name_context}-pipeline", artifact_bucket=bucket, cross_account_keys=False, stages=[source_stage, build_stage, deploy_stage]) # give pipelinerole read write to the bucket bucket.grant_read_write(pipeline.role) pipeline.add_to_role_policy( _iam.PolicyStatement(actions=["s3:*"], resources=[f"{bucket.bucket_arn}"])) # cfn output core.CfnOutput(self, "PipelineOut", description="Pipeline", value=pipeline.pipeline_name)