Cloudformation nested stacks without rollback root stack - amazon-web-services

I currently have a "master.yaml" template that runs "service-a.yaml" and "service-b.yaml" then "service-c.yaml" which relies on outputs from service-a and service-b.
Is there a way to break this nested stack into multiple nested stacks? That way when something deep inside "service-c" fails it doesn't cause a rollback all the way up the chain? I want to kick off A+B in parallel and then C when they are finished in an automated fashion.
I could have a master.yaml which builds "service-a" and "service-b" then manually kick off "service-c" when they're done but I would like to automate this somehow?

You can create a stack with Codebuild project and Codepipeline (Basically performing CI/CD) to trigger one stack after the other and thus each stack would fail and roll back separately.
For example the cloudformation template would have a Codebuld project as follows
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Artifacts:
Type: CODEPIPELINE
Environment:
ComputeType: BUILD_GENERAL1_LARGE
Image: aws/codebuild/python:3.6.5
Type: LINUX_CONTAINER
EnvironmentVariables:
- Name: bucket
Value: !Ref ArtifactStoreBucket
Type: PLAINTEXT
- Name: prefix
Value: build
Type: PLAINTEXT
Name: !Ref AWS::StackName
ServiceRole: !Ref CodeBuildRole
Source:
Type: CODEPIPELINE
BuildSpec: stack/buildspec.yaml
Tags:
- Key: owner
Value: !Ref StackOwner
- Key: task
Value: !Ref RepositoryName
In the buildspec.yaml file, you can package the cloudfromation templates as follows:
- aws cloudformation package --template-file master.yaml
--s3-bucket $bucket --s3-prefix $prefix
--output-template-file master-template.yaml
- aws cloudformation package --template-file service-a.yaml
--s3-bucket $bucket --s3-prefix $prefix
--output-template-file service-a-template.yaml
And finally, a codepipeline stage which links all together. For example in the below-provided snippet, you can have source code triggered by codecommit. So every push to the repository would build your pipeline automatically.
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
ArtifactStore:
Location: !Ref ArtifactStoreBucket
Type: S3
DisableInboundStageTransitions: []
Name: !Sub "${AWS::StackName}"
RoleArn: !GetAtt [PipelineRole, Arn]
Stages:
# Stage 1 - CodeUpdate Stage
- Name: CodeUpdate
Actions:
- Name: SourceCodeUpdate
ActionTypeId:
Category: Source
Owner: AWS
Version: '1'
Provider: CodeCommit
OutputArtifacts:
- Name: SourceCode
Configuration:
PollForSourceChanges: 'false'
RepositoryName: !Ref RepositoryName
BranchName: !Ref BranchName
RunOrder: '1'
# Stage 2 - Build Stage
- Name: Build
Actions:
- Name: UpdateLambda
ActionTypeId:
Category: Build
Owner: AWS
Version: '1'
Provider: CodeBuild
InputArtifacts:
- Name: SourceCode
OutputArtifacts:
- Name: BuildArtifact
Configuration:
ProjectName: !Ref 'CodeBuildProject'
RunOrder: '1'
# Stage 3 - Build master stack
- Name: MasterSetup
Actions:
- Name: CreateMasterChangeset
ActionTypeId:
Category: Deploy
Owner: AWS
Version: '1'
Provider: CloudFormation
InputArtifacts:
- Name: BuildArtifact
Configuration:
ActionMode: CHANGE_SET_REPLACE
StackName: !Sub "${AWS::StackName}-master"
ChangeSetName: !Sub "${AWS::StackName}-master-update"
RoleArn: !GetAtt [CFNRole, Arn]
TemplatePath: BuildArtifact::master-template.yaml
Capabilities: CAPABILITY_IAM
ParameterOverrides: !Sub
- |
{
"MasterStack": "${w}",
"StackOwner": "${x}",
"Task": "${y}"
}
- {
w: !Sub '${AWS::StackName}',
x: !Sub '${StackOwner}',
y: !Sub '${RepositoryName}'
}
RunOrder: '1'
- Name: ExecuteMasterChangeset
ActionTypeId:
Category: Deploy
Owner: AWS
Version: '1'
Provider: CloudFormation
Configuration:
ActionMode: CHANGE_SET_EXECUTE
StackName: !Sub "${AWS::StackName}-master"
ChangeSetName: !Sub "${AWS::StackName}-master-update"
RunOrder: '2'
# Stage 4 - Build service-a stack
- Name: ServiceASetup
Actions:
- Name: CreateServiceAChangeset
ActionTypeId:
Category: Deploy
Owner: AWS
Version: '1'
Provider: CloudFormation
InputArtifacts:
- Name: BuildArtifact
Configuration:
ActionMode: CHANGE_SET_REPLACE
StackName: !Sub "${AWS::StackName}-service-a"
ChangeSetName: !Sub "${AWS::StackName}-service-a-update"
RoleArn: !GetAtt [CFNRole, Arn]
TemplatePath: BuildArtifact::service-a-template.yaml
Capabilities: CAPABILITY_IAM
ParameterOverrides: !Sub
- |
{
"MasterStack": "${w}",
"StackOwner": "${x}",
"Task": "${y}"
}
- {
w: !Sub '${AWS::StackName}',
x: !Sub '${StackOwner}',
y: !Sub '${RepositoryName}'
}
RunOrder: '1'
- Name: ExecuteServiceAChangeset
ActionTypeId:
Category: Deploy
Owner: AWS
Version: '1'
Provider: CloudFormation
Configuration:
ActionMode: CHANGE_SET_EXECUTE
StackName: !Sub "${AWS::StackName}-service-a"
ChangeSetName: !Sub "${AWS::StackName}-service-a-update"
RunOrder: '2'
If you want to have stacks executing in parallel, you can add more than 1 stack in each stage.
Obviously you need to setup the roles and buckets yourself and this should give you basic idea how to get started.
For more information, you can read up more about codepipeline as follows:
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-cd-pipeline.html
https://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html

