Skip to content

CLD-485: only create cloud trail if nesessary - #14

Open
johnnally wants to merge 6 commits into
mainfrom
CLD-485/avoid_creating_additional_cloud_trail_unless_necessary
Open

CLD-485: only create cloud trail if nesessary#14
johnnally wants to merge 6 commits into
mainfrom
CLD-485/avoid_creating_additional_cloud_trail_unless_necessary

Conversation

@johnnally

@johnnally johnnally commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

EventBridge receives Route 53 management events from any active trail in the account - it does not require a specific trail. Most AWS accounts already have a default multi-region trail (e.g. management-events) with IncludeGlobalServiceEvents enabled, which is sufficient. The custom trail we were creating was a redundant duplicate costing customers ~$3/month extra (2nd trail + KMS key + S3 storage).

Changes:

  • Add CreateCloudTrail parameter (default: false) with AllowedValues true/false
  • Add ShouldCreateTrail condition gating TrailKMSKey, TrailS3Bucket, TrailBucketPolicy and Trail behind it
  • Use !If in ConfigureCustomResource to pass an empty CloudTrailBucketName when the bucket was not created
  • Use !If with AWS::NoValue in ConfigureFunctionRole to omit S3 policy statements when the bucket does not exist
  • Guard S3 bucket cleanup in configure/app.py behind a non-empty bucket name check to avoid errors on stack delete when no bucket was created
  • Document the new parameter in README.md

Customers who have no existing trail can set CreateCloudTrail=true to retain the previous behaviour.

One Key Point is :

  • During upgrades it is risky to auto delete the Cloud Trail bucket. If the bucket is large (maybe a few months old) - it can take > 1 hour to delete. This prolongs the upgrade but if it exceeds the 1 hour mark cloud formation can give up and attempt a rollback, which may even fail if the original cloud trail has aleady beenb deleted.
    So safest option is to not try and uto delete the bucket, but warn the user to manually delete it after.

I have more detailed and test details documented here: https://ns1inc.atlassian.net/browse/CLD-485?focusedCommentId=1655636

@johnnally johnnally self-assigned this Jun 26, 2026
@johnnally
johnnally force-pushed the CLD-485/avoid_creating_additional_cloud_trail_unless_necessary branch 2 times, most recently from c9dc1dc to 3f6b63a Compare July 3, 2026 12:00
@johnnally
johnnally requested a review from cbertinato July 7, 2026 09:18
@johnnally
johnnally marked this pull request as ready for review July 7, 2026 09:18
@johnnally
johnnally force-pushed the CLD-485/avoid_creating_additional_cloud_trail_unless_necessary branch from 3f6b63a to 30fb115 Compare July 16, 2026 16:21
@johnnally

johnnally commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

rebased with main branch in above push -f to resolve conflicts to template file after Mounika's PR was merged to main

@johnnally

johnnally commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

next steps:

  1. need to be reviewed
  2. if review is ok , generate documentation tickets
  3. create package
  4. publish package and release docs

NB version has increased to v0.4.4 on main recently, so this version will need to be bumped also before merging

EventBridge receives Route 53 management events from any active trail in the
account - it does not require a specific trail. Most AWS accounts already have
a default multi-region trail (e.g. management-events) with IncludeGlobalServiceEvents
enabled, which is sufficient. The custom trail we were creating was a redundant
duplicate costing customers ~$3/month extra (2nd trail + KMS key + S3 storage).

Changes:
- Add CreateCloudTrail parameter (default: false) with AllowedValues true/false
- Add ShouldCreateTrail condition gating TrailKMSKey, TrailS3Bucket,
  TrailBucketPolicy and Trail behind it
- Use !If in ConfigureCustomResource to pass an empty CloudTrailBucketName
  when the bucket was not created
- Use !If with AWS::NoValue in ConfigureFunctionRole to omit S3 policy
  statements when the bucket does not exist
- Guard S3 bucket cleanup in configure/app.py behind a non-empty bucket name
  check to avoid errors on stack delete when no bucket was created
- Document the new parameter in README.md

Customers who have no existing trail can set CreateCloudTrail=true to retain
the previous behaviour.
Surfaces CloudTrail status, EventBridge rule ARN, SQS queue URL and
Lambda ARN in the CloudFormation console. The CloudTrailStatus output
explicitly tells customers whether the stack is managing a trail and
reminds them to clean up any orphaned NS1CloudSyncTrail after upgrade.
- Add Update handler in configure/app.py to stop, delete and empty the
  CloudSync-created trail and S3 bucket when upgrading from CreateCloudTrail=true
  to false, preventing CloudFormation from failing to delete a non-empty bucket
- Handle 0.4.3 upgrade path where CreateCloudTrail did not exist in
  OldResourceProperties, defaulting old value to 'true' (trail was always createdi in 0.4.3 and earlier)
- Fall back to s3:ListAllMyBuckets scan for bucket name when upgrading from 0.4.3
  where CloudTrailBucketName was not present in OldResourceProperties
- Safety guard: only delete trails named NS1CloudSyncTrail or CloudSyncTrail —
  all other trail names (management-events, org trails etc.) are skipped
- Pass CloudTrailName and CreateCloudTrail through ConfigureCustomResource so
  the Lambda has access to them in OldResourceProperties during updates
- Add s3:ListAllMyBuckets, s3:DeleteObject/GetObject/ListBucket, cloudtrail:
  StopLogging/DeleteTrail permissions to ConfigureFunctionRole
