Package: aws-cdk.aws-codepipeline-actions
AWS CodePipeline Actions---AWS CDK v1 has reached End-of-Support on 2023-06-01.
This package is no longer being updated, and users should migrate to AWS CDK v2.For more information on how to migrate, see theMigrating to AWS CDK v2guide.This package contains Actions that can be used in a CodePipeline.importaws_cdk.aws_codepipelineascodepipelineimportaws_cdk.aws_codepipeline_actionsascodepipeline_actionsSourcesAWS CodeCommitTo use a CodeCommit Repository in a CodePipeline:repo=codecommit.Repository(self,"Repo",repository_name="MyRepo")pipeline=codepipeline.Pipeline(self,"MyPipeline",pipeline_name="MyPipeline")source_output=codepipeline.Artifact()source_action=codepipeline_actions.CodeCommitSourceAction(action_name="CodeCommit",repository=repo,output=source_output)pipeline.add_stage(stage_name="Source",actions=[source_action])If you want to use existing role which can be used by on commit event rule.
You can specify the role object in eventRole property.# repo: codecommit.Repositoryevent_role=iam.Role.from_role_arn(self,"Event-role","roleArn")source_action=codepipeline_actions.CodeCommitSourceAction(action_name="CodeCommit",repository=repo,output=codepipeline.Artifact(),event_role=event_role)If you want to clone the entire CodeCommit repository (only available for CodeBuild actions),
you can set thecodeBuildCloneOutputproperty totrue:# project: codebuild.PipelineProject# repo: codecommit.Repositorysource_output=codepipeline.Artifact()source_action=codepipeline_actions.CodeCommitSourceAction(action_name="CodeCommit",repository=repo,output=source_output,code_build_clone_output=True)build_action=codepipeline_actions.CodeBuildAction(action_name="CodeBuild",project=project,input=source_output,# The build action must use the CodeCommitSourceAction output as input.outputs=[codepipeline.Artifact()])The CodeCommit source action emits variables:# project: codebuild.PipelineProject# repo: codecommit.Repositorysource_output=codepipeline.Artifact()source_action=codepipeline_actions.CodeCommitSourceAction(action_name="CodeCommit",repository=repo,output=source_output,variables_namespace="MyNamespace")# later:codepipeline_actions.CodeBuildAction(action_name="CodeBuild",project=project,input=source_output,environment_variables={"COMMIT_ID":codebuild.BuildEnvironmentVariable(value=source_action.variables.commit_id)})GitHubIf you want to use a GitHub repository as the source, you must create:AGitHub Access Token,
with scopesrepoandadmin:repo_hook.ASecrets Manager Secretwith the value of theGitHub Access Token. Pick whatever name you want (for examplemy-github-token).
This token can be stored either as Plaintext or as a Secret key/value.
If you stored the token as Plaintext,
setSecretValue.secretsManager('my-github-token')as the value ofoauthToken.
If you stored it as a Secret key/value,
you must setSecretValue.secretsManager('my-github-token', { jsonField : 'my-github-token' })as the value ofoauthToken.To use GitHub as the source of a CodePipeline:# Read the secret from Secrets Managerpipeline=codepipeline.Pipeline(self,"MyPipeline")source_output=codepipeline.Artifact()source_action=codepipeline_actions.GitHubSourceAction(action_name="GitHub_Source",owner="awslabs",repo="aws-cdk",oauth_token=SecretValue.secrets_manager("my-github-token"),output=source_output,branch="develop")pipeline.add_stage(stage_name="Source",actions=[source_action])The GitHub source action emits variables:# source_output: codepipeline.Artifact# project: codebuild.PipelineProjectsource_action=codepipeline_actions.GitHubSourceAction(action_name="Github_Source",output=source_output,owner="my-owner",repo="my-repo",oauth_token=SecretValue.secrets_manager("my-github-token"),variables_namespace="MyNamespace")# later:codepipeline_actions.CodeBuildAction(action_name="CodeBuild",project=project,input=source_output,environment_variables={"COMMIT_URL":codebuild.BuildEnvironmentVariable(value=source_action.variables.commit_url)})BitBucketCodePipeline can use a BitBucket Git repository as a source:Note: you have to manually connect CodePipeline through the AWS Console with your BitBucket account.
This is a one-time operation for a given AWS account in a given region.
The simplest way to do that is to either start creating a new CodePipeline,
or edit an existing one, while being logged in to BitBucket.
Choose BitBucket as the source,
and grant CodePipeline permissions to your BitBucket account.
Copy & paste the Connection ARN that you get in the console,
or use thecodestar-connections list-connectionsAWS CLI operationto find it.
After that, you can safely abort creating or editing the pipeline -
the connection has already been created.source_output=codepipeline.Artifact()source_action=codepipeline_actions.CodeStarConnectionsSourceAction(action_name="BitBucket_Source",owner="aws",repo="aws-cdk",output=source_output,connection_arn="arn:aws:codestar-connections:us-east-1:123456789012:connection/12345678-abcd-12ab-34cdef5678gh")You can also use theCodeStarConnectionsSourceActionto connect to GitHub, in the same way
(you just have to select GitHub as the source when creating the connection in the console).Similarly toGitHubSourceAction,CodeStarConnectionsSourceActionalso emits the variables:# project: codebuild.Projectsource_output=codepipeline.Artifact()source_action=codepipeline_actions.CodeStarConnectionsSourceAction(action_name="BitBucket_Source",owner="aws",repo="aws-cdk",output=source_output,connection_arn="arn:aws:codestar-connections:us-east-1:123456789012:connection/12345678-abcd-12ab-34cdef5678gh",variables_namespace="SomeSpace")# later:codepipeline_actions.CodeBuildAction(action_name="CodeBuild",project=project,input=source_output,environment_variables={"COMMIT_ID":codebuild.BuildEnvironmentVariable(value=source_action.variables.commit_id)})AWS S3 SourceTo use an S3 Bucket as a source in CodePipeline:source_bucket=s3.Bucket(self,"MyBucket",versioned=True)pipeline=codepipeline.Pipeline(self,"MyPipeline")source_output=codepipeline.Artifact()source_action=codepipeline_actions.S3SourceAction(action_name="S3Source",bucket=source_bucket,bucket_key="path/to/file.zip",output=source_output)pipeline.add_stage(stage_name="Source",actions=[source_action])The region of the action will be determined by the region the bucket itself is in.
When using a newly created bucket,
that region will be taken from the stack the bucket belongs to;
for an imported bucket,
you can specify the region explicitly:source_bucket=s3.Bucket.from_bucket_attributes(self,"SourceBucket",bucket_name="my-bucket",region="ap-southeast-1")By default, the Pipeline will poll the Bucket to detect changes.
You can change that behavior to use CloudWatch Events by setting thetriggerproperty toS3Trigger.EVENTS(it'sS3Trigger.POLLby default).
If you do that, make sure the source Bucket is part of an AWS CloudTrail Trail -
otherwise, the CloudWatch Events will not be emitted,
and your Pipeline will not react to changes in the Bucket.
You can do it through the CDK:importaws_cdk.aws_cloudtrailascloudtrail# source_bucket: s3.Bucketsource_output=codepipeline.Artifact()key="some/key.zip"trail=cloudtrail.Trail(self,"CloudTrail")trail.add_s3_event_selector([cloudtrail.S3EventSelector(bucket=source_bucket,object_prefix=key)],read_write_type=cloudtrail.ReadWriteType.WRITE_ONLY)source_action=codepipeline_actions.S3SourceAction(action_name="S3Source",bucket_key=key,bucket=source_bucket,output=source_output,trigger=codepipeline_actions.S3Trigger.EVENTS)The S3 source action emits variables:# source_bucket: s3.Bucket# later:# project: codebuild.PipelineProjectkey="some/key.zip"source_output=codepipeline.Artifact()source_action=codepipeline_actions.S3SourceAction(action_name="S3Source",bucket_key=key,bucket=source_bucket,output=source_output,variables_namespace="MyNamespace")codepipeline_actions.CodeBuildAction(action_name="CodeBuild",project=project,input=source_output,environment_variables={"VERSION_ID":codebuild.BuildEnvironmentVariable(value=source_action.variables.version_id)})AWS ECRTo use an ECR Repository as a source in a Pipeline:importaws_cdk.aws_ecrasecr# ecr_repository: ecr.Repositorypipeline=codepipeline.Pipeline(self,"MyPipeline")source_output=codepipeline.Artifact()source_action=codepipeline_actions.EcrSourceAction(action_name="ECR",repository=ecr_repository,image_tag="some-tag",# optional, default: 'latest'output=source_output)pipeline.add_stage(stage_name="Source",actions=[source_action])The ECR source action emits variables:importaws_cdk.aws_ecrasecr# ecr_repository: ecr.Repository# later:# project: codebuild.PipelineProjectsource_output=codepipeline.Artifact()source_action=codepipeline_actions.EcrSourceAction(action_name="Source",output=source_output,repository=ecr_repository,variables_namespace="MyNamespace")codepipeline_actions.CodeBuildAction(action_name="CodeBuild",project=project,input=source_output,environment_variables={"IMAGE_URI":codebuild.BuildEnvironmentVariable(value=source_action.variables.image_uri)})Build & testAWS CodeBuildExample of a CodeBuild Project used in a Pipeline, alongside CodeCommit:# project: codebuild.PipelineProjectrepository=codecommit.Repository(self,"MyRepository",repository_name="MyRepository")project=codebuild.PipelineProject(self,"MyProject")source_output=codepipeline.Artifact()source_action=codepipeline_actions.CodeCommitSourceAction(action_name="CodeCommit",repository=repository,output=source_output)build_action=codepipeline_actions.CodeBuildAction(action_name="CodeBuild",project=project,input=source_output,outputs=[codepipeline.Artifact()],# optionalexecute_batch_build=True,# optional, defaults to falsecombine_batch_build_artifacts=True)codepipeline.Pipeline(self,"MyPipeline",stages=[codepipeline.StageProps(stage_name="Source",actions=[source_action]),codepipeline.StageProps(stage_name="Build",actions=[build_action])])The default category of the CodeBuild Action isBuild;
if you want aTestAction instead,
override thetypeproperty:# project: codebuild.PipelineProjectsource_output=codepipeline.Artifact()test_action=codepipeline_actions.CodeBuildAction(action_name="IntegrationTest",project=project,input=source_output,type=codepipeline_actions.CodeBuildActionType.TEST)Multiple inputs and outputsWhen you want to have multiple inputs and/or outputs for a Project used in a
Pipeline, instead of using thesecondarySourcesandsecondaryArtifactsproperties of theProjectclass, you need to use theextraInputsandoutputsproperties of the CodeBuild CodePipeline
Actions. Example:# repository1: codecommit.Repository# repository2: codecommit.Repository# project: codebuild.PipelineProjectsource_output1=codepipeline.Artifact()source_action1=codepipeline_actions.CodeCommitSourceAction(action_name="Source1",repository=repository1,output=source_output1)source_output2=codepipeline.Artifact("source2")source_action2=codepipeline_actions.CodeCommitSourceAction(action_name="Source2",repository=repository2,output=source_output2)build_action=codepipeline_actions.CodeBuildAction(action_name="Build",project=project,input=source_output1,extra_inputs=[source_output2],outputs=[codepipeline.Artifact("artifact1"),# for better buildspec readability - see belowcodepipeline.Artifact("artifact2")])Note: when a CodeBuild Action in a Pipeline has more than one output, it
only uses thesecondary-artifactsfield of the buildspec, never the
primary output specification directly underartifacts. Because of that, it
pays to explicitly name all output artifacts of that Action, like we did
above, so that you know what name to use in the buildspec.Example buildspec for the above project:project=codebuild.PipelineProject(self,"MyProject",build_spec=codebuild.BuildSpec.from_object({"version":"0.2","phases":{"build":{"commands":[]}},"artifacts":{"secondary-artifacts":{"artifact1":{},"artifact2":{}}}}))VariablesThe CodeBuild action emits variables.
Unlike many other actions, the variables are not static,
but dynamic, defined in the buildspec,
in the 'exported-variables' subsection of the 'env' section.
Example:# later:# project: codebuild.PipelineProjectsource_output=codepipeline.Artifact()build_action=codepipeline_actions.CodeBuildAction(action_name="Build1",input=source_output,project=codebuild.PipelineProject(self,"Project",build_spec=codebuild.BuildSpec.from_object({"version":"0.2","env":{"exported-variables":["MY_VAR"]},"phases":{"build":{"commands":"export MY_VAR=\"some value\""}}})),variables_namespace="MyNamespace")codepipeline_actions.CodeBuildAction(action_name="CodeBuild",project=project,input=source_output,environment_variables={"MyVar":codebuild.BuildEnvironmentVariable(value=build_action.variable("MY_VAR"))})JenkinsIn order to use Jenkins Actions in the Pipeline,
you first need to create aJenkinsProvider:jenkins_provider=codepipeline_actions.JenkinsProvider(self,"JenkinsProvider",provider_name="MyJenkinsProvider",server_url="http://my-jenkins.com:8080",version="2")If you've registered a Jenkins provider in a different CDK app,
or outside the CDK (in the CodePipeline AWS Console, for example),
you can import it:jenkins_provider=codepipeline_actions.JenkinsProvider.from_jenkins_provider_attributes(self,"JenkinsProvider",provider_name="MyJenkinsProvider",server_url="http://my-jenkins.com:8080",version="2")Note that a Jenkins provider
(identified by the provider name-category(build/test)-version tuple)
must always be registered in the given account, in the given AWS region,
before it can be used in CodePipeline.With aJenkinsProvider,
we can create a Jenkins Action:# jenkins_provider: codepipeline_actions.JenkinsProviderbuild_action=codepipeline_actions.JenkinsAction(action_name="JenkinsBuild",jenkins_provider=jenkins_provider,project_name="MyProject",type=codepipeline_actions.JenkinsActionType.BUILD)DeployAWS CloudFormationThis module contains Actions that allows you to deploy to CloudFormation from AWS CodePipeline.For example, the following code fragment defines a pipeline that automatically deploys a CloudFormation template
directly from a CodeCommit repository, with a manual approval step in between to confirm the changes:# Source stage: read from repositoryrepo=codecommit.Repository(stack,"TemplateRepo",repository_name="template-repo")source_output=codepipeline.Artifact("SourceArtifact")source=cpactions.CodeCommitSourceAction(action_name="Source",repository=repo,output=source_output,trigger=cpactions.CodeCommitTrigger.POLL)source_stage={"stage_name":"Source","actions":[source]}# Deployment stage: create and deploy changeset with manual approvalstack_name="OurStack"change_set_name="StagedChangeSet"prod_stage={"stage_name":"Deploy","actions":[cpactions.CloudFormationCreateReplaceChangeSetAction(action_name="PrepareChanges",stack_name=stack_name,change_set_name=change_set_name,admin_permissions=True,template_path=source_output.at_path("template.yaml"),run_order=1),cpactions.ManualApprovalAction(action_name="ApproveChanges",run_order=2),cpactions.CloudFormationExecuteChangeSetAction(action_name="ExecuteChanges",stack_name=stack_name,change_set_name=change_set_name,run_order=3)]}codepipeline.Pipeline(stack,"Pipeline",stages=[source_stage,prod_stage])Seethe AWS documentationfor more details about using CloudFormation in CodePipeline.Actions for updating individual CloudFormation StacksThis package contains the following CloudFormation actions:CloudFormationCreateUpdateStackAction- Deploy a CloudFormation template directly from the pipeline. The indicated stack is created,
or updated if it already exists. If the stack is in a failure state, deployment will fail (unlessreplaceOnFailureis set totrue, in which case it will be destroyed and recreated).CloudFormationDeleteStackAction- Delete the stack with the given name.CloudFormationCreateReplaceChangeSetAction- Prepare a change set to be applied later. You will typically use change sets if you want
to manually verify the changes that are being staged, or if you want to separate the people (or system) preparing the
changes from the people (or system) applying the changes.CloudFormationExecuteChangeSetAction- Execute a change set prepared previously.Actions for deploying CloudFormation StackSets to multiple accountsYou can use CloudFormation StackSets to deploy the same CloudFormation template to multiple
accounts in a managed way. If you use AWS Organizations, StackSets can be deployed to
all accounts in a particular Organizational Unit (OU), and even automatically to new
accounts as soon as they are added to a particular OU. For more information, see
theWorking with StackSetssection of the CloudFormation developer guide.The actions available for updating StackSets are:CloudFormationDeployStackSetAction- Create or update a CloudFormation StackSet directly from the pipeline, optionally
immediately create and update Stack Instances as well.CloudFormationDeployStackInstancesAction- Update outdated Stack Instaces using the current version of the StackSet.Here's an example of using both of these actions:# pipeline: codepipeline.Pipeline# source_output: codepipeline.Artifactpipeline.add_stage(stage_name="DeployStackSets",actions=[# First, update the StackSet itself with the newest templatecodepipeline_actions.CloudFormationDeployStackSetAction(action_name="UpdateStackSet",run_order=1,stack_set_name="MyStackSet",template=codepipeline_actions.StackSetTemplate.from_artifact_path(source_output.at_path("template.yaml")),# Change this to 'StackSetDeploymentModel.organizations()' if you want to deploy to OUsdeployment_model=codepipeline_actions.StackSetDeploymentModel.self_managed(),# This deploys to a set of accountsstack_instances=codepipeline_actions.StackInstances.in_accounts(["111111111111"],["us-east-1","eu-west-1"])),# Afterwards, update/create additional instances in other accountscodepipeline_actions.CloudFormationDeployStackInstancesAction(action_name="AddMoreInstances",run_order=2,stack_set_name="MyStackSet",stack_instances=codepipeline_actions.StackInstances.in_accounts(["222222222222","333333333333"],["us-east-1","eu-west-1"]))])Lambda deployed through CodePipelineIf you want to deploy your Lambda through CodePipeline,
and you don't use assets (for example, because your CDK code and Lambda code are separate),
you can use a special LambdaCodeclass,CfnParametersCode.
Note that your Lambda must be in a different Stack than your Pipeline.
The Lambda itself will be deployed, alongside the entire Stack it belongs to,
using a CloudFormation CodePipeline Action. Example:lambda_stack=cdk.Stack(app,"LambdaStack")lambda_code=lambda_.Code.from_cfn_parameters()lambda_.Function(lambda_stack,"Lambda",code=lambda_code,handler="index.handler",runtime=lambda_.Runtime.NODEJS_14_X)# other resources that your Lambda needs, added to the lambdaStack...pipeline_stack=cdk.Stack(app,"PipelineStack")pipeline=codepipeline.Pipeline(pipeline_stack,"Pipeline")# add the source code repository containing this code to your Pipeline,# and the source code of the Lambda Function, if they're separatecdk_source_output=codepipeline.Artifact()cdk_source_action=codepipeline_actions.CodeCommitSourceAction(repository=codecommit.Repository(pipeline_stack,"CdkCodeRepo",repository_name="CdkCodeRepo"),action_name="CdkCode_Source",output=cdk_source_output)lambda_source_output=codepipeline.Artifact()lambda_source_action=codepipeline_actions.CodeCommitSourceAction(repository=codecommit.Repository(pipeline_stack,"LambdaCodeRepo",repository_name="LambdaCodeRepo"),action_name="LambdaCode_Source",output=lambda_source_output)pipeline.add_stage(stage_name="Source",actions=[cdk_source_action,lambda_source_action])# synthesize the Lambda CDK template, using CodeBuild# the below values are just examples, assuming your CDK code is in TypeScript/JavaScript -# adjust the build environment and/or commands accordinglycdk_build_project=codebuild.Project(pipeline_stack,"CdkBuildProject",environment=codebuild.BuildEnvironment(build_image=codebuild.LinuxBuildImage.UBUNTU_14_04_NODEJS_10_1_0),build_spec=codebuild.BuildSpec.from_object({"version":"0.2","phases":{"install":{"commands":"npm install"},"build":{"commands":["npm run build","npm run cdk synth LambdaStack -- -o ."]}},"artifacts":{"files":"LambdaStack.template.yaml"}}))cdk_build_output=codepipeline.Artifact()cdk_build_action=codepipeline_actions.CodeBuildAction(action_name="CDK_Build",project=cdk_build_project,input=cdk_source_output,outputs=[cdk_build_output])# build your Lambda code, using CodeBuild# again, this example assumes your Lambda is written in TypeScript/JavaScript -# make sure to adjust the build environment and/or commands if they don't match your specific situationlambda_build_project=codebuild.Project(pipeline_stack,"LambdaBuildProject",environment=codebuild.BuildEnvironment(build_image=codebuild.LinuxBuildImage.UBUNTU_14_04_NODEJS_10_1_0),build_spec=codebuild.BuildSpec.from_object({"version":"0.2","phases":{"install":{"commands":"npm install"},"build":{"commands":"npm run build"}},"artifacts":{"files":["index.js","node_modules/**/*"]}}))lambda_build_output=codepipeline.Artifact()lambda_build_action=codepipeline_actions.CodeBuildAction(action_name="Lambda_Build",project=lambda_build_project,input=lambda_source_output,outputs=[lambda_build_output])pipeline.add_stage(stage_name="Build",actions=[cdk_build_action,lambda_build_action])# finally, deploy your Lambda Stackpipeline.add_stage(stage_name="Deploy",actions=[codepipeline_actions.CloudFormationCreateUpdateStackAction(action_name="Lambda_CFN_Deploy",template_path=cdk_build_output.at_path("LambdaStack.template.yaml"),stack_name="LambdaStackDeployedName",admin_permissions=True,parameter_overrides=lambda_code.assign(lambda_build_output.s3_location),extra_inputs=[lambda_build_output])])Cross-account actionsIf you want to update stacks in a different account,
pass theaccountproperty when creating the action:source_output=codepipeline.Artifact()codepipeline_actions.CloudFormationCreateUpdateStackAction(action_name="CloudFormationCreateUpdate",stack_name="MyStackName",admin_permissions=True,template_path=source_output.at_path("template.yaml"),account="123456789012")This will create a new stack, called<PipelineStackName>-support-123456789012, in yourApp,
that will contain the role that the pipeline will assume in account 123456789012 before executing this action.
This support stack will automatically be deployed before the stack containing the pipeline.You can also pass a role explicitly when creating the action -
in that case, theaccountproperty is ignored,
and the action will operate in the same account the role belongs to:fromaws_cdk.coreimportPhysicalName# in stack for account 123456789012...# other_account_stack: Stackaction_role=iam.Role(other_account_stack,"ActionRole",assumed_by=iam.AccountPrincipal("123456789012"),# the role has to have a physical name setrole_name=PhysicalName.GENERATE_IF_NEEDED)# in the pipeline stack...source_output=codepipeline.Artifact()codepipeline_actions.CloudFormationCreateUpdateStackAction(action_name="CloudFormationCreateUpdate",stack_name="MyStackName",admin_permissions=True,template_path=source_output.at_path("template.yaml"),role=action_role)AWS CodeDeployServer deploymentsTo use CodeDeploy for EC2/on-premise deployments in a Pipeline:# deployment_group: codedeploy.ServerDeploymentGrouppipeline=codepipeline.Pipeline(self,"MyPipeline",pipeline_name="MyPipeline")# add the source and build Stages to the Pipeline...build_output=codepipeline.Artifact()deploy_action=codepipeline_actions.CodeDeployServerDeployAction(action_name="CodeDeploy",input=build_output,deployment_group=deployment_group)pipeline.add_stage(stage_name="Deploy",actions=[deploy_action])Lambda deploymentsTo use CodeDeploy for blue-green Lambda deployments in a Pipeline:lambda_code=lambda_.Code.from_cfn_parameters()func=lambda_.Function(self,"Lambda",code=lambda_code,handler="index.handler",runtime=lambda_.Runtime.NODEJS_14_X)# used to make sure each CDK synthesis produces a different Versionversion=func.current_versionalias=lambda_.Alias(self,"LambdaAlias",alias_name="Prod",version=version)codedeploy.LambdaDeploymentGroup(self,"DeploymentGroup",alias=alias,deployment_config=codedeploy.LambdaDeploymentConfig.LINEAR_10PERCENT_EVERY_1MINUTE)Then, you need to create your Pipeline Stack,
where you will define your Pipeline,
and deploy thelambdaStackusing a CloudFormation CodePipeline Action
(see above for a complete example).ECSCodePipeline can deploy an ECS service.
The deploy Action receives one input Artifact which contains theimage definition file:importaws_cdk.aws_ecsasecs# service: ecs.FargateServicepipeline=codepipeline.Pipeline(self,"MyPipeline")build_output=codepipeline.Artifact()deploy_stage=pipeline.add_stage(stage_name="Deploy",actions=[codepipeline_actions.EcsDeployAction(action_name="DeployAction",service=service,# if your file is called imagedefinitions.json,# use the `input` property,# and leave out the `imageFile` propertyinput=build_output,# if your file name is _not_ imagedefinitions.json,# use the `imageFile` property,# and leave out the `input` propertyimage_file=build_output.at_path("imageDef.json"),deployment_timeout=Duration.minutes(60))])Deploying ECS applications to existing servicesCodePipeline can deploy to an existing ECS service which uses theECS service ARN format that contains the Cluster name.
This also works if the service is in a different account and/or region than the pipeline:importaws_cdk.aws_ecsasecsservice=ecs.BaseService.from_service_arn_with_cluster(self,"EcsService","arn:aws:ecs:us-east-1:123456789012:service/myClusterName/myServiceName")pipeline=codepipeline.Pipeline(self,"MyPipeline")build_output=codepipeline.Artifact()# add source and build stages to the pipeline as usual...deploy_stage=pipeline.add_stage(stage_name="Deploy",actions=[codepipeline_actions.EcsDeployAction(action_name="DeployAction",service=service,input=build_output)])When deploying across accounts, especially in a CDK Pipelines self-mutating pipeline,
it is recommended to provide theroleproperty to theEcsDeployAction.
The Role will need to have permissions assigned to it for ECS deployment.
Seethe CodePipeline documentationfor the permissions needed.Deploying ECS applications stored in a separate source code repositoryThe idiomatic CDK way of deploying an ECS application is to have your Dockerfiles and your CDK code in the same source code repository,
leveragingDocker Assets,
and use theCDK Pipelines module.However, if you want to deploy a Docker application whose source code is kept in a separate version control repository than the CDK code,
you can use theTagParameterContainerImageclass from the ECS module.
Here's an example:## This is the Stack containing a simple ECS Service that uses the provided ContainerImage.#classEcsAppStack(cdk.Stack):def__init__(self,scope,id,*,image,description=None,env=None,stackName=None,tags=None,synthesizer=None,terminationProtection=None,analyticsReporting=None):super().__init__(scope,id,image=image,description=description,env=env,stackName=stackName,tags=tags,synthesizer=synthesizer,terminationProtection=terminationProtection,analyticsReporting=analyticsReporting)task_definition=ecs.TaskDefinition(self,"TaskDefinition",compatibility=ecs.Compatibility.FARGATE,cpu="1024",memory_mi_b="2048")task_definition.add_container("AppContainer",image=image)ecs.FargateService(self,"EcsService",task_definition=task_definition,cluster=ecs.Cluster(self,"Cluster",vpc=ec2.Vpc(self,"Vpc",max_azs=1)))## This is the Stack containing the CodePipeline definition that deploys an ECS Service.#classPipelineStack(cdk.Stack):def__init__(self,scope,id,*,description=None,env=None,stackName=None,tags=None,synthesizer=None,terminationProtection=None,analyticsReporting=None):super().__init__(scope,id,description=description,env=env,stackName=stackName,tags=tags,synthesizer=synthesizer,terminationProtection=terminationProtection,analyticsReporting=analyticsReporting)# ********* ECS part ****************# this is the ECR repository where the built Docker image will be pushedapp_ecr_repo=ecr.Repository(self,"EcsDeployRepository")# the build that creates the Docker image, and pushes it to the ECR repoapp_code_docker_build=codebuild.PipelineProject(self,"AppCodeDockerImageBuildAndPushProject",environment=codebuild.BuildEnvironment(# we need to run Dockerprivileged=True),build_spec=codebuild.BuildSpec.from_object({"version":"0.2","phases":{"build":{"commands":["$(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)","docker build -t $REPOSITORY_URI:$CODEBUILD_RESOLVED_SOURCE_VERSION ."]},"post_build":{"commands":["docker push $REPOSITORY_URI:$CODEBUILD_RESOLVED_SOURCE_VERSION","export imageTag=$CODEBUILD_RESOLVED_SOURCE_VERSION"]}},"env":{# save the imageTag environment variable as a CodePipeline Variable"exported-variables":["imageTag"]}}),environment_variables={"REPOSITORY_URI":codebuild.BuildEnvironmentVariable(value=app_ecr_repo.repository_uri)})# needed for `docker push`app_ecr_repo.grant_pull_push(app_code_docker_build)# create the ContainerImage used for the ECS application Stackself.tag_parameter_container_image=ecs.TagParameterContainerImage(app_ecr_repo)cdk_code_build=codebuild.PipelineProject(self,"CdkCodeBuildProject",build_spec=codebuild.BuildSpec.from_object({"version":"0.2","phases":{"install":{"commands":["npm install"]},"build":{"commands":["npx cdk synth --verbose"]}},"artifacts":{# store the entire Cloud Assembly as the output artifact"base-directory":"cdk.out","files":"**/*"}}))# ********* Pipeline part ****************app_code_source_output=codepipeline.Artifact()cdk_code_source_output=codepipeline.Artifact()cdk_code_build_output=codepipeline.Artifact()app_code_build_action=codepipeline_actions.CodeBuildAction(action_name="AppCodeDockerImageBuildAndPush",project=app_code_docker_build,input=app_code_source_output)codepipeline.Pipeline(self,"CodePipelineDeployingEcsApplication",artifact_bucket=s3.Bucket(self,"ArtifactBucket",removal_policy=cdk.RemovalPolicy.DESTROY),stages=[codepipeline.StageProps(stage_name="Source",actions=[# this is the Action that takes the source of your application codecodepipeline_actions.CodeCommitSourceAction(action_name="AppCodeSource",repository=codecommit.Repository(self,"AppCodeSourceRepository",repository_name="AppCodeSourceRepository"),output=app_code_source_output),# this is the Action that takes the source of your CDK code# (which would probably include this Pipeline code as well)codepipeline_actions.CodeCommitSourceAction(action_name="CdkCodeSource",repository=codecommit.Repository(self,"CdkCodeSourceRepository",repository_name="CdkCodeSourceRepository"),output=cdk_code_source_output)]),codepipeline.StageProps(stage_name="Build",actions=[app_code_build_action,codepipeline_actions.CodeBuildAction(action_name="CdkCodeBuildAndSynth",project=cdk_code_build,input=cdk_code_source_output,outputs=[cdk_code_build_output])]),codepipeline.StageProps(stage_name="Deploy",actions=[codepipeline_actions.CloudFormationCreateUpdateStackAction(action_name="CFN_Deploy",stack_name="SampleEcsStackDeployedFromCodePipeline",# this name has to be the same name as used below in the CDK code for the application Stacktemplate_path=cdk_code_build_output.at_path("EcsStackDeployedInPipeline.template.json"),admin_permissions=True,parameter_overrides={# read the tag pushed to the ECR repository from the CodePipeline Variable saved by the application build step,# and pass it as the CloudFormation Parameter for the tag"self.tag_parameter_container_image.tag_parameter_name":app_code_build_action.variable("imageTag")})])])app=cdk.App()# the CodePipeline Stack needs to be created firstpipeline_stack=PipelineStack(app,"aws-cdk-pipeline-ecs-separate-sources")# we supply the image to the ECS application Stack from the CodePipeline StackEcsAppStack(app,"EcsStackDeployedInPipeline",image=pipeline_stack.tag_parameter_container_image)AWS S3 DeploymentTo use an S3 Bucket as a deployment target in CodePipeline:source_output=codepipeline.Artifact()target_bucket=s3.Bucket(self,"MyBucket")pipeline=codepipeline.Pipeline(self,"MyPipeline")deploy_action=codepipeline_actions.S3DeployAction(action_name="S3Deploy",bucket=target_bucket,input=source_output)deploy_stage=pipeline.add_stage(stage_name="Deploy",actions=[deploy_action])Invalidating the CloudFront cache when deploying to S3There is currently no native support in CodePipeline for invalidating a CloudFront cache after deployment.
One workaround is to add another build step after the deploy step,
and use the AWS CLI to invalidate the cache:# Create a Cloudfront Web Distributionimportaws_cdk.aws_cloudfrontascloudfront# distribution: cloudfront.Distribution# Create the build project that will invalidate the cacheinvalidate_build_project=codebuild.PipelineProject(self,"InvalidateProject",build_spec=codebuild.BuildSpec.from_object({"version":"0.2","phases":{"build":{"commands":["aws cloudfront create-invalidation --distribution-id ${CLOUDFRONT_ID}--paths\"/*\""]}}}),environment_variables={"CLOUDFRONT_ID":codebuild.BuildEnvironmentVariable(value=distribution.distribution_id)})# Add Cloudfront invalidation permissions to the projectdistribution_arn=f"arn:aws:cloudfront::{this.account}:distribution/{distribution.distributionId}"invalidate_build_project.add_to_role_policy(iam.PolicyStatement(resources=[distribution_arn],actions=["cloudfront:CreateInvalidation"]))# Create the pipeline (here only the S3 deploy and Invalidate cache build)deploy_bucket=s3.Bucket(self,"DeployBucket")deploy_input=codepipeline.Artifact()codepipeline.Pipeline(self,"Pipeline",stages=[codepipeline.StageProps(stage_name="Deploy",actions=[codepipeline_actions.S3DeployAction(action_name="S3Deploy",bucket=deploy_bucket,input=deploy_input,run_order=1),codepipeline_actions.CodeBuildAction(action_name="InvalidateCache",project=invalidate_build_project,input=deploy_input,run_order=2)])])Alexa SkillYou can deploy to Alexa using CodePipeline with the following Action:# Read the secrets from ParameterStoreclient_id=SecretValue.secrets_manager("AlexaClientId")client_secret=SecretValue.secrets_manager("AlexaClientSecret")refresh_token=SecretValue.secrets_manager("AlexaRefreshToken")# Add deploy actionsource_output=codepipeline.Artifact()codepipeline_actions.AlexaSkillDeployAction(action_name="DeploySkill",run_order=1,input=source_output,client_id=client_id.to_string(),client_secret=client_secret,refresh_token=refresh_token,skill_id="amzn1.ask.skill.12345678-1234-1234-1234-123456789012")If you need manifest overrides you can specify them asparameterOverridesArtifactin the action:# Deploy some CFN change set and store outputexecute_output=codepipeline.Artifact("CloudFormation")execute_change_set_action=codepipeline_actions.CloudFormationExecuteChangeSetAction(action_name="ExecuteChangesTest",run_order=2,stack_name="MyStack",change_set_name="MyChangeSet",output_file_name="overrides.json",output=execute_output)# Provide CFN output as manifest overridesclient_id=SecretValue.secrets_manager("AlexaClientId")client_secret=SecretValue.secrets_manager("AlexaClientSecret")refresh_token=SecretValue.secrets_manager("AlexaRefreshToken")source_output=codepipeline.Artifact()codepipeline_actions.AlexaSkillDeployAction(action_name="DeploySkill",run_order=1,input=source_output,parameter_overrides_artifact=execute_output,client_id=client_id.to_string(),client_secret=client_secret,refresh_token=refresh_token,skill_id="amzn1.ask.skill.12345678-1234-1234-1234-123456789012")AWS Service CatalogYou can deploy a CloudFormation template to an existing Service Catalog product with the following Action:cdk_build_output=codepipeline.Artifact()service_catalog_deploy_action=codepipeline_actions.ServiceCatalogDeployActionBeta1(action_name="ServiceCatalogDeploy",template_path=cdk_build_output.at_path("Sample.template.json"),product_version_name="Version - "+Date.now.to_string,product_version_description="This is a version from the pipeline with a new description.",product_id="prod-XXXXXXXX")Approve & invokeManual approval ActionThis package contains an Action that stops the Pipeline until someone manually clicks the approve button:importaws_cdk.aws_snsassnspipeline=codepipeline.Pipeline(self,"MyPipeline")approve_stage=pipeline.add_stage(stage_name="Approve")manual_approval_action=codepipeline_actions.ManualApprovalAction(action_name="Approve",notification_topic=sns.Topic(self,"Topic"),# optionalnotify_emails=["some_email@example.com"],# optionaladditional_information="additional info")approve_stage.add_action(manual_approval_action)If thenotificationTopichas not been provided,
butnotifyEmailswere,
a new SNS Topic will be created
(and accessible through thenotificationTopicproperty of the Action).If you want to grant a principal permissions to approve the changes,
you can invoke the methodgrantManualApprovalpassing it aIGrantable:pipeline=codepipeline.Pipeline(self,"MyPipeline")approve_stage=pipeline.add_stage(stage_name="Approve")manual_approval_action=codepipeline_actions.ManualApprovalAction(action_name="Approve")approve_stage.add_action(manual_approval_action)role=iam.Role.from_role_arn(self,"Admin",Arn.format(ArnComponents(service="iam",resource="role",resource_name="Admin"),self))manual_approval_action.grant_manual_approval(role)AWS LambdaThis module contains an Action that allows you to invoke a Lambda function in a Pipeline:# fn: lambda.Functionpipeline=codepipeline.Pipeline(self,"MyPipeline")lambda_action=codepipeline_actions.LambdaInvokeAction(action_name="Lambda",lambda_=fn)pipeline.add_stage(stage_name="Lambda",actions=[lambda_action])The Lambda Action can have up to 5 inputs,
and up to 5 outputs:# fn: lambda.Functionsource_output=codepipeline.Artifact()build_output=codepipeline.Artifact()lambda_action=codepipeline_actions.LambdaInvokeAction(action_name="Lambda",inputs=[source_output,build_output],outputs=[codepipeline.Artifact("Out1"),codepipeline.Artifact("Out2")],lambda_=fn)The Lambda Action supports custom user parameters that pipeline
will pass to the Lambda function:# fn: lambda.Functionpipeline=codepipeline.Pipeline(self,"MyPipeline")lambda_action=codepipeline_actions.LambdaInvokeAction(action_name="Lambda",lambda_=fn,user_parameters={"foo":"bar","baz":"qux"},# ORuser_parameters_string="my-parameter-string")The Lambda invoke action emits variables.
Unlike many other actions, the variables are not static,
but dynamic, defined by the function calling thePutJobSuccessResultAPI with theoutputVariablesproperty filled with the map of variables
Example:# later:# project: codebuild.PipelineProjectlambda_invoke_action=codepipeline_actions.LambdaInvokeAction(action_name="Lambda",lambda_=lambda_.Function(self,"Func",runtime=lambda_.Runtime.NODEJS_14_X,handler="index.handler",code=lambda_.Code.from_inline("""const AWS = require('aws-sdk');exports.handler = async function(event, context) {const codepipeline = new AWS.CodePipeline();await codepipeline.putJobSuccessResult({jobId: event['CodePipeline.job'].id,outputVariables: {MY_VAR: "some value",},}).promise();}""")),variables_namespace="MyNamespace")source_output=codepipeline.Artifact()codepipeline_actions.CodeBuildAction(action_name="CodeBuild",project=project,input=source_output,environment_variables={"MyVar":codebuild.BuildEnvironmentVariable(value=lambda_invoke_action.variable("MY_VAR"))})Seethe AWS documentationon how to write a Lambda function invoked from CodePipeline.AWS Step FunctionsThis module contains an Action that allows you to invoke a Step Function in a Pipeline:importaws_cdk.aws_stepfunctionsasstepfunctionspipeline=codepipeline.Pipeline(self,"MyPipeline")start_state=stepfunctions.Pass(self,"StartState")simple_state_machine=stepfunctions.StateMachine(self,"SimpleStateMachine",definition=start_state)step_function_action=codepipeline_actions.StepFunctionInvokeAction(action_name="Invoke",state_machine=simple_state_machine,state_machine_input=codepipeline_actions.StateMachineInput.literal({"IsHelloWorldExample":True}))pipeline.add_stage(stage_name="StepFunctions",actions=[step_function_action])TheStateMachineInputcan be created with one of 2 static factory methods:literal, which takes an arbitrary map as its only argument, orfilePath:importaws_cdk.aws_stepfunctionsasstepfunctionspipeline=codepipeline.Pipeline(self,"MyPipeline")input_artifact=codepipeline.Artifact()start_state=stepfunctions.Pass(self,"StartState")simple_state_machine=stepfunctions.StateMachine(self,"SimpleStateMachine",definition=start_state)step_function_action=codepipeline_actions.StepFunctionInvokeAction(action_name="Invoke",state_machine=simple_state_machine,state_machine_input=codepipeline_actions.StateMachineInput.file_path(input_artifact.at_path("assets/input.json")))pipeline.add_stage(stage_name="StepFunctions",actions=[step_function_action])Seethe AWS documentationfor information on Action structure reference.