Outcome
By the end of this lesson, you will be able to work the pending and accepted queues, decide when to write a derived tag back to the cloud provider, and explain what the apply job does and what it deliberately skips.
| Tier | Engineer |
| JTBD | ”Turn derived tags into attribution today, and into real cloud tags only where that is worth doing.” |
| Personas | Platform Engineer · FinOps Lead |
| Prerequisites | M2.8.L1 · M2.8.L2 |
| Time | 9 minutes |
| Bloom verb | Work (Apply), Decide (Evaluate), Explain (Understand) |
1. Concept
There are two separate decisions here, and keeping them separate is most of the skill.
DECISION 1: ACCEPT "Should this derived value count toward cost attribution inside ZopNight?" Cheap. Reversible. Writes nothing to your cloud.
DECISION 2: APPLY TO CLOUD "Should this value also become a real tag on the real resource in AWS / GCP / Azure?" A cloud write. Reversible only by another cloud write.Most teams should do a lot of the first and very little of the second.
The two-state model
derived │ ├── value == live cloud tag ──> ACCEPTED (automatic) │ └── otherwise ──────────────────> PENDING │ accept ───┤ ▼ ACCEPTED │ revoke ───┤ ▼ PENDING
pending = derived − accepted. There is no reject state.Accept and revoke operate per key, not per resource. Revoking returns a key to pending; it does not blocklist it, and it does not remove anything from the cloud.
What accepting actually changes
ACCEPTED TAGS: ✓ count toward cost attribution in Reports → Tags ✓ carry dimension_source = "auto" (cloud tags carry "cloud"), so a report can tell you which is which ✓ move spend out of the Unattributed bucket ✗ do NOT appear in your cloud console ✗ do NOT affect your cloud provider's own cost allocation tags or your invoiceThat last pair is why accepting is cheap: attribution improves inside ZopNight, and nothing outside ZopNight changes. It is also why dimension_source matters. A tag showing as auto is one ZopNight derived; a tag showing as cloud came from the resource. When a report is challenged, that column is how you answer “where did this number come from.”
Apply to cloud
Applying writes the accepted value onto the real resource as a real tag, executed as an apply_tag remediation job. It is on demand, never automatic.
THE APPLY PATH 1. You select accepted tags and choose Apply to cloud 2. An apply_tag remediation job is created per resource 3. For each resource, the job SKIPS the write if the tag already matches the live cloud value 4. Successful writes land as real provider tags 5. The whole thing is audit-logged like any other mutation
The skip in step 3 holds on BOTH the web path and the MCPpath. There is no configuration that turns it off, because aredundant write has no upside and consumes provider API quota.When applying is worth it
APPLY WHEN BECAUSE─────────────────────────────────────────────────────────────Another tool reads the tag Your cloud-native budget, an SCP, a Tag Policy, or a third-party tool cannot see a ZopNight virtual tagThe provider's own cost Provider-side allocation tagsallocation report needs it need a real tagYou are exiting the gap for Making it real ends thegood and want IaC to own it derivation dependency
DO NOT APPLY WHEN BECAUSE─────────────────────────────────────────────────────────────You only need ZopNight reporting Accepting already did itThe policy is still being tuned You will be writing, then rewriting, real cloud tagsThe resource is IaC-managed Your next terraform apply reverts it, and now you have drift that looks like an incidentThe IaC row is the one that bites. A resource under Terraform with default_tags will have your applied tag removed on the next apply, silently, and the resource will reappear as pending. If you want the tag to persist on an IaC-managed resource, the change belongs in the IaC, not in an apply job.
Working the queue efficiently
Do not review pending resource by resource. Review by derived value.
GROUP BY VALUE, THEN DECIDE ONCE:
team = payments 184 resources -> accept team = platform 96 resources -> accept team = analytics 72 resources -> accept team = i-0a3f9c2b1d 1 resource -> leave pending team = eksctl 41 resources -> leave pending
Five decisions, 394 resources. One decision per distinctvalue is the whole review.If a value needs per-resource judgement, that is a signal the policy axis is wrong, not that you need to work harder. Go back to L2 and change the policy.
2. Demo
A quarterly Smart Tags review, and the one apply that was justified:
STATE AT REVIEW Pending 412 across 7 distinct values Accepted 1,847
STEP 1: GROUP THE PENDING QUEUE BY VALUE cost-allocation = aws-us-east-1 184 -> accept cost-allocation = aws-eu-west-1 96 -> accept cost-allocation = gcp-europe-west1 72 -> accept cost-allocation = aws-ap-south-1 60 -> leave (wrong axis for the analytics estate) team = eksctl 41 -> leave (junk) team = i-0a3f... 1 -> leave (junk) workload-class = ec2-compute 18 -> accept
Accepted: 370 resources, in 4 clicks. Unattributed: 31% -> 9%. Nothing written to any cloud account.
STEP 2: DID ANYTHING NEED TO BE REAL? One case: the finance team's AWS cost-allocation report is built provider-side, off real AWS tags, and cannot see a ZopNight virtual tag.
Applied cost-allocation to the 184 us-east-1 resources. apply_tag jobs created: 184 skipped (already matched): 31 written: 153 failed: 0
The 31 skips are the invariant working. Those resources already carried the tag; the job did not spend a write on them.
STEP 3: WHAT WAS NOT APPLIED, AND WHY The gcp-europe-west1 group is entirely Terraform-managed. Applying would have been reverted on the next plan. Raised a PR against the module's default labels instead.Step 3 is the mature move. The apply button was available and would have appeared to work.
3. Hands-on (6 min)
1. Open Smart Tags → Pending. Group by derived value. How many DISTINCT values are pending? ______ How many RESOURCES? ______ The ratio is your review cost. If distinct values is above about 20, fix the policy, not the queue.
2. Accept one value group. Then open Reports → Tags and confirm the Unattributed figure moved. Before ____% After ____%
3. Find one accepted tag and check its Source badge. Does it read `auto` or `cloud`? _________ Explain in one line why that distinction matters when someone challenges a chargeback number: ______________________________________________
4. For one value group you have NOT applied, answer: - does any tool outside ZopNight read this tag? Y / N - is the resource set IaC-managed? Y / N Apply only if the first is Y and the second is N.
5. If you apply anything, record the skip count: jobs ____ skipped ____ written ____ A high skip count is good news about your existing tagging, not a failure.Do it through MCP. The same task you just did in the console, asked in one sentence.
BEFORE A ZopNight account with one cloud connected. Enough discovered resources for predictions to be worth reviewing, and a tag convention you already use.ASK "Show me the pending tag suggestions for this account and accept the ones above 90% confidence."CHECK a sample by hand before accepting in bulk. Confidence is a prediction, not a promise.Tools behind it: list_smart_tags (read, Govern), accept_smart_tag (write, tier 1, metadata only), revoke_smart_tag (write, tier 1, metadata only). The full catalogue is at zop.dev/learn/mcp-tools.
4. Knowledge check
Q1
You accept a derived team tag on 370 resources. What changed in your AWS account?
A. Nothing
B. 370 resources now carry a new AWS tag
C. The tags are queued for a nightly sync
D. Only the AWS resources are tagged; GCP and Azure are not
Show answer
Correct: A. Accepting makes the value count toward cost attribution inside ZopNight and marks it dimension_source = auto; it writes nothing to any cloud provider. Writing to the cloud is a separate, explicit “Apply to cloud” action that runs as an apply_tag remediation job. This separation is what makes accepting cheap and reversible. Revoking an accepted tag returns it to pending and, likewise, touches nothing in the cloud.
Q2
An “Apply to cloud” run reports 184 jobs, 31 skipped, 153 written. The 31 skips mean:
A. 31 jobs failed and need retrying
B. 31 of the resources were deleted mid-run, so the provider rejected the tag write for all of them entirely
C. Those 31 resources already carried a live cloud tag matching the derived value, so the job skipped a redundant write
D. 31 resources lacked permission
Show answer
Correct: C. This is the same matching rule that auto-accepts a derived tag at derivation, and it holds on both the web and MCP apply paths. A high skip count is good news: it means your existing cloud tagging already agreed with the policy. Failures are reported separately; a skip is a success that cost no provider API quota.
Q3
A group of 72 pending resources is entirely managed by Terraform with default_tags. Applying the derived tag to the cloud would:
A. Work permanently, since ZopNight writes the tag directly to the cloud provider’s own tag store directly
B. Fail immediately with a permission error
C. Trigger a drift alert in Terraform that blocks the apply
D. Succeed now and be silently reverted on the next terraform apply, after which the resources reappear as pending
Show answer
Correct: D. The durable fix is a PR against the IaC module’s default tags; the apply button is available here and is the wrong tool. This is the trap worth remembering: the operation appears to work. Nothing warns you that the resource is IaC-managed, so the check has to be yours, and the reappearing pending entries are the only signal you will get.
5. Apply
Work the Smart Tags pending queue grouped by value, not by resource, and treat accepting as the default. Reserve Apply to cloud for the cases where something outside ZopNight genuinely needs to read the tag, and check for IaC management first.
Related lessons
- L2: Writing a tagging policy
- L4: Reconciliation and drift (next)
- T3.M3.5.L3: Tag attribution
- T5.M5.6.L1: Tag enforcement in Terraform / CDK / Pulumi
Glossary terms touched
Apply to cloud · dimension_source · apply_tag job · Revoke