Related

How To Pass CloudFormation Outputs To A CodeBuild Stage?

I have 2 CloudFormation and CodeBuild CodePipeline stages:
- Actions:
- ActionTypeId:
Category: "Deploy"
Owner: "AWS"
Provider: "CloudFormation"
Version: "1"
Configuration:
ActionMode: "CREATE_UPDATE"
Capabilities: "CAPABILITY_AUTO_EXPAND,CAPABILITY_NAMED_IAM,CAPABILITY_IAM"
RoleArn: !GetAtt CodePipelineServiceRole.Arn
StackName: !Ref CFNStackName
TemplatePath: !Sub "BuildArtifact::${ArtifactName}"
TemplateConfiguration: BuildArtifact::CFTemplateConfig.json
ParameterOverrides: !Sub '{"Env": "${Env}"}'
Name: "CloudFormation-step"
Region: !Sub ${AWS::Region}
InputArtifacts:
- Name: BuildArtifact
RunOrder: 1
Name: "Deploy"
- Actions:
- ActionTypeId:
Category: "Test"
Owner: "AWS"
Provider: "CodeBuild"
Version: "1"
Configuration:
ProjectName: !Ref CodeBuildTest
InputArtifacts:
- Name: SourceArtifact
Name: "Test"
Region: !Sub ${AWS::Region}
RunOrder:
The CloudFormation stage is creating an ALB. I would like to pass that ALB's ARN to the CodeBuild stage. How do I pass the ALB ARN to the CodeBuild buildspec.yml file?
Assuming that your CFN template returns the ALB ARN in its outputs, e.g. called AlbArn, then you can provide a namespace for it and use the namespace later to get the value.
For example (may need some further adjustments):
- Actions:
- ActionTypeId:
Category: "Deploy"
Owner: "AWS"
Provider: "CloudFormation"
Version: "1"
Configuration:
ActionMode: "CREATE_UPDATE"
Capabilities: "CAPABILITY_AUTO_EXPAND,CAPABILITY_NAMED_IAM,CAPABILITY_IAM"
RoleArn: !GetAtt CodePipelineServiceRole.Arn
StackName: !Ref CFNStackName
TemplatePath: !Sub "BuildArtifact::${ArtifactName}"
TemplateConfiguration: BuildArtifact::CFTemplateConfig.json
ParameterOverrides: !Sub '{"Env": "${Env}"}'
Name: "CloudFormation-step"
Region: !Sub ${AWS::Region}
InputArtifacts:
- Name: BuildArtifact
Namespace: CloudFromationDeployNamespace # <--- namespace
RunOrder: 1
Name: "Deploy"
- Actions:
- ActionTypeId:
Category: "Test"
Owner: "AWS"
Provider: "CodeBuild"
Version: "1"
Configuration:
ProjectName: !Ref CodeBuildTest
EnvironmentVariables: | # <- pass the AlbArn as ENV variable
{
"ALB_NAME": "#{CloudFromationDeployNamespace.AlbArn}"
}
InputArtifacts:
- Name: SourceArtifact
Name: "Test"
Region: !Sub ${AWS::Region}
RunOrder:

