<section class="blog-post-content"><p>Delivering optimized content is critical to a positive end user experience. As data access requirements evolve, the end user may need a transformed version of the original content. The transformations may include masking an image’s metadata, watermarking, or resizing an image before returning it to the user. The object should be stored in its original format, while the transformed version should be cached at the edge for quick access. In many cases, both the original and transformed versions of an object are stored. When a new access requirement is added, existing objects need to be transformed again and a new version saved. This leads to unnecessary storage and data processing costs.</p><p>Customers use a combination of <a href="https://aws.amazon.com/cloudfront/" target="_blank" rel="noopener noreferrer">Amazon CloudFront</a> and <a href="https://aws.amazon.com/s3/" target="_blank" rel="noopener noreferrer">Amazon Simple Storage Service (S3)</a> to publicly serve static content for websites, applications, and more. With <a href="https://aws.amazon.com/s3/features/object-lambda/" target="_blank" rel="noopener noreferrer">Amazon S3 Object Lambda</a>, you can add code to modify the data returned by standard Amazon S3 GET requests to resize and watermark images, customize metadata, and much more. Amazon S3 Object Lambda eliminates the need to create and store derivative copies of your data or to run expensive proxies, all with no changes required to your applications.</p><p>In this post, I show you how to build a solution that lets you dynamically modify images cached in Amazon CloudFront via Amazon S3 Object Lambda using <a href="https://aws.amazon.com/lambda/edge/" target="_blank" rel="noopener noreferrer">Lambda@Edge</a>. After you’ve built the solution, a user uses a public URL to retrieve multiple transformed versions of an image stored in a private Amazon S3 bucket. This access pattern allows you to quickly adapt to changing business needs by serving a derived set of images without spending time copying and converting your existing image library.</p><h2>Solution overview</h2><p><img class="aligncenter wp-image-13600 size-large" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/19/01-Solution-architecture-diagram-1024x300.png" alt="Modify images cached in Amazon CloudFront using Amazon S3 Object Lambda" width="1024" height="300" /></p><p>The user accesses an object via a public URL, which routes the request to the nearest edge location. If the object is not in the edge cache, Amazon CloudFront sends an <em>origin request</em> to retrieve the object. We configure the origin to be the Amazon S3 Object Lambda Access Point, which allows us to retrieve the transformed object. However, Amazon S3 Object Lambda requires all access to be made by authenticated principals (that is, no anonymous access) and over HTTPS. Amazon CloudFront can redirect HTTP to HTTPS, but we need a method of adding authentication the request.</p><p><a href="https://aws.amazon.com/lambda/edge/" target="_blank" rel="noopener noreferrer">Lambda@Edge</a> is an Amazon CloudFront feature that lets you run code at the edge in response to events generated by Amazon CloudFront. In our solution, we use a Lambda@Edge function to intercept the origin request and <a href="https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html" target="_blank" rel="noopener noreferrer">sign the request</a> using the Lambda execution credentials. The updated, authenticated request is returned to Amazon CloudFront, which sends the request to the Amazon S3 Object Lambda Access Point.</p><p>Amazon S3 Object Lambda receives the authenticated request and returns the transformed object to Amazon CloudFront, where the object is returned to the user and cached at the edge location. Subsequent requests for the object at this edge location receive the cached version of the transformed object, thus avoiding extra Amazon S3 Object Lambda executions.</p><h2>Solution configuration and walkthrough</h2><p>There are two methods of deploying this solution: infrastructure-as-code (IaC) using an <a href="https://github.com/aws-samples/amazon-s3-object-lambda-with-amazon-cloudfront" target="_blank" rel="noopener noreferrer">open-sourced CDK project</a>, or by configuring services in the AWS Console.</p><p>If you’d like to deploy the solution using IaC, please refer to the <a href="https://github.com/aws-samples/amazon-s3-object-lambda-with-amazon-cloudfront/blob/main/README.md#installation" target="_blank" rel="noopener noreferrer">installation section</a> of the CDK project’s <a href="https://github.com/aws-samples/amazon-s3-object-lambda-with-amazon-cloudfront/blob/main/README.md" target="_blank" rel="noopener noreferrer">README file</a> for instructions. This deploys the entire solution in minutes and allows you to run an end-to-end workflow.</p><p>In the next sections, I show you how to set up the solution by configuring AWS services via the Console:</p><ol><li>Create an AWS Lambda function for S3 Object Lambda.</li><li>Create a supporting Amazon S3 Access Point.</li><li>Create an S3 Object Lambda Access Point.</li><li>Grant AWS Lambda Function additional privileges.</li><li>Create and publish an AWS Lambda function for Lambda@Edge.</li><li>Configure Lambda@Edge function permissions and trust relationships.</li><li>Configure the Amazon CloudFront distribution.</li></ol><h3>Prerequisites</h3><p>You will need an S3 bucket to store source images. Configure this bucket to block public access with encryption enabled.</p><h3>1. Create an AWS Lambda function for Amazon S3 Object Lambda</h3><p>This AWS Lambda function is responsible for accessing an object in Amazon S3, transforming it, and returning the transformed object to Amazon S3 Object Lambda. The function is subject to standard <a href="https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html" target="_blank" rel="noopener noreferrer">AWS Lambda quotas</a>, and has up to 60 seconds to send a complete response to the caller through the <code>WriteGetObjectResponse</code> request. The original Amazon S3 object is accessed via the presigned URL generated by Amazon S3 Object Lambda.</p><p>Navigate to the <a href="https://console.aws.amazon.com/lambda" target="_blank" rel="noopener noreferrer">AWS Lambda console</a>. <em>Confirm that the Region displayed in the console is the same as the prerequisite S3 bucket Region.</em> This is because data transfer between AWS Lambda and Amazon S3 in the same Region is free.</p><p><img class="aligncenter wp-image-13604 size-full" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/19/05-Verify-the-selected-region-in-the-AWS-Lambda-console-is-the-same-as-where-you-created-the-Amazon-S3-bucket.png" alt="Verify the selected Region in the AWS Lambda console is the same as the prerequisite S3 bucket Region." width="654" height="494" /></p><p>Select <strong>Create function</strong>:</p><ol><li>Give your AWS Lambda a descriptive name.</li><li>Leave the runtime at <strong>Node.js 16.x</strong> and architecture at <strong>x86_64</strong>.</li><li>Select the <strong>Create</strong> <strong>function</strong> button at the bottom of the page.</li></ol><p>In the following example, I’ve named the Lambda function <strong>images-exif-transform</strong>.</p><p><img class="aligncenter wp-image-13686 size-full" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/26/06-Create-a-new-Lambda-function-named-images-exif-transform-using-the-Node.js-16.x-runtime-on-the-x86_64-architecture-updated.png" alt="Create a new Lambda function, named images-exif-transform, using the Node.js 16.x runtime on the x86_64 architecture" width="750" height="752" /></p><p>The AWS Lambda function, including dependencies, is available to download <a href="https://awsstorageblogresources.s3.us-west-2.amazonaws.com/realtimeobjectprocessing/images-exif-transform-lambda-package.zip" target="_blank" rel="noopener noreferrer">as a .zip file</a>. In the Code Source section of the AWS Lambda editor, select <strong>Upload from</strong>, then .<strong>zip file</strong>, and select the .zip file you downloaded. Select <strong>Save</strong>. The source code can be <a href="https://github.com/aws-samples/amazon-s3-object-lambda-with-amazon-cloudfront/blob/main/lambda/exif_js/index.js" target="_blank" rel="noopener noreferrer">viewed here.</a></p><p>After uploading the AWS Lambda source code, navigate to the <strong>Configuration</strong> tab, select <strong>General configuration</strong>, and select <strong>Edit</strong>:</p><ol><li>Increase <strong>Memory</strong> to <strong>512 MB</strong>.</li><li>Increase <strong>Timeout</strong> to <strong>1 min</strong>.</li><li>Select <strong>Save</strong>.</li></ol><p><img class="aligncenter wp-image-13606 size-full" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/19/07-Configure-the-Lambda-function-with-512MB-of-memory-and-1-minute-timeout.png" alt="Configure the Lambda function with 512MB of memory and 1 minute timeout" width="433" height="208" /></p><p>This function returns different objects to Amazon S3 Object Lambda:</p><ul><li>If the object is not a supported image type, return an error.</li><li>Return a modified image with no metadata, including stripping EXIF.</li><li>If a parameter named <code>showExif</code> is set to true, then return a JSON of the EXIF data.</li></ul><h3>2. Create a supporting Amazon S3 Access Point</h3><p>Amazon S3 Object Lambda requires a supporting Amazon <a href="https://aws.amazon.com/s3/features/access-points/" target="_blank" rel="noopener noreferrer">S3 Access Point</a>. This allows you to create unique access control policies to easily control access to data in Amazon S3.</p><p>Navigate to the <a href="https://s3.console.aws.amazon.com/s3/ap" target="_blank" rel="noopener noreferrer">Access Points section</a> of the Amazon S3 console. Select <strong>Create access point</strong>:</p><ol><li>Give your access point a unique name.</li><li>Select the bucket where you have stored the original images.</li><li>Under <strong>Network origin</strong> select <strong>Internet</strong>.</li><li>Under <strong>Block Public Access settings for this Access Point</strong>, confirm that <strong>block all public access is selected</strong>.</li></ol><p>In the following example, I’ve named my Access Point <code>my-images-blog-access-point</code>.</p><p><img class="aligncenter wp-image-13607 size-large" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/19/08-Create-an-Amazon-S3-Access-Point-named-my-images-blog-access-point-for-the-my-images-blog-bucket-S3-bucket.-Block-all-public-access-for-this-Access-Point-1024x851.png" alt="Create an Amazon S3 Access Point named my-images-blog-access-point for the my-images-blog-bucket S3 bucket. Block all public access for this Access Point" width="1024" height="851" /></p><ol start="5"><li>For the Access Point policy, we want to grant our AWS Lambda function access to all objects via the Access Point. Note the trailing <code>/object/</code> on the resource. This allows the principal (that is, our AWS Lambda execution role) access to the Amazon S3 objects via this Access Point.</li></ol><pre class="lang-json">{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "<Lambda execution role ARN>" }, "Action": "s3:GetObject", "Resource": "<Access point ARN>/object/" } ]}</pre><p><img class="aligncenter size-large wp-image-13608" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/19/09-Update-the-Amazon-S3-Access-Point-policy-to-grant-the-Exif-Transform-Lambda-execution-role-access-1024x594.png" alt="Update the Amazon S3 Access Point policy to grant the Exif Transform Lambda execution role access" width="1024" height="594" /></p><ol start="6"><li>Select <strong>Create access point</strong>.</li></ol><h3>3. Create an S3 Object Lambda Access Point</h3><p>An Amazon S3 Object Lambda Access Point is associated with exactly one standard Amazon S3 Access Point. A GET request through this Access Point invokes the associated AWS Lambda function and returns a transformed object.</p><p>Navigate to the <a href="https://s3.console.aws.amazon.com/s3/olap" target="_blank" rel="noopener noreferrer">Object Lambda Access Points</a> section of the Amazon S3 console. Select <strong>Create S3 Object Lambda Access Point</strong>:</p><ol><li>Give your Amazon S3 Object Lambda Access Point a unique name.</li><li>Under <strong>Supporting Access Point</strong>, select the Access Point created earlier.</li><li>Under AWS Lambda function, select the function created earlier.</li></ol><p>In the following example, I’ve named my Access Point <code>my-images-blog-s3-object-lambda-ap</code>.</p><p><img class="aligncenter wp-image-13690 size-large" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/26/Create-an-S3-OL-Access-Point-updated-993x1024.png" alt="Create an Amazon S3 Object Lambda Access Point named my-images-blog-s3-object-lambda-ap. This is associated with the previously-created supporting access point and the images-exif-transform Lambda function" width="993" height="1024" /></p><ol start="4"><li>For the <strong>Object Lambda Access Point policy</strong>, we want to add a statement that grants our Lambda function access to all objects via the Amazon S3 Object Lambda Access Point. Note: You can find the Amazon S3 Object Lambda Access Point Amazon Resource Name (ARN) just above the policy editor.</li></ol><pre class="lang-json">{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "<Lambda execution role ARN>" }, "Action": "s3-object-lambda:GetObject", "Resource": "<S3 Object Lambda access point ARN>" } ]}</pre><p><img class="aligncenter size-large wp-image-13610" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/19/11-Update-the-Amazon-S3-Object-Lambda-Access-Point-policy-to-grant-the-Exif-Transform-Lambda-execution-role-access-1024x511.png" alt="Update the Amazon S3 Object Lambda Access Point policy to grant the Exif Transform Lambda execution role access" width="1024" height="511" /></p><ol start="5"><li>Select <strong>Create Object Lambda Access Point</strong>.</li></ol><h3>4. Grant AWS Lambda Function additional privileges</h3><p>We need to grant the AWS Lambda function permission to write its response to Amazon S3 Object Lambda. We do this by adding policies to the AWS Lambda execution role. This is an <a href="https://docs.aws.amazon.com/iam/?id=docs_gateway" target="_blank" rel="noopener noreferrer">AWS Identity and Access Management</a> (IAM) role that grants the function permission to access AWS services and resources.</p><p>Navigate to the <a href="https://console.aws.amazon.com/lambda" target="_blank" rel="noopener noreferrer">AWS Lambda console</a> and select the Lambda function you created earlier. In the <strong>Lambda code edito</strong>r, select the <strong>Configuration</strong> tab, then select <strong>Permissions</strong>. The execution role is a link to the right.</p><p><img class="aligncenter size-large wp-image-13618" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/19/12-Select-the-AWS-Lambda-function-execution-role-1024x409.png" alt="Select the AWS Lambda function execution role" width="1024" height="409" /></p><p>Select this role name, which launches a new window to the IAM service.</p><ol><li>Select <strong>Add permissions</strong>, then <strong>Create</strong> <strong>inline policy</strong>.</li><li>In the <strong>Create policy</strong> page that opens, select the <strong>JSON</strong> tab<strong>.</strong></li><li>Paste the following JSON in the editor.</li></ol><pre class="lang-json">{ "Version": "2012-10-17", "Statement": [ { "Action": "s3-object-lambda:WriteGetObjectResponse", "Resource": "<S3 Object Lambda access point ARN>", "Effect": "Allow" } ]}</pre><ol start="4"><li>Select <strong>Review policy</strong>.</li><li>Give the policy a descriptive name (for example, <code>S3OL-WriteObjectResponse</code>)</li><li>Select <strong>Create policy</strong>.</li></ol><p><img class="aligncenter wp-image-13687 size-full" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/26/13-Create-an-inline-IAM-policy-that-allows-the-AWS-Lambda-function-access-to-write-the-response-back-to-Amazon-S3-Object-Lambda-updated.png" alt="Create an inline IAM policy that allows the AWS Lambda function access to write the response back to Amazon S3 Object Lambda" width="859" height="353" /></p><p>Note the execution role’s ARN, because we reference it later in this post.</p><p><img class="aligncenter size-large wp-image-13620" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/19/14-IAM-role-for-the-images-exif-transform-AWS-Lambda-function.-Note-the-Amazon-Resource-Name-ARN-1024x360.png" alt="IAM role for the images-exif-transform AWS Lambda function. Note the Amazon Resource Name (ARN)" width="1024" height="360" /></p><h3>5. Create and publish an AWS Lambda function for Lambda@Edge</h3><p>This AWS Lambda function runs at the edge using <a href="https://aws.amazon.com/lambda/edge/" target="_blank" rel="noopener noreferrer">Lambda@Edge</a> and performs the following:</p><ul><li>Receives the Amazon CloudFront origin request. This is the request that Amazon CloudFront sends to Amazon S3 Object Lambda in order to retrieve an object.</li><li>Signs the request using its execution credentials.</li><li>Returns the updated, signed request to Amazon CloudFront.</li></ul><p>Note: This AWS Lambda function <em>must</em> be deployed in the <code>us-east-1</code> Region. After we configure our Amazon CloudFront distribution to use this function, it propagates to all edge locations.</p><p>Navigate to the <a href="https://us-east-1.console.aws.amazon.com/lambda" target="_blank" rel="noopener noreferrer">AWS Lambda console</a>. Confirm that the Region displayed is <code>us-east-1 (N. Virginia)</code>. Select <strong>Create function</strong>.</p><ol><li>Give your Lambda function a descriptive name, (for example, <code>edge-origin-request-signer</code>).</li><li>For <strong>Runtime</strong>, choose <strong>Python 3.9</strong> and verify the architecture is <strong>x86_64</strong>.</li><li>Select the <strong>Create function</strong> button at the bottom of the page.</li></ol><p>In the code editor, paste <a href="https://github.com/aws-samples/amazon-s3-object-lambda-with-amazon-cloudfront/blob/main/lambda/edge_signer/edge_signer.py" target="_blank" rel="noopener noreferrer">the source code</a> into lambda_function.py. Then select <strong>Deploy</strong>.</p><p><img class="aligncenter size-full wp-image-13621" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/19/15-Deploy-the-code-for-the-Amazon-CloudFront-Lambda@Edge-function-in-the-AWS-Lambda-console.png" alt="Deploy the code for the Amazon CloudFront Lambda@Edge function in the AWS Lambda console" width="748" height="376" /></p><p>Lambda@Edge points to a specific version of an AWS Lambda function. To publish a version, in the AWS Lambda function editor, select the <strong>Versions</strong> tab, select <strong>Publish new version</strong>, then select <strong>Publish</strong>.</p><p><img class="aligncenter size-full wp-image-13632" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/20/19-Publish-a-version-of-the-Amazon-CloudFront-Lambda@Edge-function.png" alt="Publish a version of the Amazon CloudFront Lambda@Edge function" width="787" height="332" /></p><p>After publishing the version, you are redirected to the AWS Lambda version page. Note the <code>Function ARN</code>, which is used later in our Amazon CloudFront configuration.</p><p><img class="aligncenter wp-image-13633 size-large" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/20/20-AWS-Lambda-function-version.-Note-the-Function-ARN-1024x485.png" alt="AWS Lambda function version. Note the Function ARN" width="1024" height="485" /></p><h3>6. Configure Lambda@Edge function permissions and trust relationships</h3><p>The previous function’s execution role is used to sign anonymous requests from Amazon CloudFront. We grant policies to this role that allow it to:</p><ul><li>Get objects from the Amazon S3 bucket and S3 supporting Access Point (<code>s3:GetObject</code>).</li><li>Get objects from the Amazon S3 Object Lambda Access Point (<code>s3-object-lambda:GetObject</code>).</li><li>Invoke the AWS Lambda function associated with the Amazon S3 Object Lambda (<code>lambda:InvokeFunction</code>).</li><li>Create <strong>log groups</strong>, <strong>log streams</strong>, and put log events to <strong>all regions</strong> as Lambda@Edge functions run in edge locations – that is, not just the Region where the function was created – (<code>logs:CreateLogGroup</code>, <code>logs:CreateLogStream</code>, <code>logs:PutLogEvents</code>).</li></ul><p>Update the policy for this function using the role link from the AWS Lambda editor.</p><p><img class="aligncenter size-full wp-image-13622" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/19/16-Update-the-policy-for-the-edge-Lambda-function.png" alt="Deploy the code for the Amazon CloudFront Lambda@Edge function in the AWS Lambda console" width="992" height="752" /></p><p>Select the <strong>JSON</strong> tab and replace the policy with the following.</p><pre class="lang-json">{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "logs:CreateLogGroup", "Resource": "arn:aws:logs::<account ID>:" }, { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": [ "arn:aws:logs::<account ID>:log-group:/aws/lambda/<Edge function name>:" ] }, { "Action": "lambda:InvokeFunction", "Resource": "<Lambda ARN associated with S3 Object Lambda>:$LATEST", "Effect": "Allow" }, { "Action": "s3:GetObject", "Resource": [ "<S3 bucket ARN>/", "<S3 supporting access point ARN>/object/*" ], "Effect": "Allow" }, { "Action": "s3-object-lambda:GetObject", "Resource": "<S3 Object Lambda access point ARN>", "Effect": "Allow" } ]}</pre><p>Select <strong>Review policy.</strong> It should look similar to the following:</p><p><img class="aligncenter size-full wp-image-13630" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/20/17-Verify-the-IAM-policy-permissions-for-the-Amazon-CloudFront-Lambda@Edge-function-execution-role.png" alt="Verify the IAM policy permissions for the Amazon CloudFront Lambda@Edge function execution role" width="745" height="352" /></p><p>Select <strong>Save changes</strong>.</p><p>Typically, an AWS Lambda function role is assumed by the AWS Lambda service. In our use case, the Lambda@Edge service also assumes this role.</p><p>From the IAM window, select the <strong>Trust relationships</strong> tab, and select <strong>Edit trust policy</strong>. Replace the service section of the JSON with the following:</p><pre class="lang-json">"Service": [ "lambda.amazonaws.com", "edgelambda.amazonaws.com"]</pre><p>Select <strong>Update policy</strong>.</p><p><img class="aligncenter size-full wp-image-13631" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/20/18-Edit-the-IAM-role-for-the-Amazon-CloudFront-Lambda@Edge-function-to-also-include-edgelambda.amazonaws.com-as-a-trusted-principal.png" alt="Edit the IAM role for the Amazon CloudFront Lambda@Edge function to also include edgelambda.amazonaws.com as a trusted principal" width="647" height="487" /></p><h3>7. Configure the Amazon CloudFront distribution</h3><p><a href="https://aws.amazon.com/cloudfront/" target="_blank" rel="noopener noreferrer">Amazon CloudFront</a> is a low-latency content delivery network (CDN) that delivers data through 410+ globally dispersed Points of Presence (PoPs) with automated network mapping and intelligent routing. By default, static content is cached for 24 hours at edge locations. In our use case, Amazon S3 Object Lambda processes each requested image once per 24 hours (twice if we request the <code>JSON EXIF</code> data).</p><h4>Create a cache policy</h4><p>An Amazon CloudFront <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/controlling-the-cache-key.html">cache policy</a> determines whether an HTTP request results in a cache hit (that is, the object is served to the viewer from the Amazon CloudFront cache) via the cache key. Since our Amazon S3 Object Lambda function supports the <code>showExif</code> query string, we want to use that as part of the cache key.</p><p>Navigate to the <a href="https://console.aws.amazon.com/cloudfront/v3/home#/policies/cache" target="_blank" rel="noopener noreferrer">Policies section</a> of the Amazon CloudFront console. Select the <strong>Cache</strong> tab, then select <strong>Create cache policy</strong>.</p><p class="c4">1. Give the cache policy a descriptive name (i.e., <code>object-lambda-query-string</code>).</p><p><img class="aligncenter size-full wp-image-13634" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/20/21-Create-an-Amazon-CloudFront-cache-policy.png" alt="Create an Amazon CloudFront cache policy" width="824" height="293" /></p><p class="c4">2. For Query strings, select <strong>Include specified query strings</strong>, and add <code>showExif</code> to the <strong>Allow</strong> list.</p><p><img class="aligncenter size-full wp-image-13635" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/20/22-The-CloudFront-cache-policy-only-allows-the-showExif-query-string.png" alt="The CloudFront cache policy only allows the showExif query string" width="824" height="248" /></p><p class="c4">3. Select <strong>Create</strong>.</p><h4>Create the Amazon CloudFront distribution</h4><p>Navigate to the Amazon <a href="https://console.aws.amazon.com/cloudfront" target="_blank" rel="noopener noreferrer">CloudFront console</a>. Select <strong>Create distribution</strong>.</p><p>Origin:</p><p class="c5">1. For <strong>Origin domain</strong>, type in the URL prefix for our Amazon S3 Object Lambda Access Point. The format is:</p><pre><s3 object lambda access point name>-<account ID>.s3-object-lambda.<access point region>.amazonaws.com</pre><ul><li>For example:</li></ul><pre>my-images-blog-s3-object-lambda-ap-123456789012.s3-object-lambda.us-west-2.amazonaws.com</pre><p class="c5">2. For <strong>Protocol</strong>, select <strong>HTTPS only</strong>.3. For <strong>Minimum origin SSL protocol</strong>, select <strong>2</strong>.</p><p>Default cache behavior:</p><ol><li>For <strong>Viewer protocol policy</strong>, select <strong>Redirect HTTP to HTTPS</strong>.</li><li>Under <strong>Cache key and origin requests</strong>, for cache policy, select the policy created in the earlier section.</li></ol><p>Function associations:</p><ol><li>Next to <strong>origin request</strong>, select <strong>Lambda@Edge</strong>.</li><li>In the <strong>Function ARN/Name</strong> text field, insert the ARN of the Lambda@Edge Lambda version we created earlier. Note: Make sure the ARN ends with :<code>#</code>, where <code>#</code> is the version number.</li><li>Select <strong>Create distribution.</strong></li></ol><p>The distribution is ready when the distribution state changes from <strong>Deploying</strong> to a timestamp.</p><h2>Solution testing</h2><p>Upload an image containing <code>EXIF</code> data to the S3 image storage bucket. Typically, a picture taken by a mobile phone has this data. A sample <a href="https://github.com/aws-samples/amazon-s3-object-lambda-with-amazon-cloudfront/raw/main/images/TestImage.jpg" target="_blank" rel="noopener noreferrer">picture is provided here</a>.</p><p>Navigate to the Amazon CloudFront domain name and append the picture name and path to access the image anonymously through Amazon S3 Object Lambda.</p><p>In the following screenshot, the original picture is on the left, while the image downloaded through Amazon CloudFront is on the right. Notice that the <code>EXIF</code> data has been stripped.</p><p><img class="aligncenter wp-image-13689 size-full" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/26/Comparison-of-the-original-image-and-the-image-downloaded-through-this-solution-via-Amazon-CloudFront-updated.png" alt="Comparison of the original image and the image downloaded through this solution via Amazon CloudFront. The original image has EXIF data while the downloaded image does not" width="725" height="318" /></p><p>Add a search parameter <code>&showExif=true</code> to the end of the URL in order to return a JSON of the image <code>EXIF</code> data.</p><p><img class="aligncenter wp-image-13688 size-full" src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2022/09/26/24-Adding-showExiftrue-to-the-public-URL-returns-the-images-EXIF-data-in-JSON-format-updated.png" alt="Adding showExif=true to the public URL returns the image's EXIF data in JSON format" width="574" height="178" /></p><h2>Cleaning up</h2><p>To delete the resources created by the automated CDK deployment, refer to the <a href="https://github.com/aws-samples/amazon-s3-object-lambda-with-amazon-cloudfront#uninstall" target="_blank" rel="noopener noreferrer">uninstall section</a> of the repository. To delete the resources created using the walkthrough, clean up the following:</p><ul><li>Amazon CloudFront: <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/HowToDeleteDistribution.html" target="_blank" rel="noopener noreferrer">Delete</a> the distribution.</li><li>AWS Lambda functions: Refer to the clean-up section of the <a href="https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html" target="_blank" rel="noopener noreferrer">AWS Lambda guide</a>.</li><li>Amazon S3 bucket: <a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/delete-bucket.html" target="_blank" rel="noopener noreferrer">Delete</a> the bucket.</li></ul><h2>Conclusion</h2><p>In this post, I showed you how to use a combination of Amazon S3 Object Lambda and Amazon CloudFront Lambda@Edge to serve transformed versions of images stored in Amazon S3. This post described the general building blocks of anonymous Amazon S3 Object Lambda access, and can be extended easily using other transformation functions. With Amazon CloudFront, you can extend functionality by making use of HTTP headers or search parameters, like we have done with the <code>showExif=true</code> parameter.</p><p>With this solution, you can store original images in Amazon S3 without having to worry about future data-access requirements or running backfills for transformations. You also save on storage costs by storing fewer derivative copies of data. By adapting this solution to your use case, you can deliver optimized content to end users securely and consistently without the heavy lifting of maintaining an object transformation pipeline.</p><p>To learn more about Amazon S3 Object Lambda, visit the <a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/transforming-objects.html" target="_blank" rel="noopener noreferrer">S3 User Guide</a>. To learn more about Amazon CloudFront Lambda@Edge, visit the <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/edge-functions.html" target="_blank" rel="noopener noreferrer">CloudFront user guide</a>.</p></section>