### Access content through CloudFront
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/get-started-cli-tutorial.md
Example URL to access content served by the CloudFront distribution.
```bash
https://{{d111111abcdef8.cloudfront.net}}/index.html
```
--------------------------------
### Install and package solution artifacts
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/getting-started-secure-static-website-cloudformation-template.md
Command to install dependencies and package the solution's artifacts locally.
```bash
make package-static
```
--------------------------------
### Clean up local files
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/get-started-cli-tutorial.md
Commands to remove local files created during the tutorial setup.
```bash
# Clean up local files
rm -f distribution-config.json bucket-policy.json temp_disabled_config.json
rm -rf ~/cloudfront-demo
```
--------------------------------
### Shell Script for CloudFront Setup
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/example_cloudfront_GettingStarted_section.md
This script automates the process of creating an S3 bucket, uploading content, setting up Origin Access Control (OAC), and creating a CloudFront distribution.
```bash
# Get AWS account ID early
ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
if [ $? -ne 0 ]; then
handle_error "Failed to get AWS account ID"
fi
# Validate account ID format
if ! [[ "$ACCOUNT_ID" =~ ^[0-9]{12}$ ]]; then
handle_error "Invalid AWS account ID format: $ACCOUNT_ID"
fi
# Create a temporary directory for content with restrictive permissions
TEMP_DIR="temp_content"
mkdir -p "$TEMP_DIR/css"
chmod 700 "$TEMP_DIR"
if [ $? -ne 0 ]; then
handle_error "Failed to create temporary directory"
fi
# Step 1: Create an S3 bucket (only if not shared)
if [ "$BUCKET_IS_SHARED" != "true" ]; then
echo "Creating S3 bucket: $BUCKET_NAME"
aws s3 mb "s3://$BUCKET_NAME" --region us-east-1
if [ $? -ne 0 ]; then
handle_error "Failed to create S3 bucket"
fi
aws s3api put-bucket-tagging --bucket "$BUCKET_NAME" --tagging 'TagSet=[{Key=project,Value=doc-smith},{Key=tutorial,Value=cloudfront-gettingstarted}]'
# Batch bucket configuration calls for efficiency
aws s3api put-bucket-versioning --bucket "$BUCKET_NAME" --versioning-configuration Status=Enabled &
aws s3api put-public-access-block \
--bucket "$BUCKET_NAME" \
--public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true" &
aws s3api put-bucket-encryption \
--bucket "$BUCKET_NAME" \
--server-side-encryption-configuration '{ \
"Rules": [ \
{ \
"ApplyServerSideEncryptionByDefault": { \
"SSEAlgorithm": "AES256" \
} \
} \
] \
}' &
wait
fi
# Step 2: Create sample content
echo "Creating sample content..."
cat > "$TEMP_DIR/index.html" << 'EOF'
Hello World
Hello world!
EOF
cat > "$TEMP_DIR/css/styles.css" << 'EOF'
body {
font-family: Arial, sans-serif;
margin: 40px;
background-color: #f5f5f5;
}
h1 {
color: #333;
text-align: center;
}
EOF
# Set restrictive permissions on content files
chmod 600 "$TEMP_DIR/index.html" "$TEMP_DIR/css/styles.css"
# Step 3: Upload content to the S3 bucket with encryption and metadata
echo "Uploading content to S3 bucket..."
aws s3 cp "$TEMP_DIR/" "s3://$BUCKET_NAME/" --recursive \
--sse AES256 \
--metadata "Source=CloudFrontTutorial"
if [ $? -ne 0 ]; then
handle_error "Failed to upload content to S3 bucket"
fi
# Step 4: Create Origin Access Control
echo "Creating Origin Access Control..."
OAC_RESPONSE=$(aws cloudfront create-origin-access-control \
--origin-access-control-config Name="oac-for-$BUCKET_NAME",SigningProtocol=sigv4,SigningBehavior=always,OriginAccessControlOriginType=s3)
if [ $? -ne 0 ]; then
handle_error "Failed to create Origin Access Control"
fi
OAC_ID=$(echo "$OAC_RESPONSE" | jq -r '.OriginAccessControl.Id')
if [ -z "$OAC_ID" ] || [ "$OAC_ID" = "null" ]; then
handle_error "Failed to extract OAC ID from response"
fi
# Validate OAC ID format (alphanumeric and hyphens)
if ! [[ "$OAC_ID" =~ ^[A-Z0-9]+$ ]]; then
handle_error "Invalid OAC ID format: $OAC_ID"
fi
echo "Created Origin Access Control with ID: $OAC_ID"
# Step 5: Create CloudFront distribution
echo "Creating CloudFront distribution..."
# Validate bucket name format
if ! [[ "$BUCKET_NAME" =~ ^[a-z0-9][a-z0-9.-]*[a-z0-9]$ ]]; then
handle_error "Invalid S3 bucket name format: $BUCKET_NAME"
fi
# Create distribution configuration with improved security settings
cat > distribution-config.json << EOF
{
"CallerReference": "cli-tutorial-$(date +%s)",
"Origins": {
"Quantity": 1,
"Items": [
{
"Id": "S3-$BUCKET_NAME",
"DomainName": "$BUCKET_NAME.s3.amazonaws.com",
"S3OriginConfig": {
"OriginAccessIdentity": ""
},
"OriginAccessControlId": "$OAC_ID"
}
]
},
"DefaultCacheBehavior": {
"TargetOriginId": "S3-$BUCKET_NAME",
"ViewerProtocolPolicy": "redirect-to-https",
"AllowedMethods": {
"Quantity": 2,
"Items": ["GET", "HEAD"],
"CachedMethods": {
"Quantity": 2,
"Items": ["GET", "HEAD"]
}
},
"DefaultTTL": 86400,
"MinTTL": 0,
"MaxTTL": 31536000,
"Compress": true,
"ForwardedValues": {
"QueryString": false,
"Cookies": {
"Forward": "none"
}
}
},
"Comment": "CloudFront distribution for tutorial",
"Enabled": true,
"WebACLId": "",
"HttpVersion": "http2and3"
}
EOF
```
--------------------------------
### Get method request example
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-custom-methods.md
Example of how to use the get() method to fetch a key's value, specifying the format as a string.
```javascript
const value = await kvsHandle.get("myFunctionKey", { format: "string"});
```
--------------------------------
### Download and extract sample content files
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/get-started-cli-tutorial.md
These commands create a temporary directory, download sample "Hello World" webpage files, and extract them.
```bash
# Create a temporary directory
mkdir -p ~/cloudfront-demo
# Download the sample Hello World files
curl -o ~/cloudfront-demo/hello-world-html.zip https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/samples/hello-world-html.zip
# Extract the zip file
unzip ~/cloudfront-demo/hello-world-html.zip -d ~/cloudfront-demo/hello-world
```
--------------------------------
### Upload the content to the bucket
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/get-started-cli-tutorial.md
This command uploads the extracted "Hello World" files to your S3 bucket. Replace {{amzn-s3-demo-bucket}} with your bucket name.
```bash
aws s3 cp ~/cloudfront-demo/hello-world/ s3://{{amzn-s3-demo-bucket}}/ --recursive
```
--------------------------------
### Example: Creating a "dummy" origin
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/live-streaming.md
This example demonstrates how to configure CloudFront origins and cache behaviors to handle MediaPackage endpoints, including a "dummy" origin for wildcard paths.
```text
MediaPackage endpoints:
https://3ae97e9482b0d011.mediapackage.us-west-2.amazonaws.com/out/v1/abc123/index.m3u8
https://3ae97e9482b0d011.mediapackage.us-west-2.amazonaws.com/out/v1/def456/index.m3u8
CloudFront Origin A:
Domain: 3ae97e9482b0d011.mediapackage.us-west-2.amazonaws.com
Path: None
CloudFront Origin B:
Domain: mediapackage.us-west-2.amazonaws.com
Path: None
CloudFront cache behavior:
1. Path: /out/v1/abc123/* forward to Origin A
2. Path: /out/v1/def456/* forward to Origin A
3. Path: * forward to Origin B
```
--------------------------------
### Get method error handling example (sequential await)
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-custom-methods.md
Example demonstrating sequential await for fetching multiple values to avoid high memory usage.
```javascript
var value1 = await kvs.get('key1');
var value2 = await kvs.get('key2');
```
--------------------------------
### CloudFront Distribution Creation and Configuration
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/example_cloudfront_GettingStarted_section.md
This script demonstrates how to create a CloudFront distribution, extract its ID and domain name, and tag it. It also includes error handling and validation steps.
```bash
chmod 600 distribution-config.json
DIST_RESPONSE=$(aws cloudfront create-distribution --distribution-config file://distribution-config.json)
if [ $? -ne 0 ]; then
handle_error "Failed to create CloudFront distribution"
fi
DISTRIBUTION_ID=$(echo "$DIST_RESPONSE" | jq -r '.Distribution.Id')
DOMAIN_NAME=$(echo "$DIST_RESPONSE" | jq -r '.Distribution.DomainName')
if [ -z "$DISTRIBUTION_ID" ] || [ "$DISTRIBUTION_ID" = "null" ] || [ -z "$DOMAIN_NAME" ] || [ "$DOMAIN_NAME" = "null" ]; then
handle_error "Failed to extract distribution ID or domain name from response"
fi
# Validate distribution ID format
if ! [[ "$DISTRIBUTION_ID" =~ ^[A-Z0-9]+$ ]]; then
handle_error "Invalid distribution ID format: $DISTRIBUTION_ID"
fi
echo "Created CloudFront distribution with ID: $DISTRIBUTION_ID"
echo "CloudFront domain name: $DOMAIN_NAME"
# Tag the CloudFront distribution
aws cloudfront tag-resource --resource "arn:aws:cloudfront::$ACCOUNT_ID:distribution/$DISTRIBUTION_ID" --tags 'Items=[{Key=project,Value=doc-smith},{Key=tutorial,Value=cloudfront-gettingstarted}]'
```
--------------------------------
### Get method error handling example (Promise.all - not recommended)
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-custom-methods.md
Example showing the use of Promise.all for fetching multiple values, which is not recommended due to potential high memory usage.
```javascript
var values = await Promise.all([kvs.get('key1'), kvs.get('key2'),]);
```
--------------------------------
### Invalidation paths examples
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/invalidation-specifying-objects.md
Examples of how to specify paths for invalidation using wildcards.
```text
/{{directory-path}}/*
```
```text
/{{directory-path}}/*
```
```text
/{{directory-path}}/{{file-name}}.*
```
```text
/{{directory-path}}/{{initial-characters-in-file-name}}*
```
```text
/{{directory-path}}/{{file-name}}.{{file-name-extension}}*
```
```text
/*
```
--------------------------------
### Displaying Access Information
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/example_cloudfront_GettingStarted_section.md
This section displays the final access information for the created CloudFront distribution, including the domain name and a summary of created resources.
```bash
echo "CloudFront distribution is now deployed."
# Step 8: Display access information
echo ""
echo "===== CloudFront Distribution Setup Complete ====="
echo "You can access your content at: https://$DOMAIN_NAME/index.html"
echo ""
echo "Resources created:"
echo "- S3 Bucket: $BUCKET_NAME"
echo "- CloudFront Origin Access Control: $OAC_ID"
echo "- CloudFront Distribution: $DISTRIBUTION_ID"
echo ""
echo "To clean up resources, run: cleanup"
echo ""
echo "Tutorial completed at $(date)"
```
--------------------------------
### Example Log File
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/standard-logs-reference.md
An example of a standard CloudFront log file, showing the header and a data row.
```log
#Version: 1.0
#Fields: date time x-edge-location sc-bytes c-ip cs-method cs(Host) cs-uri-stem sc-status cs(Referer) cs(User-Agent) cs-uri-query cs(Cookie) x-edge-result-type x-edge-request-id x-host-header cs-protocol cs-bytes time-taken x-forwarded-for ssl-protocol ssl-cipher x-edge-response-result-type cs-protocol-version fle-status fle-encrypted-fields c-port time-to-first-byte x-edge-detailed-result-type sc-content-type sc-content-len sc-range-start sc-range-end
2019-12-04 21:02:31 LAX1 392 192.0.2.100 GET d111111abcdef8.cloudfront.net /index.html 200 - Mozilla/5.0%20(Windows%20NT%2010.0;%20Win64;%20x64)%20AppleWebKit/537.36%20(KHTML,%20like%20Gecko)%20Chrome/78.0.3904.108%20Safari/537.36 - - Hit SOX4xwn4XV6Q4rgb7XiVGOHms_BGlTAC4KyHmureZmBNrjGdRLiNIQ== d111111abcdef8.cloudfront.net https 23 0.001 - TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 Hit HTTP/2.0 - - 11040 0.001 Hit text/html 78 - -
```
--------------------------------
### AWS CLI with Bash script
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/example_wafv2_GettingStarted_052_section.md
This script automates the creation of an AWS WAF Web ACL with a string match rule and AWS Managed Rules. It associates the Web ACL with a CloudFront distribution and includes steps for logging and resource cleanup. The script is designed for use with the AWS CLI and is available in a GitHub repository for a complete example.
```bash
#!/bin/bash
# AWS WAF Getting Started Script
# This script creates a Web ACL with a string match rule and AWS Managed Rules,
# associates it with a CloudFront distribution, and then cleans up all resources.
set -euo pipefail
# Security: Restrict file permissions
umask 077
# Set up logging with secure file handling
LOG_FILE="waf-tutorial.log"
touch "$LOG_FILE"
chmod 600 "$LOG_FILE"
exec > >(tee -a "$LOG_FILE") 2>&1
echo "==================================================="
echo "AWS WAF Getting Started Tutorial"
echo "==================================================="
echo "This script will create AWS WAF resources and associate"
echo "them with a CloudFront distribution."
echo ""
# Maximum number of retries for operations
MAX_RETRIES=3
# Function to handle errors securely
handle_error() {
local error_msg="$1"
echo "ERROR: $error_msg" >&2
echo "Check the log file for details: $LOG_FILE" >&2
cleanup_resources
exit 1
}
# Function to validate AWS CLI JSON output
validate_json() {
local json_string="$1"
if ! echo "$json_string" | jq empty 2>/dev/null; then
return 1
fi
return 0
}
# Function to safely extract JSON values
extract_json_value() {
local json_string="$1"
local key_path="$2"
if ! validate_json "$json_string"; then
return 1
fi
echo "$json_string" | jq -r "$key_path" 2>/dev/null || return 1
}
# Function to clean up resources securely
cleanup_resources() {
echo ""
echo "==================================================="
echo "CLEANING UP RESOURCES"
echo "==================================================="
if [ -n "${DISTRIBUTION_ID:-}" ] && [ -n "${WEB_ACL_ARN:-}" ]; then
echo "Disassociating Web ACL from CloudFront distribution..."
local account_id
account_id=$(aws sts get-caller-identity --query Account --output text 2>/dev/null) || account_id=""
if [ -z "$account_id" ]; then
echo "Warning: Could not retrieve AWS account ID"
return
fi
local disassociate_result
disassociate_result=$(aws wafv2 disassociate-web-acl \
--resource-arn "arn:aws:cloudfront::${account_id}:distribution/${DISTRIBUTION_ID}" \
--region us-east-1 2>&1) || true
if echo "$disassociate_result" | grep -qi "error"; then
echo "Warning: Failed to disassociate Web ACL: $disassociate_result"
else
echo "Web ACL disassociated successfully."
fi
fi
if [ -n "${WEB_ACL_ID:-}" ] && [ -n "${WEB_ACL_NAME:-}" ]; then
echo "Deleting Web ACL..."
local get_result
get_result=$(aws wafv2 get-web-acl \
--name "$WEB_ACL_NAME" \
--scope CLOUDFRONT \
--id "$WEB_ACL_ID" \
--region us-east-1 2>&1) || true
if echo "$get_result" | grep -qi "error"; then
echo "Warning: Failed to get Web ACL for deletion: $get_result"
echo "You may need to manually delete the Web ACL using the AWS Console."
else
local latest_token
latest_token=$(extract_json_value "$get_result" '.WebACL.LockToken' 2>/dev/null) || latest_token=""
if [ -n "$latest_token" ]; then
local delete_result
delete_result=$(aws wafv2 delete-web-acl \
--name "$WEB_ACL_NAME" \
--scope CLOUDFRONT \
--id "$WEB_ACL_ID" \
--lock-token "$latest_token" \
--region us-east-1 2>&1) || true
if echo "$delete_result" | grep -qi "error"; then
echo "Warning: Failed to delete Web ACL: $delete_result"
echo "You may need to manually delete the Web ACL using the AWS Console."
else
echo "Web ACL deleted successfully."
fi
else
echo "Warning: Could not extract lock token for deletion. You may need to manually delete the Web ACL."
fi
fi
fi
echo "Cleanup process completed."
}
# Security: Trap EXIT to ensure cleanup on any exit
trap cleanup_resources EXIT
```
--------------------------------
### Wait for distribution deployment
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/get-started-cli-tutorial.md
This command waits until the CloudFront distribution has finished deploying.
```bash
aws cloudfront wait distribution-deployed --id $DISTRIBUTION_ID
```
--------------------------------
### Create a distribution configuration file
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/get-started-cli-tutorial.md
This snippet shows how to create a JSON file named `distribution-config.json` with the necessary configuration for a CloudFront distribution. It includes placeholders for bucket names and Origin Access Control ID.
```bash
cat > distribution-config.json << EOF
{
"CallerReference": "cli-example-$(date +%s)",
"Origins": {
"Quantity": 1,
"Items": [
{
"Id": "S3-{{amzn-s3-demo-bucket}}",
"DomainName": "{{amzn-s3-demo-bucket}}.s3.amazonaws.com",
"S3OriginConfig": {
"OriginAccessIdentity": ""
},
"OriginAccessControlId": "$OAC_ID"
}
]
},
"DefaultCacheBehavior": {
"TargetOriginId": "S3-{{amzn-s3-demo-bucket}}",
"ViewerProtocolPolicy": "redirect-to-https",
"AllowedMethods": {
"Quantity": 2,
"Items": ["GET", "HEAD"],
"CachedMethods": {
"Quantity": 2,
"Items": ["GET", "HEAD"]
}
},
"DefaultTTL": 86400,
"MinTTL": 0,
"MaxTTL": 31536000,
"Compress": true,
"ForwardedValues": {
"QueryString": false,
"Cookies": {
"Forward": "none"
}
}
},
"Comment": "CloudFront distribution for S3 bucket",
"Enabled": true
}
EOF
```
--------------------------------
### Java CloudFront Utilities Examples
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/example_cloudfront_CloudFrontUtilities_section.md
Examples demonstrating how to get cookies for canned and custom policies using CloudFront utilities in Java.
```java
CookiesForCannedPolicy cookiesForCannedPolicy = cloudFrontUtilities
.getCookiesForCannedPolicy(cannedSignerRequest);
logger.info("Cookie EXPIRES header [{}]", cookiesForCannedPolicy.expiresHeaderValue());
logger.info("Cookie KEYPAIR header [{}]", cookiesForCannedPolicy.keyPairIdHeaderValue());
logger.info("Cookie SIGNATURE header [{}]", cookiesForCannedPolicy.signatureHeaderValue());
return cookiesForCannedPolicy;
}
public static CookiesForCustomPolicy getCookiesForCustomPolicy(CustomSignerRequest customSignerRequest) {
CookiesForCustomPolicy cookiesForCustomPolicy = cloudFrontUtilities
.getCookiesForCustomPolicy(customSignerRequest);
logger.info("Cookie POLICY header [{}]", cookiesForCustomPolicy.policyHeaderValue());
logger.info("Cookie KEYPAIR header [{}]", cookiesForCustomPolicy.keyPairIdHeaderValue());
logger.info("Cookie SIGNATURE header [{}]", cookiesForCustomPolicy.signatureHeaderValue());
return cookiesForCustomPolicy;
}
}
```
--------------------------------
### Tenant Configuration Example
Source: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/example_cloudfront_CreateDistribution_section.md
An example of tenant configuration parameters for a CloudFront distribution.
```json
{
"TenantConfig": {
"ParameterDefinitions": [
{
"Name": "tenantName",
"Definition": {
"StringSchema": {
"Comment": "tenantName parameter",
"DefaultValue": "root",
"Required": false
}
}
}
]
},
"ConnectionMode": "tenant-only"
}
}
}
```