26
votes

I am working on the AWS creation of the bucket and distribution using the scripts. I have created a script and running the stack template script in AWS cloud formation console.

I am creating the bucketpolicy for S3 bucket using the script and canonical ID. Once I create a bucket policy I want to assign it to the "OriginAccessIdentity" dynamically in the script. I want to add the id generated from the bucket policy to "OriginAccessIdentity" attributes.

How to achieve this functionality?

Script :

{
    "AWSTemplateFormatVersion" : "2010-09-09",

    "Description" : "AWS CloudFormation Template S3_With_CloudFront_Distribution",

    "Parameters" : {
        "bucketname" : {
          "Type" : "String",
          "Description" : "test"          
        },

        "cannonicalid" : {
          "Type" : "String",
          "Description" : "234213523145314534523452345234523452345"       
        }
    },

     "Conditions" : {
        "CreateProdResources" : {"Fn::Equals" : [{"Ref" : "EnvType"}, "dev"]}
    },

    "Resources" : {
        "testbucket" : {
          "Type" : "AWS::S3::Bucket",
          "Properties" : {      
            "BucketName" : { "Ref" : "bucketname" },          
            "WebsiteConfiguration" : {
               "IndexDocument" : "index.html"              
            }
          }
        },


        "mybucketpolicy" : {
           "Type" : "AWS::S3::BucketPolicy",
           "Properties" : {
              "PolicyDocument" : {
                 "Id" : "MyPolicy",
                 "Statement" : [ {
                    "Sid" : "Grant a CloudFront Origin Identity access to support private content",
                    "Action" : [ "s3:GetObject" ],
                    "Effect" : "Allow",
                    "Resource" : { "Fn::Join" : [
                          "", [ "arn:aws:s3:::", { "Ref" : "testbucket" } , "/*" ]
                       ] },
                    "Principal" : {
                       "CanonicalUser":{ "Ref" : "cannonicalid" }
                    }
                 } ]
              },
              "Bucket" : { "Ref" : "testbucket" }
              }
        },


        "testdistribution" : {
            "Type" : "AWS::CloudFront::Distribution",
            "Properties" : {
               "DistributionConfig" : {
                   "Origins" : [ {
                           "Id" : "S3Origin",
                           "DomainName" : { "Fn::GetAtt" : [ "testbucket", "DomainName" ] },
                           "S3OriginConfig" : {
                               "OriginAccessIdentity" : "How to configure the id dynamically here"
                           }
                       }
                   ],

                   "Enabled" : "true",
                   "Comment" : "",
                   "DefaultRootObject" : "index.html",                    
                   "Aliases" : [ "test.com" ],

                   "CacheBehaviors" : [ {
                            "TargetOriginId" : "S3Origin",
                            "ForwardedValues" : {
                                "QueryString" : "false"
                            },                            
                            "ViewerProtocolPolicy" : "allow-all",
                            "MinTTL" : "1",
                            "PathPattern" : "resources/*.json"
                        }
                   ],
                   "DefaultCacheBehavior" : {
                       "TargetOriginId" : "S3Origin",
                       "ForwardedValues" : {
                           "QueryString" : "false"
                        },                       
                       "ViewerProtocolPolicy" : "allow-all",
                       "MinTTL" : "1"
                   }
                }
            }
        }
    },
    "Outputs" : {
        "DistributionId" : {
            "Description" : "CloudFront Distribution Id",
            "Value" : { "Ref" : "testdistribution" }
        },
        "DistributionName" : {
             "Description" : "URL to access the CloudFront distribution",
             "Value" : { "Fn::Join" : [ "", ["http://", {"Fn::GetAtt" : ["testdistribution", "DomainName"]} ]]}
        },
        "S3OriginDNSName" : {
             "Description" : "Name of S3 bucket to hold website content.",
             "Value" : { "Fn::GetAtt" : [ "testbucket", "DomainName"] }
        }
  }
}  
3
Using the scripts its not advisable to create a OAI user in cloud formation, reason it may take little time to create user in the cloud. So we are creating the OAI user using the .NET code, then making use of the OAI user in script. Please let me know if there is any other better solution for creation of OAI user.Mohammed Irfan
refer the url to create OAI using .NET code. irfanshirur.blogspot.in/2014/01/…Mohammed Irfan

3 Answers

28
votes

Since November 2, 2017 CloudFormation supports this using AWS::CloudFront::CloudFrontOriginAccessIdentity resource.

With a origin access identity resource defined as:

"OriginAccessId": {
    "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity",
    "Properties": {
        "CloudFrontOriginAccessIdentityConfig": {
            "Comment": "MyDescription"
        }
    }
}

you can reference it in the distribution config with:

"OriginAccessIdentity" : { 
  "Fn::Sub": "origin-access-identity/cloudfront/${OriginAccessId}"
}
6
votes

This works :)
ApplicationName and DomainName are paramters:

TheCloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Aliases:
          - !Sub '${ApplicationName}-ui.${DomainName}'
        DefaultCacheBehavior:
          Compress: true
          ForwardedValues:
            QueryString: false
          TargetOriginId: !Sub '${ApplicationName}.${DomainName}'
          ViewerProtocolPolicy: redirect-to-https
        DefaultRootObject: index.html
        CustomErrorResponses:
          - ErrorCachingMinTTL: 300
            ErrorCode: 403
            ResponseCode: 404
            ResponsePagePath: /404.html
        Enabled: true
        HttpVersion: http2
        Origins:
          - DomainName:
              !Sub '${TheBucket}.s3.amazonaws.com'
            Id: !Sub '${ApplicationName}.${DomainName}'
            S3OriginConfig:
              OriginAccessIdentity: !Join [ "", [ "origin-access-identity/cloudfront/", !Ref TheCloudFrontOriginAccessIdentity ] ]
--
--
--
TheCloudFrontOriginAccessIdentity:
    Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: !Sub 'CloudFront OAI for sd-${ApplicationName}.${DomainName}'
1
votes

An Origin Access Identity cannot be created with CloudFormation. The only CloudFront resource available through Cloudformation is the AWS::CloudFront::Distribution resource.

You can avoid hard coding a reference to an OAI in your template by using a parameter to pass in an existing OAI when your stack is created. You can then use this parameter as the value for OriginAccessIdentity in the S3Origin type associated with the S3OriginConfig key.

This isn't ideal, but it allows you to make your templates more generic.