skip to content

Given that Ref and Fn::GetAtt already order resource creation in an AWS CloudFormation template, when do you still need an explicit DependsOn attribute?

level: middleimportance: should knowfreq 58%

answer

  1. the graph is built from references
  2. some ordering has no reference
  3. gateway attachment is the textbook case
  4. permissions attached after the consumer starts
  5. every extra edge costs parallelism

basics

~20 s

You need DependsOn when a resource must exist before another for a reason CloudFormation cannot see because no property references it. The classic cases are an internet gateway attachment and an IAM policy the workload needs at boot but never references.

solid answer

~40 s

CloudFormation builds a dependency graph from the references in the template: any `Ref`, `Fn::GetAtt`, or `Fn::Sub` that names another resource makes that resource a prerequisite, and everything unrelated is created in parallel. `DependsOn` is the escape hatch for ordering that no reference expresses — a *behavioural* dependency. The canonical example is `AWS::EC2::VPCGatewayAttachment`: an instance references the subnet, not the attachment, so without `DependsOn` its user data can start before the VPC has internet access. The other common one is an `AWS::IAM::Policy` attached to a role: the consumer references the role, which exists before the policy is attached, so a function or instance can start unauthorized. `DependsOn` takes a logical ID or a list, and it reverses on delete. Use it sparingly — every edge you add removes parallelism and slows the stack.

code

yaml · 24 lines
yaml
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  Vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
  Igw:
    Type: AWS::EC2::InternetGateway
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref Vpc
      InternetGatewayId: !Ref Igw
  RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref Vpc
  DefaultRoute:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref Igw

go deeper

for a junior

Know that CloudFormation works out ordering from references such as Ref and Fn::GetAtt, and that DependsOn is how you state an ordering the template does not otherwise show.

for a middle

Explain that DependsOn is a resource attribute, name the internet gateway attachment and the IAM policy attachment cases, and say that unrelated resources are otherwise created in parallel.

for a senior

Show the cost side: unnecessary edges serialize a deployment, and a circular dependency is broken by extracting the mutual association into a third resource rather than by adding ordering.

for a principal

Own the review rule for a template estate — every DependsOn should name the failure it prevents, because unexplained ordering edges accumulate and quietly turn stack deployment time into a delivery constraint.

## The graph you get for free CloudFormation does not deploy resources in the order they appear in the template. It reads every intrinsic function, records which resource each one points at, and derives a directed graph. If `Instance` contains `!Ref Subnet`, then `Subnet` is created first. Resources with no edge between them are created concurrently, which is why a large stack does not take the sum of its resource creation times. Three things create an edge: - `Ref` naming another resource, - `Fn::GetAtt` naming another resource, - `Fn::Sub` whose string contains `${Logical}` or `${Logical.Attribute}`. What does **not** create an edge: sitting next to something in the file, sharing a `Condition`, referring to the same parameter, or `Fn::ImportValue` — that reads another *stack's* export and says nothing about ordering inside this one. ## Where the graph is blind The graph only knows about data flow. It cannot know that a resource must be *functional*, not merely *created*, before another starts. Two cases dominate real templates. **Internet gateway attachment.** You declare a VPC, an `AWS::EC2::InternetGateway`, an `AWS::EC2::VPCGatewayAttachment` joining them, and a route in a route table. An EC2 instance in a public subnet references the subnet and a security group — never the attachment. So CloudFormation may create the instance while the gateway is still unattached, its user data tries to reach a package repository, and the bootstrap fails intermittently. `DependsOn: AttachGateway` on the instance, or on the route, fixes it. This is the example AWS itself documents. **A policy the workload needs but does not reference.** A Lambda function or an instance profile references the *role*. An `AWS::IAM::Policy` with `Roles: [!Ref AppRole]` references the role too — so both depend on the role and neither depends on the other. The function can therefore be created, and invoked, before its permissions are attached. `DependsOn` on the consumer, naming the policy, closes that window. Other cases in the same family: a resource that needs a custom resource's side effect to have completed, or a database that must be seeded before something starts reading it. ```yaml AttachGateway: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref Vpc InternetGatewayId: !Ref Igw Web: Type: AWS::EC2::Instance DependsOn: AttachGateway ``` ## Syntax and semantics `DependsOn` is a **resource attribute**, a sibling of `Type` and `Properties`, not a property. Its value is a logical ID or a list of them. It affects create ordering, update ordering, and — reversed — delete ordering: CloudFormation deletes dependents before their dependencies, which is why a stack teardown does not race the way a hand-rolled script does. It does not make a value available. `DependsOn` is pure ordering; if you also need the other resource's ARN, you still write the reference, and once you do, the `DependsOn` is redundant. ## The cost of overusing it A `DependsOn` sprinkled defensively on every resource turns a parallel deployment into a serial one. On a stack of a hundred resources that is the difference between a few minutes and a long coffee. It also hides intent: a reader cannot tell which edges are real requirements and which are superstition. Add one only when you can name the failure it prevents. ## Circular dependencies The flip side of the graph is that a cycle is fatal — CloudFormation fails the stack with a *circular dependency between resources* error naming the logical IDs involved. Two security groups that reference each other are the classic case: group A allows traffic from B, and B allows traffic from A. The fix is not `DependsOn` — adding ordering to a cycle only makes the cycle explicit. Instead break the reference: declare the groups with no cross-references and add standalone `AWS::EC2::SecurityGroupIngress` resources that each point at both groups. The trick generalises — move the mutual association into a third resource that depends on both sides. ## What to say in an interview The crisp answer is: references create ordering automatically, `DependsOn` exists for ordering that references cannot express, the two examples everyone hits are gateway attachment and IAM policy attachment, and overuse costs parallelism. If you can add that a cycle is broken by extracting the association rather than by adding `DependsOn`, you have covered the whole topic.

  • How does DependsOn behave when the stack is deleted?
    It reverses. CloudFormation deletes dependents before the resources they depend on, so a resource with `DependsOn: AttachGateway` is removed before the attachment is. That is usually what you want, and it is one reason to express ordering declaratively rather than with scripts wrapped around the deployment.
  • Would adding DependsOn on both resources fix a circular dependency error?
    No — it makes the cycle explicit rather than removing it, and the stack still fails. You have to break one of the references: extract the mutual association into a third resource, such as standalone AWS::EC2::SecurityGroupIngress resources naming both groups, or reference a value you can construct rather than one you must read back.
  • Does Fn::ImportValue create a dependency edge?
    Not inside your stack. It reads an export published by a different stack, so it orders nothing locally — the exporting stack must simply already exist and hold that export. It does create a cross-stack coupling, which is a different constraint: the exporting stack cannot then be deleted or have that output changed while your import stands.

saying these in an interview costs you the question

  • Adding DependsOn defensively to every resource
  • Thinking DependsOn makes the other resource's values available
  • Expecting DependsOn to resolve a circular dependency error
  • Assuming template file order determines creation order
  • Believing Fn::ImportValue orders resources within the stack

context