AWS Cloud Operations Blog

How AWS Control Tower users can proactively verify compliance in AWS CloudFormation stacks

AWS Control Tower customers leverage infrastructure as code (IaC) to consistently deploy resources within their AWS multi-account setup. Enterprises want their developers to create and manage resources that they need to build applications while maintaining compliance with the organizations’ security, operational, and cost optimization best practices. Most solutions today inform customers about non-compliant resources only after those resources have been provisioned. These noncompliant resources increase security risk, operational overhead, and cost for customers. Customers that want a more proactive compliance enforcement system would have to build their own control mechanism, ensuring resource provisioning is subjected to the organizational compliance checks. However, many enterprises don’t have the internal expertise or the development capacity required to build these comprehensive mechanisms.

This led us to create AWS Control Tower proactive controls, a new AWS Control Tower feature that lets customers configure compliance rules which will be evaluated before creating, updating, or deleting a resource. Customers can select the applicable evaluation rules for their enterprise requirements from a relevant set of rules provided by AWS Control Tower. These rules will be created, enabled and managed across the Customer’s multi-account setup by AWS Control Tower. These proactive controls will be supported through AWS CloudFormation Hooks, allowing customers to proactively validate compliance while deploying resources through AWS CloudFormation. Resources will only be provisioned if they pass these checks, effectively lowering security and compliance risks, reducing overhead of fixing compliance issues, and optimizing costs.

In this post, we will consider a simple AWS Control Tower customer setup with two managed accounts (application account and pipeline). The steps below will help the customer create a pipeline automation AWS Identity and Access Management (IAM) role defined in one managed account (application account) and an IAM pipeline user or role in the other account (pipeline account). The customer setup will look this way:

Customer setup architecture

Figure 1. Customer setup architecture

Second, as a walkthrough of the new feature, we will configure the new proactive controls to enforce a compliance requirement that every S3 bucket needs to have an AWS KMS encryption key enabled. We will then try to provision an AWS S3 bucket without an AWS KMS encryption key through AWS CloudFormation console to visualize how the new AWS Control Tower controls work.

Key Terms and Concepts

Proactive Control

A proactive control inspects the configuration of your AWS resources before provisioning. If non-compliant resources are found, the control returns a failure status. Proactive controls in AWS Control Tower are implemented with AWS CloudFormation hooks.

Category

A higher-level entity that can be mapped to a control. Services, frameworks, and control objectives are categories.

Control Relationships

An entity that defines a relationship between two controls. For example, the relationship between two controls could be Inclusive, Mutually-exclusive, Alternate, or Dependent.

Control Severity

A label to help prioritize controls.

Control Objective

A statement of the desired result or purpose to be achieved by implementing control procedures in a particular process. Every control is mapped to one or more control objectives that it will help achieve, when enabled.

Examples: Encryption in transit, Encryption at Rest

Framework

Compliance and regulatory frameworks are sets of guidelines and best practices. Organizations follow these guidelines to meet regulatory requirements, improve processes, strengthen security, and achieve other business objectives. Every control is mapped to certain control IDs of a framework or framework revision that it helps fulfill, when enabled.

Examples: NIST, CIS, PCI

Service

An AWS Product that, for controls, may represent multiple AWS services. For instance, Amazon EC2 includes ec2 and ebs. Every control is mapped to one or more services based on the service it is targeted for, when enabled. Service is also part of a Control definition.

Examples: Amazon S3, Amazon EC2

Activating the AWS Control Tower proactive control

Pre-requisites

This post assumes that you’re familiar with AWS CloudFormation templates, AWS Control Tower, and Amazon Simple Storage Service. For this walkthrough, you must have an existing AWS Control Tower customer setup with two AWS Control Tower enrolled AWS accounts, similar to the following setup:

Blog post use case customer setup structure

Figure 2. Blog post use case customer setup structure

Figure 3. AWS Organizations structure.

In addition to the above customer accounts setup, you need the permissions to create/delete/update AWS IAM role through AWS CloudFormation stacks using YAML templates. An AWS IAM user or role is also needed within the pipeline account that has the ability to assume the pipeline AWS IAM role created in Step 1 below. See Granting a user permissions to switch roles for more information.

Step 1 To create the automation IAM role within the managed account using the AWS CloudFormation (console)

  1. Log into the managed account (application account) using an IAM Administrator Role with permissions to create an AWS CloudFormation stack with an AWS IAM Role. Open the AWS CloudFormation console and choose Create stack.
AWS CloudFormation Console

Figure 4. AWS CloudFormation Console

  1. Under PrerequisitePrepare template, select Template is ready. Under Specify template, select Upload a template file. Copy and paste the below code snippet into an empty YAML file. Upload this file by choosing Choose file on the console. Choose Next to provide stack name and parameters for the template
AWSTemplateFormatVersion: 2010-09-09
Description: Pipeline IAM role and permissions
Parameters:
  MaxSessionDuration:
    Description: The desired max role session duration (seconds)
    Default: 3600
    MaxValue: 43200
    MinValue: 900
    Type: Number
  TrustedPipelineRoleUserArn:
    AllowedPattern: '^arn:aws[a-z0-9-]*:iam::\d{12}:(role|user)\/([\w-\/.@+=,]{1,1017})*$'
    ConstraintDescription: Must start with arn. Also special characters supported [+, =, ., @, -].
    Description:
      The AWS IAM role or user AWS IAM ARN to trust so it can assume the pipeline role.
      Example = arn:aws:iam::123456789012:user/my-pipeline-user
    Type: String

Resources:
  PipelineIAMRole:
    Type: AWS::IAM::Role
    Properties:
      Description: Pipeline AWS IAM Role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              AWS:
                - !Ref TrustedPipelineRoleUserArn
      ManagedPolicyArns:
        - !Sub arn:${AWS::Partition}:iam::aws:policy/job-function/ViewOnlyAccess
        - !Sub arn:${AWS::Partition}:iam::aws:policy/AWSCloudFormationFullAccess
        - !Ref PipelineIAMRolePolicy
      MaxSessionDuration: !Ref MaxSessionDuration

  PipelineIAMRolePolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      Description: Pipeline AWS IAM role permissions
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Sid: CloudFormationDefaultS3BucketGetObjectAccess
            Effect: Allow
            Action: s3:GetObject
            Condition:
              StringEquals:
                aws:ResourceAccount: ${aws:PrincipalAccount}
              ForAnyValue:StringEquals:
                aws:CalledVia: !Sub cloudformation.${AWS::URLSuffix}
            Resource: !Sub arn:${AWS::Partition}:s3:::cf-templates-*/*

          - Sid: CloudFormationDefaultS3BucketPutObjectAccess
            Effect: Allow
            Action: s3:PutObject
            Condition:
              StringEquals:
                aws:ResourceAccount: ${aws:PrincipalAccount}
            Resource: !Sub arn:${AWS::Partition}:s3:::cf-templates-*/*

          - Sid: S3BucketAccess
            Effect: Allow
            Action:
              - s3:CreateBucket
              - s3:DeleteBucket
              - s3:DeleteBucketPolicy
              - s3:PutEncryptionConfiguration
              - s3:PutBucketOwnershipControls
              - s3:PutBucketPolicy
              - s3:PutBucketPublicAccessBlock
              - s3:PutBucketVersioning
            Condition:
              ForAnyValue:StringEquals:
                aws:CalledVia: !Sub cloudformation.${AWS::URLSuffix}
            Resource: !Sub arn:${AWS::Partition}:s3:::*

Outputs:
  PipelineIAMRoleName:
    Description: Pipeline IAM Role Name
    Value: !Ref PipelineIAMRole
  PipelineIAMRoleArn:
    Description: Pipeline IAM Role ARN
    Value: !GetAtt PipelineIAMRole.Arn
Create stack first form page

Figure 5. Create stack first form page

  1. Enter an IAM role or user ARN in the TrustedPipelineRoleUserArn parameter. This is the role or user within the pipeline account that will assume the created pipeline IAM role from the template. Under Stack name pane, enter the stack name demo-myfirstcontrol-role-stack. Choose Next to proceed
Create stack parameter details page

Figure 6. Create stack parameter details page

  1. Select the defaults for the rest of the pages and choose Next to proceed
  2. On the Review page, acknowledge and select the Capabilities warning. Choose Create stack to proceed
Create stack review page

Figure 7. Create stack review page

  1. Once the IAM role is created and the stack deployment is complete, select the Outputs tab and copy the PipelineIAMRoleName and PipelineIAMRoleArn values
CloudFormation stack details page, output tab for demo-myfirstcontrol-role-stack

