|
| 1 | +# secrets-dynamic-ref |
| 2 | + |
| 3 | +Failure-seeking integration test for CloudFormation **dynamic references** in |
| 4 | +resource properties: |
| 5 | + |
| 6 | +- `{{resolve:secretsmanager:...}}` |
| 7 | +- `{{resolve:ssm:...}}` |
| 8 | + |
| 9 | +cdkd resolves these itself in `resolveDynamicReferences` |
| 10 | +([src/deployment/intrinsic-function-resolver.ts](../../../src/deployment/intrinsic-function-resolver.ts)) |
| 11 | +BEFORE the property is handed to the provider — CloudFormation never sees the |
| 12 | +literal `{{resolve:...}}` token. This test surfaces bugs where a dynamic |
| 13 | +reference resolves to the **wrong value** or **stays literal** in the deployed |
| 14 | +resource. |
| 15 | + |
| 16 | +## Stack |
| 17 | + |
| 18 | +`CdkdSecretsDynamicRefExample` (cheap, no VPC): |
| 19 | + |
| 20 | +- A SecretsManager secret with a **known JSON value** |
| 21 | + (`{"username":"cdkd-user","password":"cdkd-known-pw-123"}`). |
| 22 | +- An SSM `String` parameter with a **known value** (`cdkd-known-ssm-value`). |
| 23 | +- A consumer `AWS::Lambda::Function` (inline code, asset-free) whose |
| 24 | + **environment variables** are literal `{{resolve:...}}` dynamic-reference |
| 25 | + strings. The handler is never invoked; `verify.sh` reads |
| 26 | + `GetFunctionConfiguration` and asserts each env var carries the resolved |
| 27 | + value. |
| 28 | + |
| 29 | +The env-var values are authored as literal `{{resolve:...}}` strings (CDK |
| 30 | +emits them as `Fn::Join` arrays interpolating `AWS::AccountId`), NOT via CDK's |
| 31 | +`secretValueFromJson` token — so the test pins the exact dynamic-reference |
| 32 | +grammar regardless of the CDK version's token shape. |
| 33 | + |
| 34 | +## Dynamic-reference forms exercised |
| 35 | + |
| 36 | +| Form | Example | cdkd support | |
| 37 | +| --- | --- | --- | |
| 38 | +| secretsmanager JSON-key | `{{resolve:secretsmanager:NAME:SecretString:password}}` | SUPPORTED | |
| 39 | +| secretsmanager whole-secret | `{{resolve:secretsmanager:NAME:SecretString}}` | SUPPORTED | |
| 40 | +| secretsmanager version-stage | `{{resolve:secretsmanager:NAME:SecretString:password:AWSCURRENT}}` | SUPPORTED | |
| 41 | +| ssm plaintext param | `{{resolve:ssm:NAME}}` | SUPPORTED | |
| 42 | +| ssm-secure SecureString | `{{resolve:ssm-secure:NAME}}` | **NOT** resolved by cdkd — out of scope (see below) | |
| 43 | + |
| 44 | +`ssm-secure` is intentionally **not** exercised: cdkd's |
| 45 | +`resolveDynamicReferences` routes only `secretsmanager` and `ssm`; an |
| 46 | +`ssm-secure:` reference hits the `else` branch (warn + leave literal), so it |
| 47 | +would deploy a broken value. A secret **version-ID** form |
| 48 | +(`...:SecretString:key::<uuid>`) is also not exercised because the version id |
| 49 | +is not knowable ahead of deploy; the version-**stage** slot (`AWSCURRENT`) |
| 50 | +covers the optional-trailing-field grammar. |
| 51 | + |
| 52 | +## What verify.sh asserts |
| 53 | + |
| 54 | +1. Deploy the stack with the local cdkd binary. |
| 55 | +2. Read the consumer Lambda's env vars via `GetFunctionConfiguration`. |
| 56 | +3. For each env var: it is **not** still a literal `{{resolve:...}}` token, AND |
| 57 | + it equals the known expected value. A wrong-or-literal value FAILS with |
| 58 | + specifics. |
| 59 | +4. Destroy, then assert the Lambda, secret, SSM parameter, and state file are |
| 60 | + all gone. |
| 61 | + |
| 62 | +**Security:** secret-derived values are never printed; assertions mask them |
| 63 | +(`xx***(len=N)`). Only PASS/FAIL plus a masked snippet appears in the log. |
| 64 | + |
| 65 | +## Run |
| 66 | + |
| 67 | +```bash |
| 68 | +vp run build # from repo root — verify.sh runs node dist/cli.js |
| 69 | +/run-integ secrets-dynamic-ref |
| 70 | +``` |
| 71 | + |
| 72 | +`verify.sh` requires `STATE_BUCKET` (e.g. `cdkd-state-{accountId}`) and honors |
| 73 | +`AWS_REGION` (defaults to `us-east-1`). |
0 commit comments