惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Check Point Blog
H
Help Net Security
B
Blog RSS Feed
Microsoft Security Blog
Microsoft Security Blog
阮一峰的网络日志
阮一峰的网络日志
Engineering at Meta
Engineering at Meta
The Register - Security
The Register - Security
U
Unit 42
Hugging Face - Blog
Hugging Face - Blog
雷峰网
雷峰网
酷 壳 – CoolShell
酷 壳 – CoolShell
IT之家
IT之家
云风的 BLOG
云风的 BLOG
腾讯CDC
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
D
Docker
T
The Blog of Author Tim Ferriss
Recorded Future
Recorded Future
月光博客
月光博客
H
Hackread – Cybersecurity News, Data Breaches, AI and More
罗磊的独立博客
G
Google Developers Blog
Jina AI
Jina AI
P
Proofpoint News Feed
J
Java Code Geeks
I
InfoQ
博客园 - 司徒正美
D
DataBreaches.Net
博客园 - 叶小钗
F
Fortinet All Blogs
The GitHub Blog
The GitHub Blog
Google DeepMind News
Google DeepMind News
L
LangChain Blog
博客园_首页
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
S
SegmentFault 最新的问题
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - Franky
人人都是产品经理
人人都是产品经理
V
V2EX
F
Full Disclosure
A
About on SuperTechFans
Stack Overflow Blog
Stack Overflow Blog
Martin Fowler
Martin Fowler
MongoDB | Blog
MongoDB | Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
美团技术团队
V
Visual Studio Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Dynamic Looping Comes to AWS SAM
Eric D Johns · 2026-05-19 · via DEV Community

AWS SAM CLI, the command-line tool for building and deploying serverless applications, now supports AWS CloudFormation Language Extensions. The one I am most excited about is Fn::ForEach, which brings dynamic looping to your YAML templates, but it's close. If you, like me, have been copy-pasting resource definitions to infinity, that stops today.

ForEach is the star, but it ships alongside Length, ToJsonString, FindInMap with default values, and conditional deletion policies. All of them work across your full local SAM workflow: build, invoke, validate, package, deploy, and sync.

In this post, I walk through what CloudFormation Language Extensions brings to SAM CLI, show you how each extension works, and demonstrate the full local development experience.

The problem: template duplication

To show why this matters, take a look at the following example. I have three AWS Lambda functions, Lambda being the serverless compute service, that each handle a different endpoint on the same API. But, almost everything about them is the same. They have the same runtime, the same memory configuration, and nearly the same structure. The only differences are the name, handler, and possibly some environment variables.

The template looks like this:

Resources:
  UsersFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.11
      Handler: users.handler
      CodeUri: ./src
      MemorySize: 256
      Environment:
        Variables:
          FUNCTION_NAME: Users

  OrdersFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.11
      Handler: orders.handler
      CodeUri: ./src
      MemorySize: 256
      Environment:
        Variables:
          FUNCTION_NAME: Orders

  ProductsFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.11
      Handler: products.handler
      CodeUri: ./src
      MemorySize: 256
      Environment:
        Variables:
          FUNCTION_NAME: Products

Enter fullscreen mode Exit fullscreen mode

Three resources, nearly identical, and if I need to change the memory size or add a tracing configuration, I'm making the same edit three times. The template is fragile and hard to maintain, and it only gets worse at ten or twenty functions. So what can I do about it? That's where Language Extensions come in.

What are CloudFormation Language Extensions?

CloudFormation Language Extensions is a transform (AWS::LanguageExtensions) that unlocks a suite of extended intrinsic functions for your CloudFormation templates. These functions have existed in CloudFormation for a while. What's new is that SAM CLI now processes them locally for your entire development workflow, meaning you can build, invoke, and test locally before deploying.

The full suite includes:

Extension What it does
Fn::ForEach Iterate over a collection and generate resources for each item
Fn::Length Return the length of an array
Fn::ToJsonString Convert an object or array to a JSON string
Fn::FindInMap with DefaultValue Look up a value in a Mappings section with a fallback when the key doesn't exist
Conditional DeletionPolicy Use Fn::If in DeletionPolicy (e.g., Retain in prod, Delete in dev)
Conditional UpdateReplacePolicy Use Fn::If in UpdateReplacePolicy

To enable them, I add AWS::LanguageExtensions to my template's Transform section alongside the SAM transform:

Transform:
  - AWS::LanguageExtensions
  - AWS::Serverless-2016-10-31

Enter fullscreen mode Exit fullscreen mode

With that in place, I can start using Fn::ForEach to solve the duplication problem I showed earlier.

Fn::ForEach: define once, generate many

Take a look at the same three functions rewritten with Fn::ForEach. Instead of repeating the definition three times, I define it once and let the loop generate the rest:

