There are more AWS SDK examples available in the AWS Doc SDK Examples
Getting started with WAF
The following code example shows how to:
Create a web ACL
Add a string match rule
Add managed rules
Configure logging
Clean up resources
- Bash
-
- AWS CLI with Bash script
-
Note
There's more on GitHub. Find the complete example and learn how to set up and run in the Sample developer tutorials
repository. #!/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 # Generate a random identifier for resource names using secure method RANDOM_ID=$(openssl rand -hex 4) || handle_error "Failed to generate random ID" WEB_ACL_NAME="MyWebACL-${RANDOM_ID}" METRIC_NAME="MyWebACLMetrics-${RANDOM_ID}" echo "Using Web ACL name: $WEB_ACL_NAME" # Step 1: Create a Web ACL echo "" echo "===================================================" echo "STEP 1: Creating Web ACL" echo "===================================================" local create_result create_result=$(aws wafv2 create-web-acl \ --name "$WEB_ACL_NAME" \ --scope "CLOUDFRONT" \ --default-action Allow={} \ --visibility-config "SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=$METRIC_NAME" \ --region us-east-1 2>&1) || handle_error "Failed to create Web ACL" if ! validate_json "$create_result"; then handle_error "Invalid JSON response from create-web-acl" fi # Extract Web ACL ID, ARN, and Lock Token from the response WEB_ACL_ID=$(extract_json_value "$create_result" '.Summary.Id') || handle_error "Failed to extract Web ACL ID" WEB_ACL_ARN=$(extract_json_value "$create_result" '.Summary.ARN') || handle_error "Failed to extract Web ACL ARN" LOCK_TOKEN=$(extract_json_value "$create_result" '.Summary.LockToken') || handle_error "Failed to extract Lock Token" echo "Web ACL created successfully with ID: $WEB_ACL_ID" echo "Lock Token: [REDACTED]" # Step 2: Add a String Match Rule echo "" echo "===================================================" echo "STEP 2: Adding String Match Rule" echo "===================================================" for ((i=1; i<=MAX_RETRIES; i++)); do echo "Attempt $i to add string match rule..." # Get the latest lock token before updating 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 update: $get_result" if [ "$i" -eq "$MAX_RETRIES" ]; then handle_error "Failed to get Web ACL after $MAX_RETRIES attempts" fi sleep 2 continue fi if ! validate_json "$get_result"; then echo "Warning: Invalid JSON response from get-web-acl" if [ "$i" -eq "$MAX_RETRIES" ]; then handle_error "Invalid JSON response after $MAX_RETRIES attempts" fi sleep 2 continue fi local latest_token latest_token=$(extract_json_value "$get_result" '.WebACL.LockToken' 2>/dev/null) || true if [ -z "$latest_token" ]; then echo "Warning: Could not extract lock token for update" if [ "$i" -eq "$MAX_RETRIES" ]; then handle_error "Failed to extract lock token after $MAX_RETRIES attempts" fi sleep 2 continue fi local update_result update_result=$(aws wafv2 update-web-acl \ --name "$WEB_ACL_NAME" \ --scope "CLOUDFRONT" \ --id "$WEB_ACL_ID" \ --lock-token "$latest_token" \ --default-action Allow={} \ --rules '[{ "Name": "UserAgentRule", "Priority": 0, "Statement": { "ByteMatchStatement": { "SearchString": "MyAgent", "FieldToMatch": { "SingleHeader": { "Name": "user-agent" } }, "TextTransformations": [ { "Priority": 0, "Type": "NONE" } ], "PositionalConstraint": "EXACTLY" } }, "Action": { "Count": {} }, "VisibilityConfig": { "SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "UserAgentRuleMetric" } }]' \ --visibility-config "SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=$METRIC_NAME" \ --region us-east-1 2>&1) || true if echo "$update_result" | grep -qi "WAFOptimisticLockException"; then echo "Optimistic lock exception encountered. Will retry with new lock token." if [ "$i" -eq "$MAX_RETRIES" ]; then handle_error "Failed to add string match rule after $MAX_RETRIES attempts" fi sleep 2 continue elif echo "$update_result" | grep -qi "error"; then handle_error "Failed to add string match rule: $update_result" else echo "String match rule added successfully." break fi done # Step 3: Add AWS Managed Rules echo "" echo "===================================================" echo "STEP 3: Adding AWS Managed Rules" echo "===================================================" for ((i=1; i<=MAX_RETRIES; i++)); do echo "Attempt $i to add AWS Managed Rules..." # Get the latest lock token before updating 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 update: $get_result" if [ "$i" -eq "$MAX_RETRIES" ]; then handle_error "Failed to get Web ACL after $MAX_RETRIES attempts" fi sleep 2 continue fi if ! validate_json "$get_result"; then echo "Warning: Invalid JSON response from get-web-acl" if [ "$i" -eq "$MAX_RETRIES" ]; then handle_error "Invalid JSON response after $MAX_RETRIES attempts" fi sleep 2 continue fi local latest_token latest_token=$(extract_json_value "$get_result" '.WebACL.LockToken' 2>/dev/null) || true if [ -z "$latest_token" ]; then echo "Warning: Could not extract lock token for update" if [ "$i" -eq "$MAX_RETRIES" ]; then handle_error "Failed to extract lock token after $MAX_RETRIES attempts" fi sleep 2 continue fi local update_result update_result=$(aws wafv2 update-web-acl \ --name "$WEB_ACL_NAME" \ --scope "CLOUDFRONT" \ --id "$WEB_ACL_ID" \ --lock-token "$latest_token" \ --default-action Allow={} \ --rules '[{ "Name": "UserAgentRule", "Priority": 0, "Statement": { "ByteMatchStatement": { "SearchString": "MyAgent", "FieldToMatch": { "SingleHeader": { "Name": "user-agent" } }, "TextTransformations": [ { "Priority": 0, "Type": "NONE" } ], "PositionalConstraint": "EXACTLY" } }, "Action": { "Count": {} }, "VisibilityConfig": { "SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "UserAgentRuleMetric" } }, { "Name": "AWS-AWSManagedRulesCommonRuleSet", "Priority": 1, "Statement": { "ManagedRuleGroupStatement": { "VendorName": "AWS", "Name": "AWSManagedRulesCommonRuleSet", "ExcludedRules": [] } }, "OverrideAction": { "Count": {} }, "VisibilityConfig": { "SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "AWS-AWSManagedRulesCommonRuleSet" } }]' \ --visibility-config "SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=$METRIC_NAME" \ --region us-east-1 2>&1) || true if echo "$update_result" | grep -qi "WAFOptimisticLockException"; then echo "Optimistic lock exception encountered. Will retry with new lock token." if [ "$i" -eq "$MAX_RETRIES" ]; then handle_error "Failed to add AWS Managed Rules after $MAX_RETRIES attempts" fi sleep 2 continue elif echo "$update_result" | grep -qi "error"; then handle_error "Failed to add AWS Managed Rules: $update_result" else echo "AWS Managed Rules added successfully." break fi done # Step 4: List CloudFront distributions echo "" echo "===================================================" echo "STEP 4: Listing CloudFront Distributions" echo "===================================================" local cf_result cf_result=$(aws cloudfront list-distributions --query "DistributionList.Items[*].{Id:Id,DomainName:DomainName}" --output table 2>&1) || cf_result="" if echo "$cf_result" | grep -qi "error"; then echo "Warning: Failed to list CloudFront distributions: $cf_result" echo "Continuing without CloudFront association." DISTRIBUTION_ID="" else echo "$cf_result" # Auto-select first CloudFront distribution if available echo "" echo "===================================================" echo "STEP 5: Associate Web ACL with CloudFront Distribution" echo "===================================================" local first_dist first_dist=$(aws cloudfront list-distributions --query "DistributionList.Items[0].Id" --output text 2>&1) || first_dist="" if [ -n "$first_dist" ] && [ "$first_dist" != "None" ] && ! echo "$first_dist" | grep -qi "error"; then DISTRIBUTION_ID="$first_dist" echo "Using CloudFront distribution: $DISTRIBUTION_ID" 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 for association" DISTRIBUTION_ID="" else local associate_result associate_result=$(aws wafv2 associate-web-acl \ --web-acl-arn "$WEB_ACL_ARN" \ --resource-arn "arn:aws:cloudfront::${account_id}:distribution/${DISTRIBUTION_ID}" \ --region us-east-1 2>&1) || true if echo "$associate_result" | grep -qi "error"; then echo "Warning: Failed to associate Web ACL with CloudFront distribution: $associate_result" echo "Continuing without CloudFront association." DISTRIBUTION_ID="" else echo "Web ACL associated with CloudFront distribution successfully." fi fi else echo "No CloudFront distributions available. Skipping association." DISTRIBUTION_ID="" fi fi # Display summary of created resources echo "" echo "===================================================" echo "RESOURCE SUMMARY" echo "===================================================" echo "Web ACL Name: $WEB_ACL_NAME" echo "Web ACL ID: $WEB_ACL_ID" echo "Web ACL ARN: $WEB_ACL_ARN" if [ -n "${DISTRIBUTION_ID:-}" ]; then echo "Associated CloudFront Distribution: $DISTRIBUTION_ID" fi echo "" # Auto-confirm cleanup echo "===================================================" echo "CLEANUP CONFIRMATION" echo "===================================================" echo "Proceeding with automatic cleanup of all created resources..." echo "" echo "===================================================" echo "Tutorial completed!" echo "===================================================" echo "Log file: $LOG_FILE"-
For API details, see the following topics in AWS CLI Command Reference.
-
Scenarios
WorkSpaces