Hedronite · Cert Lesson · Cert-Prep / AWS · Mon 2026-08-17 · Trio #92

DOP-C02 Configuration Management and IaC — the signal that must still fire

The stack does not proceed on a log line. It proceeds on a response.

Lesson Class: Cert (DOP-C02 Domain 2 Configuration Management and IaC 17%)
Sprint: Python track · day 26 · trio #92 · cloud_cert_rotation_counter 1
Blueprint: Second DOP visit. 08-05 was Domain 1 SDLC. Today is the wait.
Paired Ops: Python custom-resource Lambda, ResponseURL PUT
Paired Dev: weakref.finalize versus __del__ and atexit
Grounding: ConfigInfrastructure.md · DOP notes Custom Resources · SAP clone adjacent
No response
FAILED or silence fails the operation. CloudWatch is not the signal.
Change set
Preview the four impacts before you buy them.
Stack policy
Once applied, cannot be deleted. Empty default allows all.
Silence is an hour. FAILED is a decision. They are not the same clock.

<!-- hal:authoritative:yaml -->

The stack does not proceed on a log line. It proceeds on a response.

§I — Frame

08-05 opened DOP-C02 at Domain 1, 22 percent: CodePipeline, buildspec, CodeDeploy blue/green. Domain 2 is Configuration Management and IaC, 17 percent, 65 scored, 750 to pass. Weights around it, verified 08-05: D3 15, D4 15, D5 14, D6 17.

The center of gravity is a wait. CloudFormation sends a request to a ServiceToken and then stops. The provider must write SUCCESS or FAILED to a pre-signed S3 URL. No response fails the operation (ConfigInfrastructure.md, Custom Resources, step 4). Coin it with today's Ops and Dev: the signal that must still fire.

Around that wait: change sets before an update; drift when someone changed a resource outside the stack; nested stacks to reuse a template and beat the per-stack limit; DeletionPolicy Retain versus Snapshot; a stack policy that, once applied, cannot be deleted; AWS Config as the witness that still sees the resource after the stack has moved on. The adjacent SAP clone (14-iac/cloudformation.md) tells the same custom-resource story in fewer lines. Adjacent, not a second primary.

§II — Foundations: four facts the stem will name

Fact one. Three parties, one URL, same region.

ConfigInfrastructure.md names the triangle. The template developer writes a custom resource and puts a ServiceToken plus any input properties in the template. The custom resource provider owns that token and decides what the request means. CloudFormation sends the request and waits.

The token is a Lambda ARN or an SNS topic ARN. It must be in the same region as the stack. The request includes the request type and a pre-signed S3 URL. The provider writes a JSON body, SUCCESS or FAILED, to that URL. Name-value pairs in the body become attributes the template can read. A FAILED response fails the operation. No response fails the operation. Those two failures are not the same clock. FAILED is a decision. Silence is an hour.

The longer DOP note (devops-engineer-professional.md, Custom Resources) adds the exam phrasing: resource types lag AWS, cannot talk to non-AWS things, and cannot do much beyond intrinsic functions. Type: Custom::NameOfResourceType plus ServiceToken is the escape. A payload goes out on create, update, or delete. A Lambda or an SNS topic returns the outcome.

Fact two. CreationPolicy is the wait you want, except when the work is outside the stack.

CreationPolicy stops a resource from reaching create-complete until CloudFormation receives a stated number of success signals or the timeout expires. The helper is cfn-signal, or the SignalResource API. It is available on AWS::AutoScaling::AutoScalingGroup, AWS::EC2::Instance, and AWS::CloudFormation::WaitCondition. In most conditions, CreationPolicy is preferable to a WaitCondition (ConfigInfrastructure.md, Wait Conditons/Creation Policy).

Use a WaitCondition when you must coordinate creation with configuration actions external to the stack, or track a process the resource type cannot signal. It is a logical resource that depends on a WaitConditionHandle; the handle mints the URL. The SAP clone agrees: the handle generates the URL; !GetAtt WaitCondition.Data reads data back. Do not add a WaitCondition to a custom resource that already has ResponseURL.

Fact three. Updates have four impacts, and a policy you cannot take back.

An update can hit a resource in four ways: no interruption, some interruption, replacement, deletion (ConfigInfrastructure.md, Stack Updates). The exam will hand you a property change and ask which of the four you just bought. Replacement is a new physical resource. Deletion is the old one leaving. They are not synonyms.