Transform:
  - AWS::LanguageExtensions
  - AWS::Serverless-2016-10-31

Resources:
  Fn::ForEach::Functions:
    - Name
    - [Users, Orders, Products]
    - ${Name}Function:
        Type: AWS::Serverless::Function
        Properties:
          Runtime: python3.11
          Handler: !Sub "${Name}.handler"
          CodeUri: ./src
          MemorySize: 256
          Environment:
            Variables:
              FUNCTION_NAME: !Sub ${Name}

Enter fullscreen mode Exit fullscreen mode

That single definition generates three functions: UsersFunction, OrdersFunction, and ProductsFunction. If I need to add a fourth, I add one item to the collection array. If I need to change the memory size, I change it in one place.

The anatomy of Fn::ForEach breaks down into four parts:

  • Loop name: Fn::ForEach::Functions, a unique identifier for this loop
  • Iterator variable: Name, the variable that takes each value in turn
  • Collection: [Users, Orders, Products], the values to iterate over
  • Template body: The resource definition using ${Name} for substitution

That covers the basic case where all functions share the same source code. However, what happens when each function needs its own code directory?

Per-function code directories

In many projects, each function lives in its own folder. Fn::ForEach handles this through dynamic artifact properties, where the CodeUri itself uses the loop variable:

Resources:
  Fn::ForEach::Services:
    - Name
    - [Users, Orders, Products]
    - ${Name}Service:
        Type: AWS::Serverless::Function
        Properties:
          Runtime: python3.11
          Handler: index.handler
          CodeUri: ./services/${Name}

Enter fullscreen mode Exit fullscreen mode

With this directory structure:

services/
├── Users/index.py
├── Orders/index.py
└── Products/index.py

Enter fullscreen mode Exit fullscreen mode

SAM CLI builds each function from its own directory and generates Mappings sections automatically to preserve the Fn::ForEach structure in the deployed template. To see this in action, I check .aws-sam/build/template.yaml after a build:

Mappings:
  SAMCodeUriServices:
    Users:
      CodeUri: UsersService
    Orders:
      CodeUri: OrdersService
    Products:
      CodeUri: ProductsService

Resources:
  Fn::ForEach::Services:
    - Name
    - [Users, Orders, Products]
    - ${Name}Service:
        Type: AWS::Serverless::Function
        Properties:
          CodeUri:
            Fn::FindInMap:
              - SAMCodeUriServices
              - Ref: Name
              - CodeUri
          Handler: index.handler

Enter fullscreen mode Exit fullscreen mode

SAM CLI generates the SAMCodeUriServices mapping so that each collection value resolves to its own build artifact. At package time, those paths become Amazon S3 URIs. I don't need to manage any of this.

The same pattern works for API endpoints. Let me show one more example before moving on to the other extensions.

API endpoints from a loop

I can generate multiple API endpoints from a single definition by attaching an Amazon API Gateway event source inside the loop:

Resources:
  Fn::ForEach::Endpoints:
    - Endpoint
    - [users, products, orders]
    - ${Endpoint}Function:
        Type: AWS::Serverless::Function
        Properties:
          Runtime: python3.11
          Handler: index.handler
          CodeUri: ./endpoints/${Endpoint}
          Events:
            Api:
              Type: Api
              Properties:
                Path: !Sub /${Endpoint}
                Method: get

Enter fullscreen mode Exit fullscreen mode

I run sam local start-api, and I get three working endpoints: /users, /products, /orders, all generated from that single resource definition.

Fn::ForEach is the biggest addition, but the other extensions in the suite solve real problems of their own.

Beyond Fn::ForEach: Length, ToJsonString, FindInMap, and more

Each of the remaining extensions addresses a specific gap in what CloudFormation templates could express before.

Fn::Length

When I generate resources from a collection, I sometimes need to know how many items are in that collection. Maybe I'm setting a concurrency limit based on the number of services, or creating an Amazon CloudWatch alarm that scales with the fleet. Previously, I'd hardcode that number and forget to update it when the collection changed. Fn::Length returns the length of an array at deploy time:

Parameters:
  ServiceNames:
    Type: CommaDelimitedList
    Default: "api,worker,scheduler"

Resources:
  ServiceCountMetric:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: !Sub "Monitoring ${Fn::Length(ServiceNames)} services"

Enter fullscreen mode Exit fullscreen mode

Fn::ToJsonString

Lambda functions frequently need structured configuration passed as environment variables. The problem is that environment variables are strings, so I end up building JSON by hand inside !Sub with escaped quotes and line breaks, and it breaks the moment someone forgets a backslash.

Fn::ToJsonString solves this by converting an object to a JSON string inline:

Environment:
  Variables:
    CONFIG:
      Fn::ToJsonString:
        region: !Ref AWS::Region
        table: !Ref MyTable
        version: "2.0"

