@@ -394,65 +394,30 @@ export class AgentStack extends Stack {
394394
395395 runtimeArnHolder = runtime . agentRuntimeArn ;
396396
397- // --- AgentCore log-delivery: pin to the already-deployed identity ---
398- // The agentcore-alpha Runtime auto-creates AWS::Logs::DeliverySource +
399- // Delivery + DeliveryDestination for each loggingConfig. A construct-path
400- // rename across alpha builds CHURNED both the CFN logical IDs AND the
401- // account-scoped DeliverySource ``Name`` (live stack:
402- // ``RuntimeCDKSource…``/``cdk-{type}-source-…``; current synth:
403- // ``RuntimeApplicationLogsDeliverySource…``/``backgroundagentdevRuntime…``).
404- // Because a DeliverySource Name is account-unique, CFN's create-before-
405- // delete on the churned id hits ``AlreadyExists`` and rolls the whole stack
406- // back. Pin the two DeliverySources' logical IDs AND Names back to the
407- // deployed values so CFN sees them as the SAME resources (no-op update),
408- // not new ones. Log delivery is stateless routing config — no data risk.
409- // Best-effort lookup by child id; if a future alpha renames the children
410- // this silently no-ops (and a fresh stack just deploys clean).
411- // Pin ALL THREE auto-created resource kinds to their deployed logical IDs:
412- // - DeliverySource + DeliveryDestination: also pin ``Name`` (both are
413- // account-unique, so a churned logical id collides on create).
414- // - Delivery: logical-id pin ONLY. A Delivery has no Name, but it IS
415- // unique per (source, destination) pair — and since the source+dest
416- // are now pinned, a churned Delivery logical id would create-before-
417- // delete a SECOND link over the same pair and hit ``AlreadyExists``
418- // ('identifier null already exists'). Pinning the logical id makes CFN
419- // update it in place. (Pass liveName undefined for these.)
420- const pinLogResource = ( childId : string , liveLogicalId : string , liveName ?: string ) : void => {
421- const res = runtime . node . tryFindChild ( childId ) as CfnResource | undefined ;
422- if ( ! res ) return ;
423- res . overrideLogicalId ( liveLogicalId ) ;
424- if ( liveName !== undefined ) res . addPropertyOverride ( 'Name' , liveName ) ;
425- } ;
426- pinLogResource (
427- 'ApplicationLogsDeliverySource' ,
428- 'RuntimeCDKSourceAPPLICATIONLOGSbackgroundagentdevRuntimeBC0AE9ED96A02E02' ,
429- 'cdk-applicationlogs-source-backgroundagentdevRuntimeBC0AE9ED' ,
430- ) ;
431- pinLogResource (
432- 'UsageLogsDeliverySource' ,
433- 'RuntimeCDKSourceUSAGELOGSbackgroundagentdevRuntimeBC0AE9ED544FBB22' ,
434- 'cdk-usagelogs-source-backgroundagentdevRuntimeBC0AE9ED' ,
435- ) ;
436- pinLogResource (
437- 'ApplicationLogsDest' ,
438- 'RuntimeCdkLogGroupApplicationLogsDeliverybackgroundagentdevRuntimeBC0AE9EDbackgroundagentdevRuntimeApplicationLogGroup454A95E8DestapplicationlogsE09F77DC' ,
439- 'cdk-cwl-Destapplication-logs-dest-backgrounp454A95E829BF8A27' ,
440- ) ;
441- pinLogResource (
442- 'UsageLogsDest' ,
443- 'RuntimeCdkLogGroupUsageLogsDeliverybackgroundagentdevRuntimeBC0AE9EDbackgroundagentdevRuntimeUsageLogGroup7FA1FA67Destusagelogs9AB608D0' ,
444- 'cdk-cwl-Destusage-logs-dest-backgroundagroup7FA1FA67A8A16CEE' ,
445- ) ;
446- // The two Delivery links: logical-id pin only (no Name — unique per
447- // source/destination pair, which are now pinned).
448- pinLogResource (
449- 'ApplicationLogsDelivery' ,
450- 'RuntimeCdkLogGroupApplicationLogsDeliverybackgroundagentdevRuntimeBC0AE9EDbackgroundagentdevRuntimeApplicationLogGroup454A95E8Delivery92FE492C' ,
451- ) ;
452- pinLogResource (
453- 'UsageLogsDelivery' ,
454- 'RuntimeCdkLogGroupUsageLogsDeliverybackgroundagentdevRuntimeBC0AE9EDbackgroundagentdevRuntimeUsageLogGroup7FA1FA67Delivery40F023D7' ,
455- ) ;
397+ // --- AgentCore log-delivery: OPT-IN migration shim for ONE pre-existing
398+ // stack whose logical IDs churned under an agentcore-alpha bump ---
399+ //
400+ // Background: the agentcore-alpha Runtime auto-creates AWS::Logs::
401+ // DeliverySource + Delivery + DeliveryDestination per loggingConfig. An
402+ // alpha construct-path rename CHURNED both the CFN logical IDs and the
403+ // account-scoped DeliverySource/DeliveryDestination ``Name`` of an
404+ // ALREADY-DEPLOYED stack. Because those Names are account-unique, CFN's
405+ // create-before-delete on the new ids collides with the live ones →
406+ // ``AlreadyExists`` → whole-stack rollback. The fix is to re-pin the
407+ // churned resources to the values CFN already has so it updates them in
408+ // place instead of recreating.
409+ //
410+ // CRITICAL: this is needed ONLY by a stack that was deployed BEFORE the
411+ // alpha bump. A fresh stack (a new env, CI, this PR on a clean account)
412+ // has NO pre-existing resources to collide with and MUST synth the
413+ // current alpha's natural ids — so the shim is OFF by default and is
414+ // enabled per-stack via context:
415+ // cdk deploy -c pinnedLogDeliveryStack=<stackName>
416+ // (or the `pinnedLogDelivery` map in cdk.json). When the running stack
417+ // doesn't match, NONE of the overrides apply and synth is pristine.
418+ // Once the affected stack has been migrated + a clean redeploy confirmed,
419+ // this shim and its context entry can be deleted outright.
420+ maybePinChurnedLogResources ( this , runtime ) ;
456421
457422 // --- Session storage (preview) ---
458423 // The L2 construct does not yet expose filesystemConfigurations; use the
@@ -1303,3 +1268,90 @@ export class AgentStack extends Stack {
13031268 } ) ;
13041269 }
13051270}
1271+
1272+ /**
1273+ * A churned log-delivery resource to re-pin: the construct child id under the
1274+ * Runtime, the logical id CFN already has deployed, and (for the account-unique
1275+ * Source/Destination kinds) the deployed ``Name``. ``liveName`` is omitted for
1276+ * Delivery links, which have no Name.
1277+ */
1278+ interface PinnedLogResource {
1279+ readonly childId : string ;
1280+ readonly liveLogicalId : string ;
1281+ readonly liveName ?: string ;
1282+ }
1283+
1284+ /**
1285+ * Per-stack pin tables for the agentcore-alpha log-delivery churn (#247 #58).
1286+ * Keyed by ``stackName``. ONLY the listed stack is migrated; every other stack
1287+ * (fresh deploys, CI, new envs) is absent here → synth is pristine. A stack can
1288+ * also be supplied at deploy time via context (see {@link maybePinChurnedLogResources}).
1289+ *
1290+ * ``backgroundagent-dev`` was deployed before an alpha bump churned its
1291+ * DeliverySource/Destination/Delivery logical ids + account-unique Names; these
1292+ * values come from `aws cloudformation list-stack-resources` on that live stack.
1293+ * Delete this entry once that stack is migrated + a clean redeploy is confirmed.
1294+ */
1295+ const PINNED_LOG_DELIVERY_BY_STACK : Record < string , readonly PinnedLogResource [ ] > = {
1296+ 'backgroundagent-dev' : [
1297+ {
1298+ childId : 'ApplicationLogsDeliverySource' ,
1299+ liveLogicalId : 'RuntimeCDKSourceAPPLICATIONLOGSbackgroundagentdevRuntimeBC0AE9ED96A02E02' ,
1300+ liveName : 'cdk-applicationlogs-source-backgroundagentdevRuntimeBC0AE9ED' ,
1301+ } ,
1302+ {
1303+ childId : 'UsageLogsDeliverySource' ,
1304+ liveLogicalId : 'RuntimeCDKSourceUSAGELOGSbackgroundagentdevRuntimeBC0AE9ED544FBB22' ,
1305+ liveName : 'cdk-usagelogs-source-backgroundagentdevRuntimeBC0AE9ED' ,
1306+ } ,
1307+ {
1308+ childId : 'ApplicationLogsDest' ,
1309+ liveLogicalId : 'RuntimeCdkLogGroupApplicationLogsDeliverybackgroundagentdevRuntimeBC0AE9EDbackgroundagentdevRuntimeApplicationLogGroup454A95E8DestapplicationlogsE09F77DC' ,
1310+ liveName : 'cdk-cwl-Destapplication-logs-dest-backgrounp454A95E829BF8A27' ,
1311+ } ,
1312+ {
1313+ childId : 'UsageLogsDest' ,
1314+ liveLogicalId : 'RuntimeCdkLogGroupUsageLogsDeliverybackgroundagentdevRuntimeBC0AE9EDbackgroundagentdevRuntimeUsageLogGroup7FA1FA67Destusagelogs9AB608D0' ,
1315+ liveName : 'cdk-cwl-Destusage-logs-dest-backgroundagroup7FA1FA67A8A16CEE' ,
1316+ } ,
1317+ // Delivery links: logical-id pin only (no Name — unique per source/dest pair).
1318+ {
1319+ childId : 'ApplicationLogsDelivery' ,
1320+ liveLogicalId : 'RuntimeCdkLogGroupApplicationLogsDeliverybackgroundagentdevRuntimeBC0AE9EDbackgroundagentdevRuntimeApplicationLogGroup454A95E8Delivery92FE492C' ,
1321+ } ,
1322+ {
1323+ childId : 'UsageLogsDelivery' ,
1324+ liveLogicalId : 'RuntimeCdkLogGroupUsageLogsDeliverybackgroundagentdevRuntimeBC0AE9EDbackgroundagentdevRuntimeUsageLogGroup7FA1FA67Delivery40F023D7' ,
1325+ } ,
1326+ ] ,
1327+ } ;
1328+
1329+ /**
1330+ * OPT-IN migration shim (#247 #58): re-pin the agentcore-alpha-churned
1331+ * log-delivery resources of ONE already-deployed stack to the logical ids +
1332+ * Names CFN already has, so a stack deployed before an alpha bump updates them
1333+ * in place instead of hitting ``AWS::Logs::DeliverySource AlreadyExists`` on
1334+ * create-before-delete. NO-OP unless the running ``stackName`` is listed in
1335+ * {@link PINNED_LOG_DELIVERY_BY_STACK} OR named via context
1336+ * (`-c pinnedLogDeliveryStack=<name>`, which selects which table entry applies)
1337+ * — so fresh stacks, CI, and other accounts synth the current alpha's natural
1338+ * ids untouched. Once the affected stack is migrated, delete this helper + its
1339+ * table entry.
1340+ */
1341+ function maybePinChurnedLogResources ( stack : Stack , runtime : agentcore . Runtime ) : void {
1342+ // A deploy can override WHICH stack name to treat as the pinned one (e.g. a
1343+ // renamed env that inherited the churned resources); defaults to the running
1344+ // stack's own name, so the table is matched by stackName out of the box.
1345+ const targetStackName = ( stack . node . tryGetContext ( 'pinnedLogDeliveryStack' ) as string | undefined )
1346+ ?? stack . stackName ;
1347+ if ( targetStackName !== stack . stackName ) return ; // context names a different stack → don't touch this one
1348+ const pins = PINNED_LOG_DELIVERY_BY_STACK [ stack . stackName ] ;
1349+ if ( ! pins ) return ; // not a pre-existing churned stack → pristine synth
1350+
1351+ for ( const pin of pins ) {
1352+ const res = runtime . node . tryFindChild ( pin . childId ) as CfnResource | undefined ;
1353+ if ( ! res ) continue ; // a future alpha rename → silently skip (re-derive then)
1354+ res . overrideLogicalId ( pin . liveLogicalId ) ;
1355+ if ( pin . liveName !== undefined ) res . addPropertyOverride ( 'Name' , pin . liveName ) ;
1356+ }
1357+ }
0 commit comments