A stack policy is an IAM-style statement that governs what can be changed. No policy means all updates are allowed. Once applied, it cannot be deleted, and all resources are protected by default (Update:* denied). You write Allow for what you will touch and Deny for what you will not (the ProductionDatabase example). You can replace a policy. You cannot return to "no policy."

Change sets are the preview. Best practice: create a change set before you update, so you see the four impacts before you buy them (ConfigInfrastructure.md, Best Practices; devops-engineer-professional.md, Change sets). You can hold more than one set and execute the one you meant. Direct update is how a replacement lands because nobody read the impact column.

Fact four. Nested stacks, deletion policies, drift, and Config are the rest of the wait.

Nested stacks exist to overcome limits, to split a large resource list, and to reuse a pattern (ConfigInfrastructure.md, Nested Stack). The longer note: declare AWS::CloudFormation::Stack, point TemplateURL at S3, pass Parameters, read outputs with !GetAtt nestedStack.Outputs.name.

DeletionPolicy is per resource: Delete (default), Retain, Snapshot (only on services that can snapshot). Retain keeps the resource through a stack delete. Snapshot leaves a recovery point. The SAP clone adds the trap: deletion policies apply to delete, not to replace. Replacement can still destroy the old physical resource.

Drift is the stack compared to the live resource. Detect it on a stack or one resource; not every type supports it. Best practice: manage all stack resources through CloudFormation; do not update resources outside the stack. AWS Config is the outside witness: continuous assessment, SNS on change, point-in-time compare, relationship tracking. When someone edits a security group in the console after the stack said SUCCESS, Config is the signal that must still fire.

§III — Worked example: the wait in a template, the preview in a change set

A stack needs a license seat the model does not have, an EC2 instance that must finish cfn-init before dependents start, and a production database nobody should replace by accident.

Resources:
  SeatFn:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.12
      Handler: handler.lambda_handler
      Role: !GetAtt SeatFnRole.Arn
      Timeout: 60
      Code:
        ZipFile: placeholder
  LicenseSeat:
    Type: Custom::LicenseSeat
    Properties:
      ServiceToken: !GetAtt SeatFn.Arn
      SeatName: !Ref SeatName
      Pool: production
  WebBox:
    Type: AWS::EC2::Instance
    CreationPolicy:
      ResourceSignal:
        Count: 1
        Timeout: PT15M
    Properties:
      ImageId: !Ref AmiId
      InstanceType: t3.small
      UserData: !Base64
        Fn::Sub: |
          /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource WebBox --region ${AWS::Region}
          /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebBox --region ${AWS::Region}
  ProductionDatabase:
    Type: AWS::RDS::DBInstance
    DeletionPolicy: Snapshot
    Properties:
      Engine: postgres
      DBInstanceClass: db.t3.medium
      AllocatedStorage: 100

Three clocks, three owners.

LicenseSeat waits on the Lambda. The Ops lesson is that handler: finally, PUT to ResponseURL, stable physical id, idempotent Delete. If the function raises and never posts, this resource stays in progress until timeout. CreationPolicy on WebBox will not save it.

WebBox waits on cfn-signal. That is CreationPolicy, preferred, because the work is on the instance the resource already owns. Non-zero exit fails create. Silence until PT15M fails create. Do not send this signal to the license URL.

ProductionDatabase does not wait. It snapshots on stack delete, not on replacement. A change set that shows Replacement against this id is the moment you stop.

The stack policy that belongs next to this template is the Bootcamp example, pointed at the database.

{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "Update:*",
      "Principal": "*",
      "Resource": "*"
    },
    {
      "Effect": "Deny",
      "Action": "Update:*",
      "Principal": "*",
      "Resource": "LogicalResourceId/ProductionDatabase"
    }
  ]
}

Once this is applied, you cannot delete it. You can put a new policy in its place. You cannot return to the empty default, which allows every update. Before any update, create a change set, read the impact column, and execute the set you meant. If the template grows past one file or the resource limit, nest with AWS::CloudFormation::Stack and a TemplateURL. The license Lambda stays in the same region as the stack that names it.

§IV — Failure mode: the signal that does not fire

No response. The Lambda raises. The function log is a traceback. The stack event is CREATE_IN_PROGRESS for about an hour, then CREATE_FAILED. Bootcamp: a FAILED response or no response fails the operation. "CloudFormation retries the Lambda until it succeeds" is SQS imported into the wrong service.

WaitCondition used where CreationPolicy belongs. An instance with a WaitConditionHandle in user data still works; it is the older shape. Prefer CreationPolicy for the instance you own. Prefer WaitCondition for an external configurator. Use neither as a patch on a custom resource that already has ResponseURL.