Enter fullscreen mode Exit fullscreen mode

No more escaping quotes in YAML, and no more !Sub gymnastics to build JSON strings. I define the object naturally and let Fn::ToJsonString handle serialization. The function reads CONFIG as a standard JSON string at runtime, and if I add a field, I add it to the YAML object and the serialization stays correct.

Fn::FindInMap with DefaultValue

Mappings are great for region-specific or environment-specific configuration. However, Fn::FindInMap throws a hard error if the key doesn't exist. So if I add a new region or deploy to one I didn't explicitly map, the stack fails. I end up maintaining an exhaustive list of every possible key, or wrapping lookups in conditions.

Now I can provide a default value that CloudFormation uses when the key isn't found:

Mappings:
  RegionConfig:
    us-east-1:
      BucketPrefix: "use1"
    eu-west-1:
      BucketPrefix: "euw1"

Resources:
  MyBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub
        - "${Prefix}-my-app"
        - Prefix:
            Fn::FindInMap:
              - RegionConfig
              - !Ref AWS::Region
              - BucketPrefix
              - DefaultValue: "default"

Enter fullscreen mode Exit fullscreen mode

If I deploy to ap-southeast-1, no crash. I get "default" instead of a stack failure.

Conditional DeletionPolicy and UpdateReplacePolicy

In a multi-environment setup, I want production Amazon DynamoDB tables and S3 buckets to survive accidental stack deletions. But in dev, I want clean teardowns without orphaned resources cluttering the account. Previously, I needed separate templates or manual post-deploy steps because DeletionPolicy only accepted a static string.

Now it accepts intrinsic functions:

Conditions:
  IsProd: !Equals [!Ref Environment, prod]

Resources:
  MyTable:
    Type: AWS::DynamoDB::Table
    DeletionPolicy: !If [IsProd, Retain, Delete]
    UpdateReplacePolicy: !If [IsProd, Retain, Delete]
    Properties:
      TableName: !Sub "${Environment}-data"
      BillingMode: PAY_PER_REQUEST

Enter fullscreen mode Exit fullscreen mode

One template handles both: production retains data on deletion, dev cleans up after itself.

That covers all the extensions. The next question is how they fit into the SAM CLI workflow.

Full SAM CLI workflow support

Every SAM CLI command supports Language Extensions:

  • sam build: Expands loops in memory, builds each generated function
  • sam local invoke: Invoke expanded functions by name
  • sam local start-api: Serves all generated API endpoints
  • sam validate: Catches syntax errors and unsupported patterns locally
  • sam package: Preserves the Fn::ForEach structure with S3 URIs
  • sam deploy: Uploads your original template for CloudFormation to process
  • sam sync: Syncs changes to the cloud, including code-only updates

SAM CLI expands language extensions in memory for local operations because it needs to know which functions to build and invoke. But your original unexpanded template is what goes to CloudFormation. You get the full local development experience with no template modification for deployment.

A typical workflow looks like this:

sam build
sam local invoke UsersFunction --event events/get-user.json
sam local start-api
# Test your endpoints at http://localhost:3000/users
sam deploy --guided

Enter fullscreen mode Exit fullscreen mode

You don't need special flags or additional configuration to use language extensions with any of these commands.

Before you get started, there are a few constraints worth knowing about.

Limitations and constraints

Collections must be locally resolvable. Your Fn::ForEach collection can be a static list ([A, B, C]) or a parameter reference (!Ref MyParam). It cannot use Fn::GetAtt, Fn::ImportValue, or SSM/Secrets Manager dynamic references. These require cloud API calls that SAM CLI can't make locally. The error messages are clear and suggest workarounds.

Maximum 5 levels of nesting. You can nest Fn::ForEach loops (environments x services, for example), but CloudFormation caps it at 5 levels deep. You probably won't hit this in practice.

Collection values are fixed at package time. If you use a parameter-based collection with dynamic CodeUri, the parameter values you use at sam package must match what you use at sam deploy. SAM CLI warns you when this applies.

With those constraints in mind, getting started is straightforward.

Get started

This feature is available now in the latest SAM CLI. Update and try it:

pip install --upgrade aws-sam-cli
sam --version

Enter fullscreen mode Exit fullscreen mode

Take one of your templates with duplicated resources, add the AWS::LanguageExtensions transform, and replace the copy-paste with Fn::ForEach. If you don't have the latest CLI, the install guide has you covered.

This has been one of the most requested features in SAM CLI history (#5647 had years of community upvotes), and the implementation covers the full command surface. Dynamic looping in YAML, supported end-to-end. Define your resources once, generate as many as you need, and deploy with the same workflow you already know.

If you run into issues or want to see what's next, the SAM CLI repo is where it all happens.