Published 2021-03-22.
Time to read: 1 minutes.
Here are some command-line utilities I have written for AWS. They are dependent on aws cli. You can download all of these utilities in tar format. Extract them into the current directory like this:
$ tar xf mslinn_aws.tar
awsCfInvalidate
Given a CloudFront distribution ID, invalidate the distribution.
#!/bin/bash
function help {
printf "$1$(basename $0) - Invalidate the CloudFront distribution for the given ID.
If no distribution with the given ID exists, the empty string is returned and the return code is 2.
A message is printed asynchronously to the console when the invalidation completes.
Syntax: $(basename $0) distId
Syntax: awsCfS3Dist www.mslinn.com | $(basename $0)
"
exit 1
}
function waitForInvalidation {
echo "Waiting for invalidation $2 to complete."
aws cloudfront wait invalidation-completed \
--distribution-id "$1" \
--id "$2"
echo "Invalidation $2 has completed."
}
if [ "$1" == -h ]; then help; fi
if [ "$1" ]; then
DIST_ID="$1"
shift
elif [ ! -t 0 ]; then
read -r DIST_ID
fi
if [ -z "$DIST_ID" ]; then help 'Error: No CloudFront distribution ID was specified.\n\n'; fi
if [ "$1" ]; then help 'Error: Too many arguments provided.\n\n'; fi
JSON="$( aws cloudfront create-invalidation \
--distribution-id "$DIST_ID" \
--paths "/*"
)"
INVALIDATION_ID="$( jq -r .Invalidation.Id <<< "$JSON" )"
waitForInvalidation "$DIST_ID" "$INVALIDATION_ID" &
Example usages:
$ awsCfInvalidate E2P5S6OYKQNB6B Waiting for invalidation IFOPKECU4YYHD to complete. ... do other things ... $ Invalidation IFOPKECU4YYHD has completed.
$ awsCfS3Dist www.mslinn.com | awsCfInvalidate Waiting for invalidation IFOPKECU4YYHD to complete. ... do other things ... $ Invalidation IFOPKECU4YYHD has completed.
awsCfS3Dist
Given an S3 bucket name, return the CloudFront distribution JSON.
#!/bin/bash
function help {
printf "$1$(basename $0) - Obtain the CloudFront distribution JSON for an S3 bucket.
If no S3 bucket with the given name exists, the empty string is returned and the return code is 2.
Syntax: $(basename $0) bucketName
Syntax: echo bucketName | $(basename $0)
"
exit 1
}
if [ "$1" == -h ]; then help; fi
if [ "$1" ]; then
BUCKET_NAME="$1"
shift
elif [ ! -t 0 ]; then
read -r BUCKET_NAME
fi
if [ -z "$BUCKET_NAME" ]; then help 'Error: No S3 bucket name was specified.\n\n'; fi
if [ "$1" ]; then help 'Error: Too many arguments provided.\n\n'; fi
if [ "$( aws s3api head-bucket --bucket $BUCKET_NAME 2> >(grep -i 'Not Found') )" ]; then
>&2 echo "Error: Bucket $BUCKET_NAME does not exist."
exit 2
fi
DIST_ID="$( awsCfS3DistId "$BUCKET_NAME" )"
if [ -z "$DIST_ID" ]; then exit 2; fi
aws cloudfront get-distribution-config --id "$DIST_ID"
Example usages:
$ awsCfS3Dist www.mslinn.com { "ETag": "E1DIZUSLMOLXKP", "DistributionConfig": { "CallerReference": "1454487160038", "Aliases": { "Quantity": 2, "Items": [ "www.mslinn.com", "mslinn.com" ] }, "DefaultRootObject": "index.html", "Origins": { "Quantity": 1, "Items": [ { "Id": "S3-www.mslinn.com", "DomainName": "www.mslinn.com.s3-website-us-east-1.amazonaws.com", "OriginPath": "", "CustomHeaders": { "Quantity": 0 }, "CustomOriginConfig": { "HTTPPort": 80, "HTTPSPort": 443, "OriginProtocolPolicy": "http-only", "OriginSslProtocols": { "Quantity": 3, "Items": [ "TLSv1", "TLSv1.1", "TLSv1.2" ] }, "OriginReadTimeout": 30, "OriginKeepaliveTimeout": 5 }, "ConnectionAttempts": 3, "ConnectionTimeout": 10 } ] }, "OriginGroups": { "Quantity": 0 }, "DefaultCacheBehavior": { "TargetOriginId": "S3-www.mslinn.com", "TrustedSigners": { "Enabled": false, "Quantity": 0 }, "ViewerProtocolPolicy": "redirect-to-https", "AllowedMethods": { "Quantity": 2, "Items": [ "HEAD", "GET" ], "CachedMethods": { "Quantity": 2, "Items": [ "HEAD", "GET" ] } }, "SmoothStreaming": false, "Compress": true, "LambdaFunctionAssociations": { "Quantity": 0 }, "FieldLevelEncryptionId": "", "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6" }, "CacheBehaviors": { "Quantity": 0 }, "CustomErrorResponses": { "Quantity": 2, "Items": [ { "ErrorCode": 403, "ResponsePagePath": "", "ResponseCode": "", "ErrorCachingMinTTL": 60 }, { "ErrorCode": 404, "ResponsePagePath": "", "ResponseCode": "", "ErrorCachingMinTTL": 60 } ] }, "Comment": "", "Logging": { "Enabled": false, "IncludeCookies": false, "Bucket": "", "Prefix": "" }, "PriceClass": "PriceClass_All", "Enabled": true, "ViewerCertificate": { "ACMCertificateArn": "arn:aws:acm:us-east-1:031372724784:certificate/2be42926-829c-4db9-be7d-a72e951256d4", "SSLSupportMethod": "sni-only", "MinimumProtocolVersion": "TLSv1", "Certificate": "arn:aws:acm:us-east-1:031372724784:certificate/2be42926-829c-4db9-be7d-a72e951256d4", "CertificateSource": "acm" }, "Restrictions": { "GeoRestriction": { "RestrictionType": "none", "Quantity": 0 } }, "WebACLId": "", "HttpVersion": "http1.1", "IsIPV6Enabled": false } }
$ echo www.mslinn.com | awsCfS3Dist { "ETag": "E1DIZUSLMOLXKP", "DistributionConfig": { "CallerReference": "1454487160038", "Aliases": { "Quantity": 2, "Items": [ "www.mslinn.com", "mslinn.com" ] }, "DefaultRootObject": "index.html", "Origins": { "Quantity": 1, "Items": [ { "Id": "S3-www.mslinn.com", "DomainName": "www.mslinn.com.s3-website-us-east-1.amazonaws.com", "OriginPath": "", "CustomHeaders": { "Quantity": 0 }, "CustomOriginConfig": { "HTTPPort": 80, "HTTPSPort": 443, "OriginProtocolPolicy": "http-only", "OriginSslProtocols": { "Quantity": 3, "Items": [ "TLSv1", "TLSv1.1", "TLSv1.2" ] }, "OriginReadTimeout": 30, "OriginKeepaliveTimeout": 5 }, "ConnectionAttempts": 3, "ConnectionTimeout": 10 } ] }, "OriginGroups": { "Quantity": 0 }, "DefaultCacheBehavior": { "TargetOriginId": "S3-www.mslinn.com", "TrustedSigners": { "Enabled": false, "Quantity": 0 }, "ViewerProtocolPolicy": "redirect-to-https", "AllowedMethods": { "Quantity": 2, "Items": [ "HEAD", "GET" ], "CachedMethods": { "Quantity": 2, "Items": [ "HEAD", "GET" ] } }, "SmoothStreaming": false, "Compress": true, "LambdaFunctionAssociations": { "Quantity": 0 }, "FieldLevelEncryptionId": "", "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6" }, "CacheBehaviors": { "Quantity": 0 }, "CustomErrorResponses": { "Quantity": 2, "Items": [ { "ErrorCode": 403, "ResponsePagePath": "", "ResponseCode": "", "ErrorCachingMinTTL": 60 }, { "ErrorCode": 404, "ResponsePagePath": "", "ResponseCode": "", "ErrorCachingMinTTL": 60 } ] }, "Comment": "", "Logging": { "Enabled": false, "IncludeCookies": false, "Bucket": "", "Prefix": "" }, "PriceClass": "PriceClass_All", "Enabled": true, "ViewerCertificate": { "ACMCertificateArn": "arn:aws:acm:us-east-1:031372724784:certificate/2be42926-829c-4db9-be7d-a72e951256d4", "SSLSupportMethod": "sni-only", "MinimumProtocolVersion": "TLSv1", "Certificate": "arn:aws:acm:us-east-1:031372724784:certificate/2be42926-829c-4db9-be7d-a72e951256d4", "CertificateSource": "acm" }, "Restrictions": { "GeoRestriction": { "RestrictionType": "none", "Quantity": 0 } }, "WebACLId": "", "HttpVersion": "http1.1", "IsIPV6Enabled": false } }
awsCfS3DistId
Given an S3 bucket name, return the CloudFront distribution ID.
#!/bin/bash
function help {
printf "$1$(basename $0) - Obtain the CloudFront distribution ID for an S3 bucket.
If no S3 bucket with the given name exists, the empty string is returned and the return code is 2.
Syntax: $(basename $0) bucketName
Syntax: echo bucketName | $(basename $0)
"
exit 1
}
if [ "$1" == -h ]; then help; fi
if [ "$1" ]; then
BUCKET_NAME="$1"
shift
elif [ ! -t 0 ]; then
read -r BUCKET_NAME
fi
if [ -z "$BUCKET_NAME" ]; then help 'Error: No S3 bucket name was specified.\n\n'; fi
if [ "$1" ]; then help 'Error: Too many arguments provided.\n\n'; fi
if [ "$( aws s3api head-bucket --bucket $BUCKET_NAME 2> >(grep -i 'Not Found') )" ]; then
>&2 echo "Error: Bucket $BUCKET_NAME does not exist."
exit 2
fi
DIST_ID="$(
aws cloudfront list-distributions \
--query "DistributionList.Items[*].{id:Id,origin:Origins.Items[0].Id}[?origin=='S3-$BUCKET_NAME'].id" \
--output text
)"
if [ -z "$DIST_ID" ]; then exit 2; fi
echo "$DIST_ID"
Example usages:
$ awsCfS3DistId www.mslinn.com E2P5S6OYKQNB6B
$ echo www.mslinn.com | awsCfS3DistId E2P5S6OYKQNB6B
awsCfS3MakeDist
Creates a CloudFront distribution for the given bucket name. Returns the new distribution's ID.
#!/bin/bash
function help {
printf "$1$(basename $0) - Make a new CloudFront distribution for the given S3 bucket name.
Returns the new distribution's ID.
Syntax: $(basename $0) bucketName
Syntax: echo bucketName | $(basename $0)
"
exit 1
}
function doesDistributionExist {
DIST_ID="$( awsCfS3Dist "$BUCKET_NAME" )"
if [ "$DIST_ID" ]; then echo true; fi
}
function createDist {
read -r -d '' NEW_DIST_JSON <<EOF
{
"CallerReference": "$BUCKET_NAME",
"Aliases": {
"Quantity": 0
},
"DefaultRootObject": "index.html",
"Origins": {
"Quantity": 1,
"Items": [
{
"Id": "$BUCKET_NAME",
"DomainName": "$BUCKET_NAME.s3.amazonaws.com",
"S3OriginConfig": {
"OriginAccessIdentity": ""
}
}
]
},
"DefaultCacheBehavior": {
"TargetOriginId": "$BUCKET_NAME",
"ForwardedValues": {
"QueryString": true,
"Cookies": {
"Forward": "none"
}
},
"TrustedSigners": {
"Enabled": false,
"Quantity": 0
},
"ViewerProtocolPolicy": "redirect-to-https",
"MinTTL": 3600
},
"CacheBehaviors": {
"Quantity": 0
},
"Comment": "",
"Logging": {
"Enabled": false,
"IncludeCookies": true,
"Bucket": "",
"Prefix": ""
},
"PriceClass": "PriceClass_All",
"Enabled": true
}
EOF
NEW_DIST_RESULT_JSON = "$(
aws cloudfront create-distribution --distribution-config "$NEW_DIST_JSON"
)"
DISTRIBUTION_ID="$( jq -r '.Distribution.Id' <<< "$NEW_DIST_RESULT_JSON" )"
echo "$DISTRIBUTION_ID"
}
if [ "$1" == -h ]; then help; fi
if [ -t 0 ]; then
if [ -z "$1" ]; then help 'Error: No S3 bucket name was specified.\n\n'; fi
BUCKET_NAME="$1"
shift
else
read -r BUCKET_NAME
fi
if [ -z "$BUCKET_NAME" ]; then help 'Error: No S3 bucket name was specified.\n\n'; fi
if [ "$1" ]; then help 'Error: Too many arguments provided.\n\n'; fi
if [ "$( aws s3api head-bucket --bucket $BUCKET_NAME 2> >(grep -i 'Not Found') )" ]; then
>&2 echo "Error: Bucket $BUCKET_NAME does not exist."
exit 2
fi
if [ "$(doesDistributionExist)" ]; then
>&2 echo "Error: a CloudFront distibution already exists for S3 bucket $BUCKET_NAME"
exit 3
fi
createDist
Example usages:
$ awsCfS3MakeDist my_bucket E2P5S6OYKQNB6B
$ echo my_bucket | awsCfS3MakeDist E2P5S6OYKQNB6B
awsS3Mb
Make a new S3 bucket with the given name in the default AWS region.
If the --public-read option is provided, set the ACL to public-read
#!/bin/bash
function help {
printf "$1$(basename $0) - Make a new S3 bucket with the given name in the default AWS region.
Syntax: $(basename $0) bucketName [OPTIONS]
Syntax: echo bucketName | $(basename $0) [OPTIONS]
Options are:
--public-read Set bucket ACL to public-read
"
exit 1
}
if [ "$1" == -h ]; then help; fi
if [ "$1" == "--public-read" ]; then
ACL="public-read"
shift
fi
if [ "$1" ]; then
BUCKET_NAME="$1"
shift
elif [ ! -t 0 ]; then
read -r BUCKET_NAME
fi
if [ -z "$BUCKET_NAME" ]; then help 'Error: No S3 bucket name was specified.\n\n'; fi
if [ "$1" ]; then help 'Error: Too many arguments provided.\n\n'; fi
aws s3 mb s3://$BUCKET_NAME
if [ "$ACL" ]; then
aws s3api put-bucket-acl --bucket $BUCKET_NAME --acl $ACL
fi
Example usages:
$ awsS3Mb my_bucket
$ awsS3Mb my_bucket --public-read
$ echo my_bucket | awsS3Mb --public-read
awsS3Website
Enable an S3 bucket to be a website.
#!/bin/bash
function help {
printf "$1$(basename $0) - Enable an S3 bucket to be a website.
Syntax: $(basename $0) bucketName
Syntax: echo bucketName | $(basename $0)
"
exit 1
}
if [ "$1" == -h ]; then help; fi
if [ "$1" ]; then
BUCKET_NAME="$1"
shift
elif [ ! -t 0 ]; then
read -r BUCKET_NAME
fi
if [ -z "$BUCKET_NAME" ]; then help 'Error: No S3 bucket name was specified.\n\n'; fi
if [ "$1" ]; then help 'Error: Too many arguments provided.\n\n'; fi
aws s3 website s3://$BUCKET_NAME \
--index-document index.html \
--error-document 404.html
Example usages:
$ awsS3Website my_bucket
$ echo my_bucket | awsS3Website