Figure 8. CloudFormation stack details page, output tab for demo-myfirstcontrol-role-stack

  1. Log into the other managed account (pipeline account) with the existing IAM role or user entered in the TrustedPipelineRoleUserArn parameter. Assume the pipeline IAM role created by the AWS CloudFormation stack to log into the managed account (application account). Refer the Enabling proactive controls later in this post for steps to configure the AWS Control Tower provided controls.
Account federation page to access application account from pipeline account

Figure 9. Account federation page to access application account from pipeline account

Step 2 To configure and enable proactive AWS Control Tower controls (console)

  1. Log into the management account using an IAM role or IAM user with administrator permissions. Open the AWS Control Tower console
  2. On the AWS Control Tower navigation menu, choose Controls library, then All controls.
Controls view after selecting All Controls under Controls library from AWS Control Tower navigation menu

Figure 10. Controls view after selecting All Controls under Controls library from AWS Control Tower navigation menu

  1. For Controls, Name = CT.S3.PR.7 in the Controls text box. Select the filtered control and choose Enable Controls in the upper right corner of the window to apply the selected control.
Filtered control for Name = [CT.S3.PR.7] CT.S3.PR.7 filter text in Controls view

Figure 11. Filtered control for Name = [CT.S3.PR.7] CT.S3.PR.7 filter text in Controls view

  1. On the Enable control on OU page, select the Sandbox OU from the list and then, choose Enable control on OU.
4.On the Enable control on OU page, select the Sandbox OU from the list and then, choose Enable control on OU.

Figure 12. 4. On the Enable control on OU page, select the Sandbox OU from the list and then, choose Enable control on OU

  1. To verify the control was enabled successfully, choose Controls library, then All controls in the AWS Control Tower navigation menu. Enter Name = CT.S3.PR.7 in the Controls text box, then select Name attribute of the filtered control row in the list.
  2. Select the OUs enabled tab on the View Control page, then confirm Sandbox is listed with Enable state attribute value as Enabled.
OUs enabled tab on View Controls page for control CT.S3.PR.7

Figure 13. OUs enabled tab on View Controls page for control CT.S3.PR.7

Verifying compliance using the active AWS Control Tower controls

To test enabled control functionality using a non-compliant AWS CloudFormation template for Amazon S3 bucket (console)

  1. Log into the managed account (pipeline account) using TrustedPipelineRole IAM role or user. Open the AWS CloudFormation console and choose Create stack
  2. Under Prerequisite Prepare template, select Template is ready. Under Specify template, select Upload a template file. Copy and paste the below code snippet into an empty YAML file. Upload this file by choosing Choose file on the console. Choose Next to provide stack name and parameters for the template
AWSTemplateFormatVersion: 2010-09-09
Description: AWS S3 Bucket without server-side encryption
Resources:
  S3BucketWithoutEncryption:
    Type: AWS::S3::Bucket
    Properties:
      OwnershipControls:
        Rules:
          - ObjectOwnership: BucketOwnerPreferred
      PublicAccessBlockConfiguration:
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
        RestrictPublicBuckets: True
      VersioningConfiguration:
        Status: Enabled
Outputs:
  S3BucketName:
    Description: S3 Bucket without encryption
    Value: !Ref S3BucketWithoutEncryption
  1. Under Stack name pane, enter the stack name demo-testing-noncompliant-bucket-stack. Choose Next to proceed
  2. Select the defaults for the rest of the pages and choose Next to proceed
  3. On the Review page, acknowledge and select the Capabilities warning. Choose Create stack to proceed. The stack deployment to create the non-compliant S3 bucket will fail.
  4. To verify the enabled controls are invoked, select the Events tab after choosing the newly created stack under the Stacks context menu.
Events tab for the newly created stack demo-testing-noncompliant-bucket-stack

Figure 14. Events tab for the newly created stack demo-testing-noncompliant-bucket-stack

  1. Select the Preferences icon wheel in the upper right corner of the window (highlighted above). Turn on Hook invocations, and then choose Confirm to make the column visible within the Events tab.
Events tab for the newly created stack demo-testing-noncompliant-bucket-stack

Figure 15. Events tab for the newly created stack demo-testing-noncompliant-bucket-stack

  1. Select each message within the Hook invocations to verify the message details for the configured controls
Failed hook invocation message for non-compliant S3 bucket

Figure 16. Failed hook invocation message for non-compliant S3 bucket

