In an AWS CloudFormation template, how do you make a whole resource and a single property exist only in production, and what happens to that resource when the condition later evaluates to false?
answer
- a named boolean over parameters
- attach it to a resource or an output
- a pseudo parameter meaning omit entirely
- re-evaluated on every single update
- false now means gone, not ignored
basics
~20 sDeclare a named condition in the Conditions section, attach it to a resource with the Condition attribute, and use Fn::If with AWS::NoValue to include or omit a single property. If an existing resource's condition turns false on update, CloudFormation deletes that resource.
solid answer
~40 sThe `Conditions` section names boolean expressions built from `Fn::Equals`, `Fn::And`, `Fn::Or` and `Fn::Not` over parameter values, mappings and pseudo parameters — for example `IsProd: !Equals [!Ref EnvType, prod]`. A resource, and an output, can then carry a `Condition:` attribute naming one, and it is created only when that condition is true. For a single property you use `Fn::If`, and the pseudo parameter `AWS::NoValue` as the branch meaning *omit this property entirely*, which is different from setting it to null or an empty string. The sharp edge is on update: conditions are re-evaluated every deployment, so flipping the parameter from `prod` to `dev` does not merely stop managing the resource — CloudFormation deletes it. Conditions are evaluated before any resource exists, so they cannot read resource attributes with `Fn::GetAtt`.
code
yaml · 26 linesAWSTemplateFormatVersion: '2010-09-09'
Parameters:
EnvType:
Type: String
AllowedValues: [dev, prod]
Default: dev
Conditions:
IsProd: !Equals [!Ref EnvType, prod]
Resources:
Data:
Type: AWS::S3::Bucket
Properties:
VersioningConfiguration:
Status: !If [IsProd, Enabled, Suspended]
AuditLogs:
Type: AWS::S3::Bucket
Condition: IsProd
Properties:
LifecycleConfiguration:
Rules:
- Status: Enabled
ExpirationInDays: !If [IsProd, 365, !Ref 'AWS::NoValue']
Outputs:
AuditBucket:
Condition: IsProd
Value: !Ref AuditLogsgo deeper
Know that the Conditions section names true/false expressions over parameters, and that a resource carrying a Condition attribute is created only when that expression is true.
Show both granularities — Condition on a resource, Fn::If with AWS::NoValue on a property — and explain that AWS::NoValue omits the property rather than blanking it.
Lead with the update behaviour: conditions are re-evaluated every deployment, so a flipped parameter deletes the resource, and say how you protect stateful resources and review the change set before executing.
Own the boundary between one parameterized template and per-environment templates, and argue when condition sprawl has made production unreviewable and mappings or separate stacks are the cheaper structure.
## The section and what may go in it `Conditions` is a top-level section, a map of names to boolean expressions: ```yaml Parameters: EnvType: Type: String AllowedValues: [dev, staging, prod] Conditions: IsProd: !Equals [!Ref EnvType, prod] IsNotDev: !Not [!Equals [!Ref EnvType, dev]] ProdInIreland: !And - !Condition IsProd - !Equals [!Ref 'AWS::Region', eu-west-1] ``` The available functions are `Fn::Equals`, `Fn::And`, `Fn::Or`, `Fn::Not`, `Fn::If` and `Condition` (referencing another condition by name). Inputs may be parameter values via `Ref`, pseudo parameters, mapping lookups via `Fn::FindInMap`, and other conditions. What they may **not** reference is a resource. Conditions are evaluated at the start of a create or update, before anything is provisioned, so `Fn::GetAtt` on a resource is meaningless here and is rejected. If your branch depends on something only knowable after a resource exists, conditions are the wrong tool — that is a custom resource, or a value supplied by the pipeline. ## Two granularities **Whole resource.** `Condition` is a resource attribute alongside `Type` and `Properties`: ```yaml ReadReplica: Type: AWS::RDS::DBInstance Condition: IsProd Properties: ... ``` Outputs take the same attribute, which matters because an output referencing a conditional resource must itself be conditional or the stack fails when the resource is absent. **Single property.** `Fn::If` chooses between two values inline, and `AWS::NoValue` is the branch that removes the property: ```yaml MultiAZ: !If [IsProd, true, false] BackupRetentionPeriod: !If [IsProd, 30, !Ref 'AWS::NoValue'] ``` Omitting a property is genuinely different from setting it to a default, because omission lets the service apply its own default — and for some properties, supplying a value at all changes update behaviour. `AWS::NoValue` is only meaningful inside `Fn::If`; it is not a general-purpose empty value. `Fn::If` nests, which is how you get more than two branches, though a three-deep nest is usually the signal that a `Mappings` lookup keyed by environment would read better. ## The dangerous part: conditions are re-evaluated on every update A condition is not a create-time switch that freezes. Every stack update recomputes it. If `ReadReplica` was created because `EnvType` was `prod`, and someone updates the stack with `EnvType=dev`, the resource is no longer part of the desired template — so CloudFormation **deletes it**. The same effect follows from editing the condition expression, or flipping the branch of an `Fn::If` that guarded whether a resource was declared. For a read replica that is inconvenient. For a database, a bucket that holds data, or anything with state, it is an incident. The mitigations are the ones you would expect: read the change set before executing it, because a conditional resource going away shows up there as a removal; and put a retention policy on stateful resources so an accidental removal does not destroy the data. ## When conditions are the wrong answer Conditions are seductive because one template feels DRY. Past a handful of them the template becomes hard to read — you can no longer tell what production looks like without mentally evaluating a dozen booleans, and no environment is exercised by testing another. Common alternatives: - **Mappings keyed by environment** for values that merely differ (instance size, retention days, AMI per region). `!FindInMap [EnvConfig, !Ref EnvType, InstanceType]` is far more readable than three nested `Fn::If`s. - **Separate templates**, or the same template deployed with different parameter files, when environments differ *structurally* rather than in a few settings. The rule of thumb interviewers like: conditions for a small number of genuine structural differences; mappings for values; separate templates when the branching starts to dominate the file. ## Summary shape of the answer Name the section, name the boolean functions, show `Condition:` on a resource and `Fn::If` with `AWS::NoValue` on a property, and then volunteer the update behaviour — that a condition turning false deletes the resource. That last point is what separates someone who has read the docs from someone who has operated a stack.
- How is Fn::If with AWS::NoValue different from setting the property to an empty string?AWS::NoValue removes the property from the request entirely, so the service applies its own default and the property is genuinely unset. An empty string is a supplied value — it may fail validation, override a default you wanted, or count as a change that forces replacement on a later update.
- Can a condition depend on whether a resource already exists in the stack?No. Conditions are evaluated before any resource is created or updated, so they can read only parameter values, mappings, pseudo parameters and other conditions — never Fn::GetAtt on a resource. Branching on live state needs a value supplied from outside, such as a pipeline-provided parameter or a custom resource.
- When would you prefer Mappings over another condition?When the difference is a value rather than a structure. `!FindInMap [EnvConfig, !Ref EnvType, InstanceType]` expresses per-environment sizing, retention or AMI IDs in one readable table, whereas nested Fn::If chains hide it. Reserve conditions for resources that genuinely exist in some environments and not others.
saying these in an interview costs you the question
- Thinking a condition turning false just stops managing the resource
- Using AWS::NoValue outside Fn::If as a generic empty value
- Trying to base a condition on Fn::GetAtt of a resource
- Referencing a conditional resource from an unconditional output
- Branching an entire multi-environment template on a dozen conditions