A loop's fragment can hold several resources that refer to each other. In infra/stages.yaml, each name in the Stages list (default dev,test) gets an orders table and a parameter holding its ARN, via a computed Fn::GetAtt:
Fn::ForEach::StageOrders:
- Stage
- !Ref Stages
- ${Stage}Orders:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub booknest-${Stage}-orders
BillingMode: PAY_PER_REQUEST
AttributeDefinitions: [{AttributeName: orderId, AttributeType: S}]
KeySchema: [{AttributeName: orderId, KeyType: HASH}]
${Stage}OrdersArn:
Type: AWS::SSM::Parameter
Properties:
Name: !Sub /booknest/${Stage}/orders-arn
Type: String
Value: {Fn::GetAtt: [!Sub "${Stage}Orders", Arn]}The rest of the file adds a condition, SeveralStages: {Fn::Not: [{Fn::Equals: [{Fn::Length: !Ref Stages}, 1]}]}, guarding a StageSummary parameter set to {Fn::ToJsonString: {count: {Fn::Length: !Ref Stages}, names: !Ref Stages}}.
aws cloudformation deploy --stack-name booknest-stages --template-file stages.yaml \
--capabilities CAPABILITY_AUTO_EXPAND
aws ssm get-parameters-by-path --path /booknest --recursive --output text \
--query 'Parameters[].[Name,Value]'...
Successfully created/updated stack - booknest-stages
/booknest/stages {"count":{"Fn::Length":{"Ref":"Stages"}},"names":{"Ref":"Stages"}}
/booknest/dev/orders-arn arn:aws:dynamodb:us-east-1:000000000000:table/booknest-dev-orders
/booknest/test/orders-arn arn:aws:dynamodb:us-east-1:000000000000:table/booknest-test-ordersEach Fn::GetAtt resolved, and Fn::Length returned 2, keeping StageSummary (a Stages=solo deploy omits it). Fn::ToJsonString repeats LocalStack 63,725 's gap from Fn::ForEach; AWS 24 stores {"count":2,"names":["dev","test"]}. Keep loops small and flat: every iteration counts toward the 500-resource quota.