DEV Community

Cover image for CloudFormation Hook 101
Sri for AWS Community Builders

Posted on

CloudFormation Hook 101

Contents

Overview

AWS offers two types of guardrails:

  1. Preventive guardrails enforce specific policies to help ensure that your accounts operate in alignment to compliance standards, and disallow actions that lead to policy violations.

  2. Detective guardrails detect and alert on unexpected activity and noncompliance of resources within your accounts, such as policy violations. These are helpful in alerting when something requires remediation (either manual or automated).

This purpose of this blog is to demonstrate a demo of CloudFormation hook, however to provide some context about CloudFormation Hook, I have taken the terminology from AWS CloudFormation Hook workshop

AWS CloudFormation Hooks are preventive guardrails, a feature that allows users to invoke custom logic to automate actions or inspect resource configurations prior to a create, update or delete CloudFormation stack operation. It allows the users to manage their cloud applications and infrastructure in a safe, predictable, and repeatable way. With AWS CloudFormation Hooks, the users can now validate resource properties and send a warning, or prevent the provisioning operation, for non-compliant resources to reduce security and compliance risk, lower operational overhead, and optimise cost.

With AWS CloudFormation Hooks, the users can publish their policy and controls to the CloudFormation Registry and enforce them against all stack and resource operations in their AWS accounts. For example, the users can inspect their Amazon S3 bucket properties for encryption, public access and logging best practice policies to ensure that developers always create secure S3 buckets in the first place.

A hook includes a hook specification represented by a JSON schema and handlers that are invoked at each hook invocation point.
In this blog, we will use a pre-existing hook "" to enforce S3 bucket encryption turned on during the bucket creation.


Basic Terminology:

Hook

A hook contains code that is invoked immediately before CloudFormation creates, updates, or deletes specific resources. Hooks are able to inspect the resources that CloudFormation is about to provision. If hooks find any resources that don’t comply with your organisational guidelines, they are able to prevent CloudFormation from continuing the provisioning process.

Hook Targets

Hook targets are the destination where hooks will be invoked against. Hooks support AWS and non-AWS resources supported by CloudFormation as target. You specify target(s) in the hook schema while authoring your hook. For example, the users can author a hook targeting AWS::S3::Bucket resource. We support ‘n’ targets for a hook. That means, a hook can support multiple targets. There is no limit on the number of resource targets a hook can support.

Target Invocation Point

Invocation points are the point in provisioning workflow where hooks will be executed. CloudFormation supports PRE (before) invocation point for this release. Meaning, hook authors can write a hook that will be executed before provisioning logic for the target is started. For example, a hook with PRE invocation point for target S3 bucket will be executed before CloudFormation starts provisioning the S3 bucket in the user’s account.

Target Action

Target actions are the type of operation hooks will be executed at. These actions are tied with hook targets. Hook targets support CREATE, UPDATE, DELETE actions. For example, when a hook for a CREATE action on an S3 target is authored, it will only be executed during a create operation for an S3 bucket.

Hook Handlers

An invocation point and action makes an exact point where the hook will be executed. Hook authors write handlers which host logic for these specific points. For example, a PRE invocation point with a CREATE action makes a preCreate handler. Hook authors will write code which get executed any time there is a matching target and CloudFormation is performing a matching action.

Hook Configuration Schema

All Hook extension types must have the type configuration schema set to be invoked during stack operations. There are three parts to the Configuration Schema:

  • TargetStacks: This property determines if the hook is on (TargetStacks = ALL) or off (TargetStacks = NONE). If the mode is set to ALL, the hook applies to all stacks in your account during a CREATE, UPDATE, or DELETE operation. If the mode is set to NONE, the hook won't apply to stacks in your account. Valid values: ALL | NONE
  • FailureMode: This field tells the service how to treat hook failures. If the mode is set to FAIL, and the hook fails, then the fail configuration stops provisioning resources. If the mode is set to WARN and the hook fails, then the warn configuration allows provisioning to continue with a warning message defined in your Hook logic. Valid values: FAIL | WARN
  • Properties: You can include specific hook runtime properties. These should match the shape of the properties supported by the hooks schema. For example, you can define your desired encryption setting (AES256 or KMS) as a property. At runtime, the hook will refer to the configuration property to evaluate against your desired encryption setting.