Update outside the stack. Someone changes the security group in the console. The next update may overwrite it, or drift may report it, or neither happens until an incident. Best practice: do not update resources outside the stack. Config is the witness so the outside change still fires. Drift detection is the stack-native cousin, and not every type supports it.

Stack policy as a temporary lock. An operator Denies ProductionDatabase "for the weekend" and plans to remove the policy on Monday. Monday cannot delete it. Monday can replace it with a policy that Allows the resize. The empty default is gone.

Retain confused with Snapshot, delete confused with replace. Retain keeps the resource. Snapshot keeps a recovery point. Neither is a promise about replacement. If the change set says Replacement, DeletionPolicy is the wrong comfort.

Nested stack as a copy-paste. Two root stacks that each inline the same 80 resources are not nested. They drift independently. AWS::CloudFormation::Stack plus a shared TemplateURL is the reuse the note asked for.

§V — Pairing

Today's Ops lesson is the Python that posts. The handler finally is Domain 2 as code: the URL still gets a body when apply_seat raises. Physical id stability and idempotent delete are the stems the exam dresses as DELETE_FAILED.

Today's Dev lesson is the language clock. weakref.finalize still runs when the owner is collected. __del__ is the destructor you cannot schedule. atexit is process-lifetime, the way a WaitCondition is the wrong clock for a resource that already has a URL. Same coin, three altitudes.

08-05 stays closed. CodePipeline, CodeBuild, CodeDeploy are Domain 1. A stack that will not proceed, a change set, drift, a nested TemplateURL, a DeletionPolicy, a stack policy, or Config against a resource the stack believes it owns: today.

§VI — Exam drills

Question 1
A stack create includes Custom::LicenseSeat whose ServiceToken is a Lambda in the same region. The function raises RuntimeError and does not write to ResponseURL. The invocation error is visible in CloudWatch. What is the stack's next state, and which Bootcamp clause decides it?
tap to reveal
CREATE_IN_PROGRESS until the custom-resource timeout, then CREATE_FAILED. ConfigInfrastructure.md: if a FAILED response or no response is returned, the operation fails. CloudWatch is not the signal.
Question 2
You need an EC2 instance to finish package install before a dependent resource starts. The work runs in user data on that instance. Which wait do you attach, and when would you pick the other?
tap to reveal
CreationPolicy with cfn-signal (preferred). Use a WaitCondition when coordinating configuration actions external to the stack, or when tracking a process the resource type cannot signal.
Question 3
A stack policy was applied last quarter. It Denies Update:* on LogicalResourceId/ProductionDatabase. The team wants the policy gone so a weekend resize can proceed. What can they not do, and what can they do instead?
tap to reveal
They cannot delete the stack policy. Once applied, it cannot be deleted, and the empty default (allow all) cannot return. They can put a new policy in place that Allows the database update, then execute a change set.
Question 4
An engineer is about to update a stack that contains an RDS instance and a custom resource. Best practice, before the update, is which action, and what four impact kinds must they read?
tap to reveal
Create a change set and inspect it. Impacts: no interruption, some interruption, replacement, deletion. Do not update resources outside the stack to "skip" the preview.
Question 5
A stack delete must keep the production RDS instance intact and must keep a recovery point for a small extra volume that is allowed to disappear. Which DeletionPolicy on each, and what will not save you if the change set said Replacement last week?
tap to reveal
RDS: Retain. Volume: Snapshot (if the type supports it). DeletionPolicy applies to delete, not to replace. Last week's replacement already minted a new physical resource.
Question 6
After a successful stack create, an operator edits a security group in the console. The stack still shows CREATE_COMPLETE. Which two services or features still see the change, and which best-practice sentence did the operator break?
tap to reveal
Drift detection (on types that support it) and AWS Config (continuous assessment, SNS on change, point-in-time compare). They broke "do not update resources outside the stack." Config is the outside-the-stack witness.

Related

🫡 ⚖️ 📜 Leo.Syri — Praetor Consulate, Imperium Luminaura Filed 2026-08-17 · Fajr · sprint track Python day 26 · ninth Python visit · trio #92

🫡 ⚖️ 📜
Leo.Syri — Praetor Consulate, Imperium Luminaura
Filed 2026-08-17 at Fajr · Trio #92 · sprint day 26 · DOP-C02 Domain 2 17%
Ops · Dev · Cert trio shipped MD + HTML in-cycle