DEV Community

Robert Slootjes
Robert Slootjes

Posted on

CloudFront & S3 Versioning

I was looking into options how to serve specific versions of objects on a versioned S3 bucket with CloudFront. After doing some diggin it turned out to be pretty easy. As described in the documentation of S3 on retrieving versions, a specific version can be retrieved from S3 by adding ?version={version} to the url. When configuring CloudFront you must also allow the versionId as query string parameter in the cache settings so it will be passed to the S3 Origin. After this is done and you try to retrieve a specific version you will get a permission denied error. To solve this, you must give CloudFront s3:GetObjectVersion permission to your S3 bucket in the policy.

Complete example in CloudFormation:

Resources:
  S3:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${AWS::StackName}-assets'
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
  CloudFrontOriginAccessIdentity:
    Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: !Sub '${AWS::StackName}'
  S3Policy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref S3
      PolicyDocument:
        Statement:
          - Sid: CloudFront
            Action:
              - 's3:GetObject'
              - 's3:GetObjectVersion'
            Effect: Allow
            Resource: { 'Fn::Join': [ '/', [ !GetAtt S3.Arn , '*' ] ] }
            Principal:
              AWS: { 'Fn::Join': [ ' ', [ 'arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity', { Ref: CloudFrontOriginAccessIdentity } ] ] }
  CloudFront:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        IPV6Enabled: true
        Enabled: true
        Comment: !Sub '${AWS::StackName}'
        Origins:
          - Id: S3
            DomainName: !GetAtt S3.DomainName
            S3OriginConfig:
              OriginAccessIdentity: { 'Fn::Join': [ '/', [ 'origin-access-identity', 'cloudfront', { Ref: CloudFrontOriginAccessIdentity } ] ] }
        DefaultCacheBehavior:
          AllowedMethods: [ HEAD, OPTIONS, GET ]
          CachedMethods: [ HEAD, OPTIONS, GET ]
          ForwardedValues:
            QueryString: true
            QueryStringCacheKeys: [ versionId ]
          TargetOriginId: S3
          ViewerProtocolPolicy: https-only
          Compress: true

Enter fullscreen mode Exit fullscreen mode

You can now get the latest version of your asset by accessing:

https://xyz.cloudfront.net/some_image.jpg
Enter fullscreen mode Exit fullscreen mode

and you can request a specific version like this:

https://xyz.cloudfront.net/some_image.jpg?versionId=abcdef
Enter fullscreen mode Exit fullscreen mode

You now have the benefits of servering versioned content over CloudFront, also giving you the possibility to restrict access to your content with signed cookies or signed urls.

Top comments (0)