s3-bucket-generic+cloudfront.yaml
---

S3 Bucket CloudFormation Deployment with CloudFront Distribution

This CloudFormation template will deploy an S3 bucket with it's own IAM user and a role to go along with it, plus a CloudFront distribution.

  • An S3 bucket with encryption enabled
  • An IAM user with full access to that S3 bucket
  • An IAM Policy which allows access to that S3 bucket
  • A CloudFront Distribution that defaults to accessing the S3 bucket for content
AWSTemplateFormatVersion: '2010-09-09' Description: Generic S3 bucket + CloudFront Distribution

Parameters

These are the input parameters for this template. All of these parameters must be supplied for this template to be deployed.

Parameters:

The name of the bucket.

BucketName: Type: String Description: The name of the S3 Bucket. AllowedPattern: '(^((?![-])[a-z0-9-]{0,63}(?<![-]))$)' EnableVersioningParameter: Type: String Description: Enable S3 Bucket Versioning Default: "Yes" AllowedValues: ["Yes", "No"] ExpireOldVersions: Type: Number Description: If Versioning is enabled, expire old versions after this many days (20 - 720, default 360) MinValue: 20 MaxValue: 720 Default: 360 EnableIntelligentTieringParameter: Type: String Description: Enable S3 Bucket Intelligent Tiering for all objects (90 days to archive, 360 to deep archive) Default: "Yes" AllowedValues: ["Yes", "No"] EnableRemoteAccountParameter: Type: String Description: Enable S3 Bucket Remote Account Access Default: "No" AllowedValues: ["Yes", "No"] RemoteAccountsList: Type: CommaDelimitedList Description: Comma delimited list of full root account ARNs S3DefaultWebPage: Type: String Description: Default web page document for CloudFront from S3 bucket AllowedPattern: '(^((?![-])[a-zA-Z0-9-.]{0,63}(?<![-]))$)' PublicKeyEncoded: Type: String Description: Encoded Public Key for access to the CloudFront Distro CallerReference: Type: String Description: Caller CloudFrontDistroName: Type: String Description: >- Initial portion of CloudFront FQDN. Max 15 alphanumeric characters plus dash and underscore. The dash and underscore cannot be first or last in the name. AllowedPattern: '(^((?![-_])(?=.*[a-z].*)[a-z0-9-_]{1,20}(?<![-_]))$)' ConstraintDescription: Max 20 alphanumeric characters plus dash and underscore. The dash and underscore cannot be first or last in the name. HostedZoneName: Type: String Description: route53 hosted zone name (lowercase only) Default: uits-prod-aws.arizona.edu AllowedPattern: '(?=^.{4,253}$)(^((?!-)[a-z0-9-]{1,63}(?<!-)\.)+[a-z]{2,63}$)' ConstraintDescription: A dot-delimited DNS zone name, permitting numbers, lowercase letters, and dashes. EnableURLSigningParameter: Type: String Description: Enable CloudFront URL Signing requirement Default: "Yes" AllowedValues: ["Yes", "No"] AcmCertificateArn:

The certificate needs to be in us-east-1 to be accessible to CloudFront

Type: String Description: ARN of ACM Certificate to use for the CloudFront Distribution AllowedPattern: 'arn:aws:acm:us-east-1:[0-9]+:[\w+=,.@-]+(/[\w+=,.@-]+)*' ConstraintDescription: ARN of ACM Certificate in us-east-1. Must be valid and current. CloudFrontCacheTTL: Type: Number Description: Default cache time to live for CloudFront Distro (in seconds, 1 to 86400, default 1800) MinValue: 1 MaxValue: 86400 Default: 1800

Tags

The following tags are applied to all resources created by this template.

TagService: Description: Service name (from the service catalog) that is utilizing this resource Type: String TagEnvironment: Description: Type of environment that is using this resource, such as 'dev', 'tst', 'prd'. Type: String AllowedValues: [dev, tst, prd, trn, stg, cfg, sup, rpt] Default: dev TagContactNetId: Description: NetID of the person to contact for information about this resource Type: String TagAccountNumber: Description: Financial system account number for the service utilizing this resource Type: String TagTicketNumber: Description: Ticket number that this resource is for Type: String

Metadata

