Overview

Turbot Resources can be managed as Custom Resources in CloudFormation.

Managing Turbot through CloudFormation supports a wide range of scenarios like:

  • Managing Turbot Resource Groups and Policies.
  • Managing Turbot Policies (particularly exceptions) in code with the AWS Resources.
  • Automatic cleanup of Turbot-modified resources to prevent CloudFormation conflicts.

Turbot Custom Resources use Turbot APIs

Internally, Turbot Custom Resources are implemented as API calls to Turbot. All aspects of authentication, data validation and audit trail logging are identical.

Authentication

Turbot Custom Resources are implemented as API calls to Turbot. Each Turbot Resource must specify a Turbot Access Key and Secret Key pair for authentication.

The basic format is:

MyTurbotResource:
  Type: "Custom::Turbot{ResourceType}"
  Properties:
    Turbot:
      AccessKey: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
      SecretKey: yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy

Typically this will be implemented through Parameters:

MyTurbotResource:
  Type: "Custom::Turbot{ResourceType}"
  Properties:
    Turbot:
      AccessKey: !Ref TurbotAccessKey
      SecretKey: !Ref TurbotSecretKey

Note: CloudFormation does not pass any information to the Custom Resource about the AWS identity running the CloudFormation stack. As such, the only identity information available to Turbot is provided by the authentication information passed in the Custom Resource definition.

Resource Data, API Endpoints and Field Name Format

The Custom Resource defines the data that will be used for Turbot API calls related to the resource type. For example, a TurbotPolicy resource will work with the /api/{apiVersion}/resources/{resourceUrn}/policies endpoints for CREATE, GET, PATCH and DELETE.

Generally, valid resource data is defined by the CREATE endpoint - please refer to the appropriate API documentation for full details.

Data fields for Turbot APIs are always defined in camel case and start with a lower case letter (e.g. resourceUrn). CloudFormation resources are typically defined in camel case and start with an upper case letter (e.g. ResourceUrn). To make Turbot resources consistent with CloudFormation format, Turbot accepts either format for fields and will automatically convert them to start with lower case. For example, using either resourceUrn or ResourceUrn in the Custom Resource makes no difference - both are passed to the API as resourceUrn.

ARN to URN Conversion

It’s common to use both AWS resources (defined by ARNs) and Turbot resources (defined by URNs) in the same CloudFormation stack. Turbot makes this easy by allowing ARNs to be passed in Turbot URN fields. For example, the following is valid:

ResourceUrn: !GetAtt TestRole.Arn

Output from Turbot Custom Resources

Turbot Custom Resources generally return the same results as the underlying API call. The result is not converted to uppercase camel, but always returned exactly as defined in the API (lowercase camel).

ServiceToken for Custom Resources

Custom Resources in CloudFormation must specify a ServiceToken, defining the handler for the resource. Turbot Resources can be used from CloudFormation stacks in any region of any Turbot-managed AWS account. The ServiceToken is always set to be:

ServiceToken: !Sub 'arn:aws:sns:${AWS::Region}:${AWS::AccountId}:turbot_aws_api_handler'

Turbot Custom Resources

Resource Group

MyPatchingRG:
  Type: "Custom::TurbotResourceGroup"
  Properties:
    ServiceToken: !Sub 'arn:aws:sns:${AWS::Region}:${AWS::AccountId}:turbot_aws_api_handler'
    Turbot: { AccessKey: !Ref TurbotAccessKey, SecretKey: !Ref TurbotSecretKey }
    Id: my-patching-rg
    Title: My Patching Resource Group
    ResourceUrn: "urn:turbot:c1:abc"
    Description: I am a RG to enforce patching.

Policy