Can AWS CodePipeline deploy with tags like the aws-cli?

I'm using AWS CodePipeline to deploy Cloudformation stacks. Previously I've used the aws-cli to create stacks with the aws create-stack --tags flag to propogate tags to the resources created. Is this possible with CodePipeline using the builtin CloudFormation provider? Here's an example of a deploy step I use:
- Name: DeployToDev
Actions:
- Name: CreateChangeSetDev
ActionTypeId:
Category: Deploy
Owner: AWS
Version: 1
Provider: CloudFormation
Configuration:
ChangeSetName: !Sub ${PipelineName}
ActionMode: CHANGE_SET_REPLACE
StackName: !Sub ${PipelineName}
Capabilities: CAPABILITY_NAMED_IAM
TemplatePath: BuildOutput::outputtemplate.yaml
TemplateConfiguration: !If [HasStackConfig,
!Sub 'SourceOutput::${StackConfigDev}',
!Ref "AWS::NoValue"]
RoleArn: !Sub arn:aws:iam::123456789012:role/${CloudFormationDeploymentRole}
InputArtifacts:
- Name: BuildOutput
- Name: SourceOutput
RunOrder: 1
RoleArn: !Sub arn:aws:iam::123456789012:role/CICD-CF-Role-${AWS::Region}
- Name: DeployChangeSetDev
ActionTypeId:
Category: Deploy
Owner: AWS
Version: 1
Provider: CloudFormation
Configuration:
ChangeSetName: !Sub ${PipelineName}
ActionMode: CHANGE_SET_EXECUTE
StackName: !Sub ${PipelineName}
RoleArn: !Sub arn:aws:iam::123456789012:role/${CloudFormationDeploymentRole}
InputArtifacts:
- Name: BuildOutput
RunOrder: 2
RoleArn: !Sub arn:aws:iam::123456789012:role/CICD-CF-Role-${AWS::Region}
Under your Configuration add tags like this
StackTags:
application-name: xxxx
environment: xxxx
owner: xxx
or try this if it doesn't work.
Tags:
- application-name: xxxx
- environment: xxxx
- owner: xxx

Encountered unsupported property ActionTypeId

When i create my cloudformation stack for codepipeline, it fails and the error message is "Encountered unsupported property ActionTypeId".
My template is this way:
Resources:
CodePipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
ArtifactStore:
Location: !Ref BucketLocation
Type: S3
Name: !Ref Name
RestartExecutionOnUpdate: true
RoleArn: !Ref RoleAnr
DisableInboundStageTransitions:
- Reason: Approve Step
StageName: Build
Stages:
-
Name: !Ref StagesName
Actions:
-
InputArtifacts:
-
Name: CodeCommit
ActionTypeId:
Category: Source
Owner: AWS
Version: 1
Provider: CodeCommit
OutputArtifacts:
-
Name: sourceartifact
Configuration:
BranchName:
!Ref RepositoryBranch
RepositoryName:
!Ref RepositoryName
RunOrder: 1
-
Name: Build
Actions:
-
Name: BuildAction
InputArtifacts:
-
Name: CodeBuild
ActionTypeId:
Category: Build
Owner: AWS
Version: 1
Provider: CodeBuild
OutputArtifacts:
-
Name: sourceartifact
Configuration:
ProjectName:
!Ref ProjectName
RunOrder: 1
-
I've no idea what the problem might be. I've commented the action type and the stack is still faling.
Fix the YAML indentation so that action properties like ActionTypeId are at the same level.

How to run Parallel builds with AWS Codebuild?