Metadata is mostly for organizing and presenting Parameters in a better way when using CloudFormation in the AWS Web UI.

Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: S3 Bucket Configuration Parameters: - BucketName - EnableVersioningParameter - ExpireOldVersions - EnableIntelligentTieringParameter - EnableRemoteAccountParameter - RemoteAccountsList - Label: default: CloudFront Configuration Parameters: - S3DefaultWebPage - EnableURLSigningParameter - PublicKeyEncoded - CallerReference - CloudFrontDistroName - HostedZoneName - AcmCertificateArn - CloudFrontCacheTTL - Label: default: Tagging and Cost Management Parameters: - TagService - TagEnvironment - TagContactNetId - TagAccountNumber - TagTicketNumber ParameterLabels: BucketName: default: 'Bucket Name:' TagService: default: "Service Name:" TagEnvironment: default: 'Environment Type:' TagContactNetId: default: 'Technical Contact NetID:' TagAccountNumber: default: 'Financial Account Number:' TagTicketNumber: default: 'Ticket Number:' EnableVersioningParameter: default: 'Enable Versioning:' EnableIntelligentTieringParameter: default: 'Enable Intelligent Tiering:' EnableRemoteAccountParameter: default: 'Enable Remote Accounts:'

Conditions

Conditions: EnableVersioning: !Equals [!Ref EnableVersioningParameter, "Yes"] EnableIntelligentTiering: !Equals [!Ref EnableIntelligentTieringParameter, "Yes"] EnableRemoteAccount: !Equals [!Ref EnableRemoteAccountParameter, "Yes"] EnableURLSigning: !Equals [!Ref EnableURLSigningParameter, "Yes"]

Resources

These are all of the resources deployed by this template.

Resources:

S3 Bucket

This deploys the S3 bucket with some tags.

S3Bucket: Type: AWS::S3::Bucket Properties: BucketName: !Ref BucketName AccessControl: Private BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 VersioningConfiguration: Fn::If: - EnableVersioning - Status: Enabled - !Ref AWS::NoValue LifecycleConfiguration: Rules: - Id: !Join ["-",[!Ref BucketName,"Multipart-Rule"]] AbortIncompleteMultipartUpload: DaysAfterInitiation: 3 Status: Enabled - Id: !Join ["-",[!Ref BucketName,"Lifecycle-Rule"]] NoncurrentVersionExpirationInDays: !Ref ExpireOldVersions Status: !If [EnableVersioning, Enabled, Disabled] IntelligentTieringConfigurations: - Fn::If: - EnableIntelligentTiering - Id: AllS3Objects Status: Enabled Tierings: - AccessTier: ARCHIVE_ACCESS Days: 90 - AccessTier: DEEP_ARCHIVE_ACCESS Days: 360 - !Ref AWS::NoValue PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True Tags: - Key: Name Value: !Sub "${BucketName}-s3" - Key: service Value: !Ref TagService - Key: environment Value: !Ref TagEnvironment - Key: contactnetid Value: !Ref TagContactNetId - Key: accountnumber Value: !Ref TagAccountNumber - Key: ticketnumber Value: !Ref TagTicketNumber

S3 BucketPolicy

Permits remote access to bucket from indicated accounts

RemoteBucketPolicy: Type: AWS::S3::BucketPolicy Condition: EnableRemoteAccount Properties: Bucket: !Ref S3Bucket PolicyDocument: Statement: - Sid: AllowRemoteAccountAccess Action: - "s3:GetBucketLocation" - "s3:ListBucket" - "s3:PutObject" - "s3:DeleteObject" - "s3:ListBucketVersions" Effect: "Allow" Principal: AWS: !Ref RemoteAccountsList Resource: - !Sub "${S3Bucket.Arn}" - !Sub "${S3Bucket.Arn}/*" - Sid: RequireFullAccessPut Action: - "s3:PutObject" Effect: "Deny" Principal: AWS: !Ref RemoteAccountsList Resource: - !Sub "${S3Bucket.Arn}/*" Condition: StringNotEquals: s3:x-amz-acl: bucket-owner-full-control

S3 Bucket User

Creates an IAM user that can only connect to the S3 bucket specified.

S3BucketUser: Type: AWS::IAM::User Properties: Path: "/" Policies: - PolicyName: S3BucketAccess PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - s3:List* Resource: - "*" - Effect: Allow Action: - s3:* Resource: !Sub "${S3Bucket.Arn}/*"

S3 Bucket Assignable Policy

Creates an IAM policy that can only connect to the S3 bucket specified.

S3BucketPolicy: Type: AWS::IAM::Policy Properties: PolicyName: !Sub "s3access-${BucketName}" Roles: - !Ref EnvInstanceRole PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - s3:List* Resource: - "*" - Effect: Allow Action: - s3:* Resource: !Sub "${S3Bucket.Arn}/*"

Instance Assignable Role

This is an IAM role that can be applied to an EC2 Instance. Any AWS specific permissions that the node might need should be defined here.

EnvInstanceRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole Path: "/"

Instance Assignable Profile

