@intentius/chant-lexicon-aws 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/integrity.json +25 -10
  2. package/dist/manifest.json +1 -1
  3. package/dist/meta.json +5743 -896
  4. package/dist/rules/cf-refs.ts +99 -0
  5. package/dist/rules/ext001.ts +30 -21
  6. package/dist/rules/hardcoded-region.ts +1 -0
  7. package/dist/rules/iam-wildcard.ts +1 -0
  8. package/dist/rules/s3-encryption.ts +1 -0
  9. package/dist/rules/waw016.ts +86 -0
  10. package/dist/rules/waw017.ts +53 -0
  11. package/dist/rules/waw018.ts +71 -0
  12. package/dist/rules/waw019.ts +82 -0
  13. package/dist/rules/waw020.ts +64 -0
  14. package/dist/rules/waw021.ts +53 -0
  15. package/dist/rules/waw022.ts +43 -0
  16. package/dist/rules/waw023.ts +47 -0
  17. package/dist/rules/waw024.ts +54 -0
  18. package/dist/rules/waw025.ts +43 -0
  19. package/dist/rules/waw026.ts +46 -0
  20. package/dist/rules/waw027.ts +50 -0
  21. package/dist/rules/waw028.ts +47 -0
  22. package/dist/rules/waw029.ts +62 -0
  23. package/dist/rules/waw030.ts +246 -0
  24. package/dist/skills/chant-aws.md +388 -30
  25. package/dist/types/index.d.ts +1552 -1528
  26. package/package.json +2 -2
  27. package/src/actions/actions.test.ts +75 -0
  28. package/src/actions/dynamodb.ts +36 -0
  29. package/src/actions/ecr.ts +9 -0
  30. package/src/actions/ecs.ts +5 -0
  31. package/src/actions/iam.ts +3 -0
  32. package/src/actions/index.ts +9 -0
  33. package/src/actions/lambda.ts +11 -0
  34. package/src/actions/logs.ts +4 -0
  35. package/src/actions/s3.ts +34 -0
  36. package/src/actions/sns.ts +5 -0
  37. package/src/actions/sqs.ts +15 -0
  38. package/src/codegen/__snapshots__/snapshot.test.ts.snap +2 -2
  39. package/src/codegen/docs-links.test.ts +143 -0
  40. package/src/codegen/docs.ts +247 -132
  41. package/src/codegen/generate-lexicon.ts +8 -0
  42. package/src/codegen/generate-typescript.ts +25 -1
  43. package/src/composites/composites.test.ts +442 -0
  44. package/src/composites/fargate-alb.ts +253 -0
  45. package/src/composites/index.ts +20 -0
  46. package/src/composites/lambda-api.ts +20 -0
  47. package/src/composites/lambda-dynamodb.ts +64 -0
  48. package/src/composites/lambda-eventbridge.ts +36 -0
  49. package/src/composites/lambda-function.ts +76 -0
  50. package/src/composites/lambda-s3.ts +72 -0
  51. package/src/composites/lambda-sns.ts +30 -0
  52. package/src/composites/lambda-sqs.ts +44 -0
  53. package/src/composites/scheduled-lambda.ts +37 -0
  54. package/src/composites/vpc-default.ts +148 -0
  55. package/src/default-tags.test.ts +38 -0
  56. package/src/default-tags.ts +77 -0
  57. package/src/generated/index.d.ts +1552 -1528
  58. package/src/generated/lexicon-aws.json +5743 -896
  59. package/src/import/roundtrip-fixtures.test.ts +1 -1
  60. package/src/index.ts +21 -0
  61. package/src/integration.test.ts +71 -0
  62. package/src/intrinsics.ts +24 -13
  63. package/src/lint/post-synth/cf-refs.ts +99 -0
  64. package/src/lint/post-synth/ext001.test.ts +214 -31
  65. package/src/lint/post-synth/ext001.ts +30 -21
  66. package/src/lint/post-synth/waw013.test.ts +120 -0
  67. package/src/lint/post-synth/waw014.test.ts +121 -0
  68. package/src/lint/post-synth/waw015.test.ts +147 -0
  69. package/src/lint/post-synth/waw016.test.ts +141 -0
  70. package/src/lint/post-synth/waw016.ts +86 -0
  71. package/src/lint/post-synth/waw017.test.ts +130 -0
  72. package/src/lint/post-synth/waw017.ts +53 -0
  73. package/src/lint/post-synth/waw018.test.ts +109 -0
  74. package/src/lint/post-synth/waw018.ts +71 -0
  75. package/src/lint/post-synth/waw019.test.ts +138 -0
  76. package/src/lint/post-synth/waw019.ts +82 -0
  77. package/src/lint/post-synth/waw020.test.ts +125 -0
  78. package/src/lint/post-synth/waw020.ts +64 -0
  79. package/src/lint/post-synth/waw021.test.ts +81 -0
  80. package/src/lint/post-synth/waw021.ts +53 -0
  81. package/src/lint/post-synth/waw022.test.ts +54 -0
  82. package/src/lint/post-synth/waw022.ts +43 -0
  83. package/src/lint/post-synth/waw023.test.ts +53 -0
  84. package/src/lint/post-synth/waw023.ts +47 -0
  85. package/src/lint/post-synth/waw024.test.ts +64 -0
  86. package/src/lint/post-synth/waw024.ts +54 -0
  87. package/src/lint/post-synth/waw025.test.ts +42 -0
  88. package/src/lint/post-synth/waw025.ts +43 -0
  89. package/src/lint/post-synth/waw026.test.ts +54 -0
  90. package/src/lint/post-synth/waw026.ts +46 -0
  91. package/src/lint/post-synth/waw027.test.ts +63 -0
  92. package/src/lint/post-synth/waw027.ts +50 -0
  93. package/src/lint/post-synth/waw028.test.ts +68 -0
  94. package/src/lint/post-synth/waw028.ts +47 -0
  95. package/src/lint/post-synth/waw029.test.ts +179 -0
  96. package/src/lint/post-synth/waw029.ts +62 -0
  97. package/src/lint/post-synth/waw030.test.ts +800 -0
  98. package/src/lint/post-synth/waw030.ts +246 -0
  99. package/src/lint/rules/hardcoded-region.ts +1 -0
  100. package/src/lint/rules/iam-wildcard.ts +1 -0
  101. package/src/lint/rules/s3-encryption.ts +1 -0
  102. package/src/lsp/hover.ts +15 -0
  103. package/src/nested-stack-integration.test.ts +100 -0
  104. package/src/nested-stack.ts +1 -1
  105. package/src/plugin.ts +468 -36
  106. package/src/serializer.test.ts +330 -2
  107. package/src/serializer.ts +62 -1
  108. package/src/spec/fetch.ts +10 -0
  109. package/src/spec/parse.test.ts +141 -0
  110. package/src/spec/parse.ts +40 -0
  111. package/src/taggable.ts +44 -0
  112. package/src/testdata/nested-stacks/app.ts +26 -0
  113. package/src/testdata/nested-stacks/network/outputs.ts +17 -0
  114. package/src/testdata/nested-stacks/network/security.ts +17 -0
  115. package/src/testdata/nested-stacks/network/vpc.ts +54 -0
