Book Image

Mastering AWS CloudFormation

By : Karen Tovmasyan
Book Image

Mastering AWS CloudFormation

By: Karen Tovmasyan

Overview of this book

DevOps and the cloud revolution have forced software engineers and operations teams to rethink how to manage infrastructures. With this AWS book, you'll understand how you can use Infrastructure as Code (IaC) to simplify IT operations and manage the modern cloud infrastructure effectively with AWS CloudFormation. This comprehensive guide will help you explore AWS CloudFormation from template structures through to developing complex and reusable infrastructure stacks. You'll then delve into validating templates, deploying stacks, and handling deployment failures. The book will also show you how to leverage AWS CodeBuild and CodePipeline to automate resource delivery and apply continuous integration and continuous delivery (CI/CD) practices to the stack. As you advance, you'll learn how to generate templates on the fly using macros and create resources outside AWS with custom resources. Finally, you'll improve the way you manage the modern cloud in AWS by extending CloudFormation using AWS serverless application model (SAM) and AWS cloud development kit (CDK). By the end of this book, you'll have mastered all the major AWS CloudFormation concepts and be able to simplify infrastructure management.
Table of Contents (17 chapters)
1
Section 1: CloudFormation Internals
4
Section 2: Provisioning and Deployment at Scale
9
Section 3: Extending CloudFormation

Understanding CloudFormation IAM permissions

We already know that CloudFormation performs API calls when we create or update the stack. Now the question is, does CloudFormation have the same powers as a root user?

When you work with production-grade AWS accounts, you need to control access to your environment for both humans (yourself and your coworkers) and machines (build systems, AWS resources, and so on). That is why controlling access for CloudFormation is important.

By default, when the user runs stack creation, they invoke the API method cloudformation:CreateStack. CloudFormation will use that user's access to invoke other API methods during the stack creation.

This means that if our user has an IAM policy with an allowed action ec2:*, but attempts to create an RDS instance with CloudFormation, the stack will fail to create with an error, User is unauthorized to perform this action.

Let's try this. We will create an IAM role with ec2:*, assume that role, and try to create the same bucket stack:

Important note

We already have an IAM user Admin in our AWS account and we will add that user as a principal.

MyIamRole.yaml

AWSTemplateFormatVersion: "2010-09-09"
Description: "This is a dummy role"
Resources:
  IamRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Sid: AllowAssumeRole
            Effect: Allow
            Principal:
              AWS:
                - !Join
                  - ""
                  - - "arn:aws:iam::"
                    - !Ref "AWS::AccountId"
                    - ":user/Admin"
            Action: "sts:AssumeRole"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/AmazonEC2FullAccess"
        - "arn:aws:iam::aws:policy/AWSCloudformationFullAccess"
Outputs:
  IamRole:
    Value: !GetAtt IamRole.Arn

If we create this stack, assume that role, and try to create the previous mybucket stack, it will fail to create with an error. Let's take a look:

$ aws cloudformation create-stack \
                     --stack-name iamrole \
                     --capabilities CAPABILITY_IAM \
                     --template-body file://IamRole.yaml
$ IAM_ROLE_ARN=$(aws cloudformation describe-stacks \
                                    --stack-name iamrole \
--query "Stacks[0].Outputs[?OutputKey=='IamRole'].OutputValue" \
--output text)
$ aws sts assume-role --role-arn $IAM_ROLE_ARN \
                      --role-session-name tmp
# Here goes the output of the command. I will store the access credentials in the env vars
$ export AWS_ACCESS_KEY_ID=… 
$ export AWS_SECRET_ACCESS_KEY=…
$ export AWS_SESSION_TOKEN=…
$ aws cloudformation create-stack \
                     --stack-name mybucket \
                     --template-body file://MyBucket.yaml

We will see the following error on the AWS console:

Figure 1.1 – CloudFormation console – stack events

Figure 1.1 – CloudFormation console – stack events

On the other hand, we cannot provide everyone with an AdminAccess policy, so we need to find a way to use CloudFormation with the necessary permissions while only letting CloudFormation use those permissions.

CloudFormation supports service roles. Service roles are the IAM roles that are used by different AWS services (such as EC2, ECS, Lambda, and so on). CloudFormation service roles are used by CloudFormation during stacks and StackSets operations—creation, update, and deletion:

  1. Let's create a specific role for CloudFormation:

    CfnIamRole.yaml

    AWSTemplateFormatVersion: "2010-09-09"
    Description: "This is a CFN role"
    Resources:
      IamRole:
        Type: AWS::IAM::Role
        Properties:
          AssumeRolePolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: AllowAssumeRole
                Effect: Allow
                Principal:
                  Service: "cloudformation.amazonaws.com"
                Action: "sts:AssumeRole"
          ManagedPolicyArns:
            - "arn:aws:iam::aws:policy/AdministratorAccess"
    Outputs:
      IamRole:
        Value: !GetAtt IamRole.Arn
  2. We create this stack for the service role and obtain the CloudFormation role ARN:
    $ aws cloudformation create-stack \
                         --stack-name cfniamrole \
                         --capabilities CAPABILITY_IAM \
                         --template-body file://CfnIamRole.yaml
    $ IAM_ROLE_ARN=$(aws cloudformation describe-stacks \
                                        --stack-name cfniamrole \
    --query "Stacks[0].Outputs[?OutputKey=='IamRole'].OutputValue" \
    --output text)
  3. Now we run the creation of the stack, which will use our role, specifying the Role ARN:
    $ aws cloudformation create-stack \
                         --stack-name mybucket \
                         --template-body file://MyBucket.yaml \
                         --role-arn $IAM_ROLE_ARN
  4. After a while, we can verify that our stack has been created, and we see our bucket!
    $ aws s3 ls
    # Output
    2019-10-16 14:14:24 mybucket-mybucket-jqjpr6wmz19q

    Before we continue, don't forget to clean your account:

    $ for i in mybucket iamrole cfniamrole; do aws cloudformation delete-stack --stack-name $i ; done

    Important note

    Note that in the preceding example, we provide the CloudFormation role with an AdminPolicy (the one that is provided by AWS by default).

In production-grade systems, we want to allow CloudFormation only those privileges that are required for the stack.

There are two permission schemas that are being applied for CloudFormation roles:

  • We have a certain list of services that we can use (for example, EC2, RDS, VPC, DynamoDB, S3, and so on).
  • Each template/stack combination will use only those services it needs—for example, if we declare Lambda functions with Simple Notification Service (SNS), then we should create the role with policies only for Lambda and SNS.