This is just a little construct to connect a role together into a profile. The profile can be referenced by an EC2 Instance.

EnvInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: "/" Roles: - !Ref EnvInstanceRole

S3 BucketPolicy for CloudFront

Permits remote access to bucket from indicated CloudFront distro

PolicyForCloudFrontPrivateContent: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref S3Bucket PolicyDocument: Statement: - Sid: PolicyForCloudFrontPrivateContent Action: - "s3:GetObject" Effect: "Allow" Principal: CanonicalUser: !GetAtt OriginAccessIdentity.S3CanonicalUserId Resource: - !Sub "${S3Bucket.Arn}/*" OriginAccessIdentity: Type: AWS::CloudFront::CloudFrontOriginAccessIdentity Properties: CloudFrontOriginAccessIdentityConfig: Comment: !Ref S3Bucket DnsRecord: Type: AWS::Route53::RecordSet Properties: Name: !Sub "${CloudFrontDistroName}.${HostedZoneName}" Type: A HostedZoneName: !Sub "${HostedZoneName}." AliasTarget: DNSName: !GetAtt Distribution.DomainName HostedZoneId: Z2FDTNDATAQYW2 # magic string for CloudFront CachePolicy: Type: AWS::CloudFront::CachePolicy Properties: CachePolicyConfig: DefaultTTL: !Ref CloudFrontCacheTTL MaxTTL: 86400 MinTTL: 1 Name: !Sub "${AWS::StackName}Caching" ParametersInCacheKeyAndForwardedToOrigin: CookiesConfig: CookieBehavior: none EnableAcceptEncodingBrotli: true EnableAcceptEncodingGzip: true HeadersConfig: HeaderBehavior: none QueryStringsConfig: QueryStringBehavior: none Distribution: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: Enabled: true Comment: !Sub "CloudFront Distro for ${AWS::StackName}" HttpVersion: 'http2' DefaultRootObject: !Ref S3DefaultWebPage Aliases: - !Sub "${CloudFrontDistroName}.${HostedZoneName}" DefaultCacheBehavior: TargetOriginId: !Sub "S3-${S3Bucket}" ViewerProtocolPolicy: redirect-to-https CachePolicyId: !Ref CachePolicy AllowedMethods: - GET - HEAD TrustedKeyGroups: - Fn::If: - EnableURLSigning - !Ref KeyGroup - !Ref AWS::NoValue Origins: - DomainName: !GetAtt S3Bucket.DomainName Id: !Sub "S3-${S3Bucket}" S3OriginConfig: OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${OriginAccessIdentity}"

PriceClass limit to edge locations in N America and Europe.

PriceClass: 'PriceClass_100' ViewerCertificate: AcmCertificateArn: !Ref AcmCertificateArn MinimumProtocolVersion: 'TLSv1.2_2019' SslSupportMethod: sni-only Tags: - Key: Name Value: !Sub "${BucketName}-CloudFront" - Key: service Value: !Ref TagService - Key: environment Value: !Ref TagEnvironment - Key: contactnetid Value: !Ref TagContactNetId - Key: accountnumber Value: !Ref TagAccountNumber - Key: ticketnumber Value: !Ref TagTicketNumber KeyGroup: Type: AWS::CloudFront::KeyGroup Condition: EnableURLSigning Properties: KeyGroupConfig: Items: - !Ref PublicKey Name: !Sub "${AWS::StackName}SigningKeyGroup" PublicKey: Type: AWS::CloudFront::PublicKey Condition: EnableURLSigning Properties: PublicKeyConfig: CallerReference: !Ref CallerReference EncodedKey: !Ref PublicKeyEncoded Name: !Sub "${AWS::StackName}SigningKeyGroup"

Outputs

Output values that can be viewed from the AWS CloudFormation console.

Outputs: BucketName: Description: "Bucket Name" Value: !Ref S3Bucket Export: Name: !Sub "${AWS::StackName}-bucket-name" BucketRole: Description: "Instance Role" Value: !Ref EnvInstanceRole Export: Name: !Sub "${AWS::StackName}-instance-role" InstanceProfile: Description: "Instance Profile" Value: !Ref EnvInstanceProfile Export: Name: !Sub "${AWS::StackName}-instance-profile" IAMUser: Description: "IAM User" Value: !Ref S3BucketUser Export: Name: !Sub "${AWS::StackName}-iam-user" DNSName: Description: "DNS Name" Value: !Ref DnsRecord Export: Name: !Sub "${AWS::StackName}-dns-name" Distribution: Description: "CloudFront Distribution" Value: !Ref Distribution Export: Name: !Sub "${AWS::StackName}-cf-distro"