package/src/plugin.ts CHANGED
@@ -54,8 +54,8 @@ export const awsPlugin: LexiconPlugin = {
54
54
  ];
55
55
  },
56
56
 
57
- initTemplates(): Record<string, string> {
58
- return {
57
+ initTemplates() {
58
+ return { src: {
59
59
  "config.ts": `/**
60
60
  * Shared bucket configuration — encryption, versioning, public access
61
61
  */
@@ -106,6 +106,9 @@ export const dataBucket = new Bucket({
106
106
  `,
107
107
  "logs-bucket.ts": `/**
108
108
  * Logs bucket — log delivery with encryption and versioning
109
+ *
110
+ * Note: AccessControl is a legacy property. Use a bucket policy to grant
111
+ * log delivery access instead (s3:PutObject permission for the logging service principal).
109
112
  */
110
113
 
111
114
  import { Bucket, Sub, AWS } from "@intentius/chant-lexicon-aws";
@@ -113,13 +116,12 @@ import { versioningEnabled, bucketEncryption, publicAccessBlock } from "./config
113
116
 
114
117
  export const logsBucket = new Bucket({
115
118
  BucketName: Sub\`\${AWS.StackName}-\${AWS.AccountId}-logs\`,
116
- AccessControl: "LogDeliveryWrite",
117
119
  VersioningConfiguration: versioningEnabled,
118
120
  BucketEncryption: bucketEncryption,
119
121
  PublicAccessBlockConfiguration: publicAccessBlock,
120
122
  });
121
123
  `,
122
- };
124
+ } };
123
125
  },
124
126
 
125
127
  detectTemplate(data: unknown): boolean {
@@ -163,7 +165,26 @@ export const logsBucket = new Bucket({
163
165
  const { waw013 } = require("./lint/post-synth/waw013");
164
166
  const { waw014 } = require("./lint/post-synth/waw014");
165
167
  const { waw015 } = require("./lint/post-synth/waw015");
166
- return [waw010, waw011, cor020, ext001, waw013, waw014, waw015];
168
+ const { waw016 } = require("./lint/post-synth/waw016");
169
+ const { waw017 } = require("./lint/post-synth/waw017");
170
+ const { waw018 } = require("./lint/post-synth/waw018");
171
+ const { waw019 } = require("./lint/post-synth/waw019");
172
+ const { waw020 } = require("./lint/post-synth/waw020");
173
+ const { waw021 } = require("./lint/post-synth/waw021");
174
+ const { waw022 } = require("./lint/post-synth/waw022");
175
+ const { waw023 } = require("./lint/post-synth/waw023");
176
+ const { waw024 } = require("./lint/post-synth/waw024");
177
+ const { waw025 } = require("./lint/post-synth/waw025");
178
+ const { waw026 } = require("./lint/post-synth/waw026");
179
+ const { waw027 } = require("./lint/post-synth/waw027");
180
+ const { waw028 } = require("./lint/post-synth/waw028");
181
+ const { waw029 } = require("./lint/post-synth/waw029");
182
+ const { waw030 } = require("./lint/post-synth/waw030");
183
+ return [
184
+ waw010, waw011, cor020, ext001, waw013, waw014, waw015, waw016, waw017,
185
+ waw018, waw019, waw020, waw021, waw022, waw023, waw024, waw025,
186
+ waw026, waw027, waw028, waw029, waw030,
187
+ ];
167
188
  },
168
189
 
169
190
  async generate(options?: { verbose?: boolean }): Promise<void> {
@@ -254,83 +275,454 @@ export const logsBucket = new Bucket({
254
275
  return [
255
276
  {
256
277
  name: "chant-aws",
257
- description: "AWS CloudFormation template management workflows, patterns, and troubleshooting",
278
+ description: "AWS CloudFormation lifecyclebuild, diff, deploy, rollback, and troubleshoot from a chant project",
258
279
  content: `---
259
280
  skill: chant-aws
260
281
  description: Build, validate, and deploy CloudFormation templates from a chant project
261
282
  user-invocable: true
262
283
  ---
263
284
 
264
- # Deploying CloudFormation from Chant
285
+ # AWS CloudFormation Operational Playbook
286
+
287
+ ## How chant and CloudFormation relate
288
+
289
+ chant is a **synthesis-only** tool — it compiles TypeScript source files into CloudFormation JSON (or YAML). chant does NOT call AWS APIs. Your job as an agent is to bridge the two:
290
+
291
+ - Use **chant** for: build, lint, diff (local template comparison)
292
+ - Use **AWS CLI** for: validate-template, deploy, change sets, rollback, drift detection, and all stack operations
265
293
 
266
- This project defines CloudFormation resources as TypeScript in \`src/\`. Use these steps to build, validate, and deploy.
294
+ The source of truth for infrastructure is the TypeScript in \`src/\`. The generated template (\`stack.json\`) is an intermediate artifact.
267
295
 
268
- ## Build the template
296
+ ## Build and validate
297
+
298
+ ### Build the template
269
299
 
270
300
  \`\`\`bash
271
301
  chant build src/ --output stack.json
272
302
  \`\`\`
273
303
 
274
- ## Validate before deploying
304
+ Options:
305
+ - \`--format yaml\` — emit YAML instead of JSON
306
+ - \`--watch\` — rebuild on source changes
307
+
308
+ ### Lint the source
275
309
 
276
310
  \`\`\`bash
277
311
  chant lint src/
312
+ \`\`\`
313
+
314
+ Options:
315
+ - \`--fix\` — auto-fix violations where possible
316
+ - \`--format sarif\` — SARIF output for CI integration
317
+ - \`--watch\` — re-lint on changes
318
+
319
+ ### Validate with CloudFormation
320
+
321
+ \`\`\`bash
278
322
  aws cloudformation validate-template --template-body file://stack.json
279
323
  \`\`\`
280
324
 
281
- ## Deploy a new stack
325
+ ### What each step catches
326
+
327
+ | Step | Catches | When to run |
328
+ |------|---------|-------------|
329
+ | \`chant lint\` | Best-practice violations, security anti-patterns, naming issues | Every edit |
330
+ | \`chant build\` | TypeScript errors, missing properties, type mismatches | Before deploy |
331
+ | \`validate-template\` | CloudFormation schema errors, invalid intrinsic usage | Before deploy |
332
+
333
+ Always run all three before deploying. Lint catches things validate-template cannot (and vice versa).
334
+
335
+ ## Diffing and change preview
336
+
337
+ This is the most critical section for production safety. **Never deploy to production without previewing changes.**
338
+
339
+ ### Local diff
340
+
341
+ Compare your proposed template against what is currently deployed:
342
+
343
+ \`\`\`bash
344
+ # Get the currently deployed template
345
+ aws cloudformation get-template --stack-name <stack-name> --query TemplateBody --output json > deployed.json
346
+
347
+ # Build the proposed template
348
+ chant build src/ --output proposed.json
349
+
350
+ # Diff them
351
+ diff deployed.json proposed.json
352
+ \`\`\`
353
+
354
+ ### Change sets (recommended for production)
355
+
356
+ Change sets let you preview exactly what CloudFormation will do before it does it.
357
+
358
+ \`\`\`bash
359
+ # 1. Create the change set
360
+ aws cloudformation create-change-set \\
361
+ --stack-name <stack-name> \\
362
+ --template-body file://stack.json \\
363
+ --change-set-name review-$(date +%s) \\
364
+ --capabilities CAPABILITY_NAMED_IAM
365
+
366
+ # 2. Wait for it to compute
367
+ aws cloudformation wait change-set-create-complete \\
368
+ --stack-name <stack-name> \\
369
+ --change-set-name review-<id>
370
+
371
+ # 3. Review the changes
372
+ aws cloudformation describe-change-set \\
373
+ --stack-name <stack-name> \\
374
+ --change-set-name review-<id>
375
+
376
+ # 4a. Execute if changes look safe
377
+ aws cloudformation execute-change-set \\
378
+ --stack-name <stack-name> \\
379
+ --change-set-name review-<id>
380
+
381
+ # 4b. Or delete if you want to abort
382
+ aws cloudformation delete-change-set \\
383
+ --stack-name <stack-name> \\
384
+ --change-set-name review-<id>
385
+ \`\`\`
386
+
387
+ ### Interpreting change set results
388
+
389
+ Each resource change has an **Action** and a **Replacement** value. Read them together:
390
+
391
+ | Action | Replacement | Risk | Meaning |
392
+ |--------|-------------|------|---------|
393
+ | Add | — | Low | New resource will be created |
394
+ | Modify | False | Low | In-place update, no disruption |
395
+ | Modify | Conditional | **MEDIUM** | May replace depending on property — investigate further |
396
+ | Modify | True | **HIGH** | Resource will be DESTROYED and recreated — **data loss risk** |
397
+ | Remove | — | **HIGH** | Resource will be deleted |
398
+
399
+ ### Properties that always cause replacement
400
+
401
+ These property changes ALWAYS destroy and recreate the resource:
402
+ - \`BucketName\` on S3 buckets
403
+ - \`TableName\` on DynamoDB tables
404
+ - \`DBInstanceIdentifier\` on RDS instances
405
+ - \`FunctionName\` on Lambda functions
406
+ - \`CidrBlock\` on VPCs and subnets
407
+ - \`ClusterIdentifier\` on Redshift clusters
408
+ - \`DomainName\` on Elasticsearch/OpenSearch domains
409
+ - \`TopicName\` on SNS topics
410
+ - \`QueueName\` on SQS queues
411
+
412
+ **CRITICAL**: When you see \`Replacement: True\` on any stateful resource (databases, S3 buckets, queues with messages, DynamoDB tables), ALWAYS flag this to the user and get explicit confirmation before executing. This will destroy the existing resource and all its data.
413
+
414
+ ## Deploying a new stack
415
+
416
+ \`\`\`bash
417
+ aws cloudformation deploy \\
418
+ --template-file stack.json \\
419
+ --stack-name <stack-name> \\
420
+ --capabilities CAPABILITY_NAMED_IAM \\
421
+ --parameter-overrides Env=prod Version=1.0 \\
422
+ --tags Project=myapp Environment=prod
423
+ \`\`\`
424
+
425
+ ### Capabilities
426
+
427
+ | Capability | When needed |
428
+ |------------|-------------|
429
+ | \`CAPABILITY_IAM\` | Template creates IAM resources with auto-generated names |
430
+ | \`CAPABILITY_NAMED_IAM\` | Template creates IAM resources with custom names (use this by default — it's a superset) |
431
+ | \`CAPABILITY_AUTO_EXPAND\` | Template uses macros or nested stacks with transforms |
432
+
433
+ **Recommendation**: Default to \`CAPABILITY_NAMED_IAM\` unless the template also uses macros, in which case use \`--capabilities CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND\`.
434
+
435
+ ### Monitoring deployment
436
+
437
+ \`\`\`bash
438
+ # Wait for completion (blocks until done)
439
+ aws cloudformation wait stack-create-complete --stack-name <stack-name>
440
+
441
+ # Or poll events in real-time
442
+ watch -n 5 "aws cloudformation describe-stack-events --stack-name <stack-name> --max-items 10 --query 'StackEvents[].{Time:Timestamp,Resource:LogicalResourceId,Status:ResourceStatus,Reason:ResourceStatusReason}' --output table"
443
+ \`\`\`
444
+
445
+ ### Getting outputs
446
+
447
+ \`\`\`bash
448
+ aws cloudformation describe-stacks \\
449
+ --stack-name <stack-name> \\
450
+ --query 'Stacks[0].Outputs'
451
+ \`\`\`
452
+
453
+ ## Updating an existing stack
454
+
455
+ ### Safe path — change set workflow (production / stateful stacks)
456
+
457
+ 1. Build: \`chant build src/ --output stack.json\`
458
+ 2. Create change set (see Diffing section above)
459
+ 3. Review every resource change — pay special attention to Replacement values
460
+ 4. Get user confirmation for any destructive changes
461
+ 5. Execute the change set
462
+ 6. Monitor: \`aws cloudformation wait stack-update-complete --stack-name <stack-name>\`
463
+
464
+ ### Fast path — direct deploy (dev / stateless stacks)
282
465
 
283
466
  \`\`\`bash
284
467
  aws cloudformation deploy \\
285
468
  --template-file stack.json \\
286
469
  --stack-name <stack-name> \\
287
- --capabilities CAPABILITY_NAMED_IAM
470
+ --capabilities CAPABILITY_NAMED_IAM \\
471
+ --no-fail-on-empty-changeset
472
+ \`\`\`
473
+
474
+ The \`--no-fail-on-empty-changeset\` flag prevents a non-zero exit code when there are no changes (useful in CI).
475
+
476
+ ### Updating parameters only (no template change)
477
+
478
+ \`\`\`bash
479
+ aws cloudformation deploy \\
480
+ --stack-name <stack-name> \\
481
+ --use-previous-template \\
482
+ --capabilities CAPABILITY_NAMED_IAM \\
483
+ --parameter-overrides Env=staging
288
484
  \`\`\`
289
485
 
290
- Add \`--parameter-overrides Key=Value\` if the template has parameters.
486
+ ### Which path to use
291
487
 
292
- ## Update an existing stack
488
+ | Scenario | Path |
489
+ |----------|------|
490
+ | Production stack with databases/storage | Safe path (change set) |
491
+ | Any stack with \`Replacement: True\` changes | Safe path (change set) |
492
+ | Dev/test stack, stateless resources only | Fast path (direct deploy) |
493
+ | CI/CD automated pipeline with approval gate | Safe path (change set with manual approval) |
494
+ | Parameter-only change, no template diff | Fast path with \`--use-previous-template\` |
293
495
 
294
- 1. Edit the TypeScript source
295
- 2. Rebuild: \`chant build src/ --output stack.json\`
296
- 3. Preview changes:
297
- \`\`\`bash
298
- aws cloudformation create-change-set \\
299
- --stack-name <stack-name> \\
300
- --template-body file://stack.json \\
301
- --change-set-name update-$(date +%s) \\
302
- --capabilities CAPABILITY_NAMED_IAM
303
- aws cloudformation describe-change-set \\
304
- --stack-name <stack-name> \\
305
- --change-set-name update-<id>
306
- \`\`\`
307
- 4. Execute: \`aws cloudformation execute-change-set --stack-name <stack-name> --change-set-name update-<id>\`
496
+ ## Rollback and recovery
308
497
 
309
- Or deploy directly: \`aws cloudformation deploy --template-file stack.json --stack-name <stack-name> --capabilities CAPABILITY_NAMED_IAM\`
498
+ ### Stack states reference
310
499
 
311
- ## Delete a stack
500
+ | State | Meaning | Action |
501
+ |-------|---------|--------|
502
+ | \`CREATE_COMPLETE\` | Stack created successfully | None — healthy |
503
+ | \`UPDATE_COMPLETE\` | Update succeeded | None — healthy |
504
+ | \`DELETE_COMPLETE\` | Stack deleted | Gone — recreate if needed |
505
+ | \`CREATE_IN_PROGRESS\` | Creation underway | Wait |
506
+ | \`UPDATE_IN_PROGRESS\` | Update underway | Wait |
507
+ | \`DELETE_IN_PROGRESS\` | Deletion underway | Wait |
508
+ | \`ROLLBACK_IN_PROGRESS\` | Create failed, rolling back | Wait |
509
+ | \`UPDATE_ROLLBACK_IN_PROGRESS\` | Update failed, rolling back | Wait |
510
+ | \`CREATE_FAILED\` | Creation failed (rare) | Check events, delete stack |
511
+ | \`ROLLBACK_COMPLETE\` | Create failed, rollback finished | **Must delete and recreate** — cannot update |
512
+ | \`ROLLBACK_FAILED\` | Create rollback failed | Check events, may need manual cleanup |
513
+ | \`UPDATE_ROLLBACK_COMPLETE\` | Update failed, rolled back to previous | Healthy — fix template and try again |
514
+ | \`UPDATE_ROLLBACK_FAILED\` | Update rollback itself failed | **See recovery steps below** |
515
+ | \`DELETE_FAILED\` | Deletion failed | Check events, retry or use retain |
516
+
517
+ ### Recovering from UPDATE_ROLLBACK_FAILED
518
+
519
+ This is the most common "stuck" state. A resource that CloudFormation tried to roll back could not be restored.
520
+
521
+ **Step 1**: Identify the stuck resource:
522
+
523
+ \`\`\`bash
524
+ aws cloudformation describe-stack-events \\
525
+ --stack-name <stack-name> \\
526
+ --query "StackEvents[?ResourceStatus=='UPDATE_FAILED'].[LogicalResourceId,ResourceStatusReason]" \\
527
+ --output table
528
+ \`\`\`
529
+
530
+ **Step 2a** — Try continuing the rollback:
531
+
532
+ \`\`\`bash
533
+ aws cloudformation continue-update-rollback --stack-name <stack-name>
534
+ aws cloudformation wait stack-update-complete --stack-name <stack-name>
535
+ \`\`\`
536
+
537
+ **Step 2b** — If that fails, skip the stuck resources:
538
+
539
+ \`\`\`bash
540
+ aws cloudformation continue-update-rollback \\
541
+ --stack-name <stack-name> \\
542
+ --resources-to-skip LogicalResourceId1 LogicalResourceId2
543
+ \`\`\`
544
+
545
+ **WARNING**: Skipping resources causes state divergence — CloudFormation's view of the stack will no longer match reality. You may need to manually clean up skipped resources or import them back later.
546
+
547
+ ### Recovering from ROLLBACK_COMPLETE
548
+
549
+ A stack in \`ROLLBACK_COMPLETE\` cannot be updated. You must delete it and create a new one:
550
+
551
+ \`\`\`bash
552
+ aws cloudformation delete-stack --stack-name <stack-name>
553
+ aws cloudformation wait stack-delete-complete --stack-name <stack-name>
554
+ # Then deploy fresh
555
+ aws cloudformation deploy --template-file stack.json --stack-name <stack-name> --capabilities CAPABILITY_NAMED_IAM
556
+ \`\`\`
557
+
558
+ ## Stack lifecycle operations
559
+
560
+ ### Delete a stack
312
561
 
313
562
  \`\`\`bash
314
563
  aws cloudformation delete-stack --stack-name <stack-name>
315
564
  aws cloudformation wait stack-delete-complete --stack-name <stack-name>
316
565
  \`\`\`
317
566
 
318
- ## Check stack status
567
+ If deletion fails because a resource cannot be deleted (e.g., non-empty S3 bucket), use retain:
568
+
569
+ \`\`\`bash
570
+ aws cloudformation delete-stack \\
571
+ --stack-name <stack-name> \\
572
+ --retain-resources BucketLogicalId
573
+ \`\`\`
574
+
575
+ To protect a stack from accidental deletion:
576
+
577
+ \`\`\`bash
578
+ aws cloudformation update-termination-protection \\
579
+ --enable-termination-protection \\
580
+ --stack-name <stack-name>
581
+ \`\`\`
582
+
583
+ ### Drift detection
584
+
585
+ Detect whether resources have been modified outside of CloudFormation:
586
+
587
+ \`\`\`bash
588
+ # Start detection
589
+ DRIFT_ID=$(aws cloudformation detect-stack-drift --stack-name <stack-name> --query StackDriftDetectionId --output text)
590
+
591
+ # Check status
592
+ aws cloudformation describe-stack-drift-detection-status --stack-drift-detection-id $DRIFT_ID
593
+
594
+ # View drifted resources
595
+ aws cloudformation describe-stack-resource-drifts \\
596
+ --stack-name <stack-name> \\
597
+ --stack-resource-drift-status-filters MODIFIED DELETED
598
+ \`\`\`
599
+
600
+ ### Import existing resources
601
+
602
+ Bring resources that were created outside CloudFormation under stack management:
603
+
604
+ \`\`\`bash
605
+ aws cloudformation create-change-set \\
606
+ --stack-name <stack-name> \\
607
+ --template-body file://stack.json \\
608
+ --change-set-name import-resources \\
609
+ --change-set-type IMPORT \\
610
+ --resources-to-import '[{"ResourceType":"AWS::S3::Bucket","LogicalResourceId":"MyBucket","ResourceIdentifier":{"BucketName":"existing-bucket-name"}}]'
611
+ \`\`\`
612
+
613
+ ## Troubleshooting decision tree
614
+
615
+ When a deployment fails, follow this diagnostic flow:
616
+
617
+ ### Step 1: Check the stack status
319
618
 
320
619
  \`\`\`bash
620
+ aws cloudformation describe-stacks --stack-name <stack-name> --query 'Stacks[0].StackStatus' --output text
621
+ \`\`\`
622
+
623
+ ### Step 2: Branch on status
624
+
625
+ - **\`*_IN_PROGRESS\`** → Wait. Do not take action while an operation is in progress.
626
+ - **\`*_FAILED\` or \`*_ROLLBACK_*\`** → Read the events (Step 3).
627
+ - **\`*_COMPLETE\`** → Stack is stable. If behavior is wrong, check resource configuration.
628
+
629
+ ### Step 3: Read the failure events
630
+
631
+ \`\`\`bash
632
+ aws cloudformation describe-stack-events \\
633
+ --stack-name <stack-name> \\
634
+ --query "StackEvents[?contains(ResourceStatus, 'FAILED')].[LogicalResourceId,ResourceStatusReason]" \\
635
+ --output table
636
+ \`\`\`
637
+
638
+ ### Step 4: Diagnose by error pattern
639
+
640
+ | Error pattern | Likely cause | Fix |
641
+ |---------------|-------------|-----|
642
+ | "already exists" | Resource name collision — another stack or manual creation owns this name | Use dynamic names: \`Sub\\\`\\\${AWS.StackName}-myresource\\\`\` |
643
+ | "not authorized" or "AccessDenied" | Missing IAM permissions, SCP restriction, or wrong \`--capabilities\` | Check IAM policy, add \`--capabilities CAPABILITY_NAMED_IAM\` |
644
+ | "limit exceeded" or "LimitExceededException" | AWS service quota hit | Request quota increase or reduce resource count |
645
+ | "Template error" or "Template format error" | Invalid template syntax | Run \`aws cloudformation validate-template\` and \`chant lint src/\` |
646
+ | "Circular dependency" | Two resources reference each other | Break the cycle — extract one reference to an output or parameter |
647
+ | "is in UPDATE_ROLLBACK_FAILED state and can not be updated" | Stuck rollback | See UPDATE_ROLLBACK_FAILED recovery above |
648
+ | "is in ROLLBACK_COMPLETE state and can not be updated" | Failed creation, rolled back | Delete the stack and recreate |
649
+ | "No updates are to be performed" | Template unchanged | Use \`--no-fail-on-empty-changeset\` or verify your changes are in the built template |
650
+ | "Resource is not in the state" | Resource was modified outside CF | Run drift detection, then update or import |
651
+ | "Maximum number of addresses has been reached" | EIP limit (default 5) | Request EIP quota increase |
652
+
653
+ ## Quick reference
654
+
655
+ ### Stack info commands
656
+
657
+ \`\`\`bash
658
+ # List all stacks
659
+ aws cloudformation list-stacks --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE
660
+
661
+ # Describe a stack (status, params, outputs, tags)
321
662
  aws cloudformation describe-stacks --stack-name <stack-name>
322
- aws cloudformation describe-stack-events --stack-name <stack-name> --max-items 10
663
+
664
+ # List resources in a stack
665
+ aws cloudformation list-stack-resources --stack-name <stack-name>
666
+
667
+ # Get outputs only
668
+ aws cloudformation describe-stacks --stack-name <stack-name> --query 'Stacks[0].Outputs'
669
+
670
+ # Recent events
671
+ aws cloudformation describe-stack-events --stack-name <stack-name> --max-items 20
672
+
673
+ # Get deployed template
674
+ aws cloudformation get-template --stack-name <stack-name> --query TemplateBody
323
675
  \`\`\`
324
676
 
325
- ## Troubleshooting deploy failures
677
+ ### Full build-to-deploy pipeline
678
+
679
+ \`\`\`bash
680
+ # 1. Lint
681
+ chant lint src/
682
+
683
+ # 2. Build
684
+ chant build src/ --output stack.json
685
+
686
+ # 3. Validate
687
+ aws cloudformation validate-template --template-body file://stack.json
326
688
 
327
- - Check events: \`aws cloudformation describe-stack-events --stack-name <stack-name>\`
328
- - Rollback stuck: \`aws cloudformation continue-update-rollback --stack-name <stack-name>\`
329
- - Drift: \`aws cloudformation detect-stack-drift --stack-name <stack-name>\`
689
+ # 4. Create change set
690
+ aws cloudformation create-change-set \\
691
+ --stack-name <stack-name> \\
692
+ --template-body file://stack.json \\
693
+ --change-set-name deploy-$(date +%s) \\
694
+ --capabilities CAPABILITY_NAMED_IAM
695
+
696
+ # 5. Review changes
697
+ aws cloudformation describe-change-set \\
698
+ --stack-name <stack-name> \\
699
+ --change-set-name deploy-<id>
700
+
701
+ # 6. Execute (after user confirms)
702
+ aws cloudformation execute-change-set \\
703
+ --stack-name <stack-name> \\
704
+ --change-set-name deploy-<id>
705
+
706
+ # 7. Wait for completion
707
+ aws cloudformation wait stack-update-complete --stack-name <stack-name>
708
+ \`\`\`
330
709
  `,
331
710
  triggers: [
332
711
  { type: "file-pattern", value: "**/*.aws.ts" },
712
+ { type: "file-pattern", value: "**/stack.json" },
713
+ { type: "file-pattern", value: "**/template.yaml" },
333
714
  { type: "context", value: "aws" },
715
+ { type: "context", value: "cloudformation" },
716
+ { type: "context", value: "deploy" },
717
+ ],
718
+ preConditions: [
719
+ "AWS CLI is installed and configured (aws sts get-caller-identity succeeds)",
720
+ "chant CLI is installed (chant --version succeeds)",
721
+ "Project has chant source files in src/",
722
+ ],
723
+ postConditions: [
724
+ "Stack is in a stable state (*_COMPLETE)",
725
+ "No failed resources in stack events",
334
726
  ],
335
727
  parameters: [
336
728
  {
@@ -359,6 +751,46 @@ aws cloudformation describe-stack-events --stack-name <stack-name> --max-items 1
359
751
  },
360
752
  })`,
361
753
  },
754
+ {
755
+ title: "Deploy a new stack",
756
+ description: "Build a chant project and deploy it as a new CloudFormation stack",
757
+ input: "Deploy this project as a new stack called my-app-prod",
758
+ output: `chant lint src/
759
+ chant build src/ --output stack.json
760
+ aws cloudformation validate-template --template-body file://stack.json
761
+ aws cloudformation deploy \\
762
+ --template-file stack.json \\
763
+ --stack-name my-app-prod \\
764
+ --capabilities CAPABILITY_NAMED_IAM`,
765
+ },
766
+ {
767
+ title: "Preview changes before updating",
768
+ description: "Create a change set to review what will change before applying an update",
769
+ input: "Show me what will change if I deploy this update to my-app-prod",
770
+ output: `chant build src/ --output stack.json
771
+ aws cloudformation create-change-set \\
772
+ --stack-name my-app-prod \\
773
+ --template-body file://stack.json \\
774
+ --change-set-name review-$(date +%s) \\
775
+ --capabilities CAPABILITY_NAMED_IAM
776
+ # Wait for change set to compute, then review:
777
+ aws cloudformation describe-change-set \\
778
+ --stack-name my-app-prod \\
779
+ --change-set-name review-<id>`,
780
+ },
781
+ {
782
+ title: "Fix a stuck rollback",
783
+ description: "Recover a stack stuck in UPDATE_ROLLBACK_FAILED state",
784
+ input: "My stack my-app-prod is stuck in UPDATE_ROLLBACK_FAILED, help me fix it",
785
+ output: `# Identify the stuck resource
786
+ aws cloudformation describe-stack-events \\
787
+ --stack-name my-app-prod \\
788
+ --query "StackEvents[?ResourceStatus=='UPDATE_FAILED'].[LogicalResourceId,ResourceStatusReason]" \\
789
+ --output table
790
+ # Attempt to continue the rollback
791
+ aws cloudformation continue-update-rollback --stack-name my-app-prod
792
+ aws cloudformation wait stack-update-complete --stack-name my-app-prod`,
793
+ },
362
794
  ],
363
795
  },
364
796
  ];