CloudFormation’s DependsOn attribute, while seemingly straightforward, often trips people up because its primary purpose isn’t guaranteeing a specific creation order, but rather signaling a dependency that CloudFormation’s scheduler tries to honor.
Let’s watch this in action. Imagine you have two resources: a VPC and a subnet. You want the subnet to be created after the VPC.
Resources:
MyVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
MySubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref MyVPC
CidrBlock: 10.0.1.0/24
In this example, MySubnet implicitly depends on MyVPC because it references !Ref MyVPC. CloudFormation’s scheduler is smart enough to figure this out. It sees that MySubnet needs the VpcId from MyVPC, so it won’t even try to create MySubnet until MyVPC is successfully provisioned. This implicit dependency is the most common way dependencies are handled, and DependsOn is often only needed for more complex, non-obvious relationships.
Now, let’s consider a scenario where DependsOn becomes explicitly necessary, or at least helpful for clarity. Suppose you’re creating an IAM Role and an IAM Policy, and then attaching that policy to the role. The attachment resource definitely needs both the role and the policy to exist.
Resources:
MyRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
Path: "/"
MyPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "s3:ListBucket"
Resource: "*"
PolicyName: MyS3ListPolicy
Roles:
- !Ref MyRole
MyRolePolicyAttachment:
Type: AWS::IAM::RolePolicyAttachment
Properties:
PolicyArn: !GetAtt MyPolicy.Arn
RoleName: !Ref MyRole
DependsOn: # Explicitly state dependencies for clarity, though often implied
- MyRole
- MyPolicy
Here, MyPolicy has a Roles property that references MyRole. This creates an implicit dependency. Similarly, MyRolePolicyAttachment references both MyRole and MyPolicy. CloudFormation’s scheduler will detect these. However, explicitly adding DependsOn can make the template more readable and can be crucial in situations where the dependency isn’t directly expressed through a property reference, such as when you need one resource to be created before another resource starts a process that doesn’t directly consume the first resource’s ID.
The most common cause of DependsOn confusion is believing it forces an order when CloudFormation has other options. CloudFormation’s scheduler is designed to parallelize resource creation as much as possible. If two resources don’t have a dependency, it will try to create them at the same time. DependsOn is a signal to the scheduler: "Don’t even consider creating me until these other things are done." It’s not a command to create them in a specific sequence among themselves, but rather a prerequisite for the dependent resource.
Here are the common pitfalls and how to diagnose them:
-
Implicit Dependencies Not Recognized:
- Diagnosis: You see an error like
An error occurred (InvalidParameterValue) when calling the CreateVpc operation: The VPC ID 'vpc-xxxxxxxxxxxxxxxxx' does not exist.This means your subnet or other resource tried to use a VPC ID that CloudFormation hadn’t created yet. Check if a!Refor!GetAttin the dependent resource’s properties points to the resource that is supposed to be created first. - Fix: Ensure all cross-resource references use
!Refor!GetAttcorrectly. For example,VpcId: !Ref MyVPC. - Why it works: CloudFormation’s engine analyzes these intrinsic functions to build its dependency graph. If a resource property references another resource, it’s a direct signal of dependency.
- Diagnosis: You see an error like
-
DependsOnUsed for Ordering Siblings:- Diagnosis: You have
ResourceAandResourceB, neither directly referencing the other, but you wantResourceAto be created beforeResourceB. You addDependsOn: [ResourceA]toResourceB. You might still see them being created concurrently or in the "wrong" order if CloudFormation’s scheduler finds no other explicit or implicit dependencies. The error might be a genericInternalFailureor a resource-specific error indicating a prerequisite was missing. - Fix: If you truly need
ResourceAto finish beforeResourceBstarts, and there’s no other logical link, you must useDependsOnonResourceBto referenceResourceA. For example:Resources: ResourceA: Type: AWS::Some::Resource Properties: # ... ResourceB: Type: AWS::Another::Resource Properties: # ... DependsOn: - ResourceA - Why it works: This explicitly tells the scheduler that
ResourceAmust be in aCREATE_COMPLETEstate before it can even begin the creation process forResourceB.
- Diagnosis: You have
-
Circular Dependencies:
- Diagnosis: CloudFormation will detect this during the change set creation or stack update and throw an error like
The template is not valid. Circular dependency detected: .... - Fix: Review your
DependsOnattributes and intrinsic function references. You need to break the cycle. This often involves rethinking your resource design or using a temporary resource that doesn’t have a circular relationship. - Why it works: CloudFormation’s graph algorithm detects that
Adepends onB, andBdepends onA(directly or indirectly), meaning neither can ever be created.
- Diagnosis: CloudFormation will detect this during the change set creation or stack update and throw an error like
-
DependsOnfor Resource Deletion Order:- Diagnosis: When deleting a stack, resources that depend on another resource are deleted before the resource they depend on. If you want a specific deletion order (e.g., delete a dependent resource after its parent),
DependsOndoesn’t directly control deletion order. - Fix:
DependsOnprimarily influences creation order. For deletion order, you often need to ensure that the resource being deleted doesn’t have explicit or implicit dependencies on resources you want to keep. If you need a resource to be deleted last, you might need to remove its dependencies before initiating the stack deletion or use custom resources for complex deletion logic. - Why it works: CloudFormation’s default deletion order is the reverse of its creation order. If
Adepends onBfor creation,Bis deleted beforeA.
- Diagnosis: When deleting a stack, resources that depend on another resource are deleted before the resource they depend on. If you want a specific deletion order (e.g., delete a dependent resource after its parent),
-
DependsOnwith Custom Resources:- Diagnosis: Custom resources are often used for complex logic or to manage resources not directly supported by CloudFormation. If a custom resource’s
ServiceToken(the Lambda ARN) or other properties depend on a resource that isn’t ready, you’ll get errors from the Lambda function itself. - Fix: Ensure the custom resource definition correctly uses
DependsOnto reference any resources it needs to access or use as input. For example, if your custom resource needs to write to an S3 bucket created in the same stack:Resources: MyBucket: Type: AWS::S3::Bucket Properties: BucketName: my-unique-bucket-name MyCustomResource: Type: Custom::MyHandler Properties: ServiceToken: !GetAtt MyLambdaFunction.Arn BucketName: !Ref MyBucket # Implicit dependency DependsOn: # Can also explicitly add MyBucket - MyBucket - Why it works: Similar to AWS resources, the custom resource’s creation is gated by the completion of its dependencies, ensuring the Lambda function has access to necessary information or resources.
- Diagnosis: Custom resources are often used for complex logic or to manage resources not directly supported by CloudFormation. If a custom resource’s
-
Incorrect Resource States:
- Diagnosis: Sometimes, a resource might appear to be
CREATE_COMPLETEin the CloudFormation console, but it’s not fully functional or ready for other resources to interact with it. This is rare for native AWS resources but can happen with complex services or custom resources. Errors might be intermittent or appear only after the stack is "complete." - Fix: For critical dependencies, consider adding a "wait condition" resource or a custom resource that performs a health check or a simple operation on the dependent resource before signaling success.
- Why it works: This adds an extra layer of verification, ensuring the dependent resource is not just provisioned but also operational before proceeding.
- Diagnosis: Sometimes, a resource might appear to be
The most common next error you’ll hit after meticulously fixing DependsOn issues is a subtle timing problem where a resource appears complete but isn’t fully initialized, leading to transient errors when subsequent resources try to interact with it.