Update the solution
This page describes how to apply updates to an existing VAMS deployment, including standard in-place updates, A/B deployment strategies for major changes, and version-specific migration steps.
Update methods
VAMS supports two update methods depending on the scope of changes being applied.
| Method | Use case | Downtime |
|---|---|---|
| In-place update | Minor updates, bug fixes, feature additions within the same major version. | Minimal (during AWS CloudFormation changeset execution). |
| A/B deployment | Major version upgrades, KMS key changes, distribution type changes (Amazon CloudFront to ALB), region migrations. | Moderate (during data migration and DNS switchover). |
Pre-update checklist
Complete the following steps before applying any update.
VAMS uses RemovalPolicy.RETAIN on Amazon DynamoDB tables. An update that replaces a table therefore does not delete its data — AWS CloudFormation creates a new, empty table and orphans the old one, which keeps its original auto-generated name and continues to accrue storage charges. The application comes up with no data while the data still exists in the retained table, so the symptom looks like data loss even though nothing was deleted.
Back up before updating, and if an update replaces a table, locate the orphaned table (it is not listed in the updated stack's resources) and either migrate its contents into the new table or delete it once you no longer need it. See Uninstall the solution for how VAMS tables are named and listed.
-
Review the changelog. Read the CHANGELOG.md for breaking changes, required migration scripts, and known issues for the target version.
-
Back up DynamoDB tables. Export critical tables using AWS Backup or on-demand exports:
# Export a table to Amazon S3 using point-in-time exportaws dynamodb export-table-to-point-in-time \--table-arn arn:<PARTITION>:dynamodb:<REGION>:<ACCOUNT_ID>:table/<TABLE_NAME> \--s3-bucket <BACKUP_BUCKET> \--s3-prefix vams-backup/$(date +%Y%m%d) \--export-format DYNAMODB_JSONSubstitute
<PARTITION>with the partition the deployment runs in:awsfor commercial Regions,aws-us-govfor AWS GovCloud (US), oraws-euscfor the AWS European Sovereign Cloud. -
Back up S3 buckets. Sync asset buckets to a backup location:
aws s3 sync s3://<ASSET_BUCKET> s3://<BACKUP_BUCKET>/asset-backup/ \--profile <PROFILE> -
Record current stack outputs. Save existing CloudFormation outputs for reference and potential rollback:
aws cloudformation describe-stacks \--stack-name <VAMS_STACK_NAME> \--query 'Stacks[0].Outputs' \--output json > stack-outputs-backup.json -
Test in a non-production environment. Deploy the update to a test stack first and validate functionality before applying to production.
In-place update
Use this method for minor updates and configuration changes within the same major version.
Step 1: Pull the latest code
git fetch --all --tags
git checkout tags/<TARGET_VERSION>
Step 2: Install dependencies
cd web && npm install && npm run build && cd ..
cd infra && npm install && cd ..
Step 3: Update configuration
Review and update infra/config/config.json with any new configuration fields introduced in the target version. New configuration fields are typically backward-compatible and receive defaults, but review the changelog for required changes.
Step 4: Deploy the update
cd infra
npx cdk deploy --all --require-approval never
AWS CDK creates a CloudFormation changeset and applies only the modified resources. Resources that have not changed are left untouched.
CloudFormation changesets update, replace, or delete resources based on the type of change. Property updates that require replacement (such as changing a DynamoDB table's partition key) result in the old resource being deleted and a new one created. Review the cdk diff output before deploying to understand which resources will be affected.
npx cdk diff
The seeded authorization defaults are an exception to the "unchanged resources are left untouched" rule above. Every deployment — in-place or A/B — writes them back to Amazon DynamoDB, replacing each record in full and re-creating any that were deleted. This is intentional: read-only role constraints are added and adjusted between releases, and re-seeding is how an existing deployment receives those changes without a migration step.
The records rewritten on every deployment are:
- The
adminandbasicReadOnlyrole definitions, including theirmfaRequiredsetting. - The
adminrole assignments forapp.adminUserIdand the reservedSYSTEM_USERidentity. - Every default constraint — those with an id beginning
initial_admin_orinitial_basicro_. - The user records for
app.adminUserId(withapp.adminEmailAddress) andSYSTEM_USER.
Because each record is replaced in full, any hand-edit to a default role, one of its constraints, or a
seeded user record is lost on the next deployment, including attributes added to it and an
mfaRequired value changed through the web interface. Customize permissions in a separate role carrying
its own constraints rather than by editing a default one; roles, user-role assignments, and constraints
created through the web interface, the API, or the CLI are not touched by a deployment. See
Permissions model for the constraint structure.
app.adminUserId or app.adminEmailAddress after the first deploymentThe seeded Amazon Cognito administrator is an AWS::Cognito::UserPoolUser whose Username,
UserPoolId, UserAttributes and DesiredDeliveryMediums are all Update-requires-Replacement.
Editing either value therefore does not rename the account — AWS CloudFormation creates a new user and
deletes the old one. Which of two outcomes you get depends only on whether the new username already
exists in the pool:
- The new username does not exist. The deployment succeeds and the new administrator is created.
The previous identity is retained rather than deleted — the user carries a
Retainreplacement policy — so you can still sign in as it, but it is no longer managed by AWS CloudFormation and will not be removed when the stack is deleted. Review it and delete it by hand once the new administrator is working. AnyuserRolesrows keyed to the old username continue to apply to it and not to the new one. - The new username already exists (created by hand, or equal to another operator's account). The
replacement's create step fails with
AlreadyExists, the nested authorization stack fails, and the whole core stack rolls back — roughly 15 minutes, across every nested stack.
Synthesis warns whenever app.adminUserId and app.adminEmailAddress differ, restating that both are
immutable — it cannot detect that a value has changed, because it has no view of what is deployed. It
also rejects a username Amazon Cognito itself would refuse (whitespace, or over 128 characters), so that
failure arrives as a configuration message rather than as a rolled-back deployment.
Treat both values as fixed for the life of the deployment. To change who administers VAMS, leave the
seeded account alone and grant the admin role to another user through the web interface, the API, or
the CLI — those assignments are not overwritten by a deployment.
If the values in your configuration file have already drifted from the deployed stack (for example a configuration copied between environments), read the deployed value back before deploying and restore it:
aws cognito-idp list-users --user-pool-id <pool-id> \
--query "Users[].Username" --output text
This behaviour is not specific to any one release; it has always been how the resource is declared.
Each built-in pipeline ships a vamsSchema bundle — a pipeline definition, an optional workflow with its
triggers, and its templates — that the deployment uploads and registers through an AWS CloudFormation
custom resource. A hash of the bundle's files and of the deploy-time values injected into it is a property of that
resource, so the registration re-runs whenever a release revises the built-in or the resources it points
at change. The definitions are owned by the schema, not by the deployment's database, and a re-registration
replaces them.
A re-registration rewrites, for the ids the bundle names:
- The pipeline — name, category, description, execution configuration and
systemConfig, and it is written back as enabled and unarchived. - The workflow — name, category, description, referenced pipelines, sub-dashboard URL and
systemConfig, also enabled and unarchived. Its AWS Step Functions state machine is regenerated. - Each template the bundle ships — name, description, configuration format and body, web form,
custom-edit flag, input instructions,
systemConfigoverrides, theisDefaultflag, and the tag schema when the bundle declares one. A shipped template that is the bundle's default reclaims that designation from whichever template held it. - Each trigger the bundle declares, matched by trigger type — its input-file filters, default
templates, and
enabledflag, which comes fromautoRegisterAutoTriggerOnFileUploadwhen the deployment sets it.
A re-registration does not touch execution history or the outputs of past runs, pipelines and workflows
you created, templates and additional triggers you added under identifiers of your own, or the
dateCreated and createdBy provenance of the built-in's records; the rewritten records are attributed to
SYSTEM_USER as their modifier.
The consequence to plan around is that a built-in disabled or archived in the web interface returns
enabled, and that no line in the deployment output flags a replaced change — the custom resource reports
only what it registered. Turn a built-in off through the deployment configuration instead: setting its
autoRegisterWithVAMS to false removes the registration and archives the pipeline and workflow, and
autoRegisterAutoTriggerOnFileUpload controls whether its file-upload trigger fires. See
Pipelines and workflows.
Step 5: Post-update verification
- Confirm the stack deployed successfully in the AWS CloudFormation console.
- Navigate to the VAMS web interface and verify login and basic operations.
- Check Amazon CloudWatch Logs for Lambda function errors.
- If Amazon OpenSearch Service is enabled and the update requires reindexing, set
app.openSearch.reindexOnCdkDeploytotrueinconfig.jsonand redeploy, or run the reindex utility manually.
A/B deployment
Use A/B deployment when the update involves changes that cannot be safely applied through a CloudFormation changeset. This method deploys a parallel VAMS stack, migrates data, and then decommissions the original stack.
When to use A/B deployment
- Major version upgrades with breaking DynamoDB schema changes.
- Changing the KMS CMK encryption key.
- Switching distribution type between Amazon CloudFront and Application Load Balancer (ALB).
- Migrating the deployment to a different AWS Region within the same account.
A/B deployment steps
-
Deploy Stack B. Use a different
baseStackNamein yourinfra/config/config.jsonfor the new deployment. -
Redirect traffic. Update DNS records to point to a temporary maintenance page.
-
Deploy the new stack.
cd infranpx cdk deploy --all --require-approval never -
Migrate DynamoDB data. Use the A/B migration scripts provided in
infra/deploymentDataMigration/:cd infra/deploymentDataMigrationpip install boto3python tools/VAMSDataMigration.py config/<YOUR_MIGRATION_CONFIG>.json -
Migrate S3 data. Sync asset buckets from Stack A to Stack B:
aws s3 sync s3://<STACK_A_ASSET_BUCKET> s3://<STACK_B_ASSET_BUCKET>aws s3 sync s3://<STACK_A_AUXILIARY_BUCKET> s3://<STACK_B_AUXILIARY_BUCKET> -
Migrate users. If using Amazon Cognito, manually recreate users in the new user pool. Password resets may be required.
-
Validate Stack B. Test all VAMS functionality with the migrated data.
-
Switch DNS. Update DNS records to point to Stack B endpoints.
-
Decommission Stack A. After confirming Stack B is stable, destroy Stack A following the uninstall procedure.
When using the ALB configuration, the web application S3 bucket is named after the domain. This creates a naming conflict during A/B deployment. You must delete the web app bucket from Stack A before deploying Stack B with the same domain, then restore the bucket contents after deployment.
Version-specific migration instructions
Each major version upgrade may require data migration scripts to transform DynamoDB schemas or reindex Amazon OpenSearch Service. The following sections document required migrations for each version path.
v2.2 to v2.3
Breaking changes:
- API Gateway authorizers replaced with custom Lambda authorizers.
- AWS Batch Fargate CDK construct naming changed for pipeline stacks.
- Amazon OpenSearch Service indexes replaced with new dual-index schema (assets and files).
Required migration steps:
-
Deploy the v2.3 CDK stack.
-
Run the OpenSearch reindex script to populate the new indexes:
cd infra/deploymentDataMigration/v2.2_to_v2.3/upgrade -
Optionally disable and re-enable batch pipelines if experiencing CDK deployment errors with Amazon Elastic Container Service (Amazon ECS) Fargate constructs.
If Lambda functions behind a VPC were broken in v2.2, this version restores VPC support. However, MFA for roles is not supported when all Lambda functions are behind a VPC with Amazon Cognito enabled.
v2.3 to v2.4
Breaking changes:
- Permission constraints migrated to a dedicated DynamoDB table (no longer shared with auth entities).
- Metadata and metadata schema DynamoDB tables replaced with new tables supporting multi-entity types.
- Amazon OpenSearch Service index schemas changed for
MD_andAB_fields (now flat objects).
Required migration steps:
-
Deploy the v2.4 CDK stack. Default admin and read-only constraints are re-created automatically.
-
Navigate to the migration scripts directory:
cd infra/deploymentDataMigration/v2.3_to_v2.4/upgrade -
Copy and configure the migration configuration file:
cp v2.3_to_v2.4_migration_config.json my_migration_config.json -
Update the configuration file with your DynamoDB table names. Retrieve table names from CloudFormation outputs:
aws cloudformation describe-stacks --stack-name <VAMS_STACK_NAME> \--query 'Stacks[0].Outputs[?contains(OutputKey, `Table`)].{Key:OutputKey,Value:OutputValue}' \--output table -
Run the migration:
Linux / macOS
chmod +x run_migration.sh./run_migration.sh my_migration_config.jsonWindows
.\run_migration.ps1 my_migration_config.json -
The migration performs the following operations:
- Migrates metadata from the old table to the new multi-entity metadata tables.
- Migrates metadata schemas to the new schema table with support for multiple entity types.
- Migrates permission constraints from the auth entities table to the dedicated constraints table.
- Reindexes Amazon OpenSearch Service with the new field schemas.
v2.4 to v2.5
Breaking changes:
- Asset version DynamoDB tables restructured with
databaseId-prefixed composite keys to prevent cross-database collisions. - Website overhauled with Vite build framework, AWS Amplify v6, and dark/light theme support (may cause merge conflicts for forked repositories).
Required migration steps:
-
Deploy the v2.5 CDK stack. The new V2 tables are created alongside the existing V1 tables.
-
Navigate to the migration scripts directory:
cd infra/deploymentDataMigration/v2.4_to_v2.5/upgrade -
Copy and configure the migration configuration file:
cp v2.4_to_v2.5_migration_config.json my_migration_config.json -
Update the configuration file with your DynamoDB table names. The migration requires these tables:
Table Purpose AssetStorageTableLookup source for assetIdtodatabaseIdmapping.AssetVersionsStorageTableV1 source for asset versions. AssetVersionsStorageTableV2V2 destination for asset versions. AssetFileVersionsStorageTableV1 source for asset file versions. AssetFileVersionsStorageTableV2V2 destination for asset file versions. AssetFileMetadataVersionsStorageTableIn-place backfill for new databaseId:assetIdfield. -
Run the migration:
Linux / macOS
chmod +x run_migration.sh./run_migration.sh my_migration_config.jsonWindows
.\run_migration.ps1 my_migration_config.json -
The migration performs five phases:
- Phase 1: Builds a lookup cache by scanning the asset storage table for
assetIdtodatabaseIdmappings. - Phase 2: Migrates asset versions from V1 to V2 with transformed key schema (
assetIdbecomesdatabaseId:assetId). - Phase 3: Migrates asset file versions from V1 to V2 with transformed key schema.
- Phase 4: Backfills the
databaseId:assetIdfield on existing asset file metadata version records for the new Global Secondary Index (GSI). - Phase 5: Verifies record counts and key structure integrity between V1 and V2 tables.
- Phase 1: Builds a lookup cache by scanning the asset storage table for
The migration requires dynamodb:Scan on source tables, dynamodb:BatchWriteItem on V2 destination tables, and dynamodb:UpdateItem on the metadata versions table. See the v2.4 to v2.5 migration README for the full IAM policy.
v2.5 to v2.6
The migration steps below move stored pipeline and workflow definitions onto the new data model. They cannot update the code of a pipeline you wrote yourself: v2.6 delivers inputs through a manifest rather than on the payload, expects asynchronous pipelines to return a task token, and expects a pipeline to register its sub-processes and logs so aborts and log retrieval work. See Migrating custom pipelines from v2.5 to v2.6 for the porting order and checklist. Deployments that run only VAMS built-in pipelines need nothing beyond the steps here.
Breaking changes:
- The backend API moves from API Gateway HTTP API (v2) to REST API (v1), served under a stage path (default
/api). The API Gateway identifier and invoke URL change on deployment. Any client registered directly against the old API Gateway endpoint URL must be re-setup against the new endpoint — re-runvamscli setupfor the CLI, and update any external integrations or scripts that stored the API base URL. Clients that reach the API through the CloudFront or ALB front (the web application, and CLIs configured with the front's/apiURL) continue to work without change. See API Gateway REST API endpoint change. - Externally registered pipelines do not run unchanged. The workflow, pipeline, and execution overhaul changes three things a pipeline depends on: it reads its inputs from a resolved manifest rather than from the invocation payload, an asynchronous pipeline returns a Step Functions task token for the workflow to advance past it, and it registers its sub-processes and log locations so abort and log retrieval reach them. Registration itself also moves — a definition is declared in a file-based
vamsSchemabundle imported through the schema importer, and a pipeline is referenced by compositepipelineDatabaseId:pipelineId. The migration steps below reshape stored definitions; they cannot change a pipeline's code. Port every externally maintained pipeline with Migrating custom pipelines from v2.5 to v2.6. Deployments running only VAMS built-in pipelines need nothing beyond the steps here.- Three API routes are removed, and any direct API client must be repointed.
PUT /pipelines(create a pipeline) is replaced byPOST /database/{databaseId}/pipelines;PUT /workflows(create a workflow) byPOST /database/{databaseId}/workflows; andPOST /database/{databaseId}/assets/{assetId}/workflows/{workflowId}(run a workflow against one asset) byPOST /workflows/{workflowDatabaseId}/{workflowId}/execute, which is asset-less and takes an input-file array plus an output-target asset instead of a path-bound asset. The bare/pipelinesand/workflowspaths serveGETonly. A removed route is absent from the API's OpenAPI spec, so a call to it is rejected by the authorizer with a403rather than a404— this affects scripts, CI jobs, and home-built clients even when the deployment runs no custom pipeline code.
- Three API routes are removed, and any direct API client must be repointed.
- Role constraints that reference the
pipelinecriteria fieldpipelineTypeare no longer enforced as written.pipelineTypeis not part of thepipelineconstraint field set in v2.6, and a criterion naming a field VAMS does not recognize is dropped when the permission policy is compiled. For an already-stored constraint the drop is silent: stored constraints are not re-validated, so no error and no warning reaches the administrator. Because a constraint's criteria are combined with AND, dropping the criterion from an allow rule removes a restriction and widens the pipelines the role can reach. Every stored constraint must be audited and re-authored againstcategorybefore upgrading; no migration step rewrites constraints. See Permission constraint audit forpipelineType. - External asset buckets now require an explicit default, and a
createNewBucket: falseconfiguration that was valid in v2.5 failscdk synth. One bucket across the deployment holds all pipeline template bodies and execution run I/O, andapp.assetBuckets.externalAssetBuckets[].isDefaultis what names it. Whenapp.assetBuckets.createNewBucketisfalse, exactly one external entry must setisDefault: true; at most one may set it in any configuration. v2.5 only required thatexternalAssetBucketsbe non-empty when no bucket was created, so an external-bucket-only configuration carries noisDefaultkey and now throws at configuration validation. The error names the field, so recovery is a one-line edit. See the external asset bucket object. app.pipelines.usePreviewPcPotreeViewer.sqsAutoRunOnAssetModifiedis removed, along with the identically named key underapp.pipelines.useSplatToolbox. Configuration validation ignores an unrecognized key rather than rejecting it, so a stale entry left inconfig.jsonraises no error at synth or deploy, and the automatic re-run of the Potree point cloud conversion on asset modification simply stops. There is no replacement under the v2.6 trigger model:fileUploadis the only trigger type, so a workflow fires when a matching file is uploaded and not when an existing asset is edited.- New OpenSearch index names:
vams-assets-v3andvams-files-v3. The new mapping adds ageo_MD_locationfield of typegeo_shapethat powers the new geospatial search filter and map view. The previous v2 indexes are abandoned and remain in OpenSearch until you delete them manually. - Provisioned OpenSearch domains are upgraded from engine version 2.7 to 3.5. Serverless collections are reworked separately (see below).
- OpenSearch Serverless collections are reshaped onto a next-generation collection group with new
app.openSearch.useServerlesssettings (nextGen,allowPublic,enableStandbyReplicas, and configurable OCU capacity). The collection cannot be updated in place — it must be removed and re-created, then reindexed. See OpenSearch Serverless next-gen upgrade. app.openSearch.useServerless.allowPublicis new and defaults totrue, which fails configuration validation on a fully VPC-isolated deployment. Aconfig.jsoncarrying noallowPublickey resolves to a public collection, and a deployment with bothapp.useGlobalVpc.enabledandapp.useGlobalVpc.useForAllLambdasset totruethen throws atcdk synth, because an all-Lambdas-in-VPC deployment cannot reach a public collection. SetallowPublictofalse— that is the setting which reproduces the v2.5 private-collection behavior for this topology, where the collection was placed behind a VPC endpoint automatically.- The VPC is no longer enabled automatically. If a feature that requires a VPC (ALB, OpenSearch Provisioned, or any container-based pipeline) is enabled while
app.useGlobalVpc.enabledisfalse, the deployment now fails configuration validation with an error that lists the offending features, rather than silently turning the VPC on. See VPC is now required for certain features. - Provisioned OpenSearch
availabilityZoneCountnow defaults to2, and the VPC is built with exactly that many Availability Zones. Earlier releases always built the VPC across 3 Availability Zones for provisioned OpenSearch even though the domain only used 2, so on upgrade the previously-unused third AZ subnet is removed (a VPC downgrade). See OpenSearch Provisioned Availability Zone count downgrade. - AWS WAF changes from count-only monitoring to enforcement on a deployment with
app.useWafenabled. v2.5 ran the AWS Common Rule Set incountmode, recording matches without rejecting them. The three rule groups now declared ininfra/config/policy/wafPolicyConfig.json— Common Rule Set, Known Bad Inputs, and Amazon IP Reputation List — are in block mode. A request that previously only incremented a counter is answered403by AWS WAF before it reaches the authorizer or any Lambda function, so it produces no VAMS log entry to correlate with the upgrade. Two Common Rule Set rules are already overridden back tocountbecause VAMS traffic trips them:SizeRestrictions_BODYfor multi-part upload bodies, andSizeRestrictions_QUERYSTRINGfor the presigned URL the SuperSplat viewer passes in its?load=parameter. Review the AWS WAF blocked-request metrics after upgrading; set"block": falseon a group in that file to return it to monitor mode, or remove the file to restore count-only behavior. - GPU pipeline AWS Batch compute environments move to the Amazon Linux 2023 NVIDIA-accelerated AMI (
ECS_AL2023_NVIDIA). AWS Batch blocks creation of new Amazon ECS compute environments that use Batch-provided Amazon Linux 2 AMIs, so earlier image types fail on a new deployment. This affects the Gaussian Splat Toolbox, NVIDIA Cosmos (Predict, Reason, Transfer), Cosmos 3, GR00T, and Isaac Lab pipelines. Each affected GPU compute environment is replaced on upgrade, so drain or wait for in-flight GPU pipeline jobs before deploying. All supported GPU instance families (G5, G6, G6E, P4DE, P5, P5E) work with this AMI; theP3andG3families are not supported by it.
Required migration steps:
-
Deploy the v2.6 CDK stack. The schema-deploy custom resource creates the empty v3 indexes; the v2 indexes are left in place but unreferenced.
-
Navigate to the migration scripts directory:
cd infra/deploymentDataMigration/v2.5_to_v2.6/upgrade -
Copy and configure the migration configuration file:
cp v2.5_to_v2.6_migration_config.json my_migration_config.json -
Set
resource_names_ssm_param_prefixin the config to the value of the CloudFormation outputResourceNamesSSMParamPrefixOutputfrom your stack, and setaws_region(andaws_profileif needed):aws cloudformation describe-stacks --stack-name your-vams-stack \--query 'Stacks[0].Outputs[?OutputKey==`ResourceNamesSSMParamPrefixOutput`].OutputValue' \--output textThe reindexer Lambda function name is then resolved automatically from the deployment's SSM Parameter Store resource-name parameters (requires
ssm:GetParametersByPathon the prefix). To skip or override the lookup, setreindexer_function_nameexplicitly to the value of the CloudFormation outputOpenSearchReindexerFunctionNameOutputinstead. -
Do a dry run first. Each step reports the rows it would write without writing them.
Linux / macOS
chmod +x run_migration.sh./run_migration.sh my_migration_config.json --dry-runWindows
.\run_migration.ps1 -ConfigFile my_migration_config.json -DryRunA dry run reports zero rows for any step whose source rows are absent or already migrated, so a zero-row result on its own is not evidence that the step is configured correctly. Read the per-step counts against what the deployment actually holds.
-
Run the migration:
Linux / macOS
./run_migration.sh my_migration_config.jsonWindows
.\run_migration.ps1 -ConfigFile my_migration_config.json -
The migration runs seven independent steps in the order below.
--stepsselects a single step; the default (all) runs every one.Step --stepsvalueWhat it does OpenSearch reindex reindexInvokes the deployed reindexer Lambda, which re-publishes every asset record so the asset indexer writes into vams-assets-v3(including the newgeo_MD_locationfield), lists every asset bucket and re-publishes file events intovams-files-v3, and returns aggregate success/failure counts.Asset history backfill assetHistoryBackfills the new asset history table from existing asset and version records — a createrecord from each asset's v0 version, plusarchive/unarchiverecords inferred from the asset's archive fields.Workflow executions overhaul workflowExecutionsReshapes legacy workflow execution rows into the V2 workflow-keyed tables (main record, workflow inputs, per-pipeline execution records, and input files). The V1 table is never modified. Auxiliary preview relocation auxPreviewRelocationMoves auxiliary-bucket preview and viewer objects to the database-scoped per-file layout by copying each object to its new key and then deleting the old one. Previews are unavailable between the deploy and this step. Pipeline + workflow definitions pipelineWorkflowDefinitionsMigrates user-database pipeline and workflow definitions from the V1 tables to the V2 tables, preserving each pipeline's parameters as a migrated-defaulttemplate. ShippedGLOBALbuilt-ins are skipped.Global-list partition backfill globalListBackfillStamps the allListPartitionattribute on V2 pipeline, workflow, and execution rows that predate it, so the cross-database "all pipelines / workflows / executions" lists return them.Tags namespacing tagsNamespacingCopies every legacy tag and tag type into the V2 composite-key tables under the GLOBALpartition. Asset tag lists are unchanged.
pipelineWorkflowDefinitions is the one step that is not safe to repeat. Its V2 rows are keyed by the same (databaseId, pipelineId/workflowId) as the V1 source, and the overwrite is unconditional: a second run replaces each migrated pipeline, workflow, and migrated-default template with the V1-derived record, discarding every edit made since the first run — renames, archive flags, and template bodies included. Nothing reports the loss.
Run it once as part of the upgrade. If a later run is needed to pick up definitions added afterwards, restrict it to the specific rows you intend to reset, and expect to re-apply any edits to rows it touches. The other six steps are safe to repeat: reindex rewrites documents from the live source records, assetHistory and workflowExecutions use deterministic record IDs and overwrite with the same values, auxPreviewRelocation skips objects already in the new layout, and globalListBackfill and tagsNamespacing write under a condition expression that skips rows already present.
--clear-indexes defaults to falseThe v3 indexes are empty after the v2.6 CDK deploy, so the first migration run never needs to clear them. Pass --clear-indexes only if a previous run partially populated v3 and you want to start clean.
v2.6 renames the OpenSearch record-type discriminator to str_rectype and sets it on every document
write. Because it is set on WRITE, a document indexed before the upgrade does not acquire it just by
deploying: app.openSearch.reindexOnCdkDeploy is false by default and a deployment does not replay
indexing. Any search that filters on the discriminator therefore returns nothing for pre-upgrade
content until the reindex has run.
The reindex step above is the backfill. It repopulates both v3 indexes from DynamoDB and Amazon S3
through the current indexers, so every live asset and file document is rewritten with the field. No
separate command is needed — it is part of the default run, and can be run alone:
python v2.5_to_v2.6_migration.py --config my_migration_config.json --steps reindex
Two things to expect afterwards:
- The first search immediately after a large reindex can return a 500. OpenSearch Serverless is still settling; retry after about 30 seconds. It is not a failed migration.
- Documents whose source no longer exists are not rewritten. A reindex repopulates from live
DynamoDB and S3 records, so a stale document left by an asset or file deleted earlier keeps its
pre-upgrade shape indefinitely. Those documents are unreachable through any live-entity read. To
clear them out as well, re-run the step with
--clear-indexes, which empties v3 before repopulating. :::
v2.6 adds per-database tag namespacing, backed by the new composite-key TagStorageTableV2 and TagTypeStorageTableV2 DynamoDB tables (the former single-key TagStorageTable/TagTypeStorageTable are retained as legacy migration sources). The default migration run above includes the tagsNamespacing step, which copies every existing tag and tag type into the new tables under the GLOBAL partition, so all previously existing tags become GLOBAL tags. Asset tag lists are unchanged. The step is idempotent (already-copied rows are skipped on re-run) and can be run on its own:
python v2.5_to_v2.6_migration.py --config my_migration_config.json --steps tagsNamespacing
The v2.6 CDK switches OPENSEARCH_VERSION to OPENSEARCH_3_5. This applies only to provisioned deployments (app.openSearch.useProvisioned.enabled = true); serverless collections are unaffected. Amazon OpenSearch Service supports in-place version upgrades, but a major-version jump on a long-running domain can occasionally fail or exceed the CloudFormation custom-resource timeout.
If cdk deploy fails on the OpenSearch domain version upgrade, recover by deploying first with OpenSearch disabled and then re-enabling it:
- Set
app.openSearch.useProvisioned.enabled = false(anduseServerless.enabled = false) ininfra/config/config.json. - Run
cdk deploy --all --require-approval neverto delete the existing 2.7 domain. - Restore the original
useProvisionedconfiguration inconfig.json. - Run
cdk deploy --all --require-approval neverto create a fresh 3.5 domain with the empty v3 indexes. - Run this migration to repopulate the v3 indexes from source data.
This recovery path discards only the OpenSearch indexes; all source data lives in DynamoDB and S3, and the reindex restores the full search corpus. For details and other troubleshooting steps, see the v2.5 to v2.6 migration README.
OpenSearch Serverless next-gen upgrade
v2.6 reworks the OpenSearch Serverless collection and adds new app.openSearch.useServerless settings:
| Setting | Default | Behavior |
|---|---|---|
nextGen | true (commercial), false (GovCloud and EU) | Sets the collection group generation to NEXTGEN (true) or CLASSIC (false). The collection is placed in a collection group either way; NEXTGEN adds scale-to-zero support. |
allowPublic | true | When false, the collection is reachable only through a VPC endpoint and requires app.useGlobalVpc.enabled to be true (built across 2 Availability Zones). Only the OpenSearch-facing Lambda functions are placed in the VPC, so app.useGlobalVpc.useForAllLambdas does not need to be true. |
enableStandbyReplicas | tracks nextGen | Enables Serverless standby replicas. Required for NEXTGEN (must be true when nextGen is true); optional for CLASSIC. Defaults to the value of nextGen. |
minIndexingOcu / maxIndexingOcu / minSearchOcu / maxSearchOcu | 2 / 16 / 2 / 16 | OpenSearch Compute Unit (OCU) capacity bounds. Each must be one of 0, 2, 4, 8, 16, or any multiple of 16. A minimum of 0 (scale-to-zero) requires nextGen = true. |
Previously, a Serverless collection was updated by the CDK changeset like any other resource. In v2.6 the collection is placed in a collection group, the allowPublic network-policy change is applied, and the group generation is set, all of which reshape the collection, so an existing Serverless deployment cannot be updated in place. Remove the collection and re-create it, then reindex:
-
Set
app.openSearch.useServerless.enabled = falseininfra/config/config.jsonand deploy. This removes the existing collection.npx cdk deploy --all --require-approval never -
Set
app.openSearch.useServerless.enabled = trueagain with the desired settings (nextGen,allowPublic, and OCU values) and deploy. This creates the new collection group and collection.npx cdk deploy --all --require-approval never -
Reindex to repopulate the new collection — run the reindex utility (
infra/deploymentDataMigration/v2.5_to_v2.6/upgradeortools/reindex_utility.py), or setapp.openSearch.reindexOnCdkDeploy = truefor one deployment and then set it back tofalse.
Tearing down the collection deletes the indexes, but all source data lives in Amazon DynamoDB and Amazon S3, so the reindex restores the full search corpus. GovCloud and EU Sovereign Cloud deployments must keep nextGen = false.
A private collection (allowPublic = false) is reachable only from inside the VPC and not from a local machine, so the reindex utility's direct mode cannot reach it. For a private collection, reindex through the deployed Lambda (lambda mode) or set app.openSearch.reindexOnCdkDeploy = true.
VPC is now required for certain features
In earlier releases, enabling a feature that needs a VPC while app.useGlobalVpc.enabled was false silently turned the VPC on. In v2.6 this is a configuration error instead: the deployment fails and lists the features that require a VPC. The VPC-requiring features are ALB (useAlb), OpenSearch Provisioned (openSearch.useProvisioned), and the container-based pipelines (Potree viewer, 3D preview thumbnail, GenAI labeling, Gaussian splatting, RapidPipeline ECS/EKS, ModelOps, Isaac Lab, NVIDIA Cosmos, NVIDIA Gr00t).
If your existing config.json relied on the old implicit behavior, update it before deploying v2.6: set app.useGlobalVpc.enabled to true (the value the deployment was effectively using all along), or disable the listed features. No infrastructure changes result from setting the flag to the value that was already in effect — this is a configuration-file correction only.
OpenSearch Provisioned Availability Zone count downgrade
In v2.6, provisioned OpenSearch adds app.openSearch.useProvisioned.availabilityZoneCount (default 2), and the VPC is built with exactly that many Availability Zones. Earlier releases always built the VPC across 3 Availability Zones for provisioned OpenSearch, even though the OpenSearch domain itself only ever used 2 of them — the third AZ's subnet was created but unused. v2.6 now provisions 2 Availability Zones by default (or 3 only when you set availabilityZoneCount to 3) and uses them consistently.
On upgrade, this is a VPC downgrade: the previously-unused third Availability Zone's subnet is removed. Removing a subnet can fail in AWS CloudFormation when the subnet still holds elastic network interfaces — in this case the shared interface VPC endpoints placed across the isolated subnets, and the VPC-attached Lambda (Hyperplane) ENIs created when app.useGlobalVpc.useForAllLambdas is true. The OpenSearch domain itself does not change AZ count (it was already on 2), so the failure is specifically about deleting the orphaned third-AZ subnet.
You have two options:
- Keep the existing 3-AZ VPC layout (no change): set
app.openSearch.useProvisioned.availabilityZoneCountto3inconfig.jsonbefore deploying v2.6. The VPC is unchanged and the upgrade proceeds normally. - Move to the 2-AZ layout (default): because the third AZ's subnet must be deleted, follow the staged drain-and-redeploy procedure so the elastic network interfaces release first. Turn off the VPC and all VPC-associated components, deploy to release the ENIs, manually clear any orphaned ENIs / subnets / VPC resources that still fail to delete, then redeploy with the 2-AZ settings and reindex. The full step-by-step is documented in Subnet or VPC Resource Deletion Failures — set
availabilityZoneCountto2when you re-enable OpenSearch in that procedure. No asset data is lost: search data is rebuilt from the authoritative DynamoDB tables and S3 buckets by the reindex.
Decide on availabilityZoneCount (2 or 3) before upgrading. Setting it to 3 preserves the existing VPC with no teardown. Accepting the default of 2 removes a subnet and may require the manual VPC teardown above if elastic network interfaces have not finished detaching.
API Gateway REST API endpoint change
In v2.6 the backend API is an API Gateway REST API (v1) served under the fixed stage path /api, replacing the previous HTTP API (v2). On deployment the API Gateway identifier and invoke URL change.
The app.api configuration block is also restructured in v2.6: the per-implementation settings move under a new app.api.apiGatewayRest sub-block, and a new app.api.apiType field (fixed to "APIGATEWAY_REST") selects the API implementation. Configuration validation (getConfig()) applies that restructuring automatically — app.api.apiType is defaulted, and any flat globalRateLimit, globalBurstLimit, endpointType, apiGatewayTimeoutTime, and externalRegionalAPIGatewayVPCEId values are carried into app.api.apiGatewayRest, the last of them as optionalExternalPrivateApigVPCEId, which applies only to a PRIVATE endpoint. An existing config.json therefore deploys unedited (see the API configuration reference). Custom APIs added in a fork outside this project are the exception: those settings are unknown to the configuration loader, so move them under the new app.api shape by hand. Declaring app.api.apiGatewayRest yourself replaces the carry-over, so a hand-written block must list every field it needs. The REST API stage name is not a configuration option — it is the fixed value api.
The web application is unaffected: it reads the API base URL at runtime from /api/amplify-config, and the CloudFront /api/* behavior (or ALB redirect) absorbs the stage path so browser URLs remain /api/*.
The change affects any client that was configured directly against the old API Gateway endpoint URL:
- VAMS CLI: re-run
vamscli setupand provide the new API URL. When pointing the CLI at the deployment's front (CloudFront/ALB) the base URL is unchanged; when pointing it directly at the execute-api endpoint, pass the bare endpoint URL (https://{rest-api-id}.execute-api.{region}.amazonaws.com) — the CLI appends the/apistage path automatically. - External integrations and scripts: update any stored API base URL to the new endpoint.
The deployment exposes the new endpoint as the CloudFormation output APIGatewayEndpointOutput. IP allow-list enforcement continues to work for both fronted and direct callers — the authorizer resolves the true client IP from the front's forwarded headers when present, and from the direct connection otherwise — so existing direct integrations keep working once re-pointed at the new URL.
Execution visibility
In v2.6 every execution read path applies one permission rule: GET on the execution's workflow, plus the operation's action on every asset the run read — each input file's asset and each asset named as a metadata source. Earlier releases accepted access to any one of a run's assets for the listing while requiring all of them elsewhere, so a listing could offer a row whose details then returned 403. One rule across the list, the details, the logs, the abort, and the re-run removes that inconsistency.
The narrower side of this is worth checking against existing roles before upgrading. A role scoped to a subset of databases loses list visibility of runs that span databases outside its scope, and loses the ability to re-run them, even when it can read some of the assets involved. Runs whose assets all sit inside the role's scope behave as before. To restore the earlier breadth for a role, widen its asset and database GET constraints to cover the databases those runs span.
An execution has assets only when it read or wrote one, and both sides carry the check: a run is authorized on every asset it read and on the asset it wrote to. A results-only run writes no files and has no asset at all, leaving workflow GET as its whole gate.
The output-asset half narrows access for a deployment that already ran workflows writing into a database outside the reader's scope. A role that could previously list, open, re-run, and abort such a run because it held GET on the run's input assets now needs GET on the destination asset as well (and POST on it to abort or permanently delete, matching the POST the launch already required). Review roles that read one database and write to another, and widen their asset and database constraints to cover the destination where the earlier breadth is still wanted.
Deleting an asset does not delete the executions that ran against it. An asset that has been permanently deleted is authorized on the database it lived in, under the same action — a database is never removed, since deleting one archives the record — so the history of runs against a deleted asset stays reachable by whoever can read that database.
Archiving an asset is not a deletion and does not change how its executions are authorized. An archived asset's record is retained, so it is still authorized on its own attributes (name, type, tags) exactly as it was before archiving, and any asset-level constraint that applied to it continues to apply.
Permission constraint audit for pipelineType
In v2.6 the pipeline objectType offers the criteria fields databaseId, pipelineId, pipelineExecutionType, category, and name. pipelineType is not among them: the value it held moves to category, and the migration copies each migrated pipeline's pipelineType value into that field. Built-in pipelines carry descriptive category labels instead, such as Conversion and GenAI.
When the permission policy is compiled, a criterion whose field is not a recognized constraint field is dropped. For a constraint that is already stored the drop is silent: stored constraints are not re-validated, the skip is recorded at informational log level only, and no authorization response reports it. The constraint therefore continues to read as authored while enforcing something different from what it states:
- Criteria within a rule are combined with AND. Dropping one from an allow rule removes a restriction, so the role reaches pipelines the constraint was written to exclude. This widens access.
- A rule whose only criterion was
pipelineTypecompiles to no criteria at all and is not emitted, so an allow rule of that shape grants nothing and a deny rule of that shape blocks nothing.
Creating or updating a constraint through the API rejects an unrecognized field, naming it and listing the fields allowed for the objectType, so re-saving one of these constraints fails until the criterion is re-authored. That error is the only signal VAMS gives, and it appears only when someone edits the constraint.
Audit every role constraint that uses the pipeline objectType before upgrading, and re-author each pipelineType criterion against category, using the category value the target pipelines actually carry. Stored constraints are left exactly as authored — no migration step rewrites them — so this audit is the only thing that restores the intended scope. See Permissions model for the constraint criteria structure.
Bucket listing route scoped to administrators
GET /buckets returns the whole asset-bucket registry — every bucket's name and prefix — and there is
no bucket object type, so the listing cannot be filtered per role at Tier 2. The route grant is the
only control available, and in v2.6 it is an administrator grant: the database-admin template and the
seeded default administrator role carry it, while the database-user, database-readonly, and
global-readonly templates and the seeded basicReadOnly role do not.
The seeded default constraints are rewritten on every deployment, so the basicReadOnly role loses the
grant when the update runs. Constraints already stored in the deployment are not reconciled: a role
authored by hand or from an earlier copy of a template keeps its /buckets grant until that constraint
is re-authored or the updated template is re-imported. Nothing reports the difference, so review the
api constraints of any non-administrator role that was built from a template and remove the
/buckets criterion to match the shipped scope.
For a role that keeps the route withheld, the only affected surface is the default-bucket selector on
the database create and edit form, which loads no options. Every other database operation continues to
work, and a default bucket can still be set by supplying a known defaultBucketId on
POST /database or PUT /database/{databaseId}.
Switching endpointType between PRIVATE and REGIONAL
Changing app.api.apiGatewayRest.endpointType on an existing deployment is supported and requires no manual steps. A PRIVATE endpoint carries an API Gateway resource policy that only permits invocation through the execute-api VPC interface endpoint (an aws:SourceVpce condition); a REGIONAL endpoint uses a public allow-all resource policy. VAMS writes the correct resource policy for the configured endpoint type on every deployment, so a PRIVATE → REGIONAL switch overwrites the VPC-restricted policy with the public one, and a REGIONAL → PRIVATE switch re-applies the restriction.
This explicit-policy behavior exists because Amazon API Gateway does not clear a previously-set resource policy when an update stops supplying one. If a stale PRIVATE resource policy is ever left on a now-REGIONAL API (for example, after an out-of-band change to the API), every public request — including the browser CORS preflight — is denied at the resource-policy layer with 403 AccessDeniedException ("no resource-based policy allows the execute-api:Invoke action"). Because that denial happens before any CORS headers are applied, the browser surfaces it as a missing Access-Control-Allow-Origin / failed-preflight error rather than an authorization failure. Re-running the VAMS deployment re-asserts the correct policy for the configured endpointType and resolves it.
Encryption at rest for log groups and the Isaac Lab file system
When app.useKmsCmkEncryption.enabled is true, CloudWatch log groups and the Isaac Lab training Amazon
EFS file system are encrypted with the shared VAMS customer-managed key. With the setting false nothing
changes: each resource keeps its service's AWS-managed key. The two resource types update differently, and
only one of them loses data.
Log groups update in place. Attaching a key to a log group is an AssociateKmsKey call, so the group
and the events it already holds survive the deployment. CloudWatch applies a key to new events only, so a
group that carries history ends up holding both unencrypted and encrypted events. The groups newly covered
are the REST API access log, the shared workflow log group, and each pipeline's state-machine and
container log groups. The nine audit log groups and the orchestration-bus
audit log group were already encrypted and are unchanged.
Three log groups are deliberately left on their AWS-managed key: the VPC flow log group, because the VPC nested stack is created before the storage nested stack that owns the key; the AWS CloudTrail log group, because it lives in the root stack and consuming the key from a nested stack makes the two circular; and the provisioned OpenSearch domain's slow-search, slow-index, and application log groups, which Amazon OpenSearch Service creates rather than VAMS.
The Isaac Lab training file system is replaced. KmsKeyId cannot be changed in place on an Amazon EFS
file system, so AWS CloudFormation creates a new, empty file system and deletes the existing one. The file
system is declared for deletion on stack teardown, so the old copy is not retained and any training
checkpoints it holds are lost. Copy anything you need off the file system before updating. The NVIDIA
Cosmos and GR00T model caches were created with the shared key and are not replaced.
If a resource fails to update
Disable the owning pipeline, deploy, then re-enable it. This removes the resource and recreates it cleanly instead of leaving the stack in a failed update.
- Set the pipeline's
enabledflag tofalseininfra/config/config.json. - Run
npx cdk deploy --all --require-approval never. - Set the flag back to
true. - Deploy again.
The same procedure applies to the CloudTrail log group through app.addStackCloudTrailLogs.
Every pipeline log group and the CloudTrail log group are configured for deletion on stack teardown, so disabling the owning feature discards the events the group holds — audit and diagnostic history included.
To keep any of that data, rename the log group in the Amazon CloudWatch console before the disabling deployment, or export its events. AWS CloudFormation deletes the group by name, so a renamed copy is left in place while the VAMS-named group is removed. The data cannot be preserved in place, because the name is what ties the group to the stack resource.
Switching between CloudFront and ALB distribution
Changing app.useCloudFront.enabled on an existing deployment renames the regional AWS WAF stack, because
the two distributions need differently-scoped web ACLs and a CloudFront-scoped ACL must live in us-east-1:
useCloudFront.enabled | Regional WAF stack | CloudFront WAF stack (us-east-1) |
|---|---|---|
false | <name>-waf-<base> | not created |
true | <name>-waf-regional-<base> | <name>-waf-<base> |
The naming keeps an in-place update for a deployment that never changes distribution. Switching does not migrate, and the two directions fail differently:
- ALB → CloudFront.
<name>-waf-<base>is redefined as the CloudFront stack in us-east-1 while the existing stack of that name holds the regional ACL in the deployment Region. A stack cannot change Region, so the deployment attempts a new us-east-1 stack under a name already in use elsewhere and the old regional stack is left unmanaged. - CloudFront → ALB.
<name>-waf-regional-<base>is no longer referenced and is left in place.
Release the DNS record first
When both fronts are configured with the same domainHost and a hosted zone, each one creates a Route 53
alias record for that name — as separate CloudFormation resources with different logical ids. AWS
CloudFormation creates the new record before deleting the old one, and Amazon Route 53 rejects a create for
a name that already exists, so the stack update fails and rolls back.
Delete the existing alias record before deploying:
aws route53 list-resource-record-sets --hosted-zone-id <ZONE_ID> --query "ResourceRecordSets[?starts_with(Name, '<HOST>')]"
# save that output, then delete the record with a DELETE change batch
The deployment recreates it pointing at the new front, so the hostname is unresolvable only for the length of the deploy. Keep the saved record: restoring it is the fastest rollback if the deployment fails.
An alternative that needs no manual DNS change is to deploy twice — first with
useCloudFront.customDomain.enabled (or useAlb.domainHost) cleared, which lets CloudFormation remove the
old record as part of removing the old front, then again with the custom domain restored. That trades a
second deployment for not touching live DNS by hand.
Before switching, delete the WAF stack that the new configuration does not use, and confirm no web ACL is still associated with a distribution or load balancer first — AWS WAF refuses to delete an associated ACL. Then deploy. The web ACL carries no data, so nothing is lost.
Breaking changes checklist
Use this checklist to determine if additional actions are needed after updating.
| Change type | Versions affected | Action required |
|---|---|---|
| DynamoDB table schema change | v2.3 to v2.4, v2.4 to v2.5 | Run version-specific migration scripts. |
| Amazon OpenSearch Service reindex | v2.2 to v2.3, v2.3 to v2.4, v2.5 to v2.6 | Run reindex script or set reindexOnCdkDeploy: true. |
| OpenSearch engine version upgrade | v2.5 to v2.6 (provisioned only, 2.7 → 3.5) | Redeploy with OpenSearch disabled then re-enabled if the in-place upgrade fails. |
| OpenSearch Serverless next-gen reshape | v2.5 to v2.6 (serverless only) | Disable Serverless, deploy, re-enable with new settings, deploy, then reindex. Keep nextGen: false on GovCloud/EU. |
| VPC no longer auto-enabled | v2.5 to v2.6 | Set app.useGlobalVpc.enabled: true (or disable VPC-requiring features) if validation fails. |
| OpenSearch AZ count VPC downgrade | v2.5 to v2.6 (provisioned only) | Set availabilityZoneCount: 3 to keep the existing VPC, or follow the drain-and-redeploy teardown to move to 2 AZs. |
| Externally registered pipelines | v2.5 to v2.6 | Port each pipeline's input reads, task-token return, and sub-process registration, and declare its definition in a vamsSchema bundle. |
| Three API routes removed | v2.5 to v2.6 | Repoint direct API clients: PUT /pipelines → POST /database/{databaseId}/pipelines, PUT /workflows → POST /database/{databaseId}/workflows, POST /database/{databaseId}/assets/{assetId}/workflows/{workflowId} → POST /workflows/{workflowDatabaseId}/{workflowId}/execute. A removed route answers 403, not 404. |
| AWS WAF count mode → block mode | v2.5 to v2.6 (app.useWaf enabled) | Review AWS WAF blocked-request metrics after upgrading. Set "block": false on a rule group in infra/config/policy/wafPolicyConfig.json to return it to monitor mode. Blocked requests produce no VAMS log entry. |
OpenSearch Serverless allowPublic | v2.5 to v2.6 (serverless only) | Set app.openSearch.useServerless.allowPublic: false when app.useGlobalVpc.useForAllLambdas is true; the new default of true fails cdk synth on that topology. |
| API Gateway REST API endpoint change | v2.5 to v2.6 | Re-run vamscli setup; update any client or script that stored the API Gateway invoke URL. |
| External asset bucket default | v2.5 to v2.6 (createNewBucket: false) | Set isDefault: true on exactly one app.assetBuckets.externalAssetBuckets entry; configuration validation otherwise fails cdk synth. |
| Pipeline constraint field audit | v2.5 to v2.6 | Re-author any role constraint criterion that uses pipelineType against category; no script rewrites constraints. |
| Bucket listing route scoped to admins | v2.5 to v2.6 | Remove the /buckets api criterion from non-administrator roles built from an earlier template; stored constraints are not reconciled. |
| Permission constraint migration | v2.3 to v2.4, v2.4 to v2.5 | Run constraint migration script if custom constraints exist. |
| API Gateway authorizer change | v2.2 to v2.3 | Reset authorizer cache after deployment. |
| Pipeline CDK construct rename | v2.2 to v2.3 | Deploy without pipelines, then redeploy with pipelines enabled. |
| Website framework change | v2.4 to v2.5 | Clear node_modules and reinstall: cd web && rm -rf node_modules && npm install. |
| Distribution switch renames the WAF stack and collides on DNS | any version (app.useCloudFront.enabled changed) | Delete the existing Route 53 alias record for the shared domainHost before deploying — both fronts create one for the same name and Route 53 rejects the create, failing the update. Also delete the WAF stack the new configuration does not use, disassociating its web ACL first since AWS WAF refuses to delete an associated ACL. |
| Encryption at rest for log groups and EFS | v2.5 to v2.6 (app.useKmsCmkEncryption enabled) | Log groups update in place. The Isaac Lab training EFS is replaced and its checkpoints are lost — copy them off first. If a resource fails to update, disable the owning pipeline, deploy, and re-enable it; rename any log group whose data you need to keep before the disabling deploy, because the cycle deletes it. |
Rollback guidance
If an update causes issues, the rollback approach depends on the update method used.
In-place update rollback
-
Check out the previous version tag:
git checkout tags/<PREVIOUS_VERSION>cd web && npm install && npm run build && cd ..cd infra && npm installnpx cdk deploy --all --require-approval never -
If DynamoDB tables were replaced during the update, look for the orphaned tables first. Because the tables use a
RETAINremoval policy, a replacement leaves the original table (and its data) in the account under its old name, outside the stack. Migrate from the orphaned table where the data is still current, and restore from the backups taken in the pre-update checklist only where it is not. -
If Amazon OpenSearch Service indexes were modified, trigger a reindex from DynamoDB data.
A rollback deployment creates its own tables; it does not reattach the ones a replacement orphaned. Reconnecting the data is a manual step in every case, so maintain backups before updating and record the stack's table names as part of the pre-update checklist.
A/B deployment rollback
- Switch DNS records back to Stack A endpoints.
- Destroy Stack B using
cdk destroy --all. - Verify Stack A is functioning correctly.