Type configuration

AWS CloudFormation supports hook type specific configuration which can be used during set up by hook users. CloudFormation service will refer this configuration at runtime when it is executing hook in an account. Hook configuration supports enabling or disabling hook at stack level, failure mode and hook runtime property values.

Below is the example of typical hook configuration:

"{
    "CloudFormationConfiguration": {
        "HookConfiguration": {
            "TargetStacks": "ALL",
            "FailureMode": "FAIL",
            "Properties": {
                ...
            }
        }
    }
}"
Enter fullscreen mode Exit fullscreen mode

Hook configuration support below properties:

  • TargetStacks:

    • This field will enable hooks at stack level. This field gives users ability to further fine grain control over when to execute hook in a stack. Valid values are ALL or NONE
  • FailureMode:

    • This field tells the service how to treat hook failures. Valid values are FAIL or WARN. If the mode is FAIL and the hook fails, stack operations will fail too. In case of WARN mode, stack operation will be impacted and the hook failure will be shown as warning message stack events.
  • Properties:

    • Specifies hook runtime properties. These should match the shape of the properties supported by the hooks schema. Hook authors define runtime properties in the hook schema. You can identify property definitions and if they are required by examining the hook schema under TypeConfiguration.
  • Example: For the above sample hook schema, the configuration json would be

  "{
      "CloudFormationConfiguration": {
          "HookConfiguration: {
              "TargetStacks": "ALL",
              "FailureMode": "FAIL",
              "Properties": {"limitSize": "1","encryptionAlgorithm": "aws:kms"
              }
          }
      }
  }"
Enter fullscreen mode Exit fullscreen mode

Demo

Let's get started with the demo.

S3 KMS Demo:

We are going to enforce a preventive guardrail, wherein a user is not allowed to create an S3 bucket without aws-kms encryption

  1. Create an IAM Role using CloudFormation Hook Role and note down the ARN.

    IAM_Role_Permission_Policy
    IAM_Role_Permission_Policy

    IAM_Role_Trust_Policy
    IAM_Role_Trust_Policy

  2. Activate CloudFormation Hook: Navigate to CloudFormation > Public Extensions > Select Hooks under Extension Type > Select Third Party under Publisher and then search for Extension name prefix = AWSSamples::S3BucketEncrypt::Hook

    Activate_Hook
    Activate_Hook

    Hook_Execution_Role

    Note: The execution role is extremely important. An invalid role will not allow you to create any buckets.
    Hook_Execution_Role

    Hook_Config

    {
      "CloudFormationConfiguration": {
        "HookConfiguration": {
          "TargetStacks": "ALL",
          "FailureMode": "FAIL",
          "Properties": {
            "minBuckets": "1",
            "encryptionAlgorithm": "aws-kms"
          }
        }
      }
    }
    

    Hook_Config

    Hook_Resources
    Hook_Resources

    Hook_Schema
    Hook_Schema

    Hook_Config
    Hook_Config

    Hook_Activate
    Hook_Activate

  3. Use the following CloudFormaton template to create an unenrypted bucket, The expected result is a failure due to CloudFormation Hook preventive guardrail.

    AWSTemplateFormatVersion: "2010-09-09"
    Description: This CloudFormation template provisions an unencrypted S3 Bucket
    Resources:
      S3Bucket:
        Type: 'AWS::S3::Bucket'
        DeletionPolicy: Delete
        Properties:
          BucketName: examplebuketcreatedfromcloudformation
    

    unencrypted-s3
    unencrypted-s3

  4. Use the following CloudFormation Template to create an S3 bucket with aws:kms encryption, which will allow the bucket creation.

    AWSTemplateFormatVersion: "2010-09-09"
    Description: This CloudFormation template provisions an encrypted S3 Bucket
    Resources:
      EncryptedS3Bucket:
        Type: 'AWS::S3::Bucket'
        Properties:
          BucketName: !Sub 'encryptedbucket-kms-${AWS::Region}-${AWS::AccountId}'
          BucketEncryption:
            ServerSideEncryptionConfiguration:
              - ServerSideEncryptionByDefault:
                  SSEAlgorithm: 'aws:kms'
                  KMSMasterKeyID: !Ref EncryptionKey
                BucketKeyEnabled: true
          Tags: 
            - Key: "keyname1"
              Value: "value1"
    
      EncryptionKey:  
        Type: AWS::KMS::Key
        Properties:
        Description: KMS key used to encrypt the resource type artifacts
        EnableKeyRotation: true
        KeyPolicy:
          Version: "2012-10-17"
          Statement:
          - Sid: Enable full access for owning account
            Effect: Allow
            Principal: 
              AWS: !Ref "AWS::AccountId"
            Action: kms:*
            Resource: "*"
    
    Outputs:
      EncryptedBucketName:
        Value: !Ref EncryptedS3Bucket
    

    encrypted-s3-kms
    encrypted-s3-kms


