def deploy(self, stack_name: str, *, template_path: str = None, action_name: str = None, stage_it: bool = True, **deploy_config): """Deploy stage for AWS CodePipeline Args: stack_name (str): CDK/CFN stack name to deploy template_path (str, optional): the generated CFN template path. Defaults to: 'stack_name'.template.json action_name (str, optional): AWS Pipeline action name. Defaults to: deploy-'stack_name' stage_it (bool, optional): Automagically stage this in pipeline. Defaults to True. """ if not template_path: template_path = self.artifacts['builds'][0].at_path( "{}.template.json".format(stack_name)) if not action_name: action_name = "deploy-{}".format(stack_name) deploy = cpa.CloudFormationCreateUpdateStackAction( admin_permissions=True, extra_inputs=self.artifacts['builds'], template_path=template_path, action_name=action_name, stack_name=stack_name, **deploy_config) # Save deploy action self.actions['deploy'][action_name] = deploy # Stage it (execute deploy) in pipeline if stage_it: self.pipe.add_stage(stage_name=action_name, actions=[self.actions['deploy'][action_name]])
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 create_cloudformation_action(scope, action_name, stack_name, source_output, template_file, template_parameters_file, run_order=1): """ create_cloudformation_actio a CloudFormation action to be added to AWS Codepipeline stage :scope: CDK Construct scope that's needed to create CDK resources :action_name: name of the StackSet action :stack_name: name of the stack to be deployed :source_output: CDK object of the Source action's output :template_file: name of the Cloudformation template to be deployed :template_parameters_file: name of the template parameters :return: codepipeline CloudFormation action in a form of a CDK object that can be attached to a codepipeline stage """ # Create codepipeline's cloudformation action create_cloudformation_action = codepipeline_actions.CloudFormationCreateUpdateStackAction( action_name=action_name, stack_name=stack_name, capabilities=[cloudformation.CloudFormationCapabilities.NAMED_IAM], template_path=source_output.at_path(template_file), # Admin permissions are added to the deployement role used by the CF action for simplicity # and deploy different resources by different MLOps pipelines. Roles are defined by the # pipelines' cloudformation templates. admin_permissions=True, template_configuration=source_output.at_path(template_parameters_file), variables_namespace=f"{action_name}-namespace", replace_on_failure=True, run_order=run_order, ) return create_cloudformation_action
def __init__(self, scope: core.Construct, id: str, lambda_code: _lambda.CfnParametersCode, custom_resource: _lambda.CfnParametersCode, **kwargs) -> None: super().__init__(scope, id, **kwargs) # The code that defines your stack goes here self.lambda_code = lambda_code self.custom_resource = custom_resource code = _commit.Repository( self, 'CustomerServerlessCode', repository_name='spring-petclinic-customers-serverless') lambda_project = _build.PipelineProject( self, 'CustomerLambdaBuild', build_spec=_build.BuildSpec.from_object({ 'version': 0.2, 'phases': { 'install': { 'runtime-versions': { 'java': 'openjdk8' }, 'commands': [] }, 'build': { 'commands': 'mvn package', }, 'post_build': { 'commands': [ 'mkdir deploy', 'cp target/spring-petclinic-customers-serverless-2.0.7.RELEASE.jar deploy/', 'cd deploy && jar xvf spring-petclinic-customers-serverless-2.0.7.RELEASE.jar', 'rm spring-petclinic-customers-serverless-2.0.7.RELEASE.jar', ] } }, 'artifacts': { 'base-directory': 'deploy', 'files': ['**/*'] }, }), environment=_build.BuildEnvironment( build_image=_build.LinuxBuildImage.STANDARD_2_0)) cdk_project = _build.PipelineProject( self, 'CustomerCdkBuild', build_spec=_build.BuildSpec.from_object({ 'version': 0.2, 'phases': { 'install': { 'runtime-versions': { 'python': '3.7', 'nodejs': '10' }, 'commands': [ 'npm install -g [email protected]', 'pip install aws-cdk.core==1.10.0', 'pip install -r requirements.txt' ] }, 'build': { 'commands': [ 'cdk synth -o dist', ] } }, 'artifacts': { 'secondary-artifacts': { 'CdkBuildOutput': { 'base-directory': 'dist', 'files': ['customer-lambda-stack.template.json'] }, 'CustomRecoureOutput': { 'base-directory': 'custom-resource-code', 'discard-paths': 'yes', 'files': ['index.py', 'owner.json', 'cfnresponse.py'] } } } }), environment=_build.BuildEnvironment( build_image=_build.LinuxBuildImage.STANDARD_2_0)) source_output = _pipeline.Artifact('SourceOutput') cdk_build_output = _pipeline.Artifact('CdkBuildOutput') lambda_build_output = _pipeline.Artifact('LambdaBuildOutput') custom_resource_output = _pipeline.Artifact('CustomRecoureOutput') pipline = _pipeline.Pipeline( self, 'ServerlessPipeline', stages=[{ 'stageName': 'Source', 'actions': [ _action.CodeCommitSourceAction( action_name='CodeCommit_Source', repository=code, output=source_output) ] }, { 'stageName': 'Build', 'actions': [ _action.CodeBuildAction( action_name='CodeBuild_CDK', project=cdk_project, input=source_output, outputs=[cdk_build_output, custom_resource_output]), _action.CodeBuildAction(action_name='CodeBuild_Lambda', project=lambda_project, input=source_output, outputs=[lambda_build_output]) ] }, { 'stageName': 'Deploy', 'actions': [ _action.CloudFormationCreateUpdateStackAction( action_name='Lambda_CFN_Deploy', template_path=cdk_build_output.at_path( 'customer-lambda-stack.template.json'), stack_name='customer-lambda-stack', admin_permissions=True, parameter_overrides={ **self.lambda_code.assign(bucket_name=lambda_build_output.bucket_name, object_key=lambda_build_output.object_key), **self.custom_resource.assign(bucket_name=custom_resource_output.bucket_name, object_key=custom_resource_output.object_key) }, extra_inputs=[ lambda_build_output, custom_resource_output ]) ] }])
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, scope: core.Construct, id: str, *, lambda_code: lambda_.CfnParametersCode = None, sns_target, **kwargs) -> None: super().__init__(scope, id, **kwargs) # [ CodeCommit: Repository ] # # Grabs the repository the pipeline will be pulling from. code = codecommit.Repository.from_repository_name( self, "ImportedRepo", "CodeCommitRepo") # [ CodeBuild: Project: CDK ] # # Creates the project for building the Lambda CloudFormation template using CDK. cdk_build = codebuild.PipelineProject( self, "CdkBuild", build_spec=codebuild.BuildSpec.from_object( dict(version="0.2", phases=dict(install=dict(commands=[ "npm install -g aws-cdk", "cdk --version", "python --version", "pip install -r requirements.txt" ]), pre_build=dict( commands=["python -m pytest tests"]), build=dict(commands=["cdk synth -o dist"])), artifacts={ "base-directory": "dist", "files": ["LambdaStack.template.json"] }, environment=dict( buildImage=codebuild.LinuxBuildImage.STANDARD_2_0)))) # [ CodeBuild: Project: Lambda ] # # Creates the project for building the Lambda Functions. lambda_build = codebuild.PipelineProject( self, 'LambdaBuild', build_spec=codebuild.BuildSpec.from_object( dict(version="0.2", phases=dict(install=dict(commands=[ "cd lambda", "pip install -r requirements.txt" ]), post_build=dict(commands=["pytest"])), artifacts={ "base-directory": "lambda", "files": ["index.py"] }, environment=dict( buildImage=codebuild.LinuxBuildImage.STANDARD_2_0)))) # [ CodePipeline: Artifacts ] # # Creates the artifacts. source_output = codepipeline.Artifact() cdk_build_output = codepipeline.Artifact("CdkBuildOutput") lambda_build_output = codepipeline.Artifact("LambdaBuildOutput") # [ S3: Location ] lambda_location = lambda_build_output.s3_location # [ CodePipeline: Stages: Actions ] # # Creates the pipeline stages actions. repo_source_action = codepipeline_actions.CodeCommitSourceAction( action_name="CodeCommit_Source", repository=code, output=source_output) lambda_build_action = codepipeline_actions.CodeBuildAction( action_name="Lambda_Build", project=lambda_build, input=source_output, outputs=[lambda_build_output]) cdk_build_action = codepipeline_actions.CodeBuildAction( action_name="CDK_Build", project=cdk_build, input=source_output, outputs=[cdk_build_output]) cdk_deploy_action = codepipeline_actions.CloudFormationCreateUpdateStackAction( action_name="Lambda_CFN_Deploy", template_path=cdk_build_output.at_path( "LambdaStack.template.json"), stack_name="LambdaDeploymentStack", admin_permissions=True, parameter_overrides=dict( lambda_code.assign( bucket_name=lambda_location.bucket_name, object_key=lambda_location.object_key, object_version=lambda_location.object_version)), extra_inputs=[lambda_build_output]) # [ CodePipeline ] # # Creates the pipelines. pipeline = codepipeline.Pipeline( self, "Pipeline", stages=[ codepipeline.StageProps(stage_name="Source", actions=[repo_source_action]), codepipeline.StageProps( stage_name="Build", actions=[lambda_build_action, cdk_build_action]), codepipeline.StageProps(stage_name="Deploy", actions=[cdk_deploy_action]) ]) # [ CodePipeline: Events ] # # Creates the events for the pipeline, stages, and actions. repo_source_action.on_state_change( 'RepoSourceStateChange', target=sns_target, event_pattern=event.EventPattern(detail={'state': ['FAILED']})) lambda_build_action.on_state_change( 'LambdaBuildStateChange', target=sns_target, event_pattern=event.EventPattern(detail={'state': ['FAILED']})) cdk_build_action.on_state_change( 'CDKBuildStateChange', target=sns_target, event_pattern=event.EventPattern(detail={'state': ['FAILED']})) cdk_deploy_action.on_state_change( 'CDKDeployStateChange', target=sns_target, event_pattern=event.EventPattern(detail={'state': ['FAILED']})) pipeline.on_state_change( 'PipelineStateChange', target=sns_target, event_pattern=event.EventPattern(detail={'state': ['SUCCEEDED']}))