Organizing templates with Parameters, Outputs, Mappings, Conditions
Implementing cross-stack references with export/import
Using Transform for macros and reuse
CloudFormation Template Structure
Standard Format Base Template
AWSTemplateFormatVersion:2010-09-09Description:CloudFrontdistributionwithmultipleoriginsMetadata:AWS::CloudFormation::Interface:ParameterGroups:-Label:default:DistributionConfigurationParameters:-DomainName-CertificateArn-PriceClass-Label:default:OriginSettingsParameters:-OriginDomainName-OriginPath-OriginProtocolPolicyParameters:DomainName:Type:StringDefault:cdn.example.comDescription:CustomdomainnameforCloudFrontdistributionCertificateArn:Type:AWS::ACM::Certificate::ArnDescription:ACMcertificateARNforHTTPSPriceClass:Type:StringDefault:PriceClass_AllAllowedValues:-PriceClass_All-PriceClass_100-PriceClass_200Description:CloudFrontpriceclassOriginDomainName:Type:StringDescription:Domainnameoftheorigin(ALBorS3)OriginPath:Type:StringDefault:""Description:OptionaloriginpathMappings:EnvironmentConfig:us-east-1:CertificateRegion:us-east-1other:CertificateRegion:us-east-1Conditions:IsUsEast1:!Equals [!RefAWS::Region, us-east-1]
HasOriginPath:!Not [!Equals [!RefOriginPath, ""]]
Transform:-AWS::Serverless-2016-10-31Resources:# CloudFront DistributionCloudFrontDistribution:Type:AWS::CloudFront::DistributionProperties:DistributionConfig:CallerReference:!Sub"${AWS::StackName}-${AWS::AccountId}"Comment:!Sub"CloudFront distribution for ${DomainName}"DomainNames:-!RefDomainNameEnabled:truePriceClass:!RefPriceClassIPV6Enabled:trueDefaultRootObject:index.htmlOrigins:-Id:!Sub"${DomainName}-origin"DomainName:!RefOriginDomainNameOriginPath:!If [HasOriginPath, !RefOriginPath, !RefAWS::NoValue]
CustomOriginConfig:HTTPPort:80HTTPSPort:443OriginProtocolPolicy:https-onlyOriginSSLProtocols:-TLSv1.2DefaultCacheBehavior:TargetOriginId:!Sub"${DomainName}-origin"ViewerProtocolPolicy:redirect-to-httpsAllowedMethods:-GET-HEADCachedMethods:-GET-HEADCompress:trueForwardedValues:QueryString:falseCookies:Forward:noneMinTTL:0DefaultTTL:86400MaxTTL:31536000ViewerCertificate:AcmCertificateArn:!RefCertificateArnMinimumProtocolVersion:TLSv1.2_2021SslSupportMethod:sni-onlyOutputs:DistributionDomainName:Description:CloudFrontdistributiondomainnameValue:!GetAttCloudFrontDistribution.DomainNameExport:Name:!Sub"${AWS::StackName}-DistributionDomainName"DistributionId:Description:CloudFrontdistributionIDValue:!RefCloudFrontDistributionExport:Name:!Sub"${AWS::StackName}-DistributionId"
Best Practices for Parameters
AWS-Specific Parameter Types
Parameters:# ACM Certificate for domainCertificateArn:Type:AWS::ACM::Certificate::ArnDescription:ACMcertificateforthedomain# S3 Bucket originsStaticAssetsBucket:Type:AWS::S3::BucketDescription:S3bucketforstaticassetsStaticAssetsBucketDomainName:Type:AWS::S3::Bucket::RegionalDomainNameDescription:RegionaldomainnameoftheS3bucket# ALB originsLoadBalancerArn:Type:AWS::ElasticLoadBalancingV2::LoadBalancer::ArnDescription:ARNoftheApplicationLoadBalancerLoadBalancerDNSName:Type:AWS::ElasticLoadBalancingV2::LoadBalancer::DnsNameDescription:DNSnameoftheALB# Lambda function originsLambdaFunctionArn:Type:AWS::Lambda::Function::ArnDescription:ARNoftheLambdafunctionforLambda@Edge# VPC OriginVPCOriginEndpoint:Type:AWS::GlobalAccelerator::Endpoint::EndpointIdDescription:VPCOriginendpointID# IAM Role for Lambda@EdgeLambdaEdgeRoleArn:Type:AWS::IAM::Role::ArnDescription:IAMroleforLambda@Edgeexecution
# Stack A - Network/Infrastructure StackAWSTemplateFormatVersion:2010-09-09Description:InfrastructurestackexportingCloudFrontresourcesResources:# S3 Bucket for static contentStaticAssetsBucket:Type:AWS::S3::BucketProperties:BucketName:!Sub"static-assets-${AWS::AccountId}-${AWS::Region}"PublicAccessBlockConfiguration:BlockPublicAcls:trueBlockPublicPolicy:trueIgnorePublicAcls:trueRestrictPublicBuckets:trueBucketEncryption:ServerSideEncryptionConfiguration:-ServerSideEncryptionByDefault:SSEAlgorithm:AES256VersioningConfiguration:Status:EnabledCorsConfiguration:CorsRules:-AllowedHeaders:-"*"AllowedMethods:-GET-HEADAllowedOrigins:-"*"MaxAge:3600# OAI for CloudFront accessCloudFrontOAI:Type:AWS::CloudFront::CloudFrontOriginAccessIdentityProperties:CloudFrontOriginAccessIdentityConfig:Comment:!Sub"OAI for ${StaticAssetsBucket}"Outputs:StaticAssetsBucketName:Description:S3bucketnameforstaticassetsValue:!RefStaticAssetsBucketExport:Name:!Sub"${AWS::StackName}-StaticAssetsBucketName"StaticAssetsBucketArn:Description:S3bucketARNValue:!GetAttStaticAssetsBucket.ArnExport:Name:!Sub"${AWS::StackName}-StaticAssetsBucketArn"StaticAssetsBucketRegionalDomainName:Description:RegionaldomainnameoftheS3bucketValue:!GetAttStaticAssetsBucket.RegionalDomainNameExport:Name:!Sub"${AWS::StackName}-StaticAssetsBucketRegionalDomainName"CloudFrontOAIId:Description:CloudFrontOAIIDValue:!RefCloudFrontOAIExport:Name:!Sub"${AWS::StackName}-CloudFrontOAIId"CloudFrontOAIArn:Description:CloudFrontOAIARNValue:!GetAttCloudFrontOAI.ArnExport:Name:!Sub"${AWS::StackName}-CloudFrontOAIArn"
# Stack B - Application Stack (imports from Infrastructure Stack)AWSTemplateFormatVersion:2010-09-09Description:ApplicationstackimportingfrominfrastructurestackParameters:InfrastructureStackName:Type:StringDefault:infrastructure-stackDescription:NameoftheinfrastructurestackDomainName:Type:StringDefault:cdn.example.comDescription:CustomdomainnameCertificateArn:Type:AWS::ACM::Certificate::ArnDescription:ACMcertificateARNResources:CloudFrontDistribution:Type:AWS::CloudFront::DistributionProperties:DistributionConfig:CallerReference:!Sub"${AWS::StackName}-${AWS::AccountId}"Comment:!Sub"CloudFront for ${DomainName}"Enabled:trueIPV6Enabled:trueDefaultRootObject:index.htmlOrigins:-Id:StaticAssetsOriginDomainName:!ImportValue!Sub"${InfrastructureStackName}-StaticAssetsBucketRegionalDomainName"S3OriginConfig:OriginAccessIdentity:!Sub"origin-access-identity/cloudfront/${InfrastructureStackName}-CloudFrontOAIId"DefaultCacheBehavior:TargetOriginId:StaticAssetsOriginViewerProtocolPolicy:redirect-to-httpsAllowedMethods:-GET-HEADCachedMethods:-GET-HEADCompress:trueForwardedValues:QueryString:falseCookies:Forward:noneMinTTL:0DefaultTTL:86400MaxTTL:31536000ViewerCertificate:AcmCertificateArn:!RefCertificateArnMinimumProtocolVersion:TLSv1.2_2021SslSupportMethod:sni-only
Nested Stacks for Modularity
AWSTemplateFormatVersion:2010-09-09Description:MainstackwithnestedCloudFrontstacksResources:# Nested stack for static assets distributionStaticAssetsDistributionStack:Type:AWS::CloudFormation::StackProperties:TemplateURL:https://s3.amazonaws.com/bucket/cloudfront-static.yamlTimeoutInMinutes:15Parameters:DomainName:!RefDomainNameCertificateArn:!RefCertificateArnStaticAssetsBucketName:!RefStaticAssetsBucketNameEnvironment:!RefEnvironment# Nested stack for API distributionApiDistributionStack:Type:AWS::CloudFormation::StackProperties:TemplateURL:https://s3.amazonaws.com/bucket/cloudfront-api.yamlTimeoutInMinutes:15Parameters:DomainName:!RefApiDomainNameCertificateArn:!RefCertificateArnLoadBalancerDnsName:!RefLoadBalancerDnsNameEnvironment:!RefEnvironment
Resources:StaticBucket:Type:AWS::S3::BucketProperties:BucketName:!Sub"static-assets-oac-${AWS::AccountId}-${AWS::Region}"OwnershipControls:Rules:-ObjectOwnership:BucketOwnerPreferredPublicAccessBlockConfiguration:BlockPublicAcls:trueBlockPublicPolicy:trueIgnorePublicAcls:trueRestrictPublicBuckets:true# S3 Bucket Policy for OACS3BucketPolicy:Type:AWS::S3::BucketPolicyProperties:Bucket:!RefStaticBucketPolicyDocument:Version:"2012-10-17"Statement:-Effect:AllowPrincipal:Service:cloudfront.amazonaws.comAction:s3:GetObjectResource:!Sub"${StaticBucket.Arn}/*"Condition:StringEquals:AWS:SourceArn:!Sub"arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}"CloudFrontDistribution:Type:AWS::CloudFront::DistributionProperties:DistributionConfig:Origins:-Id:S3OriginDomainName:!GetAttStaticBucket.RegionalDomainNameS3OriginConfig:OriginAccessIdentity:""# For OAC, use OriginAccessControl instead of S3OriginConfig# but CloudFormation supports both
ALB Origins
Application Load Balancer Origin
AWSTemplateFormatVersion:2010-09-09Description:CloudFrontwithALBoriginResources:# Application Load BalancerApplicationLoadBalancer:Type:AWS::ElasticLoadBalancingV2::LoadBalancerProperties:Name:!Sub"${AWS::StackName}-alb"Scheme:internet-facingSecurityGroups:-!RefALBSecurityGroupSubnets:-!RefPublicSubnet1-!RefPublicSubnet2Type:application# ALB Security GroupALBSecurityGroup:Type:AWS::EC2::SecurityGroupProperties:GroupDescription:ALBsecuritygroupVpcId:!RefVPCIdSecurityGroupIngress:-IpProtocol:tcpFromPort:80ToPort:80SourceSecurityGroupId:!RefCloudFrontSecurityGroup-IpProtocol:tcpFromPort:443ToPort:443SourceSecurityGroupId:!RefCloudFrontSecurityGroup# CloudFront Security Group (for ALB ingress)CloudFrontSecurityGroup:Type:AWS::EC2::SecurityGroupProperties:GroupDescription:CloudFrontsecuritygroupforALBVpcId:!RefVPCIdSecurityGroupEgress:-IpProtocol:tcpFromPort:80ToPort:80DestinationSecurityGroupId:!RefALBSecurityGroup-IpProtocol:tcpFromPort:443ToPort:443DestinationSecurityGroupId:!RefALBSecurityGroup# CloudFront DistributionCloudFrontDistribution:Type:AWS::CloudFront::DistributionProperties:DistributionConfig:CallerReference:!Sub"${AWS::StackName}-${AWS::AccountId}"Comment:!Sub"CloudFront with ALB origin"Enabled:trueOrigins:-Id:ALBOriginDomainName:!GetAttApplicationLoadBalancer.DNSNameCustomOriginConfig:HTTPPort:80HTTPSPort:443OriginProtocolPolicy:https-onlyOriginSSLProtocols:-TLSv1.2DefaultCacheBehavior:TargetOriginId:ALBOriginViewerProtocolPolicy:redirect-to-httpsAllowedMethods:-GET-HEAD-OPTIONSCachedMethods:-GET-HEADCompress:trueForwardedValues:QueryString:trueHeaders:-Origin-Access-Control-Request-Method-Access-Control-Request-HeadersCookies:Forward:allQueryStringSettings:-Name:"*"MinTTL:0DefaultTTL:0MaxTTL:0
Multiple Origins and CacheBehaviors
Multi-Origin with Path Patterns
AWSTemplateFormatVersion:2010-09-09Description:CloudFrontwithmultipleoriginsandcachebehaviorsResources:# S3 Bucket for static assetsStaticAssetsBucket:Type:AWS::S3::BucketProperties:BucketName:!Sub"static-assets-${AWS::AccountId}-${AWS::Region}"CloudFrontOAI:Type:AWS::CloudFront::CloudFrontOriginAccessIdentityProperties:CloudFrontOriginAccessIdentityConfig:Comment:!Sub"OAI for ${StaticAssetsBucket}"# Application Load Balancer for APIApplicationLoadBalancer:Type:AWS::ElasticLoadBalancingV2::LoadBalancerProperties:Name:!Sub"${AWS::StackName}-api-alb"Scheme:internet-facingSecurityGroups:-!RefALBSecurityGroupSubnets:!RefPublicSubnetsType:application# CloudFront DistributionCloudFrontDistribution:Type:AWS::CloudFront::DistributionProperties:DistributionConfig:CallerReference:!Sub"${AWS::StackName}-${AWS::AccountId}"Comment:!Sub"Multi-origin CloudFront distribution"Enabled:trueIPV6Enabled:trueDefaultRootObject:index.htmlOrigins:# Static assets origin-Id:StaticAssetsOriginDomainName:!GetAttStaticAssetsBucket.RegionalDomainNameS3OriginConfig:OriginAccessIdentity:!Sub"origin-access-identity/cloudfront/${CloudFrontOAI}"# API origin-Id:ApiOriginDomainName:!GetAttApplicationLoadBalancer.DNSNameCustomOriginConfig:HTTPPort:80HTTPSPort:443OriginProtocolPolicy:https-only# Lambda origin-Id:LambdaOriginDomainName:!Sub"${LambdaFunction}.execute-api.${AWS::Region}.amazonaws.com"CustomOriginConfig:HTTPPort:443HTTPSPort:443OriginProtocolPolicy:https-onlyDefaultCacheBehavior:# Default: static assetsTargetOriginId:StaticAssetsOriginViewerProtocolPolicy:redirect-to-httpsAllowedMethods:-GET-HEADCachedMethods:-GET-HEADCompress:trueForwardedValues:QueryString:falseCookies:Forward:noneMinTTL:0DefaultTTL:86400MaxTTL:31536000CacheBehaviors:# API cache behavior-PathPattern:"/api/*"TargetOriginId:ApiOriginViewerProtocolPolicy:redirect-to-httpsAllowedMethods:-GET-HEAD-OPTIONS-PUT-POST-PATCH-DELETECachedMethods:-GET-HEADCompress:trueForwardedValues:QueryString:trueHeaders:-Accept-Accept-Language-AuthorizationCookies:Forward:allMinTTL:0DefaultTTL:0MaxTTL:0# Lambda function path-PathPattern:"/lambda/*"TargetOriginId:LambdaOriginViewerProtocolPolicy:redirect-to-httpsAllowedMethods:-GET-HEAD-OPTIONSCachedMethods:-GET-HEADCompress:trueForwardedValues:QueryString:trueCookies:Forward:noneMinTTL:0DefaultTTL:0MaxTTL:0
Resources:# CloudFront FunctionRewritePathFunction:Type:AWS::CloudFront::FunctionProperties:Name:!Sub"${AWS::StackName}-rewrite-path"FunctionCode:|
function handler(event) {
var request = event.request;
var uri = request.uri;
//Removetrailingslashif(uri.endsWith('/')) {
request.uri=uri.substring(0, uri.length-1);
}
//Add.htmlextensionforHTMLpagesif(!uri.includes('.')&&!uri.endsWith('/')) {
request.uri=uri+'.html';
}
returnrequest;}Runtime:cloudfront-js-1.0AutoPublish:trueCloudFrontDistribution:Type:AWS::CloudFront::DistributionProperties:DistributionConfig:DefaultCacheBehavior:TargetOriginId:StaticAssetsOriginFunctionAssociations:-FunctionARN:!GetAttRewritePathFunction.FunctionARNEventType:viewer-request
Lambda@Edge Functions
Resources:# Lambda@Edge FunctionLambdaEdgeFunction:Type:AWS::Lambda::FunctionProperties:FunctionName:!Sub"${AWS::StackName}-lambda-edge"Code:S3Bucket:!RefCodeBucketS3Key:lambda/edge-function.zipHandler:index.handlerRuntime:nodejs20.xRole:!GetAttLambdaEdgeRole.Arn# Lambda Version for Lambda@EdgeLambdaEdgeVersion:Type:AWS::Lambda::VersionProperties:FunctionName:!RefLambdaEdgeFunctionDescription:Lambda@EdgeversionCloudFrontDistribution:Type:AWS::CloudFront::DistributionProperties:DistributionConfig:Origins:-Id:OriginDomainName:!RefOriginDomainNameCustomOriginConfig:HTTPPort:443HTTPSPort:443OriginProtocolPolicy:https-onlyDefaultCacheBehavior:TargetOriginId:OriginViewerProtocolPolicy:redirect-to-httpsAllowedMethods:-GET-HEADCachedMethods:-GET-HEADLambdaFunctionAssociations:-FunctionARN:!Sub"arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${LambdaEdgeFunction}:${LambdaEdgeVersion}"EventType:origin-request
Geo-Restrictions and Price Class
Resources:CloudFrontDistribution:Type:AWS::CloudFront::DistributionProperties:DistributionConfig:CallerReference:!Sub"${AWS::StackName}-${AWS::AccountId}"Comment:!Sub"CloudFront with geo restrictions"Enabled:trueIPV6Enabled:true# Price Class - optimize costsPriceClass:PriceClass_200# Geo RestrictionsGeoRestriction:RestrictionType:whitelistLocations:-US-CA-GB-DE-FR-IT-JP-AUOrigins:-Id:OriginDomainName:!RefOriginDomainNameCustomOriginConfig:HTTPPort:443HTTPSPort:443OriginProtocolPolicy:https-onlyDefaultCacheBehavior:TargetOriginId:OriginViewerProtocolPolicy:redirect-to-httpsAllowedMethods:-GET-HEADCachedMethods:-GET-HEADCompress:trueForwardedValues:QueryString:falseCookies:Forward:noneMinTTL:0DefaultTTL:86400MaxTTL:31536000
Resources:# Kinesis Data StreamCloudFrontLogsStream:Type:AWS::Kinesis::StreamProperties:Name:!Sub"${AWS::StackName}-cloudfront-logs"ShardCount:1RetentionPeriodHours:24# IAM Role for CloudFrontCloudFrontLoggingRole:Type:AWS::IAM::RoleProperties:RoleName:!Sub"${AWS::StackName}-cloudfront-logging"AssumeRolePolicyDocument:Version:"2012-10-17"Statement:-Effect:AllowPrincipal:Service:cloudfront.amazonaws.comAction:sts:AssumeRolePolicies:-PolicyName:KinesisPutRecordPolicyDocument:Version:"2012-10-17"Statement:-Effect:AllowAction:-kinesis:PutRecord-kinesis:PutRecordsResource:!GetAttCloudFrontLogsStream.ArnCloudFrontDistribution:Type:AWS::CloudFront::DistributionProperties:DistributionConfig:CallerReference:!Sub"${AWS::StackName}-${AWS::AccountId}"Comment:!Sub"CloudFront with real-time logs"Enabled:trueRealTimeConfig:Endpoint:!GetAttCloudFrontLogsStream.ArnRoleArn:!GetAttCloudFrontLoggingRole.ArnFields:-timestamp-c-ip-cs-method-cs-uri-sc-status-time-takenOrigins:-Id:OriginDomainName:!RefOriginDomainNameCustomOriginConfig:HTTPPort:443HTTPSPort:443OriginProtocolPolicy:https-onlyDefaultCacheBehavior:TargetOriginId:OriginViewerProtocolPolicy:redirect-to-httpsAllowedMethods:-GET-HEADCachedMethods:-GET-HEADForwardedValues:QueryString:falseCookies:Forward:noneMinTTL:0DefaultTTL:86400MaxTTL:31536000
Enable termination protection to prevent accidental stack deletion.
AWSTemplateFormatVersion:2010-09-09Description:CloudFrontwithterminationprotectionResources:CloudFrontDistribution:Type:AWS::CloudFront::DistributionProperties:DistributionConfig:Enabled:true# ... configuration# Note: Termination protection is enabled via AWS Console or CLI# AWS CLI: aws cloudformation update-termination-protection --enable-termination-protection --stack-name my-stack# Or set it in a separate stack update after creation
Drift Detection
Detect when infrastructure has been modified outside of CloudFormation.
# AWS CLI commands for drift detection# Detect drift on a stackawscloudformationdetect-stack-drift--stack-namemy-cloudfront-stack# Get drift detection statusawscloudformationdescribe-stack-drift-detection-status--stack-drift-detection-id<detection-id># Get resources that have driftedawscloudformationdescribe-stack-resource-drifts--stack-namemy-cloudfront-stack# Example drift detection output format# {# "StackResourceDrifts": [# {# "ResourceType": "AWS::CloudFront::Distribution",# "LogicalResourceId": "CloudFrontDistribution",# "PhysicalResourceId": "E1X2Y3Z4W5X6Y7",# "ResourceStatus": "UPDATE",# "PropertyDifferences": [# {# "PropertyPath": "$.DistributionConfig.Enabled",# "ExpectedValue": "true",# "ActualValue": "false"# }# ],# "StackResourceDriftStatus": "MODIFIED"# }# ]# }
Change Sets
Preview and review changes before executing stack updates.
# AWS CLI commands for change sets# 1. Create a change set (preview)awscloudformationcreate-change-set\--stack-namemy-cloudfront-stack\--template-bodyfile://cloudfront-template.yaml\--change-set-namemy-changeset\--capabilitiesCAPABILITY_IAM\--parametersParameterKey=Environment,ParameterValue=production# 2. Describe the change set to review changesawscloudformationdescribe-change-set\--stack-namemy-cloudfront-stack\--change-set-namemy-changeset# 3. Execute the change set if changes are acceptableawscloudformationexecute-change-set\--stack-namemy-cloudfront-stack\--change-set-namemy-changeset# Or delete if changes are not desiredawscloudformationdelete-change-set\--stack-namemy-cloudfront-stack\--change-set-namemy-changeset
# Change set with nested stacks exampleAWSTemplateFormatVersion:2010-09-09Description:CloudFrontinfrastructurewithnestedstacksforchangesetmanagementResources:# Parent stack managing multiple CloudFront distributionsCloudFrontParentStack:Type:AWS::CloudFormation::StackProperties:TemplateURL:!Sub"https://${ArtifactBucket}.s3.amazonaws.com/cloudfront-parent.yaml"TimeoutInMinutes:30Parameters:Environment:!RefEnvironmentCertificateArn:!RefCertificateArnDomainName:!RefDomainNameTags:-Key:EnvironmentValue:!RefEnvironment-Key:ProjectValue:!RefProjectName-Key:ManagedByValue:CloudFormation# Change set will show impacts across all nested stacks# When updating, CloudFormation will show:# - Which nested stacks will be updated# - Resources being added, modified, or deleted# - IAM changes requiring special attention