How to Reference Existing AWS CloudFormation Resources in Update Scripts


3 views

When working with AWS CloudFormation in production environments, we often encounter scenarios where we need to modify specific resources without redeploying entire stacks. The technical challenge arises when your update script needs to reference existing resources that were created by separate templates.

While the Ref intrinsic function works within a single template, it cannot directly reference resources from other stacks. Here are the main approaches to solve this:

// Option 1: Using Export/Import (recommended for cross-stack references)
Outputs:
  ExistingVPC:
    Value: !Ref MyVPC
    Export:
      Name: Production-VPC-ID

// In your update template:
Parameters:
  VPCParam:
    Type: 'AWS::EC2::VPC::Id'
Resources:
  NewSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      VpcId: !Ref VPCParam

Let's examine three concrete methods with implementation details:

Method 1: Cross-Stack References with Exports

# Parent stack (original deployment)
Resources:
  MyRDSInstance:
    Type: 'AWS::RDS::DBInstance'
    Properties: {...}

Outputs:
  DBEndpoint:
    Value: !GetAtt MyRDSInstance.Endpoint.Address
    Export:
      Name: Prod-DB-Endpoint

# Update stack
Parameters:
  DBEndpoint:
    Type: String
    Default: 'Prod-DB-Endpoint'

Resources:
  NewLambda:
    Type: 'AWS::Lambda::Function'
    Properties:
      Environment:
        Variables:
          DB_HOST: !Ref DBEndpoint

Method 2: SSM Parameter Store Integration

For more flexible reference management:

# Store reference in Parameter Store (run once)
aws ssm put-parameter \\
    --name "/Prod/Network/ELB/ARN" \\
    --value "arn:aws:elasticloadbalancing:..." \\
    --type String

# Reference in template
Resources:
  NewListener:
    Type: 'AWS::ElasticLoadBalancingV2::Listener'
    Properties:
      LoadBalancerArn: '{{resolve:ssm:/Prod/Network/ELB/ARN}}'

Method 3: Custom Resource Lookup

For advanced scenarios requiring runtime resolution:

Resources:
  LookupExistingResource:
    Type: 'Custom::ResourceLookup'
    Properties:
      ServiceToken: !GetAtt LookupFunction.Arn
      ResourceType: 'AWS::S3::Bucket'
      Tags:
        Environment: 'Production'

  NewResource:
    Type: 'AWS::S3::BucketPolicy'
    Properties:
      Bucket: !GetAtt LookupExistingResource.BucketName
      PolicyDocument: {...}

When implementing these solutions, pay special attention to:

  • Export name uniqueness across your AWS account
  • IAM permissions for cross-stack operations
  • Dependency chains during stack updates
  • Cleanup of exported values when deleting stacks

AWS best practice recommends using Export/Import for most cases, as it provides:

  • Clear visibility in the CloudFormation console
  • Built-in dependency tracking
  • Validation during stack operations

When maintaining complex AWS infrastructure, we often encounter situations where we need to modify just one specific resource without redeploying the entire stack. The fundamental question is: How do we safely reference pre-existing resources that were created by separate CloudFormation templates? This becomes particularly important when following infrastructure-as-code best practices.

The most robust approach is using CloudFormation's native Export/Import functionality. Here's how it works:


# In your original template (stack-A)
Resources:
  MyExistingVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
    Outputs:
      VPCExport:
        Value: !Ref MyExistingVPC
        Export:
          Name: Production-VPC-ID

# In your update template
Parameters:
  ExistingVPC:
    Type: AWS::EC2::VPC::Id
    Default: ""

Resources:
  NewSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !ImportValue Production-VPC-ID
      GroupDescription: "SG for existing VPC"

Parameter Store Integration

For more dynamic references, consider using AWS Systems Manager Parameter Store:


# Store the value first (via CLI or template)
aws ssm put-parameter \
  --name "/CloudFormation/Production/VPC" \
  --value "vpc-123456" \
  --type String

# Reference in template
Parameters:
  VpcParam:
    Type: AWS::SSM::Parameter::Value
    Default: "/CloudFormation/Production/VPC"

Direct Physical ID Reference

For quick updates where you know the exact resource ID:


Resources:
  UpdatedBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: "my-existing-bucket-name"
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal: "*"
            Action: "s3:GetObject"
            Resource: "arn:aws:s3:::my-existing-bucket-name/*"
  • Export/Import has a 200 exports per region limit
  • Cross-region references require custom solutions
  • Always verify resource dependencies before updates
  • Consider using Change Sets for safety

Here's how to modify an existing RDS instance's parameter group:


Parameters:
  DBInstanceIdentifier:
    Description: "Existing RDS instance identifier"
    Type: String

Resources:
  NewDBParameterGroup:
    Type: AWS::RDS::DBParameterGroup
    Properties:
      Family: mysql5.7
      Parameters:
        max_connections: "100"

  DBInstanceUpdate:
    Type: AWS::RDS::DBInstance
    Properties:
      DBInstanceIdentifier: !Ref DBInstanceIdentifier
      DBParameterGroupName: !Ref NewDBParameterGroup