EnforcePatchManagement:
  Type: "Custom::TurbotPolicy"
  Properties:
    ServiceToken: !Sub 'arn:aws:sns:${AWS::Region}:${AWS::AccountId}:turbot_aws_api_handler'
    Turbot: { AccessKey: !Ref TurbotAccessKey, SecretKey: !Ref TurbotSecretKey }
    ResourceUrn: !GetAtt MyRG.urn
    Requirement: MUST
    Name: "AWS:EC2:InstancePatchManagement"
    Value: "Enforce: Enabled with Target Patch Group"
    Notes: |
      Always use the patch group defined in AWS > EC2 > Instance Target Patch
      Group. This ensures a common patch baseline across all servers unless
      they have been explicitly granted an exception.

Resource Group Attachment

Attach a resource group to a resource. The resource group will be attached to the end of the list of resource groups for the resource.

PatchMyServer:
  Type: "Custom::TurbotResourceGroupAttachment"
  Properties:
    ServiceToken: !Sub 'arn:aws:sns:${AWS::Region}:${AWS::AccountId}:turbot_aws_api_handler'
    Turbot: { AccessKey: !Ref TurbotAccessKey, SecretKey: !Ref TurbotSecretKey }
    ResourceUrn: !GetAtt MyServer.Arn
    ResourceGroupUrn: !GetAtt MyPatchingRG.urn

Shadow

CloudFormation expects to manage the complete configuration and lifecycle of resources. Changes made outside CloudFormation often result in failure of further updates or deletions to the original stack.

In contrast, Turbot is designed to detect and automatically correct the configuration of resources. For example, new IAM Roles may have lockdown policies added to them by Turbot. Unfortunately, these changes can then cause the original CloudFormation stack to fail when operating on the IAM Role.

Turbot Shadow resources provide a simple mechanism to resolve this conflict. When an AWS resource has dependencies managed by Turbot, a Shadow should be created as a dependency. During deletion, the Shadow is deleted first which provides Turbot with the opportunity to cleanup any dependencies before the AWS resource deletion is attempted.

TestRole:
  Type: "AWS::IAM::Role"
  Properties:
    AssumeRolePolicyDocument:
      Version: "2012-10-17"
      Statement:
        - Effect: Allow
          Principal:
            Service:
              - ec2.amazonaws.com
          Action:
            - sts:AssumeRole
    Path: /
    Policies:
      - PolicyName: root
        PolicyDocument:
          Version: "2012-10-17"
          Statement:
            - Effect: Allow
              Action: "*"
              Resource: "*"
    RoleName: test_role

TestRoleShadow:
  Type: "Custom::TurbotShadow"
  Properties:
    ServiceToken: !Sub 'arn:aws:sns:${AWS::Region}:${AWS::AccountId}:turbot_aws_api_handler'
    Turbot: { AccessKey: !Ref TurbotAccessKey, SecretKey: !Ref TurbotSecretKey }
    resourceArn: !GetAtt TestRole.Arn

Directory

dir:
  Type: "Custom::TurbotDirectory"
  Properties:
    ServiceToken: !Sub 'arn:aws:sns:${AWS::Region}:${AWS::AccountId}:turbot_aws_api_handler'
    Turbot: { AccessKey: !Ref TurbotAccessKey, SecretKey: !Ref TurbotSecretKey }
    ResourceUrn: "urn:turbot:dev:aau"
    Id: dir02
    Type: turbot
    Title: Dir 02
    Description: "My test dir #02."
    UrnTemplate: "urn:turbot:::profile:turbot:"
    LoginNameTemplate: |
      - default: ""

Lookup

test:
  Type: "Custom::TurbotLookup"
  Properties:
    ServiceToken: !Sub 'arn:aws:sns:${AWS::Region}:${AWS::AccountId}:turbot_aws_api_handler'
    Turbot: { AccessKey: !Ref TurbotAccessKey, SecretKey: !Ref TurbotSecretKey }
    ResourceUrn: "arn:aws:iam::123456789012:role/test_role_1"

Contact us to learn more about managing Turbot through Custom Resources in CloudFormation, or to understand how Turbot can automate networking, operations, security, and compliance for your organization. Schedule a demo to see Turbot in action.