To test the enabled control functionality using a compliant AWS CloudFormation template for an encrypted Amazon S3 Bucket (console)

  1. Follow the same steps as the non-compliant AWS CloudFormation template part above. In Step 2, copy and paste the below code snippet instead of the non-compliant snippet.
AWSTemplateFormatVersion: 2010-09-09
Description: AWS S3 Bucket with server-side encryption
Resources:
  S3BucketWithEncryption:
    Type: AWS::S3::Bucket
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
            BucketKeyEnabled: True
      OwnershipControls:
        Rules:
          - ObjectOwnership: BucketOwnerPreferred
      PublicAccessBlockConfiguration:
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
        RestrictPublicBuckets: True
      VersioningConfiguration:
        Status: Enabled
Outputs:
  S3BucketName:
    Description: S3 Bucket with encryption
    Value: !Ref S3BucketWithEncryption
  1. Under Stack name pane, enter the stack name demo-testing-compliant-bucket-stack. Choose Next to proceed
  2. To verify the S3 bucket was created successfully, verify the Hook invocation message details and the Amazon S3 bucket Arn under the Resources tab.
Successful hook message for compliant S3 bucket

Figure 17. Successful hook message for compliant S3 bucket

CloudFormation stack details page, resources tab for demo-testing-compliant-bucket-stack

Figure 18. CloudFormation stack details page, resources tab for demo-testing-compliant-bucket-stack

  1. Under the Resources tab, confirm that the S3 bucket exists by opening the link under the Physical ID attribute. Select the Properties tab and then, navigate to Default encryption pane.
Encrypted S3 bucket details showing server side encryption enabled

Figure 19. Encrypted S3 bucket details showing server side encryption enabled

Cleanup

  1. Delete the CloudFormation stack demo-testing-noncompliant-bucket-stack in the managed account (application account). This stack was created when you tested your enabled control functionality for non-compliant case. This stack fails to provision an unencrypted S3 bucket due to non-compliance.
  2. Delete the CloudFormation stack demo-testing-compliant-bucket-stack in the managed account (application account). This stack was created when you tested your enabled control functionality for compliant case. This stack provisions an encrypted S3 bucket since it passes the compliance check.
  3. Delete the CloudFormation stack demo-myfirstcontrol-role-stack. This stack was created before enabling the proactive control to federate into the managed account (application account). This stack provisions an IAM role to be assumed by the other managed account (pipeline account) during customer setup operations.
  4. Disable the AWS Control Tower proactive control CT.S3.PR.7 from the AWS Control Tower console. This control was created and enabled when you wanted to verify resource compliance on Amazon S3 buckets. This control applies compliance checks on AWS CloudFormation Stacks.

Tips and Recommendations

This post only covers AWS Control Tower proactive control features that are related to ensuring AWS CloudFormation provisioned Amazon S3 bucket resources take advantage of hook based controls for verifying compliance. There are other features and capabilities covered in the AWS Control Tower Controls Guide. Consider the following tips and recommendations as you learn more about proactive controls:

  1. AWS Control Tower control APIs allow for the ability to programmatically enable and disable controls. For more information about the control APIs, see the AWS Control Tower API Reference.
  2. Service Control Policies can be applied to the Organizational Unit (OU) and/or Account to protect modifications to IAM roles/user. See the Example Service Control Policies.
  3. To help secure your AWS resources, follow the best practices for AWS Identity and Access Management (IAM).

Conclusion

This post covers a compliance scenario where you can proactively evaluate resource configuration with the new AWS Control Tower controls feature during stack creation. You can either display a warning message or prevent resources from being provisioned if they do not pass these checks. This effectively lowers security and compliance risks, reduces the overhead of fixing compliance issues, and optimizes costs. We expect the community to leverage this new feature to optimize their costs and validate compliance with AWS and Enterprise best practices. We look forward to learning how customers use this offering in new scenarios, and your continued feedback so that we can improve it.

Further Reading

About the authors:

Rushabh Kapadia

Rushabh is a Software Engineer with AWS Control Tower. He is passionate about creating scalable and efficient systems to help customers build on AWS. When he is not working, he likes to spend time with family and friends, cook, and play soccer.

Andy Wickersham

Andy is a Senior Security Engineer with AWS Control Tower. He has a passion for converting complex security concepts into simplified solutions that are easy to consume. Outside of work, Andy enjoys spending time with his family and getting outside to golf, cycle, and hike.