diff --git a/infra/lib/constructs/app-service.ts b/infra/lib/constructs/app-service.ts index fd724dbe..48607575 100644 --- a/infra/lib/constructs/app-service.ts +++ b/infra/lib/constructs/app-service.ts @@ -8,7 +8,7 @@ import * as logs from "aws-cdk-lib/aws-logs"; import * as route53 from "aws-cdk-lib/aws-route53"; import * as iam from "aws-cdk-lib/aws-iam"; import * as ssm from "aws-cdk-lib/aws-ssm"; -import { Construct } from "constructs"; +import { Construct, IConstruct } from "constructs"; import { EnvName, prefix } from "../config"; import { bakedOpenSweArm64 } from "./ami-cache"; @@ -230,6 +230,27 @@ export class AppService extends Construct { ], }); + // `requireImdsv2: true` makes CDK auto-create a launch template, and it names + // that LT from the construct id ("Instance" -> "InstanceLaunchTemplate") with NO + // env qualifier — so OpenSweDevStack and OpenSweProdStack both want the identical + // LT name and the second env to deploy fails with + // InvalidLaunchTemplateName.AlreadyExistsException (prod rollback, 2026-06-29). + // Force a per-env LT name. Done via an aspect because the LT is created at synth + // time by the requireImdsv2 handling, not in this constructor. + cdk.Aspects.of(this.instance).add({ + visit(node: IConstruct) { + if (node instanceof ec2.CfnLaunchTemplate) { + node.launchTemplateName = `${p}-lt`; + } + // The instance references the LT BY NAME, so the reference must be renamed in + // lockstep (preserve the GetAtt version) or CFN can't find the template. + if (node instanceof ec2.CfnInstance && node.launchTemplate) { + const spec = node.launchTemplate as ec2.CfnInstance.LaunchTemplateSpecificationProperty; + node.launchTemplate = { ...spec, launchTemplateName: `${p}-lt` }; + } + }, + }); + // SSM deploy document (open-swe--deploy): runs the baked // /opt/open-swe/bin/deploy.sh to pull the latest release + restart. CI fires it // (tag-scoped to project=open-swe,env=) after uploading a release, so the