Post-deployment checklist¶
Resolves: GAP-D08 — No post-deployment verification checklist.
This checklist is the operational confirmation that a deployment is working correctly before declaring "deployment complete." Work through it after every deployment — initial provisioning or module update. It is not a test suite; it is the human or AI agent confirmation that the platform is healthy and the compliance gates are enforced.
All commands assume the correct AWS profile is active and environment variables are set for the target environment (ENV=dev|uat|prod).
Related: deployment sequence · module activation matrix · alert thresholds
Phase 1 — Infrastructure health¶
Run these checks before anything else. If any fail, do not proceed to Phase 2.
1. EventBridge buses: test event delivery
aws events put-events --entries '[{
"Source": "deployment.healthcheck",
"DetailType": "HealthCheck",
"Detail": "{\"check\":\"post-deploy\"}",
"EventBusName": "bank-core"
}]'
Repeat for buses: bank-core, bank-kyc, bank-aml, bank-payments, bank-credit, bank-platform.
Then confirm each DLQ is empty:
aws sqs get-queue-attributes \
--queue-url https://sqs.{region}.amazonaws.com/{account}/bank-{domain}-dlq \
--attribute-names ApproximateNumberOfMessages
Pass: All put-events calls return FailedEntryCount: 0. All DLQ depths are 0.
2. S3 buckets: accessible and versioning enabled
Check all buckets in the Terraform state for the environment.
Pass: Each bucket returns {"Status": "Enabled"}. Any bucket returning Suspended or no status is a fail.
3. Secrets Manager: all secrets in manifest exist and are accessible
aws secretsmanager list-secrets --filter Key=tag-key,Values=env Key=tag-value,Values={ENV} \
| jq '[.SecretList[].Name] | sort'
Compare the output against the secrets manifest for this environment.
Then confirm the Lambda execution role can read a representative secret:
Pass: All secrets in the manifest are present. The Lambda execution role access test returns the secret value without an AccessDeniedException.
4. Cognito: both user pools active, test auth flow
aws cognito-idp describe-user-pool --user-pool-id {CUSTOMER_POOL_ID} | jq '.UserPool.Status'
aws cognito-idp describe-user-pool --user-pool-id {STAFF_POOL_ID} | jq '.UserPool.Status'
For each pool, initiate an auth flow with a test user provisioned for deployment verification:
aws cognito-idp initiate-auth \
--auth-flow USER_PASSWORD_AUTH \
--auth-parameters USERNAME=deploy-verify@test.internal,PASSWORD={TEST_PASSWORD} \
--client-id {APP_CLIENT_ID}
Pass: Both pools return "Active". Auth flow returns an AuthenticationResult with AccessToken, IdToken, and RefreshToken. JWT claims include custom:jurisdiction.
5. KMS keys: all CMKs enabled and accessible
for KEY_ALIAS in alias/bank-{ENV}-pii alias/bank-{ENV}-financial alias/bank-{ENV}-operational; do
aws kms describe-key --key-id "$KEY_ALIAS" | jq '.KeyMetadata | {KeyId, KeyState, Enabled}'
done
Pass: All three CMKs show "KeyState": "Enabled" and "Enabled": true. Any key in PendingDeletion or Disabled state is a P1 blocker.
Phase 2 — Database health¶
6. All 6 Neon databases reachable via PgBouncer
for DB in core kyc aml payments credit platform; do
psql "$PGBOUNCER_BASE_URL/$DB" -c "SELECT 1 AS alive;" 2>&1
done
Connection strings are sourced from Secrets Manager (bank/{ENV}/{domain}/pgbouncer-connection-string).
Pass: All six databases return alive: 1. Any connection refused or authentication failure is a fail.
7. Flyway migration history: expected count for current schema version
psql "$PGBOUNCER_BASE_URL/core" \
-c "SELECT COUNT(*) FROM flyway_schema_history WHERE success = true;"
Compare the count against the expected value in the release notes for the current version. Repeat for all 6 databases.
Pass: Each database migration count matches the expected value. Any shortfall indicates a migration did not apply.
8. No pending migrations
Run from each code repository:
Pass: No rows returned — all migrations are in Applied state. Any Pending or Failed migration is a blocker that must be resolved before proceeding.
Phase 3 — Core banking¶
9. Double-entry posting via MOD-001
POST a test transaction using an idempotency key:
curl -s -X POST https://api-internal.{ENV}.bank/v1/ledger/postings \
-H "Idempotency-Key: deploy-verify-$(date +%s)" \
-H "Authorization: Bearer {SERVICE_TOKEN}" \
-d '{
"entries": [
{"account_id": "TEST-ASSET-001", "side": "DR", "amount": 100, "currency": "NZD"},
{"account_id": "TEST-LIABILITY-001", "side": "CR", "amount": 100, "currency": "NZD"}
],
"description": "Deployment verification posting"
}'
Pass: Response is HTTP 201 with status: committed and the same idempotency_key echoed back. Re-submit the same request (same idempotency key) and confirm HTTP 200 with the original posting returned (idempotent replay).
10. Real-time balance query via MOD-003
curl -s https://api-internal.{ENV}.bank/v1/accounts/TEST-ASSET-001/balance \
-H "Authorization: Bearer {SERVICE_TOKEN}"
Pass: Response reflects the posting from checkpoint 9 — the balance includes the test debit. Response time is under 200ms (check x-response-time header or CloudWatch).
11. Posting event on EventBridge bus within 500ms
After the posting in checkpoint 9, query CloudWatch Logs for the EventBridge target that receives bank-core events:
aws logs filter-log-events \
--log-group-name /aws/events/bank-core-audit \
--start-time $(date -d '5 minutes ago' +%s000) \
--filter-pattern "deploy-verify"
Pass: The test posting event appears in the log within 500ms of the posting timestamp. If the event is missing after 1 minute, this indicates the EventBridge rule or target is misconfigured.
Phase 4 — KYC and identity¶
12. Test customer record and KYC session initialisation
curl -s -X POST https://api-internal.{ENV}.bank/v1/kyc/customers \
-H "Authorization: Bearer {SERVICE_TOKEN}" \
-d '{
"given_name": "Deploy",
"family_name": "Verification",
"date_of_birth": "1990-01-01",
"email": "deploy-verify@test.internal"
}'
Pass: HTTP 201 with a customer_id and kyc_session_id in the response body. The KYC session status is initialised.
13. eIDV provider API key health check
curl -s https://api-internal.{ENV}.bank/v1/kyc/eidv/health \
-H "Authorization: Bearer {SERVICE_TOKEN}"
This endpoint proxies a health check call to the configured eIDV provider using the API key from Secrets Manager.
Pass: HTTP 200 with {"provider_status": "ok"}. An api_key_invalid or provider_unreachable response means the Secrets Manager value for the eIDV API key needs updating.
14. JWT claims include jurisdiction
Using the token obtained in checkpoint 4:
echo {JWT_ID_TOKEN} | cut -d. -f2 | base64 -d 2>/dev/null | jq '{sub, custom_jurisdiction: .["custom:jurisdiction"]}'
Pass: The decoded JWT payload includes custom:jurisdiction set to the correct value for this deployment (NZ, AU, or NZ+AU).
Phase 5 — Payments¶
15. Test domestic payment in pending state
curl -s -X POST https://api-internal.{ENV}.bank/v1/payments/domestic \
-H "Idempotency-Key: deploy-pmt-$(date +%s)" \
-H "Authorization: Bearer {SERVICE_TOKEN}" \
-d '{
"from_account": "TEST-ASSET-001",
"to_bsb": "123456",
"to_account": "00000001",
"amount": 1.00,
"currency": "NZD",
"reference": "Deployment verification"
}'
Pass: HTTP 201 with status: pending and a payment_id. The payment must not proceed to submitted in a test environment — confirm the test environment payment gateway is in sandbox mode.
16. Payment event on EventBridge bus
aws logs filter-log-events \
--log-group-name /aws/events/bank-payments-audit \
--start-time $(date -d '5 minutes ago' +%s000) \
--filter-pattern "deploy-pmt"
Pass: Payment created event appears on the bank-payments EventBridge bus.
17. BPAY health check (AU deployments only)
Skip if deployment.jurisdiction does not include AU.
curl -s https://api-internal.{ENV}.bank/v1/payments/bpay/health \
-H "Authorization: Bearer {SERVICE_TOKEN}"
Pass: HTTP 200 with {"bpay_gateway": "ok"}.
18. NPP service connectivity (AU deployments only)
Skip if deployment.jurisdiction does not include AU.
curl -s https://api-internal.{ENV}.bank/v1/payments/npp/connectivity \
-H "Authorization: Bearer {SERVICE_TOKEN}"
Pass: HTTP 200 confirming NPP participant connectivity. Any gateway_timeout indicates NPP connectivity credentials need verification in Secrets Manager.
Phase 6 — AML monitoring¶
19. AML engine receiving posting events
Confirm the AML rule engine (MOD-022) has processed the posting from checkpoint 9:
aws logs filter-log-events \
--log-group-name /aws/lambda/bank-aml-rule-engine \
--start-time $(date -d '5 minutes ago' +%s000) \
--filter-pattern "deploy-verify"
Pass: Log entry shows the test posting was received and evaluated by the AML rule engine. If absent after 2 minutes, check the EventBridge subscription from bank-core to bank-aml.
20. Sanctions list last-refresh timestamp
curl -s https://api-internal.{ENV}.bank/v1/aml/sanctions/status \
-H "Authorization: Bearer {SERVICE_TOKEN}" \
| jq '{last_refreshed, list_count, provider}'
Pass: last_refreshed is within the last 24 hours. If the list has never been loaded (new deployment), trigger a manual refresh:
curl -s -X POST https://api-internal.{ENV}.bank/v1/aml/sanctions/refresh \
-H "Authorization: Bearer {SERVICE_TOKEN}"
Confirm the refresh completes and last_refreshed updates before proceeding.
21. STR submission endpoint reachable
curl -s https://api-internal.{ENV}.bank/v1/aml/str/health \
-H "Authorization: Bearer {SERVICE_TOKEN}"
Pass: HTTP 200 with {"str_gateway": "ok", "regulator": "AUSTRAC|FIU"} as appropriate for the jurisdiction.
Phase 7 — Regulatory reporting pipelines¶
22. CDC pipeline running (MOD-042)
aws cloudwatch get-metric-statistics \
--namespace AWS/KinesisFirehose \
--metric-name IncomingRecords \
--dimensions Name=DeliveryStreamName,Value=bank-cdc-{ENV} \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 3600 \
--statistics Sum
Pass: Sum is greater than 0 for a running system. On a new deployment with no data flowing yet, accept 0 with a note in the sign-off record that data flow begins with first customer activity.
23. Snowflake receiving CDC records
Connect to Snowflake and check a recently-loaded table:
SELECT MAX(loaded_at) AS last_load, COUNT(*) AS records_today
FROM bank_core.public.postings_cdc
WHERE DATE(loaded_at) = CURRENT_DATE();
Pass: last_load is within the last hour for active environments. On initial deployment, this may be empty — acceptable if checkpoint 22 confirms the pipeline is running.
24. Regulatory report last-run timestamp
curl -s https://api-internal.{ENV}.bank/v1/reporting/schedule \
-H "Authorization: Bearer {SERVICE_TOKEN}" \
| jq '.reports[] | {name, last_run, next_due, status}'
Pass: No report shows status: overdue. All reports show a next_due timestamp in the future. On initial deployment, last_run may be null — acceptable if the schedule is correctly configured.
Phase 8 — Observability¶
25. CloudWatch dashboard loads with metrics
Navigate to the CloudWatch console and open the bank-{ENV}-platform dashboard.
Pass: All widgets load without Insufficient data errors. At least one metric datapoint exists from within the last hour (or from the test postings in earlier checkpoints).
26. X-Ray traces visible for each system domain
aws xray get-trace-summaries \
--time-range-type EventTime \
--start-time $(date -u -d '30 minutes ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
| jq '.TraceSummaries | length'
Pass: At least one trace per system domain that received traffic during the verification run. Confirm traces are visible in the X-Ray service map console.
27. Test alert fires and routes correctly
Manually trigger the CloudWatch alarm used for deployment verification:
aws cloudwatch set-alarm-state \
--alarm-name bank-{ENV}-deploy-verify-alarm \
--state-value ALARM \
--state-reason "Post-deployment routing test"
Pass: SNS notification arrives at the on-call channel (Slack or PagerDuty) within 2 minutes. Reset the alarm afterwards:
aws cloudwatch set-alarm-state \
--alarm-name bank-{ENV}-deploy-verify-alarm \
--state-value OK \
--state-reason "Post-deployment routing test complete"
Phase 9 — Go-live gate (production only)¶
Run this phase for production deployments only. All Phase 1–8 checks must be recorded as passed before proceeding.
28. All Phase 1–8 checks passed and recorded
Review the sign-off table below. Every checkpoint must have a Pass entry before proceeding.
29. Balance reconciliation — zero discrepancies
curl -s https://api-internal.prod.bank/v1/reconciliation/current \
-H "Authorization: Bearer {SERVICE_TOKEN}" \
| jq '{run_at, total_accounts, discrepancies}'
Pass: discrepancies: 0. Any non-zero value is a P1 incident — do not proceed and page the on-call engineer immediately.
30. AML monitoring confirmed active and processing events
Confirm that the AML rule engine has processed at least one event since the deployment completed (checkpoint 19 may be used as evidence if timing is appropriate).
Pass: Log evidence of event processing post-deployment. AML alert queue depth is 0 or within expected norms.
31. KYC gate enforced
Attempt to open an account for the test customer from checkpoint 12 without completing KYC verification:
curl -s -X POST https://api-internal.prod.bank/v1/accounts \
-H "Authorization: Bearer {SERVICE_TOKEN}" \
-d '{"customer_id": "{TEST_CUSTOMER_ID}", "product_id": "PRD-001"}'
Pass: HTTP 403 with {"error": "kyc_not_verified"}. Any HTTP 201 or 200 response means the KYC gate is not enforced — this is a P1 blocker.
32. Disclosure gate enforced
Attempt to accept a product offer for the test customer without completing the disclosure flow:
curl -s -X POST https://api-internal.prod.bank/v1/products/accept \
-H "Authorization: Bearer {SERVICE_TOKEN}" \
-d '{"customer_id": "{TEST_CUSTOMER_ID}", "product_id": "PRD-001", "bypass_disclosure": true}'
Pass: HTTP 403 with {"error": "disclosure_not_completed"}. Any 2xx response is a P1 blocker.
Sign-off record¶
Complete this table and attach it to the deployment record (Jira release ticket or equivalent).
| Checkpoint | Description | Pass / Fail | Verified by | Timestamp |
|---|---|---|---|---|
| 1 | EventBridge bus test events and DLQ depth | |||
| 2 | S3 bucket versioning | |||
| 3 | Secrets Manager completeness and access | |||
| 4 | Cognito user pools active, auth flow | |||
| 5 | KMS CMKs enabled | |||
| 6 | Neon databases reachable via PgBouncer | |||
| 7 | Flyway migration count | |||
| 8 | No pending migrations | |||
| 9 | Double-entry posting — atomic commit, idempotency | |||
| 10 | Real-time balance query | |||
| 11 | Posting event on EventBridge within 500ms | |||
| 12 | KYC session initialised | |||
| 13 | eIDV provider API key health | |||
| 14 | JWT includes jurisdiction claim | |||
| 15 | Domestic payment in pending state | |||
| 16 | Payment event on EventBridge | |||
| 17 | BPAY health check (AU only) | |||
| 18 | NPP connectivity (AU only) | |||
| 19 | AML engine receiving posting events | |||
| 20 | Sanctions list last-refresh within 24h | |||
| 21 | STR submission endpoint reachable | |||
| 22 | CDC pipeline running | |||
| 23 | Snowflake receiving CDC records | |||
| 24 | Regulatory report schedule valid | |||
| 25 | CloudWatch dashboard loads with metrics | |||
| 26 | X-Ray traces visible | |||
| 27 | Test alert routes correctly | |||
| 28 | All Phase 1–8 checks recorded (prod only) | |||
| 29 | Balance reconciliation — zero discrepancies (prod only) | |||
| 30 | AML monitoring active (prod only) | |||
| 31 | KYC gate enforced (prod only) | |||
| 32 | Disclosure gate enforced (prod only) |
Deployment declared complete by: ___ Date/time: _____