- Update CreateCloudTrail and CloudTrailName parameter descriptions with
  clearer guidance and warnings
  DNSUpdateFunctionArn
Bucket deletion during a stack update is size-dependent and may cause the
Lambda to timeout, leaving the stack in UPDATE_ROLLBACK_FAILED. A real-world
test on a 1-month-old bucket (~927MB, ~144k objects) caused a 15-minute DNS
sync outage via Lambda timeout and CloudFormation retry.

Instead, stop and delete the CloudSync-managed trail only (fast), retain the
S3 bucket, and warn the customer via CloudWatch logs to empty and delete it
manually once DNS sync is verified working.

Also surface the retained bucket name in a new RetainedCloudTrailBucket
CloudFormation output so customers can find it without digging through logs.
Add AllowedPattern to CloudTrailName to block known system trail names
(management-events, aws-controltower-BaselineCloudTrail, AccountTrail)
at CloudFormation validation time, preventing accidental deletion of
existing trails.

Prepend description with "ONLY USED when CreateCloudTrail is true" so
it is the first thing visible in the SAR console parameter UI.

Update README to bold the same message in the parameters table.

Fix wording on RetainedCloudTrailBucket output description.
@johnnally
johnnally force-pushed the CLD-485/avoid_creating_additional_cloud_trail_unless_necessary branch from 30fb115 to 2c7d9db Compare July 30, 2026 10:51
@johnnally

Copy link
Copy Markdown
Contributor Author

rebased with main in above push -f

@johnnally

Copy link
Copy Markdown
Contributor Author

@johnnally

johnnally commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Key Points for IBM docs

1. New parameter: CreateCloudTrail
Available from v0.4.6 onwards. Most customers should set it to false — if your AWS account already has an active CloudTrail trail (e.g. management-events), CloudSync doesn't need to create its own. Setting false saves ~$3/month. Only set true if you have no existing trail or are unsure.

Note: If you set CreateCloudTrail to false, the CloudTrailName parameter is completely ignored — you can leave it as the default or set it to anything. It only has effect when CreateCloudTrail is true.

2. DNS sync is not interrupted
EventBridge listens to any active trail — not a specific one. Removing the CloudSync-managed trail has no effect on DNS sync as long as another trail is active in your account.

3. Changing CreateCloudTrail on an existing stack

Scenario CreateCloudTrail before CreateCloudTrail after What happens automatically
Upgrading from v0.4.5 or earlier (trail was always created) to v0.4.6 or later n/a — parameter did not exist false Trail stopped and deleted, KMS key deleted, S3 bucket retained — manual cleanup required
Already on v0.4.6, disabling the trail true false Trail stopped and deleted, KMS key deleted, S3 bucket retained — manual cleanup required
Already on v0.4.6, re-enabling the trail false true New trail, KMS key and S3 bucket created — DNS sync continues uninterrupted
Upgrading to a future version, no change to parameter true true No trail changes — stack resources updated as normal
Upgrading to a future version, no change to parameter false false No trail changes — stack resources updated as normal

4. S3 log bucket is NOT automatically deleted — manual cleanup required
Whenever a trail is removed (rows 1 and 2 above), the cloudsync-trail-bucket-* bucket is retained. No new logs will be written to it once the trail is stopped, but it will continue to incur minor S3 storage charges until you empty and delete it manually. The bucket name is shown in the stack's RetainedCloudTrailBucket output.

aws s3 rm s3://cloudsync-trail-bucket-<suffix> --recursive
aws s3 rb s3://cloudsync-trail-bucket-<suffix>

5. Expect ~10 minutes of CloudFormation cleanup noise — this is normal
After any upgrade that removes the trail, CloudFormation will attempt to delete the retained bucket 3 times, fail each time, and eventually reach UPDATE_COMPLETE with "Update successful. One or more resources could not be deleted." This is expected. Your stack is fully operational throughout — there is no downtime.

6. Check the Outputs tab after deploying
The CloudTrailStatus output confirms whether your stack is managing a trail and what action to take. The RetainedCloudTrailBucket output shows the name of any bucket left behind for manual cleanup. If something looks wrong after deploying, this is the first place to check.

7. Downgrading from v0.4.6

There is no one-click rollback in AWS CloudFormation.
CloudFormation has no internal version history — it only knows the currently active template.
To "downgrade" you must proactively redeploy the old template yourself.
CloudFormation treats this as a forward update using the old code you supply.

Recommended approach — avoid needing to downgrade at all:
Redeploy v0.4.6 with CreateCloudTrail=true to restore trail-per-stack behaviour.
No old template needed, no stack deletion required.

Full rollback via sam deploy (non-SAR stacks):
Redeploying an older template with the same stack name works cleanly — CloudFormation runs a forward update using the old template. Only fails if a different stack name is used (hardcoded resource name conflicts).

Full rollback for SAR-installed stacks:
The SAR console only offers the latest published
version — it cannot deploy an older version. To roll back a SAR stack you must delete it
entirely and reinstall the previous version from SAR. You must manually empty and delete the
cloudsync-trail-bucket-* bucket first, otherwise the reinstall will fail due to a name conflict.

Auto-rollback risk:

If a v0.4.6 deployment fails after the configure Lambda has already
deleted the trail, CloudFormation will roll back the stack state but the trail deletion is
not reversed. Redeploy with CreateCloudTrail=true to recreate it. Use
sam deploy --no-execute-changeset to review the changeset before executing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants