How to use cfn-lint and cfn-nag to verify CloudFormation templates

You can download this article in PDF format via the link below to support us.
Download the guide in PDF formatturn off

This article guides readers how to use the cfn-lint and cfn-nag tools to verify their CloudFormation templates. It also explains how to create a pipeline for validating CloudFormation templates and deploying them to CloudFormation.

The good practice of DevOps is to always include steps to check our code/template for safety and syntax errors. Likewise, in our case, it allows us to avoid pipeline failures due to syntax errors.

Requirements/prerequisites

  • An AWS account.
  • A user is created on the account, and the user has permission to provision resources on the account.
  • A CodeCommit repository containing your buildspec.yml and CloudFormation templates to verify.

buildspec.yml directory

The buildspec.yml file uploaded in our CodeCommit repository should contain the following code.

First, it will install the cfn-lint and cfn-nag tools. Then, it uses these two tools to check CloudFormation templates.

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 It should be replaced with the name of the template you want to verify. Your cloudformation template must be uploaded to the CodeCommit repository.

CloudFormation cfn-lint

Use cfn-lint to enable syntax error checking on CloudFormation templates. To check the template, run the following command. Suppose our template is written in YAML format.

cfn-lint templatename.yaml

CloudFormation CFN-NAG

The cfn-nag tool is used for security checks. It checks whether there is any insecure infrastructure in the CloudFormation template, such as a security group that allows everyone to access. To check the template, run the following command. Suppose our template is written in YAML format.

cfn_nag_scan -i template.yaml

Create a CodeBuild project

Before deploying it to CloudFormation, we will create a CodeBuild project to check and validate CloudFormation templates. We will use the CloudFormation template below 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 first provides the CodeBuild role. Then, it creates the build project.

N/B: Users should customize the template. The names of resources, attributes and tags should meet the specific requirements of the user.

When you run the build project, you will get the following output:Build project results

After the template is validated, we will get results showing any failures or warnings. My template has no syntax or security errors, but there is 1 warning.

If we fail, the build project will fail. For example, if there is a syntax error on the template. See the results below.How to use cfn-lint and cfn-nag to verify CloudFormation templatesResult of failed project creation

Create a CodePipeline to deploy the template to CloudFormation

We can add the above build project to the pipeline to deploy our CloudFormation template. Use the following template to create a pipeline that validates your CloudFormation template and deploys it to CloudFormation for resource allocation.

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 first provides an S3 bucket to upload our CodePipeline artifacts. Then, it created three roles. CodePipeline role assumed by CodePipeline, CloudFormation role and CloudWatch event role to be used in the deployment phase. CloudWatch event rules use the CloudWatch event role to monitor changes in the CodeCommit repository and trigger the pipeline to start.

Finally, the template provides the pipeline. The pipeline is divided into three stages. Source, build and deploy. Upload code from CodeCommit in the source code phase. The build phase uses the build project to validate our CloudFormation template. The last is the deployment phase, where the template is deployed to CloudFormation.

N/B: Users should customize the template. The names of resources, attributes and tags should meet the specific requirements of the user.

How to use cfn-lint and cfn-nag to verify CloudFormation templatesCodePipeline

Other AWS guidelines:

  • How to set up AWS VPC network using CloudFormation
  • Use CloudFormation to create and configure AWS Application Load Balancer
  • Create Amazon DocumentDB (MongoDB) database on AWS

Happy Building! ! !

You can download this article in PDF format via the link below to support us.
Download the guide in PDF formatturn off

Sidebar