Disclaimer: This is sample code for non-production usage. You should work with your security and legal teams to meet your organizational security, regulatory, and compliance requirements before deployment. You are responsible for testing, securing, and optimizing this solution as appropriate for production use based on your specific quality control practices and standards. Deploying this solution may incur AWS charges for Lambda, EventBridge, CloudWatch, SSM Parameter Store, SQS, and SNS. Under the AWS Shared Responsibility Model, you are responsible for security decisions in the cloud, including the IAM roles and policies deployed by this solution.
Automatic AWS resource tagging for MAP 2.0 credit tracking
Customer-deployable CloudFormation solution that automatically tags newly created AWS resources with the map-migrated tag for MAP 2.0 credit eligibility.
- ✅ 190+ resource types proven working — validated against 9 real AWS accounts including CT org
- ✅ 100% success rate on all taggable MAP-eligible resources
- ✅ Typically 60–90 seconds automatic tagging latency (CloudTrail → EventBridge → Lambda → Tag)
- ✅ 100+ bugs found and fixed across all phases of testing
- ✅ Multi-account + multi-region — StackSet deploys to entire org automatically
- ✅ < $2/month customer cost per account
- ✅ Zero Lambda errors across full E2E test suite (single + multi-account + edge cases)
Customers manually tag resources for MAP 2.0 credits. They forget, tag incorrectly, tag before the agreement date, or miss dependent resources (EBS volumes, snapshots, read replicas). This leads to lost credits.
EventBridge catches resource creation events and triggers a Lambda that applies the correct map-migrated tag automatically — within typically 60–90 seconds of creation, across 190+ resource types spanning every major AWS service category.
| Document | Description |
|---|---|
| OVERVIEW.md | Non-technical overview — how it works, what gets tagged, cost |
| INSTRUCTIONS.md | Deployment steps — generating deploy.sh and running it |
| MAP_TAGGING_GAP_ANALYSIS.md | Gap analysis: what can't be tagged and why |
map2-auto-tagger-optimized.yaml— CloudFormation template (190+ services, IAM hardened)configurator.html— Self-service UI. Generates a customizeddeploy.shfor CloudShell or local AWS CLI deployment.
- Open
configurator.htmlin a browser - Fill in MPE ID, agreement date, deployment mode, regions
- Click Generate & Download → downloads
deploy.sh(fully self-contained)
Option 1 — AWS CloudShell (no setup required):
Open AWS CloudShell in the target account, upload deploy.sh, and run:
bash deploy.shOption 2 — Local AWS CLI:
# Credentials must be configured for the target account
bash deploy.shWorks on Linux, macOS, and Windows (via WSL or Git Bash).
One file. One command. Done.
The script handles everything automatically — preflight checks, deployment, backfill (if enabled), and generates a deployment report.
aws s3 mb s3://test-map-$(date +%s) && sleep 90
aws s3api get-bucket-tagging --bucket test-map-XXXXX
# Expected: {"TagSet": [{"Key": "map-migrated", "Value": "mig1234567890"}]}| Category | Key Services | Status |
|---|---|---|
| Compute | Lambda, EC2, ECS, EKS, Auto Scaling, App Runner, Batch, Lightsail, EMR, EMR Serverless, Elastic Beanstalk | ✅ |
| Storage | S3, EFS, FSx (Lustre/ONTAP/OpenZFS), ECR, EBS, AMIs, Backup | ✅ |
| Database | RDS (all engines + snapshots + replicas), Aurora, Neptune, DocumentDB, DynamoDB, Redshift, MemoryDB, OpenSearch, ElastiCache, MSK | ✅ |
| Messaging | Amazon MQ (ActiveMQ + RabbitMQ), SNS, SQS | ✅ |
| Networking | VPC, Subnets, Security Groups, Load Balancers, Transit Gateway, VPN, CloudFront, Route53, Global Accelerator, Network Firewall | ✅ |
| Analytics | Kinesis, MSK, Glue (all types + DataBrew), Athena, OpenSearch, EMR, CodeArtifact | ✅ |
| Integration | SNS, SQS, Step Functions, EventBridge (Rules + Buses + Pipes + Scheduler Groups), AppSync, API Gateway (REST + HTTP + WebSocket) | ✅ |
| ML & AI | SageMaker (all types incl. Pipeline/FeatureStore/Domain), Bedrock (Agents + Guardrails + Flows + Prompts + Inference Profiles + Knowledge Bases), Comprehend, Rekognition, Kendra, Lex v2 | ✅ |
| Security | KMS, ACM, WAFv2, Macie, GuardDuty, Cognito, Verified Permissions, Clean Rooms, Detective | ✅ |
| Developer | CodeCommit, CodeBuild, CodeDeploy, CodePipeline, CloudFormation, Amplify, CodeArtifact, CodeGuru Profiler | ✅ |
| Management | CloudWatch, SSM, Secrets Manager, X-Ray, AppConfig, MWAA, Transcribe | ✅ |
| Migration | Transfer Family, DataSync, DMS (Instances + Endpoints + Tasks) | ✅ |
| IoT | IoT Greengrass, IoT SiteWise, IoT TwinMaker | ✅ |
| Media | IVS (Channels + Chat), MediaConvert, MediaPackage | ✅ |
| Global | CloudFront, Route53, Global Accelerator, IVS — via us-east-1/us-west-2 Lambda | ✅ |
| Emerging | AWS Supply Chain, HealthLake, Omics, DataZone, Q Business, Location Service, Pinpoint, AppStream 2.0 | ✅ |
| Resource | AWS Reason |
|---|---|
| IoT Things | AWS API rejects thing as resource type in TagResource |
| Lambda Layers/Aliases | AWS explicitly blocks tagging of layers, aliases, and versions |
| Keyspaces Tables | Resource Groups API doesn't support Cassandra/Keyspaces |
| CloudWatch Log Streams | Inherit tags from parent Log Group by design |
| API Gateway API Keys | ARN format rejected by all tagging APIs |
| EventBridge Connections | UUID suffix in ARN makes it invalid for tagging |
| Glue Tables | Can only be tagged at creation time via Tags param; post-creation tagging rejected |
| Individual EventBridge Schedules | TagResource API only accepts schedule-group ARNs |
| S3 Glacier Deep Archive | MAP ineligible — always excluded from credit calculations |
| Fargate on EKS | MAP ineligible (Fargate on ECS IS eligible) |
| Service | Status |
|---|---|
| AWS IoT Events | No longer available to new customers |
| AWS IoT Analytics | No longer available to new customers |
| Amazon Lookout for Vision | Discontinued — removed from SDK |
| Amazon Lookout for Metrics | Discontinued — removed from SDK |
| Amazon QLDB | Discontinued July 2025 |
| Resource | Wait Needed |
|---|---|
| NAT Gateways | ~1-3 min provisioning |
| ElastiCache Clusters/RG/Serverless | ~2-5 min to become AVAILABLE |
| EMR Clusters | Use KeepJobFlowAliveWhenNoSteps=True; terminated clusters can't be tagged |
AWS Resource Created
│
▼
CloudTrail Event (logged within seconds)
│
▼
EventBridge Rule (filters: Create/Run/Put/Publish/Request/Allocate/Import/Launch)
│
▼
Lambda Function
├── Extract ARN (50+ universal patterns + 100+ service-specific handlers)
├── Check scope (account/VPC filter)
└── Apply tag (Resource Groups API + 30+ service-specific tagging APIs)
│
▼
Resource tagged: map-migrated=mig123...
⏱️ Total time: typically 60–90 seconds (up to 15 minutes at peak)
Multi-region coverage — global services need Lambda in matching region:
| Global Service | Lambda Region |
|---|---|
| CloudFront, Route53, IVS, IVS Chat | us-east-1 |
| Global Accelerator | us-west-2 |
| App Runner (if not in primary region) | ap-northeast-1 |
IAM note: The Lambda execution role name uses !Sub 'map-auto-tagger-role-${AWS::Region}' to enable safe multi-region deployment (IAM is global; same role name would conflict across regions in the same account).
- Single Account — deploy once per region where you create resources
- Multi-Account — deploy via StackSets across all member accounts
- Must be run from the management account or a delegated administrator account for CloudFormation StackSets
- Uses
SERVICE_MANAGEDpermission model — no direct IAM access to member accounts required; CloudFormation deploys using org-level service-linked roles - Lambda Custom Resource auto-discovers org root OU and deploys to all accounts
- Use account scoping in the configurator to limit deployment to specific accounts
- New accounts added to the org automatically receive the auto-tagger
- CloudTrail enabled in target region(s)
- Deployer needs:
iam:*Role*,lambda:CreateFunction,events:PutRule,ssm:PutParameter,sns:CreateTopic,sqs:CreateQueue,cloudwatch:PutMetricAlarm - Or use a CloudFormation service role
Validated across 9 real AWS accounts (single account + multi-account org with 5 linked + 2 security OU accounts), covering all 88 MAP 2.0 eligible services from the official Included Services List.
| Metric | Result |
|---|---|
| Resource types tested | 190+ unique |
| Bugs found & fixed | 100+ |
| False positives | 0 |
| Lambda errors | 0 |
| Accounts tested | 9 |
Scenarios covered: Single account, multi-account org, VPC scoping, account scoping, backfill, multi-region, IAM Identity Center (SSO) users, delegated administrator accounts, date filtering, throttle burst handling.
- Amazon Bedrock — Bedrock spend is MAP-eligible but requires customers to create Application Inference Profiles first. Once a profile is created, this solution automatically tags it. Without an inference profile, Bedrock API calls are not MAP-trackable.
- Existing resources not automatically tagged — enable the one-time backfill option in the configurator (covers resources created up to 90 days before deployment)
- S3 staging bucket (multi-account only) — the multi-account deployment creates an S3 bucket named
auto-map-tagger-{account-id}in the management account to stage CloudFormation templates for the StackSet. This bucket is intentionally retained after deployment — CloudFormation StackSets require a persistent template URL to automatically deploy to new accounts that join your organization in the future. The bucket contains only the per-account CloudFormation template (~40KB) and has Block Public Access, AES-256 encryption, and HTTPS-only enforcement applied. Single-account deployments do not create a persistent S3 bucket. - Delegated administrator accounts supported — in large enterprises the management account is often locked down and a "shared services" account is designated as the delegated administrator for CloudFormation StackSets. Running
deploy.shfrom a delegated admin account is fully supported. The preflight check verifies the caller is either the management account or a registered delegated admin forstacksets.cloudformation.amazonaws.com. - One active deployment per account — all Lambda, EventBridge, IAM, and SSM resources use fixed names (
map-auto-tagger,/auto-map-tagger/config). A second deployment to the same account will conflict with an existing one. To switch MPE IDs, delete the existing stack first and redeploy. - Management account not covered in multi-account mode —
SERVICE_MANAGEDStackSets cannot deploy to the management account (AWS hard constraint). In multi-account StackSet deployments, the management account runs the deployment but does not receive the auto-tagger Lambda. Resources created in the management account will not be tagged. AWS best practice is to not run workloads in the management account. If tagging is required there, additionally run a single-account deployment targeting the management account after the StackSet deployment completes. - New AWS accounts added post-deployment — if a new account joins the organization after the StackSet has been deployed, it will not automatically receive the Lambda. Resources created in that account will not be tagged. Re-run
deploy.shto extend the StackSet to cover new accounts. - Service Control Policies (SCPs) — two scenarios require manual verification before deployment:
- Tagging SCPs: if your organization's SCPs deny
tag:TagResourcesor service-specific tagging actions for Lambda execution roles, the auto-tagger will silently fail and events will accumulate in the DLQ. Thedeploy.shpreflight check runs an IAM simulation (iam:SimulatePrincipalPolicy) to detect explicit denies, but SCPs are not evaluated by IAM simulation and require manual review in the AWS Organizations console. - Mandatory tagging SCPs: if SCPs require the
map-migratedtag to be present at resource creation time, this solution will not satisfy that requirement — tags are applied typically 60–90 seconds (up to 15 minutes during high-volume activity) after creation. Either exemptmap-migratedfrom creation-time enforcement or configure a grace period in the SCP.
- Tagging SCPs: if your organization's SCPs deny
- ECS task tag propagation requires
propagateTags: SERVICEin ECS service definition - EKS Auto Mode nodes — tagged via NodePool config, not standard tagging
- Multi-region coverage — deploy to us-east-1 and us-west-2 for CloudFront, Route53, IVS, GA
- EventBridge 256KB event size limit — CloudTrail events exceeding 256KB are silently dropped by EventBridge and will never trigger the Lambda. This is an AWS platform hard limit. In practice this is extremely rare, only possible for unusually complex resource creation events (e.g., a Lambda function with a very large inline policy). No workaround exists within this architecture.
- API throttling during large bursts — if hundreds of resources are created simultaneously, multiple Lambda invocations may hit
tag:TagResourcesrate limits. The Lambda retries up to 3 times with exponential backoff (0.5s → 1s → 2s with ±25% jitter). Events that exhaust all retries go to the DLQ for manual review. - CloudTrail delivery latency — typical tagging latency is 60–90 seconds but CloudTrail delivery to EventBridge can take up to 15 minutes during high API activity (e.g., large Terraform/CDK deployments). Tags will always be applied eventually — this is a latency variance, not a reliability issue.
- CloudTrail must be enabled in all deployment regions — the
deploy.shpreflight verifies CloudTrail in each selected region. Resources created in a region without an active CloudTrail trail will never trigger the Lambda and will not be tagged. - MPE ID change mid-migration — if the MAP Engagement ID changes, the Lambda automatically uses the new value from SSM for all future resources. Previously tagged resources retain the old value and must be re-tagged manually via AWS Tag Editor. Do not use automated bulk re-tagging scripts in accounts with multiple concurrent MAP engagements — different resources may intentionally carry different MPE IDs.
commtag prefix — deprecated, usemigonly- Cost allocation tag — automatically activated for MAP agreements
| Component | Monthly Cost |
|---|---|
| Lambda (100-1,000 invocations/day) | $0.10 – $2.00 |
| EventBridge events | $0.01 – $0.20 |
| CloudTrail (existing) | $0.00 |
| SSM Parameter Store | $0.00 |
| Total | ~$2.00/month |
# See recent tagging activity
aws logs tail /aws/lambda/map-auto-tagger --follow
# Check for errors
aws logs filter-log-events \
--log-group-name /aws/lambda/map-auto-tagger \
--filter-pattern "Failed"
# See what was tagged
aws logs filter-log-events \
--log-group-name /aws/lambda/map-auto-tagger \
--filter-pattern "Tagged"Built-in alarm: Error rate > 3 in 5 minutes → SNS notification
Recommended deployment:
- Open
configurator.htmlin a browser and generatedeploy.sh - Run
bash deploy.shin AWS CloudShell or local AWS CLI (from the target account) - Monitor via Amazon CloudWatch for the first week
- Expand regions as needed (us-east-1 for CloudFront/Route53; us-west-2 for Global Accelerator)