Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
    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}
        )
Ejemplo n.º 3
0
    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)
Ejemplo n.º 5
0
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,
        )
Ejemplo n.º 6
0
    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)