Optional DependsOn with CloudFormation: Metadata to the rescue

Michael Wittig – 12 Nov 2024

When working with AWS CloudFormation, sometimes it’s necessary to incorporate optional dependencies into your template. Recently, I encountered a situation where I had to build a single CloudFormation template to manage both a VPC and an application—though, generally, I recommend separating these into distinct templates for easier management. Using a single template introduces complexity, as you must explicitly manage dependencies using the DependsOn attribute to ensure proper resource creation order.

Managing optional Dependencies in AWS CloudFormation

Here’s a simplified version of the template. This setup involves an Auto Scaling Group (ASG) that depends on VPC resources such as network ACLs, route table associations, and a VPC gateway endpoint for DynamoDB:

Resources:
NetworkAclEntryInPrivateAllowEphemeral: {}
NetworkAclEntryOutPrivateAllowHTTPS: {}
RouteTableAssociationPrivate: {}
VPCEndpointForDynamoDB: {}
AutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
DependsOn:
- NetworkAclEntryInPrivateAllowEphemeral
- NetworkAclEntryOutPrivateAllowHTTPS
- RouteTableAssociationPrivate
- VPCEndpointForDynamoDB
Properties: {}

This approach works well if all resources are mandatory. However, in my case, the VPCEndpointForDynamoDB is an optional resource that can be toggled via a parameter. Here’s how I attempted to handle this initially:

Parameters:
DynamoDB:
Type: String
Default: 'true'
AllowedValues: ['true', 'false']
Conditions:
HasDynamoDB: !Equals [!Ref DynamoDB, 'true']
Resources:
NetworkAclEntryInPrivateAllowEphemeral: {}
NetworkAclEntryOutPrivateAllowHTTPS: {}
RouteTableAssociationPrivate: {}
VPCEndpointForDynamoDB:
Type: AWS::EC2::VPCEndpoint
Condition: HasDynamoDB
Properties: {}
AutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
DependsOn:
- NetworkAclEntryInPrivateAllowEphemeral
- NetworkAclEntryOutPrivateAllowHTTPS
- RouteTableAssociationPrivate
- !If [HasDynamoDB, VPCEndpointForDynamoDB, !Ref AWS::NoValue] # Template format error: Every DependsOn value must be a string.
Properties: {}

In theory, this approach should work by conditionally referencing VPCEndpointForDynamoDB using an !If statement. However, CloudFormation doesn’t support !If conditions within the DependsOn attribute and returns a template format error: Every DependsOn value must be a string. This limitation required a workaround to manage conditional dependencies effectively.

The Metadata Workaround

A reliable workaround is to use the Metadata attribute that CloudFormation supports for attaching arbitrary data to resources. By leveraging Metadata, you can create an implicit dependency without using DependsOn. Here’s how:

Parameters:
DynamoDB:
Type: String
Default: 'true'
AllowedValues: ['true', 'false']
Conditions:
HasDynamoDB: !Equals [!Ref DynamoDB, 'true']
Resources:
NetworkAclEntryInPrivateAllowEphemeral: {}
NetworkAclEntryOutPrivateAllowHTTPS: {}
RouteTableAssociationPrivate: {}
VPCEndpointForDynamoDB:
Type: AWS::EC2::VPCEndpoint
Condition: HasDynamoDB
Properties: {}
AutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Metadata:
DynamoDB: !If [HasDynamoDB, !Ref VPCEndpointForDynamoDB, !Ref AWS::NoValue] # This is the workaround
DependsOn:
- NetworkAclEntryInPrivateAllowEphemeral
- NetworkAclEntryOutPrivateAllowHTTPS
- RouteTableAssociationPrivate
Properties: {}

In this solution, the Metadata attribute of AutoScalingGroup includes a conditional reference to VPCEndpointForDynamoDB. When HasDynamoDB is true, the !Ref VPCEndpointForDynamoDB effectively creates an implicit dependency on the optional resource. This way, if VPCEndpointForDynamoDB is toggled on, CloudFormation ensures it’s created before AutoScalingGroup.

Summary

When managing optional resources in AWS CloudFormation, the DependsOn attribute doesn’t support conditional dependencies. To work around this limitation, use the Metadata attribute to create implicit dependencies based on conditions. This approach enables you to control optional resources’ deployment order without violating CloudFormation’s syntax rules.

Michael Wittig

Michael Wittig

I’ve been building on AWS since 2012 together with my brother Andreas. We are sharing our insights into all things AWS on cloudonaut and have written the book AWS in Action. Besides that, we’re currently working on bucketAV, HyperEnv for GitHub Actions, and marbot.

Here are the contact options for feedback and questions.