S3 SSE-S3(AES256) Demo:

We are going to enforce a preventive guardrail, wherein a user is not allowed to create an S3 bucket without SSE-S3(AES256) encryption.

  1. Use the following CloudFormation Template to create an S3 bucket with AES256 encrytion

    AWSTemplateFormatVersion: "2010-09-09"
    Description: This CloudFormation template provisions an encrypted S3 Bucket
    Resources:
      EncryptedS3Bucket:
        Type: 'AWS::S3::Bucket'
        Properties:
          BucketName: !Sub 'encryptedbucket-s3-${AWS::Region}-${AWS::AccountId}'
          BucketEncryption:
            ServerSideEncryptionConfiguration:
              - ServerSideEncryptionByDefault:
                  SSEAlgorithm: 'AES256'
                BucketKeyEnabled: true
          Tags: 
            - Key: "keyname1"
              Value: "value1"
    

    encrypted-s3-AES256-failed
    encrypted-s3-AES256-failed

  2. In order to fix this, we need to edit the CloudFormation Hook config to update encryptionAlgorithm from aws:kms* to AES256 and re-deploy the CloudFormation Template in Step 1 of Demo 2, which will allow the bucket creation.

    edit-cf-hook

    {
      "CloudFormationConfiguration": {
        "HookConfiguration": {
          "TargetStacks": "ALL",
          "FailureMode": "FAIL",
          "Properties": {
            "minBuckets": "1",
            "encryptionAlgorithm": "AES256"
          }
        }
      }
    }
    

    edit-cf-hook

    encrypted-s3-AES256
    encrypted-s3-AES256


Clean Up

  1. Deactive CloudFormation Hook.

    Deactivate_Hook
    Deactivate_Hook

    Final_Hook
    Final_Hook

  2. Delete all S3 buckets CloudFormation Stacks.

  3. Delete IAM Role CloudFormation Stack.


Summary

  • We have learnt how to enable CloudFormation hooks to use as preventive guardrails.

  • To get started, you can explore sample hooks published to the CloudFormation Public Registry or author hooks using the CloudFormation CLI and publish them to your CloudFormation Private Registry.

  • The registry provides a central location where you can browse CloudFormation extensions, such as resources, modules, and hooks that are available for use in your account. You can also refer to sample Hooks.


Referrals:


Top comments (2)

Collapse
 
awais_684 profile image
Muhammad Awais Zahid

thanks for sharing. really very helpful.
nice to meet you here too

Collapse
 
kasukur profile image
Sri

Thank you for the kind words Muhammad.