I am trying to setup a codebuild where I have one module that would create the dependencies for other builds. And these other builds will use the artifacts generated by the first build and these build should be ran in parallel. I was looking at documentation of CodeBuild it seems there isn't information there. Sample of my buildspec.yml
version: 0.2
#env:
#variables:
# key: "value"
# key: "value"
#parameter-store:
# key: "value"
# key: "value"
#git-credential-helper: yes
phases:
#install:
#If you use the Ubuntu standard image 2.0 or later, you must specify runtime-versions.
#If you specify runtime-versions and use an image other than Ubuntu standard image 2.0, the build fails.
#runtime-versions:
# name: version
# name: version
#commands:
# - command
# - command
# pre_build:
# commands:
# - mkdir artifacts
# - pwd
# - ls
build:
commands:
- cd frontend
- npm install
- cd ..
- cd othermodule
- npm install
#post_build:
#commands:
# - cp -a $CODEBUILD_SRC_DIR/frontend/dist.
# - command
artifacts:
files:
- package-lock.json
- node_modules/*
base-directory: frontend
#cache:
#paths:
# - paths
CodeBuild is used to automate build step, not to automate the whole CICD process. In CodeBuild, you specify buildspec.yml to automate a sequence of steps that need to be performed in that particular build.
If you need to automate sequence of builds then the easiest option that you have is to use CodePipeline where you can create stage for each step in your CICD process. In your case, this would mean that one step (stage) would be CodeBuild action that you have described in your post which would transition into another stage where you can specify another CodeBuild actions and these actions can be specified to take artifacts from the previous step as an input and you can run them in parallel.
So it would look like this
INPUT -> STAGE (perform initial build) -> STAGE (specify multiple build actions in parallel - in console, this can be done by placing them next to each other horizontally instead of vertically)
The other option, without using CodePipeline, would be to use Lambda function with CloudWatch events. CodeBuild publishes event once the build is done. You can subscribe Lambda function to that event and write a code that would execute following builds as needed.
As #MEMark has pointed out, currently AWS CodePipeline service supports Bitbuket.
One suitable approach to reach Bitbucket from AWS is by providing an Star Connection(Developer Tools > Code Build > Settings > Connections).
Once the connection is created the pipeline can be deployed using for instance following code:
AWSTemplateFormatVersion: "2010-09-09"
Description: TBD
Parameters:
DeployStage:
Type: String
Description: name of the stage to deploy(dev, test, prod)
BranchName:
Type: String
Description: TBD
CodeStarConnectionARN:
Type: String
Description: ARN of the codestar connection to Bitbucket.
Resources:
CodeBuildRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AdministratorAccess
PipelineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: 'sts:AssumeRole'
Effect: Allow
Principal:
Service: codepipeline.amazonaws.com
Version: '2012-10-17'
Policies:
- PolicyName: CodePipelineAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- TBD
- ...
Effect: Allow
Resource: '*'
-
Effect: Allow
Action:
- TBD
- ...
Resource: "*"
BuildUi:
Type: AWS::CodeBuild::Project
Properties:
Name: !Join ['', ['build-ui-', !Ref DeployStage]]
Description: TBD
ServiceRole: !Ref CodeBuildRole
Artifacts:
Type: CODEPIPELINE
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/standard:5.0
EnvironmentVariables:
- Name: DEPLOY_STAGE
Type: PLAINTEXT
Value: !Ref DeployStage
- Name: DEPLOY_BRANCH_NAME
Type: PLAINTEXT
Value: !Ref BranchName
Source:
Type: CODEPIPELINE
Location: https://user#bitbucket.org/organization/projectName.git
BuildSpec: buildspec.ui.yml
SourceVersion: !Ref BranchName
Tags:
-
Key: cost
Value: ui_build
TimeoutInMinutes: 10
BuildApi:
Type: AWS::CodeBuild::Project
Properties:
Name: !Join ['', ['build-api-', !Ref DeployStage]]
Description: TBD
ServiceRole: !Ref CodeBuildRole
Artifacts:
Type: CODEPIPELINE
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/standard:5.0
EnvironmentVariables:
- Name: DEPLOY_STAGE
Type: PLAINTEXT
Value: !Ref DeployStage
- Name: DEPLOY_BRANCH_NAME
Type: PLAINTEXT
Value: !Ref BranchName
Source:
Type: CODEPIPELINE
Location: https://user#bitbucket.org/organization/projectName.git
BuildSpec:buildspec.api.yml
SourceVersion: !Ref BranchName
Tags:
-
Key: cost
Value: api_build
TimeoutInMinutes: 10
ArtifactStoreBucket:
Type: AWS::S3::Bucket
Properties:
AccessControl: 'BucketOwnerFullControl'
VersioningConfiguration:
Status: Enabled
NamePipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
ArtifactStore:
Location: !Ref ArtifactStoreBucket
Type: S3
Name: !Join ['', ['pipeline-', !Ref DeployStage]]
RoleArn: !GetAtt PipelineRole.Arn
Stages:
- Name: Source
Actions:
- Name: Source
ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeStarSourceConnection
Version: '1'
Configuration:
ConnectionArn: !Ref CodeStarConnectionARN
FullRepositoryId: "organization/projectName"
BranchName: !Ref BranchName
OutputArtifacts:
- Name: project-source
RunOrder: '1'
- Name: ParallelBuild
Actions:
- Name: BuildUi
ActionTypeId:
Category: Build
Owner: AWS
Version: 1
Provider: CodeBuild
OutputArtifacts:
- Name: project-build-ui
InputArtifacts:
- Name: project-source
Configuration:
ProjectName: !Ref BuildUi
RunOrder: 1
- Name: BuildApi
ActionTypeId:
Category: Build
Owner: AWS
Version: 1
Provider: CodeBuild
OutputArtifacts:
- Name: build-api
InputArtifacts:
- Name: project-source
Configuration:
ProjectName: !Ref BuildApi
RunOrder: 1
Notes:
it assumes the repository has this git url: https://user#bitbucket.org/organization/projectName.git
CodeBuildRole and PipelineRole should be defined properly for the use case

CloudFormation passing parameters from CodePipeline

I have a SAM application and a CodePipeline setup to deploy it. I want to pass parameters down from my Pipeline into the SAM's YAML file. I tried using ParameterOverrides but seem to still get:
Parameters: [AppName] must have values (Service: AmazonCloudFormation; Status Code: 400; Error Code: ValidationError; Request ID: 46d1dfd6-9a9a-11e7-a59d-999618d6a174)
My sam.yml parameters definations
AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Parameters:
AppName:
Type: String
Description: Prefix for resources
The part defining the parameters overrides:
- Name: ExecuteChangeSet
Actions:
- Name: Lambda
ActionTypeId:
Category: Deploy
Owner: AWS
Version: 1
Provider: CloudFormation
Configuration:
ActionMode: CHANGE_SET_EXECUTE
ChangeSetName: !Sub
- '${PipelineName}-lambda'
- {PipelineName: !Ref PipelineName}
StackName: !Sub
- '${PipelineName}-lambda'
- {PipelineName: !Ref PipelineName}
ParameterOverrides: !Sub '{"AppName": "${PipelineName}-lambda"}'
Whats is wrong with this?
It appears that you're attempting to apply the ParameterOverrides during a CHANGE_SET_EXECUTE action mode. If I'm not mistaken, under the hood, this maps down to CloudFormations ExecuteChangeSet action, which does not have a Parameters property.
The solution to this is to apply the Parameters when you create the change set. This will be done in CodePipeline with a CHANGE_SET_REPLACE action mode. Alternatively, you could look into the CREATE_UPDATE. Check out AWS CloudFormation Configuration Properties for more details.
Here's a sample that creates, then executes the change set
- Name: CreateChangeSet
Actions:
- Name: Lambda
ActionTypeId:
Category: Deploy
Owner: AWS
Version: 1
Provider: CloudFormation
InputArtifacts:
- Name: BuildOutputArtifact
Configuration:
ActionMode: CHANGE_SET_REPLACE
ChangeSetName: !Sub
- '${PipelineName}-lambda'
- {PipelineName: !Ref PipelineName}
StackName: !Sub
- '${PipelineName}-lambda'
- {PipelineName: !Ref PipelineName}
ParameterOverrides: !Ref ProjectParameterOverrides
TemplatePath: BuildOutputArtifact::SamDeploymentTemplate.yaml
- Name: ExecuteChangeSet
Actions:
- Name: Lambda
ActionTypeId:
Category: Deploy
Owner: AWS
Version: 1
Provider: CloudFormation
Configuration:
ActionMode: CHANGE_SET_EXECUTE
ChangeSetName: !Sub
- '${PipelineName}-lambda'
- {PipelineName: !Ref PipelineName}
StackName: !Sub
- '${PipelineName}-lambda'
- {PipelineName: !Ref PipelineName}