AWS Partner Network (APN) Blog

Collecting Information from AWS CloudFormation Resources Created in External Accounts with Custom Resources

By Erin McGill. Erin is a Partner Solutions Architect (SA) at AWS. 

Throughout this series, we’ve talked about easing cross-account role creation with AWS CloudFormation and a custom stack URL.  We’ve also discussed how to dynamically generate CloudFormation templates to populate a unique external ID. But once your cross-account role is created, how does the Amazon Resource Name (ARN) for the generated cross-account role in the customer account get back to your company’s onboarding portal? Asking the customer to cut and paste the output ARN is possible, but it’s a manual step that’s prone to human error.

In this post, we’ll describe how to use a custom resource to send the cross-account role ARN to the onboarding portal. A custom resource enables you to add custom provisioning logic to a CloudFormation template. AWS CloudFormation runs this code anytime a stack is created, updated, or deleted. By adding a custom resource to the template that is launched in your customer’s account, you can post a custom resource request that includes the cross-account role ARN back to an Amazon SNS topic.


Every time the CloudFormation stack is updated or deleted, it will trigger the custom resource.  

Even if no action is taking place within the custom resource during these stages, you need a mechanism to signal back to the ResponseURL, a success or failure. The ResponseURL is provided in the custom resource request. If the ResponseURL does not receive a success or failure, the CloudFormation stack will remain in an UPDATE_IN_PROGRESS or DELETE_IN_PROGRESS state, and will ultimately fail upon reaching the timeout waiting for a response.


The SNS topic runs in your company’s AWS account, which is associated with your onboarding portal, as shown in the following illustration.

The onboarding portal then takes the message that arrives in the SNS topic and uses it to associate the new cross-account role ARN with the customer’s account ID and unique external ID. You can use an Amazon DynamoDB table as a tracking mechanism. This table stores the cross-account role ARN in an item that also contains the customer’s account ID and unique external ID from earlier in the onboarding process.


The SNS topic in your company’s AWS account that receives the cross-account role ARN from a customer’s AWS account should not be publicly accessible—it should be restricted to only those authorized to send messages to the topic.  In a previous post in this series, we explained how the user-provided AWS account ID is associated with our generated unique external ID for the customer in a DynamoDB table. With the AWS account ID, you can also whitelist this account in the SNS topic policy to keep your AWS resource secure.


When your customer launches the CloudFormation stack, the template creates the custom resource. As demonstrated in our previous blog post, the Service Token for the custom resource is prepopulated with the SNS topic associated with the onboarding portal in your company’s AWS account. Once the cross-account role is created, the custom resource is triggered, and it sends the cross-account role ARN, your customer’s AWS account ID, and any other additional information you want to include to the SNS topic in your company’s AWS account.

Since the stack remains in the customer’s account, it will continue to provide a mechanism for capturing changes to the CloudFormation stack, including deletion, back to your company.  For example, if a customer deletes the CloudFormation stack, thereby deleting the cross-account role, a notification will be sent to the same SNS topic.  This will allow you or your support staff to proactively reach out for troubleshooting or assistance.  We recommend removing the customer’s AWS account ID from the SNS topic policy during the stack deletion phase, as they will need to restart the workflow to re-onboard, which will, in turn, re-whitelist their account ID.

Here’s the Resources section from an example CloudFormation template that demonstrates how to create the cross-account role and send it back to your company’s SNS topic:

Resources:
# This is the cross-account role that will be created
  CrossAccountRole:
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Action: 'sts:AssumeRole'
          Effect: Allow
          Principal:
            AWS: arn:aws:iam::111111111112:root #The Master Account root ARN
          Condition:
            StringEquals:
               sts:ExternalId: 12345 # generated Customer External ID unique to this customer
          Sid: ''
        Version: '2012-10-17'
      Path: "/"
      Policies:
      # This role should have only the permissions necessary
- PolicyDocument:
          Statement:
          - Action:
            - "ec2:Describe*"
            Effect: Allow
            Resource: "*"
          Version: '2012-10-17'
        PolicyName: CustomerAccountAccess
    Type: 'AWS::IAM::Role'
  # This custom resource phones home the cross-account ARN information to your company’s SNS topic
  PhoneHomeCustomResource:
    Properties:
      ServiceToken: arn:aws:sns:us-west-2:111111111112:ARNSnsTopic # Your Company’s SNS Topic Arn 
      RoleArn: !GetAtt CrossAccountRole.Arn # The ARN of the IAM cross-account role generated above
      AccountID: !Ref AWS::AccountId # The customer’s AWS account ID
    Type: Custom::PhoneHomeCustomResource
    Version: '1.0'

This CloudFormation YAML script has one section, Resources, with two resources defined within it:

•	CrossAccountRole
•	PhoneHomeCustomResource

The CrossAccountRole resource creates the IAM role that your company can assume in the customer’s account. It consists of three properties:

  • AssumeRolePolicyDocument – Identifies your company by your account ARN, provided in the Principal, and sets the condition that your company account is allowed to assume the role only as long as the external ID is provided when assuming the role.
  • Path – A friendly name or path for the role. We’ve chosen to set this to a forward slash for simplicity.
  • PolicyDocument – Defines the permissions that this role will be granted. In this example, we are granting the ability to describe EC2 instances in the account and naming the policy

The PhoneHomeCustomResource creates a custom resource with three properties:

  • ServiceToken – Identifies the endpoint that gives the template the ability to access the custom resource in your company’s account. In this case, the service token is the SNS topic ARN in your company’s account. This service token must be from the same region as the stack being created. This SNS topic will, in turn, trigger a Lambda function. This function can perform any onboarding required by your workflow, but, most importantly, sends back the SUCCESS status to CloudFormation so it can proceed with launching the stack.
  • RoleArn – The ARN of the CrossAccountRole
  • AccountID – The account ID of the customer’s account that is launching your CloudFormation stack.

CloudFormation is able to identify dependencies and will wait till the RoleArn resource has been created.  Once created, the custom resource will be invoked and the RoleArn and theAccountID values will be sent to the ServiceToken, which is the SNS topic in your company’s account.

In this blog series, we showed you how to create a URL to launch a stack in your customer’s account, dynamically generate a CloudFormation template to include unique data for each customer, and automatically acquire and send information back about created resources, such as IAM cross-account role ARNs. In the next, and final, post in this series, we’ll provide a walkthrough of the entire process.

Do you have any questions or comments? Let us know in the comments section, and check back soon for the final post in this series.