Help with Cross-Account Route 53 ALIAS Record Setup Using CloudFormation

0

I'm setting up a multi-account AWS environment and need assistance with configuring Route 53 ALIAS records across accounts using AWS CloudFormation.

Setup Overview: Accounts Involved: Tooling Account: Hosts the Route 53 hosted zone (accounts.example.com). Staging and Production Accounts: Where CloudFormation templates are deployed to create ALIAS records in the Tooling Account. (staging.accounts.example.com and app.accounts.example.com)

Requirements: IAM Roles: IAM roles need to be set up to allow Staging and Production accounts to assume roles in the Tooling Account for Route 53 updates. CloudFormation Templates: create ALIAS records (if possible get the Id of the Record Set) Options i am considering is using

  1. [StackSet (I would also like to know if i can return the id of the Record Set if i use StackSet )]
  2. [Templates should use a Lambda function to assume the IAM role and create ALIAS records.] Please i need it to fully automated no maual since i am using CICD for the Deployment to Staging the Prod

Example Parameters:

HostedZoneId: ID of accounts.example.com in the Tooling Account.

AliasTarget: DNS name of the environment-specific resource (e.g., CloudFront distribution).

RecordName: Domain name for the ALIAS record (e.g., staging.accounts.example.com).

I appreciate any guidance with sample code on setting up this cross-account Route 53 ALIAS record management using AWS CloudFormation. If you have examples or best practices for securely managing DNS records across AWS accounts Automatocallly, please share them. Thank you!

1 Answer
1
Accepted Answer

Hello.

I feel like StackSets probably won't be able to handle this, so how about using CloudFormation's custom resource to create an alias record in the Route53 hosted zone of the tool account with Lambda?
Lambdas created with custom resources use the IAM role in the tool account using AssumeRole.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/walkthrough-custom-resources-lambda-lookup-amiids.html
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-lambda-function-code-cfnresponsemodule.html

The following document will be helpful for Lambda's AssumeRole.
https://repost.aws/knowledge-center/lambda-function-assume-iam-role

DNS records can be created using "change_resource_record_sets()".
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/route53/client/change_resource_record_sets.html

As a custom resource, create Lambda in CloudFormation as shown below and reference it with "AWS::CloudFormation::CustomResource".

AWSTemplateFormatVersion: '2010-09-09'

Resources:
  SampleResource:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt Function.Arn

  Function:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.12
      Role: !GetAtt FunctionRole.Arn
      Handler: index.handler
      LoggingConfig:
        LogGroup: !Ref FunctionLogGroup
      Environment:
        Variables:
          CLOUDFRONT_DOMAIN: !GetAtt CloudFront.DomainName
      Code:
        ZipFile: |
          import json
          import os
          import boto3
          import cfnresponse
          def handler(event, context):
            cloudfront_domain = os.environ['CLOUDFRONT_DOMAIN']
            sts_connection = boto3.client('sts')
            tool_account = sts_connection.assume_role(
              RoleArn="arn:aws:iam::222222222222:role/role-on-tool-account",
              RoleSessionName="cross_acct_lambda"
            )
            ACCESS_KEY = tool_account['Credentials']['AccessKeyId']
            SECRET_KEY = tool_account['Credentials']['SecretAccessKey']
            SESSION_TOKEN = tool_account['Credentials']['SessionToken']
            route53 = boto3.client(
              'route53',
              aws_access_key_id=ACCESS_KEY,
              aws_secret_access_key=SECRET_KEY,
              aws_session_token=SESSION_TOKEN
            )
            route53.change_resource_record_sets(
              HostedZoneId='XXXXXXXXXXXX', # Hosted zone ID for "accounts.example.com"
              ChangeBatch={
                'Changes': [
                  {
                    'Action': 'UPSERT',
                    'ResourceRecordSet': {
                      'Name': "xxxxxx.accounts.example.com" + ".", # Domain you want to publish
                      'Type': 'A',
                      'AliasTarget': {
                        'HostedZoneId': 'Z2FDTNDATAQYW2', # When creating an alias record for CloudFront, specify "Z2FDTNDATAQYW2" for the hosted zone ID.
                        'DNSName': cloudfront_domain + ".",
                        'EvaluateTargetHealth': False
                      }
                    }
                  }
                ]
              }
            )
            cfnresponse.send(event, context, cfnresponse.SUCCESS, {})

  FunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: tool-assume
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "sts:AssumeRole"
                Resource: "arn:aws:iam::222222222222:role/role-on-tool-account"

  FunctionLogGroup:
    DeletionPolicy: Delete
    UpdateReplacePolicy: Delete
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: /aws/lambda/MyFunction

  CloudFront:
    Type: AWS::CloudFront::Distribution
    Properties:
      ......
profile picture
EXPERT
answered 12 days ago
profile picture
EXPERT
reviewed 12 days ago
  • Thanks, Riku, I will Work on it and Revert ASAP. Respect for the Effort and Dedication.

  • Thanks Riku, for the Effort Dedication and Time