From 7388f945202335d7c6a68341dbea5e02076cadb0 Mon Sep 17 00:00:00 2001 From: Michael Wunderlich Date: Thu, 30 Apr 2026 07:01:00 +0000 Subject: [PATCH] Fix set -u empty array expansion in 9 scripts, fix 015 VPC setup set -u fix: ${ARRAY[@]} on empty array triggers 'unbound variable'. Use ${ARRAY[@]+"${ARRAY[@]}"} which expands to nothing when empty. Scripts: 001, 015, 046, 049, 055, 062, 070, 077, 079. 015-vpc-peering: Replace broken VPC reuse logic with: - Check VPC quota upfront, fail fast if insufficient - Use prereq stack VPC if available (saves 1 VPC slot) - Create fresh VPCs with non-overlapping CIDRs otherwise - Use .100.0/24 subnets to avoid prereq subnet overlap Tested both scenarios locally. --- .gitignore | 21 ++++ tuts/001-lightsail-gs/lightsail-gs.sh | 4 +- tuts/015-vpc-peering/vpc-peering.sh | 113 ++++++++---------- .../aws-systems-manager-gs.sh | 2 +- .../aws-end-user-messaging-gs.sh | 4 +- .../amazon-vpc-lattice-getting-started.sh | 2 +- tuts/062-aws-support-gs/aws-support-gs.sh | 2 +- .../amazon-dynamodb-gs.sh | 4 +- .../aws-account-management-gs.sh | 2 +- .../aws-iot-device-defender-gs.sh | 2 +- 10 files changed, 79 insertions(+), 77 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8bc45c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +*.log +*.pem +aws-eump-logs/ +dynamodb-tutorial-logs/ +Dockerfile +document.png +dashboard-body-*.json +comprehend-policy.json +hello-world.json +input.json +query-results.csv +sentiment-*.json +step-functions-trust-policy.json +stepfunctions-policy.json +textract-*.json +updated-hello-world.json +webserver-template-*.yaml +qbusiness-*.json +datazone_script_v3_fixed.log +idc_setup_*.log +workspaces_creation.log diff --git a/tuts/001-lightsail-gs/lightsail-gs.sh b/tuts/001-lightsail-gs/lightsail-gs.sh index 41ca6d9..740adde 100644 --- a/tuts/001-lightsail-gs/lightsail-gs.sh +++ b/tuts/001-lightsail-gs/lightsail-gs.sh @@ -48,7 +48,7 @@ track_resource() { # Function to clean up resources cleanup_resources() { echo "Resources created by this script:" - for resource in "${CREATED_RESOURCES[@]}"; do + for resource in "${CREATED_RESOURCES[@]+"${CREATED_RESOURCES[@]}"}"; do echo " $resource" done @@ -250,7 +250,7 @@ fi # Step 7: Clean up resources echo "Step 7: Clean up resources" echo "The script has created the following resources:" -for resource in "${CREATED_RESOURCES[@]}"; do +for resource in "${CREATED_RESOURCES[@]+"${CREATED_RESOURCES[@]}"}"; do echo " $resource" done diff --git a/tuts/015-vpc-peering/vpc-peering.sh b/tuts/015-vpc-peering/vpc-peering.sh index 8e37ec9..7652049 100644 --- a/tuts/015-vpc-peering/vpc-peering.sh +++ b/tuts/015-vpc-peering/vpc-peering.sh @@ -166,54 +166,50 @@ echo "Setting up VPC peering connection..." # Validate AWS CLI validate_aws_cli -# Check for existing VPCs -echo "Checking for existing VPCs..." -EXISTING_VPCS=$(aws ec2 describe-vpcs --region "$AWS_REGION" --query 'Vpcs[?State==`available`].[VpcId,CidrBlock]' --output text 2>/dev/null || echo "") - -if [ -z "$EXISTING_VPCS" ]; then - echo "No existing VPCs found. Creating new VPCs..." - CREATE_VPCS=true -else - echo "Found existing VPCs:" - echo "$EXISTING_VPCS" - echo "" - echo "Using existing VPCs..." - CREATE_VPCS=false - # Get the first two available VPCs - VPC1_INFO=$(echo "$EXISTING_VPCS" | head -n 1) - VPC2_INFO=$(echo "$EXISTING_VPCS" | head -n 2 | tail -n 1) - - if [ -z "$VPC2_INFO" ]; then - echo "Only one VPC found. Creating a second VPC..." - VPC1_ID=$(echo "$VPC1_INFO" | awk '{print $1}') - VPC1_CIDR=$(echo "$VPC1_INFO" | awk '{print $2}') - - # Sanitize extracted values - VPC1_ID=$(sanitize_var "$VPC1_ID") || check_error 1 "Invalid VPC1_ID format" - VPC1_CIDR=$(sanitize_var "$VPC1_CIDR") || check_error 1 "Invalid VPC1_CIDR format" - - validate_cidr "$VPC1_CIDR" || check_error 1 "Invalid VPC1 CIDR" - CREATE_VPC2_ONLY=true - else - VPC1_ID=$(echo "$VPC1_INFO" | awk '{print $1}') - VPC1_CIDR=$(echo "$VPC1_INFO" | awk '{print $2}') - VPC2_ID=$(echo "$VPC2_INFO" | awk '{print $1}') - VPC2_CIDR=$(echo "$VPC2_INFO" | awk '{print $2}') - - # Sanitize extracted values - VPC1_ID=$(sanitize_var "$VPC1_ID") || check_error 1 "Invalid VPC1_ID format" - VPC1_CIDR=$(sanitize_var "$VPC1_CIDR") || check_error 1 "Invalid VPC1_CIDR format" - VPC2_ID=$(sanitize_var "$VPC2_ID") || check_error 1 "Invalid VPC2_ID format" - VPC2_CIDR=$(sanitize_var "$VPC2_CIDR") || check_error 1 "Invalid VPC2_CIDR format" - - validate_cidr "$VPC1_CIDR" || check_error 1 "Invalid VPC1 CIDR" - validate_cidr "$VPC2_CIDR" || check_error 1 "Invalid VPC2 CIDR" - CREATE_VPC2_ONLY=false +# Check VPC quota — need room for up to 2 new VPCs +VPC_COUNT=$(aws ec2 describe-vpcs --region "$AWS_REGION" --query 'length(Vpcs)' --output text 2>/dev/null || echo 99) +VPC_LIMIT=5 +VPCS_NEEDED=2 + +# Check if prereq stack provides a VPC we can use as VPC1 +PREREQ_VPC_ID="" +PREREQ_STACK=$(aws cloudformation describe-stacks --region "$AWS_REGION" --stack-name tutorial-prereqs-vpc-public --query 'Stacks[0].StackStatus' --output text 2>/dev/null || echo "") +if [[ "$PREREQ_STACK" == "CREATE_COMPLETE" || "$PREREQ_STACK" == "UPDATE_COMPLETE" ]]; then + PREREQ_VPC_ID=$(aws cloudformation describe-stacks --region "$AWS_REGION" --stack-name tutorial-prereqs-vpc-public --query 'Stacks[0].Outputs[?OutputKey==`VpcId`].OutputValue' --output text 2>/dev/null || echo "") + if [ -n "$PREREQ_VPC_ID" ]; then + echo "Found prereq stack VPC: $PREREQ_VPC_ID (10.0.0.0/16)" + VPCS_NEEDED=1 fi fi -# Create VPCs if needed -if [ "$CREATE_VPCS" = true ]; then +AVAILABLE=$((VPC_LIMIT - VPC_COUNT)) +if [ "$AVAILABLE" -lt "$VPCS_NEEDED" ]; then + echo "ERROR: Need $VPCS_NEEDED VPC slots but only $AVAILABLE available ($VPC_COUNT/$VPC_LIMIT used in $AWS_REGION)." + echo "Free up VPCs or run in a different region: AWS_REGION= bash $0" + exit 1 +fi + +# Set up VPCs +if [ -n "$PREREQ_VPC_ID" ]; then + # Use prereq VPC as VPC1, create VPC2 + VPC1_ID="$PREREQ_VPC_ID" + VPC1_CIDR="10.0.0.0/16" + echo "Using prereq stack VPC as VPC1: $VPC1_ID ($VPC1_CIDR)" + + echo "Creating VPC2..." + VPC2_ID=$(log_cmd "aws ec2 create-vpc --region '$AWS_REGION' --cidr-block 10.2.0.0/16 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=VPC2-Peering-Demo}]' --query 'Vpc.VpcId' --output text") + check_error $? "Failed to create VPC2" + VPC2_ID=$(sanitize_var "$VPC2_ID") || check_error 1 "Invalid VPC2_ID returned" + VPC2_CIDR="10.2.0.0/16" + CREATED_RESOURCES+=("VPC2: $VPC2_ID") + CLEANUP_COMMANDS+=("aws ec2 delete-vpc --region '$AWS_REGION' --vpc-id '$VPC2_ID'") + echo "VPC2 created with ID: $VPC2_ID" + + echo "Waiting for VPC2 to be available..." + log_cmd "aws ec2 wait vpc-available --region '$AWS_REGION' --vpc-ids '$VPC2_ID'" + check_error $? "Timeout waiting for VPC2 to become available" +else + # Create both VPCs echo "Creating VPC1..." VPC1_ID=$(log_cmd "aws ec2 create-vpc --region '$AWS_REGION' --cidr-block 10.1.0.0/16 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=VPC1-Peering-Demo}]' --query 'Vpc.VpcId' --output text") check_error $? "Failed to create VPC1" @@ -222,7 +218,7 @@ if [ "$CREATE_VPCS" = true ]; then CREATED_RESOURCES+=("VPC1: $VPC1_ID") CLEANUP_COMMANDS+=("aws ec2 delete-vpc --region '$AWS_REGION' --vpc-id '$VPC1_ID'") echo "VPC1 created with ID: $VPC1_ID" - + echo "Creating VPC2..." VPC2_ID=$(log_cmd "aws ec2 create-vpc --region '$AWS_REGION' --cidr-block 10.2.0.0/16 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=VPC2-Peering-Demo}]' --query 'Vpc.VpcId' --output text") check_error $? "Failed to create VPC2" @@ -231,26 +227,10 @@ if [ "$CREATE_VPCS" = true ]; then CREATED_RESOURCES+=("VPC2: $VPC2_ID") CLEANUP_COMMANDS+=("aws ec2 delete-vpc --region '$AWS_REGION' --vpc-id '$VPC2_ID'") echo "VPC2 created with ID: $VPC2_ID" - - # Wait for VPCs to be available + echo "Waiting for VPCs to be available..." log_cmd "aws ec2 wait vpc-available --region '$AWS_REGION' --vpc-ids '$VPC1_ID' '$VPC2_ID'" check_error $? "Timeout waiting for VPCs to become available" - -elif [ "$CREATE_VPC2_ONLY" = true ]; then - echo "Creating VPC2..." - VPC2_ID=$(log_cmd "aws ec2 create-vpc --region '$AWS_REGION' --cidr-block 10.2.0.0/16 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=VPC2-Peering-Demo}]' --query 'Vpc.VpcId' --output text") - check_error $? "Failed to create VPC2" - VPC2_ID=$(sanitize_var "$VPC2_ID") || check_error 1 "Invalid VPC2_ID returned" - VPC2_CIDR="10.2.0.0/16" - CREATED_RESOURCES+=("VPC2: $VPC2_ID") - CLEANUP_COMMANDS+=("aws ec2 delete-vpc --region '$AWS_REGION' --vpc-id '$VPC2_ID'") - echo "VPC2 created with ID: $VPC2_ID" - - # Wait for VPC2 to be available - echo "Waiting for VPC2 to be available..." - log_cmd "aws ec2 wait vpc-available --region '$AWS_REGION' --vpc-ids '$VPC2_ID'" - check_error $? "Timeout waiting for VPC2 to become available" fi echo "Using the following VPCs:" @@ -263,8 +243,9 @@ log_cmd "aws ec2 describe-vpcs --region '$AWS_REGION' --vpc-ids '$VPC1_ID' '$VPC check_error $? "Failed to verify VPCs" # Determine subnet CIDR blocks based on VPC CIDR blocks -VPC1_SUBNET_CIDR=$(echo "$VPC1_CIDR" | sed 's/0\.0\/16/1.0\/24/') -VPC2_SUBNET_CIDR=$(echo "$VPC2_CIDR" | sed 's/0\.0\/16/1.0\/24/') +# Use .100.0/24 to avoid overlap with prereq stack subnets (.1-.4) +VPC1_SUBNET_CIDR=$(echo "$VPC1_CIDR" | sed 's/0\.0\/16/100.0\/24/') +VPC2_SUBNET_CIDR=$(echo "$VPC2_CIDR" | sed 's/0\.0\/16/100.0\/24/') # Sanitize subnet CIDR blocks VPC1_SUBNET_CIDR=$(sanitize_var "$VPC1_SUBNET_CIDR") || check_error 1 "Invalid VPC1_SUBNET_CIDR format" @@ -382,7 +363,7 @@ echo "Route Table 2 ID: $RTB2_ID" echo "Route Table 2 Association ID: $RTB2_ASSOC_ID" echo "" echo "Created resources:" -for resource in "${CREATED_RESOURCES[@]}"; do +for resource in "${CREATED_RESOURCES[@]+"${CREATED_RESOURCES[@]}"}"; do echo "- $resource" done echo "==============================================" diff --git a/tuts/046-aws-systems-manager-gs/aws-systems-manager-gs.sh b/tuts/046-aws-systems-manager-gs/aws-systems-manager-gs.sh index 0727f3c..3fac178 100755 --- a/tuts/046-aws-systems-manager-gs/aws-systems-manager-gs.sh +++ b/tuts/046-aws-systems-manager-gs/aws-systems-manager-gs.sh @@ -528,7 +528,7 @@ echo "" echo "===========================================" echo "CREATED RESOURCES" echo "===========================================" -for resource in "${CREATED_RESOURCES[@]}"; do +for resource in "${CREATED_RESOURCES[@]+"${CREATED_RESOURCES[@]}"}"; do echo "$resource" done diff --git a/tuts/049-aws-end-user-messaging-gs/aws-end-user-messaging-gs.sh b/tuts/049-aws-end-user-messaging-gs/aws-end-user-messaging-gs.sh index 9fd8263..83802ef 100644 --- a/tuts/049-aws-end-user-messaging-gs/aws-end-user-messaging-gs.sh +++ b/tuts/049-aws-end-user-messaging-gs/aws-end-user-messaging-gs.sh @@ -43,7 +43,7 @@ cleanup() { # Optionally delete AWS resources if [ "${DELETE_AWS_RESOURCES:-false}" = "true" ]; then - for resource in "${AWS_RESOURCES[@]}"; do + for resource in "${AWS_RESOURCES[@]+"${AWS_RESOURCES[@]}"}"; do echo "Deleting AWS resource: $resource" aws pinpoint delete-app --application-id "$resource" 2>/dev/null || \ echo "Warning: Failed to delete application $resource" @@ -406,7 +406,7 @@ echo "===========================================" echo "RESOURCES CREATED" echo "===========================================" echo "AWS Resources:" -for resource in "${AWS_RESOURCES[@]}"; do +for resource in "${AWS_RESOURCES[@]+"${AWS_RESOURCES[@]}"}"; do echo "- Application: $resource" done diff --git a/tuts/055-amazon-vpc-lattice-gs/amazon-vpc-lattice-getting-started.sh b/tuts/055-amazon-vpc-lattice-gs/amazon-vpc-lattice-getting-started.sh index e86a9bf..61d0a68 100644 --- a/tuts/055-amazon-vpc-lattice-gs/amazon-vpc-lattice-getting-started.sh +++ b/tuts/055-amazon-vpc-lattice-gs/amazon-vpc-lattice-getting-started.sh @@ -290,7 +290,7 @@ log_command "aws vpc-lattice list-service-network-vpc-associations --service-net # Step 10: Cleanup - Auto-confirm echo -e "\n=== Step 10: Resource Cleanup ===" | tee -a "$LOG_FILE" echo "Resources created in this tutorial:" | tee -a "$LOG_FILE" -for resource in "${CREATED_RESOURCES[@]}"; do +for resource in "${CREATED_RESOURCES[@]+"${CREATED_RESOURCES[@]}"}"; do echo "- $resource" | tee -a "$LOG_FILE" done diff --git a/tuts/062-aws-support-gs/aws-support-gs.sh b/tuts/062-aws-support-gs/aws-support-gs.sh index 33b76c8..4802a40 100644 --- a/tuts/062-aws-support-gs/aws-support-gs.sh +++ b/tuts/062-aws-support-gs/aws-support-gs.sh @@ -277,7 +277,7 @@ else if [[ ${#CREATED_RESOURCES[@]} -eq 0 ]]; then echo "No resources were created." else - for resource in "${CREATED_RESOURCES[@]}"; do + for resource in "${CREATED_RESOURCES[@]+"${CREATED_RESOURCES[@]}"}"; do echo "- $resource" done fi diff --git a/tuts/070-amazon-dynamodb-gs/amazon-dynamodb-gs.sh b/tuts/070-amazon-dynamodb-gs/amazon-dynamodb-gs.sh index 3bd1f13..967dfca 100644 --- a/tuts/070-amazon-dynamodb-gs/amazon-dynamodb-gs.sh +++ b/tuts/070-amazon-dynamodb-gs/amazon-dynamodb-gs.sh @@ -89,7 +89,7 @@ cleanup() { echo "CLEANUP" echo "===========================================" echo "Resources to clean up:" - for resource in "${RESOURCES[@]}"; do + for resource in "${RESOURCES[@]+"${RESOURCES[@]}"}"; do echo "- $resource" done echo "" @@ -97,7 +97,7 @@ cleanup() { if [[ ${#RESOURCES[@]} -gt 0 ]]; then echo "Proceeding with cleanup of all created resources..." - for resource in "${RESOURCES[@]}"; do + for resource in "${RESOURCES[@]+"${RESOURCES[@]}"}"; do if [[ "$resource" == Table:* ]]; then local table_name="${resource#Table:}" echo "Deleting table: $table_name" diff --git a/tuts/077-aws-account-management-gs/aws-account-management-gs.sh b/tuts/077-aws-account-management-gs/aws-account-management-gs.sh index 4a00a04..1d5166c 100644 --- a/tuts/077-aws-account-management-gs/aws-account-management-gs.sh +++ b/tuts/077-aws-account-management-gs/aws-account-management-gs.sh @@ -267,7 +267,7 @@ sleep "$API_CALL_DELAY" # Security: Define valid contact types declare -a CONTACT_TYPES=("BILLING" "OPERATIONS" "SECURITY") -for contact_type in "${CONTACT_TYPES[@]}"; do +for contact_type in "${CONTACT_TYPES[@]+"${CONTACT_TYPES[@]}"}"; do { echo "" echo "Attempting to check $contact_type contact information..." diff --git a/tuts/079-aws-iot-device-defender-gs/aws-iot-device-defender-gs.sh b/tuts/079-aws-iot-device-defender-gs/aws-iot-device-defender-gs.sh index a104d4e..b9fea7d 100644 --- a/tuts/079-aws-iot-device-defender-gs/aws-iot-device-defender-gs.sh +++ b/tuts/079-aws-iot-device-defender-gs/aws-iot-device-defender-gs.sh @@ -667,7 +667,7 @@ echo "===================================================" echo "AWS IoT Device Defender setup completed successfully!" echo "===================================================" echo "The following resources were created:" -for resource in "${CREATED_RESOURCES[@]}"; do +for resource in "${CREATED_RESOURCES[@]+"${CREATED_RESOURCES[@]}"}"; do echo "- $resource" done echo ""