This article guides the reader on how to validate their CloudFormation Template using cfn-lint and cfn-nag tools. It also explains how to create a pipeline that validates a CloudFormation template and deploys it to CloudFormation.
It is good DevOps practice to always include a step for checking our code/templates for security and syntax errors. Also, In our case, it saves us from having a Pipeline fail because of syntax errors.
Requirements/Prerequisites
- An AWS Account.
- Created a User on the account with Permissions to provision resources on the account.
- A CodeCommit repo containing your buildspec.yml and the CloudFormation Template to be validated.
buildspec.yml Contents
The buildspec.yml file uploaded on our CodeCommit repo should contain the following code.
First, it installs the cfn-lint and cfn-nag tools. Then, it checks the CloudFormation template using the two tools.
version: 0.2
phases:
install:
runtime-versions:
ruby: 2.6
commands:
- pip3 install awscli --upgrade --quiet
- pip3 install cfn-lint --quiet
- apt-get install jq git -y -q
- gem install cfn-nag
build:
commands:
- cd ./
- cfn-lint ECR.yaml
- cfn_nag_scan -i ECR.yaml
Where ECR.yaml should be replaced with the name of the template you want to validate. Your cloudformation template has to be uploaded to the CodeCommit repository.
CloudFormation cfn-lint
Using cfn-lint enables syntax error checks on your CloudFormation Template. To check your template, you run the below command. Assuming that our template is written in YAML format.
cfn-lint templatename.yaml
CloudFormation cfn-nag
The cfn-nag tool is for security checks. It examines the CloudFormation Template for any insecure infrastructure e.g. security groups that allows access for everyone. To check your template, you run the below command. Assuming that our template is written in YAML format.
cfn_nag_scan -i template.yaml
Create a CodeBuild Project
We will create a CodeBuild project to check and validate our CloudFormation template before we deploy it to CloudFormation. We will use the below CloudFormation Template to create our Build Project.
AWSTemplateFormatVersion: "2010-09-09"
Description: "Template to create codebuild Project"
Parameters:
ProjectName:
Type: String
Description: The CodeBuild Project Name
Default: Validate-CF-Template
CodeBuildRole:
Type: String
Description: The name codebuild role
Default: CodeBuild-ValidateCF
Resources:
IAMRole:
Type: 'AWS::IAM::Role'
Properties:
Description: The CodeBuild Validate CF Template Role
RoleName: !Ref CodeBuildRole
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- codebuild.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSCodeBuildAdminAccess
- arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
- arn:aws:iam::aws:policy/AWSCodeCommitFullAccess
- arn:aws:iam::aws:policy/AmazonS3FullAccess
Tags:
-
Key: "Project"
Value: "test-blog"
-
Key: "Environment"
Value: "test"
-
Key: "createdBy"
Value: "Maureen Barasa"
-
Key: "Name"
Value: !Ref CodeBuildRole
BuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: !Ref ProjectName
Description: build project to validate our CF Template
ServiceRole: !Ref IAMRole
Artifacts:
Type: NO_ARTIFACTS
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/standard:4.0
PrivilegedMode: true
Source:
Location: https://git-codecommit.eu-central-1.amazonaws.com/v1/repos/test-lint
Type: CODECOMMIT
BuildSpec: buildspec.yml
TimeoutInMinutes: 15
Tags:
-
Key: "Project"
Value: "test-blog"
-
Key: "Environment"
Value: "test"
-
Key: "createdBy"
Value: "Maureen Barasa"
-
Key: "Name"
Value: !Ref ProjectName
Outputs:
Project:
Description: The codebuild build project
Value: !Ref ProjectName
Export:
Name: "BuildProject"
Value: !Ref ProjectName
First, the template starts by provisioning a CodeBuild role. Then it creates the Build Project.
N/B: The user should customize the template. The names of Resources, Properties, and Tags should match the user’s specific requirements.
When you run the build project, you get the below output:
Once our template is validated we get results showing any failures or warnings. My Template has no Syntax or Security errors but I have 1 warning.
In case we have a failure the build project will fail. For example, if we have a syntax error on our template. See results below.
Create a CodePipeline To Deploy the Template to CloudFormation
We can add the above build project to a pipeline to deploy our CloudFormation template. Use the below template to create a Pipeline that validate your CloudFormation template and deploys it to CloudFormation for provisioning of resources.
AWSTemplateFormatVersion: "2010-09-09"
Description: "Template to create codepipeline"
Parameters:
PipelineName:
Type: String
Description: The CodePipeline Name
Default: Deploy-to-CloudFormation
Stack:
Type: String
Description: The Cloudformation Stack Name
Default: eu-central-1-create-ECR-Stack
PipelineRole:
Type: String
Description: The Cloudformation Deploy Role Name
Default: codepipeline-ECR
CFRole:
Type: String
Description: The CodePipeline Role Name
Default: codepipeline-cloudformation
CWEventsRole:
Type: String
Description: The Cloudwatch Events Role Name
Default: codepipeline-cwevents
BucketName:
Type: String
Description: The vpc where our bastion hosts will be located
Default: maureen-test-cp
Resources:
S3:
Type: AWS::S3::Bucket
Properties:
AccessControl: Private
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
BucketName: !Ref BucketName
PublicAccessBlockConfiguration:
BlockPublicAcls: TRUE
BlockPublicPolicy: TRUE
IgnorePublicAcls: TRUE
RestrictPublicBuckets: TRUE
VersioningConfiguration:
Status: Enabled
Tags:
-
Key: "Project"
Value: "test-blog"
-
Key: "Environment"
Value: "test"
-
Key: "createdBy"
Value: "Maureen Barasa"
-
Key: "Name"
Value: !Ref BucketName
CodePipelineRole:
Type: 'AWS::IAM::Role'
Properties:
Description: The CodeBuild Validate CF Template Role
RoleName: !Ref PipelineRole
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- "codepipeline.amazonaws.com"
Action:
- 'sts:AssumeRole'
Policies:
- PolicyName: root
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 'iam:PassRole'
Resource: '*'
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSCodePipeline_FullAccess
- arn:aws:iam::aws:policy/AWSCodeCommitFullAccess
- arn:aws:iam::aws:policy/AmazonS3FullAccess
- arn:aws:iam::aws:policy/AWSCodeBuildAdminAccess
- arn:aws:iam::aws:policy/AWSCodeDeployFullAccess
- arn:aws:iam::aws:policy/AWSCloudFormationFullAccess
Tags:
-
Key: "Project"
Value: "test-blog"
-
Key: "Environment"
Value: "test"
-
Key: "createdBy"
Value: "Maureen Barasa"
-
Key: "Name"
Value: !Ref PipelineRole
CloudFormationRole:
Type: 'AWS::IAM::Role'
Properties:
Description: The CodeBuild Validate CF Template Role
RoleName: !Ref CFRole
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- "cloudformation.amazonaws.com"
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonS3FullAccess
- arn:aws:iam::aws:policy/PowerUserAccess
Tags:
-
Key: "Project"
Value: "test-blog"
-
Key: "Environment"
Value: "test"
-
Key: "createdBy"
Value: "Maureen Barasa"
-
Key: "Name"
Value: !Ref CFRole
AmazonCloudWatchEventRole:
Type: 'AWS::IAM::Role'
Properties:
Description: The CodeBuild Validate CF Template Role
RoleName: !Ref CWEventsRole
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- "events.amazonaws.com"
Action:
- 'sts:AssumeRole'
Policies:
- PolicyName: root
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 'codepipeline:StartPipelineExecution'
Resource: !Join [ '', [ 'arn:aws:codepipeline:', !Ref 'AWS::Region', ':', !Ref 'AWS::AccountId', ':', !Ref DeployPipeline ] ]
Tags:
-
Key: "Project"
Value: "test-blog"
-
Key: "Environment"
Value: "test"
-
Key: "createdBy"
Value: "Maureen Barasa"
-
Key: "Name"
Value: !Ref CWEventsRole
AmazonCloudWatchEventRule:
Type: AWS::Events::Rule
Properties:
EventPattern:
source:
- aws.codecommit
detail-type:
- 'CodeCommit Repository State Change'
resources:
- arn:aws:codecommit:eu-central-1:429758582529:test-lint
detail:
event:
- referenceCreated
- referenceUpdated
referenceType:
- branch
referenceName:
- master
Targets:
-
Arn:
!Join [ '', [ 'arn:aws:codepipeline:', !Ref 'AWS::Region', ':', !Ref 'AWS::AccountId', ':', !Ref DeployPipeline ] ]
RoleArn: !GetAtt AmazonCloudWatchEventRole.Arn
Id: codepipeline-DeployPipeline
DeployPipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
RoleArn: !GetAtt CodePipelineRole.Arn
Name: !Ref PipelineName
ArtifactStore:
Location: !Ref S3
Type: S3
Stages:
-
Name: Source
Actions:
-
Name: SourceAction
ActionTypeId:
Category: Source
Owner: AWS
Version: 1
Provider: CodeCommit
OutputArtifacts:
-
Name: SourceArtifact
Configuration:
RepositoryName: test-lint
BranchName: master
PollForSourceChanges: false
RunOrder: 1
-
Name: Build
Actions:
-
Name: BuildAction
InputArtifacts:
- Name: SourceArtifact
ActionTypeId:
Category: Build
Owner: AWS
Version: 1
Provider: CodeBuild
Configuration:
ProjectName: !ImportValue BuildProject
BatchEnabled: 'false'
OutputArtifacts: []
RunOrder: 1
-
Name: Deploy
Actions:
-
Name: DeployAction
InputArtifacts:
- Name: SourceArtifact
ActionTypeId:
Category: Deploy
Owner: AWS
Version: 1
Provider: CloudFormation
Configuration:
ActionMode: CREATE_UPDATE
StackName: !Ref Stack
Capabilities: CAPABILITY_IAM,CAPABILITY_NAMED_IAM
RoleArn: !GetAtt CloudFormationRole.Arn
TemplatePath: "SourceArtifact::ECR.yaml"
RunOrder: 1
Tags:
-
Key: "Project"
Value: "test-blog"
-
Key: "Environment"
Value: "test"
-
Key: "createdBy"
Value: "Maureen Barasa"
-
Key: "Name"
Value: !Ref PipelineName
The template starts by provisioning an S3 bucket to upload our CodePipeline artifacts. Then it creates three roles. The CodePipeline role to be assumed by CodePipeline, the CloudFormation role to be used on the deploy stage and the CloudWatch events role. The CloudWatch events role is used by the CloudWatch events rule to monitor for changes in the CodeCommit repository and trigger the pipeline to start.
Finally, the template provisions the pipeline. The pipeline has three stages. Source, Build and Deploy. The source stage uploads code from CodeCommit. The build stage uses the build project to validate our CloudFormation Template. Finally the deploy stage, deploys the template to CloudFormation.
N/B: The user should customize the template. The names of Resources, Properties, and Tags should match the user’s specific requirements.
Important Links
Other AWS Guides:
- How To Setup AWS VPC Network With CloudFormation
- Create and Configure AWS Application Load Balancer with CloudFormation
- Create Amazon DocumentDB (MongoDB) Database on AWS
Happy Building!!!