diff --git a/.gitignore b/.gitignore index a79d9a9..9fda474 100644 --- a/.gitignore +++ b/.gitignore @@ -1,26 +1,5 @@ -*.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 -logs/ -*.csv -*.png -Dockerfile -cfn/ +datazone_script_v3_fixed.log +idc_setup_20260429_064540.log +qbusiness-permissions-policy.json +qbusiness-trust-policy.json diff --git a/tuts/001-lightsail-gs/lightsail-gs.sh b/tuts/001-lightsail-gs/lightsail-gs.sh index 740adde..f00e010 100644 --- a/tuts/001-lightsail-gs/lightsail-gs.sh +++ b/tuts/001-lightsail-gs/lightsail-gs.sh @@ -122,7 +122,8 @@ aws lightsail create-instances \ --availability-zone "$AVAILABILITY_ZONE" \ --blueprint-id amazon_linux_2023 \ --bundle-id nano_3_0 \ - --region "$AWS_REGION" + --region "$AWS_REGION" \ + --tags key=project,value=doc-smith key=tutorial,value=lightsail-gs check_status "Failed to create Lightsail instance" track_resource "instance" "$INSTANCE_NAME" @@ -180,7 +181,8 @@ aws lightsail create-disk \ --disk-name "$DISK_NAME" \ --availability-zone "$AVAILABILITY_ZONE" \ --size-in-gb 8 \ - --region "$AWS_REGION" + --region "$AWS_REGION" \ + --tags key=project,value=doc-smith key=tutorial,value=lightsail-gs check_status "Failed to create disk" track_resource "disk" "$DISK_NAME" @@ -223,7 +225,8 @@ echo "Step 6: Creating snapshot of the instance: $SNAPSHOT_NAME" aws lightsail create-instance-snapshot \ --instance-name "$INSTANCE_NAME" \ --instance-snapshot-name "$SNAPSHOT_NAME" \ - --region "$AWS_REGION" + --region "$AWS_REGION" \ + --tags key=project,value=doc-smith key=tutorial,value=lightsail-gs check_status "Failed to create instance snapshot" track_resource "instance_snapshot" "$SNAPSHOT_NAME" diff --git a/tuts/002-vpc-gs/vpc-gs.sh b/tuts/002-vpc-gs/vpc-gs.sh index a563a54..fa97dee 100644 --- a/tuts/002-vpc-gs/vpc-gs.sh +++ b/tuts/002-vpc-gs/vpc-gs.sh @@ -115,7 +115,7 @@ fi # Create VPC echo "Creating VPC with CIDR block 10.0.0.0/16..." -VPC_ID=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=MyVPC}]' --query 'Vpc.VpcId' --output text) +VPC_ID=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=MyVPC},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-gs}]' --query 'Vpc.VpcId' --output text) if [ -z "$VPC_ID" ]; then handle_error "Failed to create VPC" @@ -146,7 +146,7 @@ PUBLIC_SUBNET_AZ1=$(aws ec2 create-subnet \ --vpc-id "$VPC_ID" \ --cidr-block 10.0.0.0/24 \ --availability-zone "$AZ1" \ - --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=Public-Subnet-AZ1}]' \ + --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=Public-Subnet-AZ1},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-gs}]' \ --query 'Subnet.SubnetId' \ --output text) @@ -162,7 +162,7 @@ PUBLIC_SUBNET_AZ2=$(aws ec2 create-subnet \ --vpc-id "$VPC_ID" \ --cidr-block 10.0.1.0/24 \ --availability-zone "$AZ2" \ - --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=Public-Subnet-AZ2}]' \ + --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=Public-Subnet-AZ2},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-gs}]' \ --query 'Subnet.SubnetId' \ --output text) @@ -179,7 +179,7 @@ PRIVATE_SUBNET_AZ1=$(aws ec2 create-subnet \ --vpc-id "$VPC_ID" \ --cidr-block 10.0.2.0/24 \ --availability-zone "$AZ1" \ - --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=Private-Subnet-AZ1}]' \ + --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=Private-Subnet-AZ1},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-gs}]' \ --query 'Subnet.SubnetId' \ --output text) @@ -195,7 +195,7 @@ PRIVATE_SUBNET_AZ2=$(aws ec2 create-subnet \ --vpc-id "$VPC_ID" \ --cidr-block 10.0.3.0/24 \ --availability-zone "$AZ2" \ - --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=Private-Subnet-AZ2}]' \ + --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=Private-Subnet-AZ2},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-gs}]' \ --query 'Subnet.SubnetId' \ --output text) @@ -209,7 +209,7 @@ echo "Private subnet created in $AZ2 with ID: $PRIVATE_SUBNET_AZ2" # Create Internet Gateway echo "Creating Internet Gateway..." IGW_ID=$(aws ec2 create-internet-gateway \ - --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=MyIGW}]' \ + --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=MyIGW},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-gs}]' \ --query 'InternetGateway.InternetGatewayId' \ --output text) @@ -228,7 +228,7 @@ aws ec2 attach-internet-gateway --internet-gateway-id "$IGW_ID" --vpc-id "$VPC_I echo "Creating public route table..." PUBLIC_RT=$(aws ec2 create-route-table \ --vpc-id "$VPC_ID" \ - --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=Public-RT}]' \ + --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=Public-RT},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-gs}]' \ --query 'RouteTable.RouteTableId' \ --output text) @@ -266,7 +266,7 @@ CREATED_RESOURCES+=("ROUTE_TABLE_ASSOCIATION:$PUBLIC_RT_ASSOC_2") echo "Creating private route table..." PRIVATE_RT=$(aws ec2 create-route-table \ --vpc-id "$VPC_ID" \ - --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=Private-RT}]' \ + --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=Private-RT},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-gs}]' \ --query 'RouteTable.RouteTableId' \ --output text) @@ -298,7 +298,7 @@ CREATED_RESOURCES+=("ROUTE_TABLE_ASSOCIATION:$PRIVATE_RT_ASSOC_2") # Allocate Elastic IP for NAT Gateway echo "Allocating Elastic IP for NAT Gateway..." -EIP_ALLOC=$(aws ec2 allocate-address --domain vpc --query 'AllocationId' --output text) +EIP_ALLOC=$(aws ec2 allocate-address --domain vpc --tag-specifications 'ResourceType=elastic-ip,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-gs}]' --query 'AllocationId' --output text) if [ -z "$EIP_ALLOC" ]; then handle_error "Failed to allocate Elastic IP" @@ -312,7 +312,7 @@ echo "Creating NAT Gateway in public subnet in $AZ1..." NAT_GW=$(aws ec2 create-nat-gateway \ --subnet-id "$PUBLIC_SUBNET_AZ1" \ --allocation-id "$EIP_ALLOC" \ - --tag-specifications 'ResourceType=natgateway,Tags=[{Key=Name,Value=MyNATGateway}]' \ + --tag-specifications 'ResourceType=natgateway,Tags=[{Key=Name,Value=MyNATGateway},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-gs}]' \ --query 'NatGateway.NatGatewayId' \ --output text) @@ -344,6 +344,7 @@ WEB_SG=$(aws ec2 create-security-group \ --group-name "WebServerSG-$(date +%s)" \ --description "Security group for web servers" \ --vpc-id "$VPC_ID" \ + --tag-specifications 'ResourceType=security-group,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-gs}]' \ --query 'GroupId' \ --output text) @@ -370,6 +371,7 @@ DB_SG=$(aws ec2 create-security-group \ --group-name "DBServerSG-$(date +%s)" \ --description "Security group for database servers" \ --vpc-id "$VPC_ID" \ + --tag-specifications 'ResourceType=security-group,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-gs}]' \ --query 'GroupId' \ --output text) @@ -428,7 +430,7 @@ echo "Deploying EC2 instances..." # Create key pair for SSH access KEY_NAME="vpc-tutorial-key-$(date +%s)" echo "Creating key pair $KEY_NAME..." -aws ec2 create-key-pair --key-name "$KEY_NAME" --query 'KeyMaterial' --output text > "${KEY_NAME}.pem" || handle_error "Failed to create key pair" +aws ec2 create-key-pair --key-name "$KEY_NAME" --tag-specifications 'ResourceType=key-pair,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-gs}]' --query 'KeyMaterial' --output text > "${KEY_NAME}.pem" || handle_error "Failed to create key pair" chmod 400 "${KEY_NAME}.pem" echo "Key pair saved to ${KEY_NAME}.pem" CREATED_RESOURCES+=("KEY_PAIR:$KEY_NAME") @@ -456,7 +458,7 @@ WEB_INSTANCE=$(aws ec2 run-instances \ systemctl start httpd systemctl enable httpd echo "

Hello from $(hostname -f) in the public subnet

" > /var/www/html/index.html' \ - --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=WebServer}]' \ + --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=WebServer},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-gs}]' \ --query 'Instances[0].InstanceId' \ --output text) || handle_error "Failed to launch web server" echo "Web server instance created with ID: $WEB_INSTANCE" @@ -486,7 +488,7 @@ DB_INSTANCE=$(aws ec2 run-instances \ yum install -y mariadb-server systemctl start mariadb systemctl enable mariadb' \ - --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=DBServer}]' \ + --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=DBServer},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-gs}]' \ --query 'Instances[0].InstanceId' \ --output text) || handle_error "Failed to launch database server" echo "Database server instance created with ID: $DB_INSTANCE" diff --git a/tuts/003-s3-gettingstarted/s3-gettingstarted.sh b/tuts/003-s3-gettingstarted/s3-gettingstarted.sh index a835eb7..f345979 100644 --- a/tuts/003-s3-gettingstarted/s3-gettingstarted.sh +++ b/tuts/003-s3-gettingstarted/s3-gettingstarted.sh @@ -169,6 +169,23 @@ if [ "$BUCKET_IS_SHARED" = "false" ]; then fi CREATED_RESOURCES+=("s3:bucket:${BUCKET_NAME}") echo "Bucket created." + + if ! aws s3api put-bucket-tagging \ + --bucket "$BUCKET_NAME" \ + --tagging '{ + "TagSet": [ + { + "Key": "project", + "Value": "doc-smith" + }, + { + "Key": "tutorial", + "Value": "s3-gettingstarted" + } + ] + }' >/dev/null 2>&1; then + echo "WARNING: Failed to tag bucket" + fi fi echo "" @@ -317,12 +334,29 @@ LOG_TARGET_BUCKET="${BUCKET_NAME}-logs" if [ "$BUCKET_IS_SHARED" = "false" ]; then REGION=$(get_region) if [ "$REGION" = "us-east-1" ]; then - aws s3api create-bucket --bucket "$LOG_TARGET_BUCKET" 2>/dev/null || true + aws s3api create-bucket --bucket "$LOG_TARGET_BUCKET" >/dev/null 2>&1 || true else aws s3api create-bucket \ --bucket "$LOG_TARGET_BUCKET" \ --region "$REGION" \ - --create-bucket-configuration LocationConstraint="$REGION" 2>/dev/null || true + --create-bucket-configuration LocationConstraint="$REGION" >/dev/null 2>&1 || true + fi + + if ! aws s3api put-bucket-tagging \ + --bucket "$LOG_TARGET_BUCKET" \ + --tagging '{ + "TagSet": [ + { + "Key": "project", + "Value": "doc-smith" + }, + { + "Key": "tutorial", + "Value": "s3-gettingstarted" + } + ] + }' >/dev/null 2>&1; then + echo "WARNING: Failed to tag log bucket" fi aws s3api put-bucket-acl --bucket "$LOG_TARGET_BUCKET" --acl log-delivery-write 2>/dev/null || true @@ -354,6 +388,14 @@ if ! aws s3api put-bucket-tagging \ --bucket "$BUCKET_NAME" \ --tagging '{ "TagSet": [ + { + "Key": "project", + "Value": "doc-smith" + }, + { + "Key": "tutorial", + "Value": "s3-gettingstarted" + }, { "Key": "Environment", "Value": "Tutorial" diff --git a/tuts/004-cloudmap-custom-attributes/cloudmap-custom-attributes.sh b/tuts/004-cloudmap-custom-attributes/cloudmap-custom-attributes.sh index 8b641ec..9d218fe 100644 --- a/tuts/004-cloudmap-custom-attributes/cloudmap-custom-attributes.sh +++ b/tuts/004-cloudmap-custom-attributes/cloudmap-custom-attributes.sh @@ -180,7 +180,7 @@ NAMESPACE_ID=$(aws servicediscovery list-namespaces --query "Namespaces[?Name==' if [[ -z "$NAMESPACE_ID" || "$NAMESPACE_ID" == "None" ]]; then log_cmd "aws servicediscovery create-http-namespace --name cloudmap-tutorial --creator-request-id namespace-request-\$(date +%s)" - OPERATION_ID=$(aws servicediscovery create-http-namespace --name cloudmap-tutorial --creator-request-id "namespace-request-$(date +%s)" --query 'OperationId' --output text) + OPERATION_ID=$(aws servicediscovery create-http-namespace --name cloudmap-tutorial --creator-request-id "namespace-request-$(date +%s)" --tags Key=project,Value=doc-smith Key=tutorial,Value=cloudmap-custom-attributes --query 'OperationId' --output text) # Wait for namespace creation to complete echo "Waiting for namespace creation to complete..." | tee -a "$LOG_FILE" @@ -202,7 +202,7 @@ echo "Step 2: Creating DynamoDB table..." | tee -a "$LOG_FILE" TABLE_EXISTS=$(aws dynamodb describe-table --table-name cloudmap 2>&1 || echo "NOT_EXISTS") if [[ $TABLE_EXISTS == *"ResourceNotFoundException"* || $TABLE_EXISTS == "NOT_EXISTS" ]]; then - log_cmd "aws dynamodb create-table --table-name cloudmap --attribute-definitions AttributeName=id,AttributeType=S --key-schema AttributeName=id,KeyType=HASH --billing-mode PAY_PER_REQUEST" + log_cmd "aws dynamodb create-table --table-name cloudmap --attribute-definitions AttributeName=id,AttributeType=S --key-schema AttributeName=id,KeyType=HASH --billing-mode PAY_PER_REQUEST --tags Key=project,Value=doc-smith Key=tutorial,Value=cloudmap-custom-attributes" # Wait for DynamoDB table to become active echo "Waiting for DynamoDB table to become active..." | tee -a "$LOG_FILE" @@ -235,7 +235,7 @@ if [[ -z "$DATA_SERVICE_ID" ]]; then echo "Data service does not exist, creating it..." | tee -a "$LOG_FILE" # Create the service and capture the ID directly echo "$ aws servicediscovery create-service --name data-service --namespace-id $NAMESPACE_ID --creator-request-id data-service-request-\$(date +%s)" | tee -a "$LOG_FILE" - CREATE_OUTPUT=$(aws servicediscovery create-service --name data-service --namespace-id "$NAMESPACE_ID" --creator-request-id "data-service-request-$(date +%s)") + CREATE_OUTPUT=$(aws servicediscovery create-service --name data-service --namespace-id "$NAMESPACE_ID" --tags Key=project,Value=doc-smith Key=tutorial,Value=cloudmap-custom-attributes --creator-request-id "data-service-request-$(date +%s)") echo "$CREATE_OUTPUT" | tee -a "$LOG_FILE" # Extract the service ID using AWS CLI query @@ -292,6 +292,7 @@ ROLE_EXISTS=$(aws iam get-role --role-name cloudmap-tutorial-role 2>&1 || echo " if [[ $ROLE_EXISTS == *"NoSuchEntity"* || $ROLE_EXISTS == "NOT_EXISTS" ]]; then log_cmd "aws iam create-role --role-name cloudmap-tutorial-role --assume-role-policy-document file://lambda-trust-policy.json" + aws iam tag-role --role-name cloudmap-tutorial-role --tags Key=project,Value=doc-smith Key=tutorial,Value=cloudmap-custom-attributes else echo "Role cloudmap-tutorial-role already exists, using existing role" | tee -a "$LOG_FILE" fi @@ -375,7 +376,7 @@ if [[ -z "$APP_SERVICE_ID" ]]; then echo "App service does not exist, creating it..." | tee -a "$LOG_FILE" # Create the service and capture the ID directly echo "$ aws servicediscovery create-service --name app-service --namespace-id $NAMESPACE_ID --creator-request-id app-service-request-\$(date +%s)" | tee -a "$LOG_FILE" - CREATE_OUTPUT=$(aws servicediscovery create-service --name app-service --namespace-id "$NAMESPACE_ID" --creator-request-id "app-service-request-$(date +%s)") + CREATE_OUTPUT=$(aws servicediscovery create-service --name app-service --namespace-id "$NAMESPACE_ID" --tags Key=project,Value=doc-smith Key=tutorial,Value=cloudmap-custom-attributes --creator-request-id "app-service-request-$(date +%s)") echo "$CREATE_OUTPUT" | tee -a "$LOG_FILE" # Extract the service ID using AWS CLI query @@ -448,7 +449,7 @@ log_cmd "zip writefunction.zip writefunction.py" # Create the Lambda function FUNCTION_EXISTS=$(aws lambda list-functions --query "Functions[?FunctionName=='writefunction'].FunctionName" --output text 2>/dev/null || echo "") if [[ -z "$FUNCTION_EXISTS" ]]; then - log_cmd "aws lambda create-function --function-name writefunction --runtime python3.12 --role $ROLE_ARN --handler writefunction.lambda_handler --zip-file fileb://writefunction.zip --architectures x86_64 --timeout 10" + log_cmd "aws lambda create-function --function-name writefunction --runtime python3.12 --role $ROLE_ARN --handler writefunction.lambda_handler --zip-file fileb://writefunction.zip --architectures x86_64 --timeout 10 --tags project=doc-smith,tutorial=cloudmap-custom-attributes" # Wait for the Lambda function to be active before updating echo "Waiting for Lambda function to become active..." | tee -a "$LOG_FILE" @@ -537,7 +538,7 @@ log_cmd "zip readfunction.zip readfunction.py" # Create the Lambda function FUNCTION_EXISTS=$(aws lambda list-functions --query "Functions[?FunctionName=='readfunction'].FunctionName" --output text 2>/dev/null || echo "") if [[ -z "$FUNCTION_EXISTS" ]]; then - log_cmd "aws lambda create-function --function-name readfunction --runtime python3.12 --role $ROLE_ARN --handler readfunction.lambda_handler --zip-file fileb://readfunction.zip --architectures x86_64 --timeout 10" + log_cmd "aws lambda create-function --function-name readfunction --runtime python3.12 --role $ROLE_ARN --handler readfunction.lambda_handler --zip-file fileb://readfunction.zip --architectures x86_64 --timeout 10 --tags project=doc-smith,tutorial=cloudmap-custom-attributes" # Wait for the Lambda function to be active before updating echo "Waiting for Lambda function to become active..." | tee -a "$LOG_FILE" diff --git a/tuts/005-cloudfront-gettingstarted/cloudfront-gettingstarted.sh b/tuts/005-cloudfront-gettingstarted/cloudfront-gettingstarted.sh index 4c8f1af..c3e9af7 100644 --- a/tuts/005-cloudfront-gettingstarted/cloudfront-gettingstarted.sh +++ b/tuts/005-cloudfront-gettingstarted/cloudfront-gettingstarted.sh @@ -179,6 +179,8 @@ if [ "$BUCKET_IS_SHARED" != "true" ]; 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 \ @@ -335,6 +337,9 @@ 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}]' + # Step 6: Update S3 bucket policy echo "Updating S3 bucket policy..." diff --git a/tuts/008-vpc-private-servers-gs/vpc-private-servers-gs.sh b/tuts/008-vpc-private-servers-gs/vpc-private-servers-gs.sh index 02e7c7f..8bb5338 100755 --- a/tuts/008-vpc-private-servers-gs/vpc-private-servers-gs.sh +++ b/tuts/008-vpc-private-servers-gs/vpc-private-servers-gs.sh @@ -184,7 +184,7 @@ echo "Using random identifier: $RANDOM_ID" # Create VPC echo "Creating VPC..." -VPC_RESULT=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications "ResourceType=vpc,Tags=[{Key=Name,Value=ProductionVPC-$RANDOM_ID}]") +VPC_RESULT=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications "ResourceType=vpc,Tags=[{Key=Name,Value=ProductionVPC-$RANDOM_ID},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]") check_command "Failed to create VPC" VPC_ID=$(echo "$VPC_RESULT" | jq -r '.Vpc.VpcId') @@ -203,19 +203,19 @@ echo "Using Availability Zones: $AZ1 and $AZ2" # Create subnets echo "Creating subnets..." -PUBLIC_SUBNET1_RESULT=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.0.0/24 --availability-zone "$AZ1" --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=PublicSubnet1-$RANDOM_ID}]") +PUBLIC_SUBNET1_RESULT=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.0.0/24 --availability-zone "$AZ1" --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=PublicSubnet1-$RANDOM_ID},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]") check_command "Failed to create public subnet 1" PUBLIC_SUBNET1_ID=$(echo "$PUBLIC_SUBNET1_RESULT" | jq -r '.Subnet.SubnetId') -PRIVATE_SUBNET1_RESULT=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.1.0/24 --availability-zone "$AZ1" --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=PrivateSubnet1-$RANDOM_ID}]") +PRIVATE_SUBNET1_RESULT=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.1.0/24 --availability-zone "$AZ1" --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=PrivateSubnet1-$RANDOM_ID},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]") check_command "Failed to create private subnet 1" PRIVATE_SUBNET1_ID=$(echo "$PRIVATE_SUBNET1_RESULT" | jq -r '.Subnet.SubnetId') -PUBLIC_SUBNET2_RESULT=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.2.0/24 --availability-zone "$AZ2" --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=PublicSubnet2-$RANDOM_ID}]") +PUBLIC_SUBNET2_RESULT=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.2.0/24 --availability-zone "$AZ2" --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=PublicSubnet2-$RANDOM_ID},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]") check_command "Failed to create public subnet 2" PUBLIC_SUBNET2_ID=$(echo "$PUBLIC_SUBNET2_RESULT" | jq -r '.Subnet.SubnetId') -PRIVATE_SUBNET2_RESULT=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.3.0/24 --availability-zone "$AZ2" --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=PrivateSubnet2-$RANDOM_ID}]") +PRIVATE_SUBNET2_RESULT=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.3.0/24 --availability-zone "$AZ2" --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=PrivateSubnet2-$RANDOM_ID},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]") check_command "Failed to create private subnet 2" PRIVATE_SUBNET2_ID=$(echo "$PRIVATE_SUBNET2_RESULT" | jq -r '.Subnet.SubnetId') @@ -227,7 +227,7 @@ echo "Private Subnet 2: $PRIVATE_SUBNET2_ID" # Create Internet Gateway echo "Creating Internet Gateway..." -IGW_RESULT=$(aws ec2 create-internet-gateway --tag-specifications "ResourceType=internet-gateway,Tags=[{Key=Name,Value=ProductionIGW-$RANDOM_ID}]") +IGW_RESULT=$(aws ec2 create-internet-gateway --tag-specifications "ResourceType=internet-gateway,Tags=[{Key=Name,Value=ProductionIGW-$RANDOM_ID},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]") check_command "Failed to create Internet Gateway" IGW_ID=$(echo "$IGW_RESULT" | jq -r '.InternetGateway.InternetGatewayId') echo "Internet Gateway created with ID: $IGW_ID" @@ -239,15 +239,15 @@ check_command "Failed to attach Internet Gateway to VPC" # Create route tables echo "Creating route tables..." -PUBLIC_RT_RESULT=$(aws ec2 create-route-table --vpc-id "$VPC_ID" --tag-specifications "ResourceType=route-table,Tags=[{Key=Name,Value=PublicRouteTable-$RANDOM_ID}]") +PUBLIC_RT_RESULT=$(aws ec2 create-route-table --vpc-id "$VPC_ID" --tag-specifications "ResourceType=route-table,Tags=[{Key=Name,Value=PublicRouteTable-$RANDOM_ID},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]") check_command "Failed to create public route table" PUBLIC_RT_ID=$(echo "$PUBLIC_RT_RESULT" | jq -r '.RouteTable.RouteTableId') -PRIVATE_RT1_RESULT=$(aws ec2 create-route-table --vpc-id "$VPC_ID" --tag-specifications "ResourceType=route-table,Tags=[{Key=Name,Value=PrivateRouteTable1-$RANDOM_ID}]") +PRIVATE_RT1_RESULT=$(aws ec2 create-route-table --vpc-id "$VPC_ID" --tag-specifications "ResourceType=route-table,Tags=[{Key=Name,Value=PrivateRouteTable1-$RANDOM_ID},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]") check_command "Failed to create private route table 1" PRIVATE_RT1_ID=$(echo "$PRIVATE_RT1_RESULT" | jq -r '.RouteTable.RouteTableId') -PRIVATE_RT2_RESULT=$(aws ec2 create-route-table --vpc-id "$VPC_ID" --tag-specifications "ResourceType=route-table,Tags=[{Key=Name,Value=PrivateRouteTable2-$RANDOM_ID}]") +PRIVATE_RT2_RESULT=$(aws ec2 create-route-table --vpc-id "$VPC_ID" --tag-specifications "ResourceType=route-table,Tags=[{Key=Name,Value=PrivateRouteTable2-$RANDOM_ID},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]") check_command "Failed to create private route table 2" PRIVATE_RT2_ID=$(echo "$PRIVATE_RT2_RESULT" | jq -r '.RouteTable.RouteTableId') @@ -290,11 +290,11 @@ echo "Creating NAT Gateways..." # Allocate Elastic IPs for NAT Gateways echo "Allocating Elastic IPs for NAT Gateways..." -EIP1_RESULT=$(aws ec2 allocate-address --domain vpc --tag-specifications "ResourceType=elastic-ip,Tags=[{Key=Name,Value=NAT1-EIP-$RANDOM_ID}]") +EIP1_RESULT=$(aws ec2 allocate-address --domain vpc --tag-specifications "ResourceType=elastic-ip,Tags=[{Key=Name,Value=NAT1-EIP-$RANDOM_ID},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]") check_command "Failed to allocate Elastic IP 1" EIP1_ALLOC_ID=$(echo "$EIP1_RESULT" | jq -r '.AllocationId') -EIP2_RESULT=$(aws ec2 allocate-address --domain vpc --tag-specifications "ResourceType=elastic-ip,Tags=[{Key=Name,Value=NAT2-EIP-$RANDOM_ID}]") +EIP2_RESULT=$(aws ec2 allocate-address --domain vpc --tag-specifications "ResourceType=elastic-ip,Tags=[{Key=Name,Value=NAT2-EIP-$RANDOM_ID},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]") check_command "Failed to allocate Elastic IP 2" EIP2_ALLOC_ID=$(echo "$EIP2_RESULT" | jq -r '.AllocationId') @@ -304,12 +304,12 @@ echo "EIP 2 Allocation ID: $EIP2_ALLOC_ID" # Create NAT Gateways echo "Creating NAT Gateway in public subnet 1..." -NAT_GW1_RESULT=$(aws ec2 create-nat-gateway --subnet-id "$PUBLIC_SUBNET1_ID" --allocation-id "$EIP1_ALLOC_ID" --tag-specifications "ResourceType=natgateway,Tags=[{Key=Name,Value=NAT-Gateway1-$RANDOM_ID}]") +NAT_GW1_RESULT=$(aws ec2 create-nat-gateway --subnet-id "$PUBLIC_SUBNET1_ID" --allocation-id "$EIP1_ALLOC_ID" --tag-specifications "ResourceType=natgateway,Tags=[{Key=Name,Value=NAT-Gateway1-$RANDOM_ID},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]") check_command "Failed to create NAT Gateway 1" NAT_GW1_ID=$(echo "$NAT_GW1_RESULT" | jq -r '.NatGateway.NatGatewayId') echo "Creating NAT Gateway in public subnet 2..." -NAT_GW2_RESULT=$(aws ec2 create-nat-gateway --subnet-id "$PUBLIC_SUBNET2_ID" --allocation-id "$EIP2_ALLOC_ID" --tag-specifications "ResourceType=natgateway,Tags=[{Key=Name,Value=NAT-Gateway2-$RANDOM_ID}]") +NAT_GW2_RESULT=$(aws ec2 create-nat-gateway --subnet-id "$PUBLIC_SUBNET2_ID" --allocation-id "$EIP2_ALLOC_ID" --tag-specifications "ResourceType=natgateway,Tags=[{Key=Name,Value=NAT-Gateway2-$RANDOM_ID},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]") check_command "Failed to create NAT Gateway 2" NAT_GW2_ID=$(echo "$NAT_GW2_RESULT" | jq -r '.NatGateway.NatGatewayId') @@ -338,14 +338,14 @@ echo "Creating VPC Endpoint for S3..." S3_PREFIX_LIST_ID=$(aws ec2 describe-prefix-lists --filters "Name=prefix-list-name,Values=com.amazonaws.$(aws configure get region).s3" --query 'PrefixLists[0].PrefixListId' --output text) check_command "Failed to get S3 prefix list ID" -VPC_ENDPOINT_RESULT=$(aws ec2 create-vpc-endpoint --vpc-id "$VPC_ID" --service-name "com.amazonaws.$(aws configure get region).s3" --route-table-ids "$PRIVATE_RT1_ID" "$PRIVATE_RT2_ID" --tag-specifications "ResourceType=vpc-endpoint,Tags=[{Key=Name,Value=S3-Endpoint-$RANDOM_ID}]") +VPC_ENDPOINT_RESULT=$(aws ec2 create-vpc-endpoint --vpc-id "$VPC_ID" --service-name "com.amazonaws.$(aws configure get region).s3" --route-table-ids "$PRIVATE_RT1_ID" "$PRIVATE_RT2_ID" --tag-specifications "ResourceType=vpc-endpoint,Tags=[{Key=Name,Value=S3-Endpoint-$RANDOM_ID},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]") check_command "Failed to create VPC endpoint for S3" VPC_ENDPOINT_ID=$(echo "$VPC_ENDPOINT_RESULT" | jq -r '.VpcEndpoint.VpcEndpointId') echo "VPC Endpoint created with ID: $VPC_ENDPOINT_ID" # Create security groups echo "Creating security groups..." -LB_SG_RESULT=$(aws ec2 create-security-group --group-name "LoadBalancerSG-$RANDOM_ID" --description "Security group for the load balancer" --vpc-id "$VPC_ID" --tag-specifications "ResourceType=security-group,Tags=[{Key=Name,Value=LoadBalancerSG-$RANDOM_ID}]") +LB_SG_RESULT=$(aws ec2 create-security-group --group-name "LoadBalancerSG-$RANDOM_ID" --description "Security group for the load balancer" --vpc-id "$VPC_ID" --tag-specifications "ResourceType=security-group,Tags=[{Key=Name,Value=LoadBalancerSG-$RANDOM_ID},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]") check_command "Failed to create load balancer security group" LB_SG_ID=$(echo "$LB_SG_RESULT" | jq -r '.GroupId') @@ -353,7 +353,7 @@ LB_SG_ID=$(echo "$LB_SG_RESULT" | jq -r '.GroupId') aws ec2 authorize-security-group-ingress --group-id "$LB_SG_ID" --protocol tcp --port 80 --cidr 0.0.0.0/0 check_command "Failed to authorize ingress to load balancer security group" -APP_SG_RESULT=$(aws ec2 create-security-group --group-name "AppServerSG-$RANDOM_ID" --description "Security group for the application servers" --vpc-id "$VPC_ID" --tag-specifications "ResourceType=security-group,Tags=[{Key=Name,Value=AppServerSG-$RANDOM_ID}]") +APP_SG_RESULT=$(aws ec2 create-security-group --group-name "AppServerSG-$RANDOM_ID" --description "Security group for the application servers" --vpc-id "$VPC_ID" --tag-specifications "ResourceType=security-group,Tags=[{Key=Name,Value=AppServerSG-$RANDOM_ID},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]") check_command "Failed to create application server security group" APP_SG_ID=$(echo "$APP_SG_RESULT" | jq -r '.GroupId') @@ -400,7 +400,7 @@ echo "Creating launch template: $LAUNCH_TEMPLATE_NAME" aws ec2 create-launch-template \ --launch-template-name "$LAUNCH_TEMPLATE_NAME" \ --version-description "Initial version" \ - --tag-specifications "ResourceType=launch-template,Tags=[{Key=Name,Value=$LAUNCH_TEMPLATE_NAME}]" \ + --tag-specifications "ResourceType=launch-template,Tags=[{Key=Name,Value=$LAUNCH_TEMPLATE_NAME},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-private-servers-gs}]" \ --launch-template-data "{ \"NetworkInterfaces\": [{ \"DeviceIndex\": 0, @@ -435,7 +435,8 @@ TARGET_GROUP_RESULT=$(aws elbv2 create-target-group \ --target-type instance \ --health-check-protocol HTTP \ --health-check-path "/" \ - --health-check-port traffic-port) + --health-check-port traffic-port \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=vpc-private-servers-gs) check_command "Failed to create target group" TARGET_GROUP_ARN=$(echo "$TARGET_GROUP_RESULT" | jq -r '.TargetGroups[0].TargetGroupArn') echo "Target group created with ARN: $TARGET_GROUP_ARN" @@ -447,7 +448,7 @@ LB_RESULT=$(aws elbv2 create-load-balancer \ --name "$LB_NAME" \ --subnets "$PUBLIC_SUBNET1_ID" "$PUBLIC_SUBNET2_ID" \ --security-groups "$LB_SG_ID" \ - --tags "Key=Name,Value=$LB_NAME") + --tags "Key=Name,Value=$LB_NAME" Key=project,Value=doc-smith Key=tutorial,Value=vpc-private-servers-gs) check_command "Failed to create load balancer" LB_ARN=$(echo "$LB_RESULT" | jq -r '.LoadBalancers[0].LoadBalancerArn') echo "Load balancer created with ARN: $LB_ARN" @@ -481,7 +482,7 @@ aws autoscaling create-auto-scaling-group \ --target-group-arns "$TARGET_GROUP_ARN" \ --health-check-type ELB \ --health-check-grace-period 300 \ - --tags "Key=Name,Value=AppServer-$RANDOM_ID,PropagateAtLaunch=true" + --tags "Key=Name,Value=AppServer-$RANDOM_ID,PropagateAtLaunch=true" Key=project,Value=doc-smith,PropagateAtLaunch=true Key=tutorial,Value=vpc-private-servers-gs,PropagateAtLaunch=true check_command "Failed to create Auto Scaling group" echo "Auto Scaling group created with name: $ASG_NAME" diff --git a/tuts/009-vpc-ipam-gs/vpc-ipam-gs.sh b/tuts/009-vpc-ipam-gs/vpc-ipam-gs.sh index d0e261e..a9113a5 100755 --- a/tuts/009-vpc-ipam-gs/vpc-ipam-gs.sh +++ b/tuts/009-vpc-ipam-gs/vpc-ipam-gs.sh @@ -179,7 +179,8 @@ wait_for_cidr_provisioning() { echo "Creating IPAM..." IPAM_RESULT=$(aws ec2 create-ipam \ --description "My IPAM" \ - --operating-regions RegionName=us-east-1 RegionName=us-west-2) + --operating-regions RegionName=us-east-1 RegionName=us-west-2 \ + --tag-specifications 'ResourceType=ipam,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-ipam-gs}]') if [ $? -ne 0 ]; then handle_error "Failed to create IPAM" @@ -213,7 +214,8 @@ echo "Creating Top-level IPv4 Pool..." TOP_POOL_RESULT=$(aws ec2 create-ipam-pool \ --ipam-scope-id "$PRIVATE_SCOPE_ID" \ --address-family ipv4 \ - --description "Top-level pool") + --description "Top-level pool" \ + --tag-specifications 'ResourceType=ipam-pool,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-ipam-gs}]') if [ $? -ne 0 ]; then handle_error "Failed to create Top-level Pool" @@ -252,7 +254,8 @@ REGIONAL_POOL_RESULT=$(aws ec2 create-ipam-pool \ --source-ipam-pool-id "$TOP_POOL_ID" \ --locale us-east-1 \ --address-family ipv4 \ - --description "Regional pool in us-east-1") + --description "Regional pool in us-east-1" \ + --tag-specifications 'ResourceType=ipam-pool,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-ipam-gs}]') if [ $? -ne 0 ]; then handle_error "Failed to create Regional Pool" @@ -291,7 +294,8 @@ DEV_POOL_RESULT=$(aws ec2 create-ipam-pool \ --source-ipam-pool-id "$REGIONAL_POOL_ID" \ --locale us-east-1 \ --address-family ipv4 \ - --description "Development pool") + --description "Development pool" \ + --tag-specifications 'ResourceType=ipam-pool,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-ipam-gs}]') if [ $? -ne 0 ]; then handle_error "Failed to create Development Pool" @@ -328,7 +332,7 @@ echo "Creating VPC using IPAM Pool CIDR..." VPC_RESULT=$(aws ec2 create-vpc \ --ipv4-ipam-pool-id "$DEV_POOL_ID" \ --ipv4-netmask-length 26 \ - --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=IPAM-VPC}]') + --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=IPAM-VPC},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-ipam-gs}]') if [ $? -ne 0 ]; then handle_error "Failed to create VPC" diff --git a/tuts/010-cloudmap-service-discovery/cloudmap-service-discovery.sh b/tuts/010-cloudmap-service-discovery/cloudmap-service-discovery.sh index 38474b0..01ad920 100755 --- a/tuts/010-cloudmap-service-discovery/cloudmap-service-discovery.sh +++ b/tuts/010-cloudmap-service-discovery/cloudmap-service-discovery.sh @@ -101,6 +101,7 @@ log "Creating AWS Cloud Map namespace: $NAMESPACE_NAME" OPERATION_RESULT=$(aws servicediscovery create-public-dns-namespace \ --name "$NAMESPACE_NAME" \ --creator-request-id "cloudmap-tutorial-$CREATOR_REQUEST_ID" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=cloudmap-service-discovery \ --region "$REGION") OPERATION_ID=$(echo "$OPERATION_RESULT" | jq -r '.OperationId') @@ -133,6 +134,7 @@ PUBLIC_SERVICE_RESULT=$(aws servicediscovery create-service \ --name "public-service" \ --namespace-id "$NAMESPACE_ID" \ --dns-config "RoutingPolicy=MULTIVALUE,DnsRecords=[{Type=A,TTL=300}]" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=cloudmap-service-discovery \ --region "$REGION") PUBLIC_SERVICE_ID=$(echo "$PUBLIC_SERVICE_RESULT" | jq -r '.Service.Id') @@ -143,6 +145,7 @@ BACKEND_SERVICE_RESULT=$(aws servicediscovery create-service \ --name "backend-service" \ --namespace-id "$NAMESPACE_ID" \ --type "HTTP" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=cloudmap-service-discovery \ --region "$REGION") BACKEND_SERVICE_ID=$(echo "$BACKEND_SERVICE_RESULT" | jq -r '.Service.Id') diff --git a/tuts/011-getting-started-batch-fargate/getting-started-batch-fargate.sh b/tuts/011-getting-started-batch-fargate/getting-started-batch-fargate.sh index 9e24f22..ebb8460 100644 --- a/tuts/011-getting-started-batch-fargate/getting-started-batch-fargate.sh +++ b/tuts/011-getting-started-batch-fargate/getting-started-batch-fargate.sh @@ -364,6 +364,7 @@ EOFPOLICY aws iam create-role \ --role-name "${ROLE_NAME}" \ --assume-role-policy-document "file://${TRUST_POLICY_FILE}" + aws iam tag-role --role-name "${ROLE_NAME}" --tags Key=project,Value=doc-smith Key=tutorial,Value=getting-started-batch-fargate CREATED_RESOURCES+=("IAM_ROLE:${ROLE_NAME}") # Attach policy @@ -384,6 +385,7 @@ EOFPOLICY --compute-environment-name "${COMPUTE_ENV_NAME}" \ --type MANAGED \ --state ENABLED \ + --tags project=doc-smith,tutorial=getting-started-batch-fargate \ --compute-resources "{ \"type\": \"FARGATE\", \"maxvCpus\": 256, @@ -405,6 +407,7 @@ EOFPOLICY --job-queue-name "${JOB_QUEUE_NAME}" \ --state ENABLED \ --priority 900 \ + --tags project=doc-smith,tutorial=getting-started-batch-fargate \ --compute-environment-order "order=1,computeEnvironment=${COMPUTE_ENV_NAME}" CREATED_RESOURCES+=("JOB_QUEUE:${JOB_QUEUE_NAME}") diff --git a/tuts/012-transitgateway-gettingstarted/transitgateway-gettingstarted.sh b/tuts/012-transitgateway-gettingstarted/transitgateway-gettingstarted.sh index 57b7a13..292f0d3 100644 --- a/tuts/012-transitgateway-gettingstarted/transitgateway-gettingstarted.sh +++ b/tuts/012-transitgateway-gettingstarted/transitgateway-gettingstarted.sh @@ -201,7 +201,7 @@ if [ "$VPC1_ID" = "None" ] || [ -z "$VPC1_ID" ]; then echo "Creating VPC1..." VPC1_ID=$(aws ec2 create-vpc \ --cidr-block 10.1.0.0/16 \ - --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=VPC1}]' \ + --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=VPC1},{Key=project,Value=doc-smith},{Key=tutorial,Value=transitgateway-gettingstarted}]' \ --query Vpc.VpcId \ --output text) validate_aws_output "$VPC1_ID" "VPC1" || exit 1 @@ -213,7 +213,7 @@ if [ "$VPC1_ID" = "None" ] || [ -z "$VPC1_ID" ]; then --vpc-id "$VPC1_ID" \ --cidr-block 10.1.0.0/24 \ --availability-zone "$AZ" \ - --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=VPC1-Subnet}]' \ + --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=VPC1-Subnet},{Key=project,Value=doc-smith},{Key=tutorial,Value=transitgateway-gettingstarted}]' \ --query Subnet.SubnetId \ --output text) validate_aws_output "$SUBNET1_ID" "VPC1 subnet" || exit 1 @@ -230,7 +230,7 @@ else --vpc-id "$VPC1_ID" \ --cidr-block 10.1.0.0/24 \ --availability-zone "$AZ" \ - --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=VPC1-Subnet}]' \ + --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=VPC1-Subnet},{Key=project,Value=doc-smith},{Key=tutorial,Value=transitgateway-gettingstarted}]' \ --query Subnet.SubnetId \ --output text) validate_aws_output "$SUBNET1_ID" "VPC1 subnet" || exit 1 @@ -244,7 +244,7 @@ if [ "$VPC2_ID" = "None" ] || [ -z "$VPC2_ID" ]; then echo "Creating VPC2..." VPC2_ID=$(aws ec2 create-vpc \ --cidr-block 10.2.0.0/16 \ - --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=VPC2}]' \ + --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=VPC2},{Key=project,Value=doc-smith},{Key=tutorial,Value=transitgateway-gettingstarted}]' \ --query Vpc.VpcId \ --output text) validate_aws_output "$VPC2_ID" "VPC2" || exit 1 @@ -256,7 +256,7 @@ if [ "$VPC2_ID" = "None" ] || [ -z "$VPC2_ID" ]; then --vpc-id "$VPC2_ID" \ --cidr-block 10.2.0.0/24 \ --availability-zone "$AZ" \ - --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=VPC2-Subnet}]' \ + --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=VPC2-Subnet},{Key=project,Value=doc-smith},{Key=tutorial,Value=transitgateway-gettingstarted}]' \ --query Subnet.SubnetId \ --output text) validate_aws_output "$SUBNET2_ID" "VPC2 subnet" || exit 1 @@ -273,7 +273,7 @@ else --vpc-id "$VPC2_ID" \ --cidr-block 10.2.0.0/24 \ --availability-zone "$AZ" \ - --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=VPC2-Subnet}]' \ + --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=VPC2-Subnet},{Key=project,Value=doc-smith},{Key=tutorial,Value=transitgateway-gettingstarted}]' \ --query Subnet.SubnetId \ --output text) validate_aws_output "$SUBNET2_ID" "VPC2 subnet" || exit 1 @@ -304,7 +304,7 @@ echo "Creating Transit Gateway..." TGW_ID=$(aws ec2 create-transit-gateway \ --description "My Transit Gateway" \ --options "AmazonSideAsn=64512,AutoAcceptSharedAttachments=disable,DefaultRouteTableAssociation=enable,DefaultRouteTablePropagation=enable,VpnEcmpSupport=enable,DnsSupport=enable,MulticastSupport=disable" \ - --tag-specifications 'ResourceType=transit-gateway,Tags=[{Key=Name,Value=MyTransitGateway}]' \ + --tag-specifications 'ResourceType=transit-gateway,Tags=[{Key=Name,Value=MyTransitGateway},{Key=project,Value=doc-smith},{Key=tutorial,Value=transitgateway-gettingstarted}]' \ --query TransitGateway.TransitGatewayId \ --output text) validate_aws_output "$TGW_ID" "Transit Gateway" || exit 1 @@ -319,7 +319,7 @@ TGW_ATTACHMENT_1_ID=$(aws ec2 create-transit-gateway-vpc-attachment \ --transit-gateway-id "$TGW_ID" \ --vpc-id "$VPC1_ID" \ --subnet-ids "$SUBNET1_ID" \ - --tag-specifications 'ResourceType=transit-gateway-attachment,Tags=[{Key=Name,Value=VPC1-Attachment}]' \ + --tag-specifications 'ResourceType=transit-gateway-attachment,Tags=[{Key=Name,Value=VPC1-Attachment},{Key=project,Value=doc-smith},{Key=tutorial,Value=transitgateway-gettingstarted}]' \ --query TransitGatewayVpcAttachment.TransitGatewayAttachmentId \ --output text) validate_aws_output "$TGW_ATTACHMENT_1_ID" "Transit Gateway VPC1 Attachment" || exit 1 @@ -330,7 +330,7 @@ TGW_ATTACHMENT_2_ID=$(aws ec2 create-transit-gateway-vpc-attachment \ --transit-gateway-id "$TGW_ID" \ --vpc-id "$VPC2_ID" \ --subnet-ids "$SUBNET2_ID" \ - --tag-specifications 'ResourceType=transit-gateway-attachment,Tags=[{Key=Name,Value=VPC2-Attachment}]' \ + --tag-specifications 'ResourceType=transit-gateway-attachment,Tags=[{Key=Name,Value=VPC2-Attachment},{Key=project,Value=doc-smith},{Key=tutorial,Value=transitgateway-gettingstarted}]' \ --query TransitGatewayVpcAttachment.TransitGatewayAttachmentId \ --output text) validate_aws_output "$TGW_ATTACHMENT_2_ID" "Transit Gateway VPC2 Attachment" || exit 1 diff --git a/tuts/013-ec2-basics/ec2-basics.sh b/tuts/013-ec2-basics/ec2-basics.sh old mode 100755 new mode 100644 index 21796e0..554e8ee --- a/tuts/013-ec2-basics/ec2-basics.sh +++ b/tuts/013-ec2-basics/ec2-basics.sh @@ -155,6 +155,7 @@ log "Creating security group..." SECURITY_GROUP_ID=$(aws ec2 create-security-group \ --group-name "$SG_NAME" \ --description "Security group for EC2 tutorial" \ + --tag-specifications 'ResourceType=security-group,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=ec2-basics}]' \ --query "GroupId" \ --output text) @@ -224,6 +225,7 @@ INSTANCE_ID=$(aws ec2 run-instances \ --security-group-ids "$SECURITY_GROUP_ID" \ --metadata-options "HttpTokens=required,HttpEndpoint=enabled" \ --block-device-mappings "DeviceName=/dev/xvda,Ebs={Encrypted=true}" \ + --tag-specifications 'ResourceType=instance,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=ec2-basics}]' 'ResourceType=volume,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=ec2-basics}]' \ --count 1 \ --query 'Instances[0].InstanceId' \ --output text) @@ -306,6 +308,7 @@ log "To connect to your instance, run: ssh -i $KEY_FILE ec2-user@$NEW_PUBLIC_IP" log "Allocating Elastic IP address..." ALLOCATION_RESULT=$(aws ec2 allocate-address \ --domain vpc \ + --tag-specifications 'ResourceType=elastic-ip,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=ec2-basics}]' \ --query '[PublicIp,AllocationId]' \ --output text) @@ -385,4 +388,4 @@ log "To connect to your instance, run: ssh -i $KEY_FILE ec2-user@$ELASTIC_IP" log "Tutorial completed successfully!" cleanup -exit 0 +exit 0 \ No newline at end of file diff --git a/tuts/015-vpc-peering/vpc-peering.sh b/tuts/015-vpc-peering/vpc-peering.sh index 7652049..b32d46e 100644 --- a/tuts/015-vpc-peering/vpc-peering.sh +++ b/tuts/015-vpc-peering/vpc-peering.sh @@ -166,71 +166,91 @@ echo "Setting up VPC peering connection..." # Validate AWS CLI validate_aws_cli -# 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 +# 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 "") -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 +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 + fi 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 +# Create VPCs if needed +if [ "$CREATE_VPCS" = true ]; then 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") + 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},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-peering}]' --query 'Vpc.VpcId' --output text") check_error $? "Failed to create VPC1" VPC1_ID=$(sanitize_var "$VPC1_ID") || check_error 1 "Invalid VPC1_ID returned" VPC1_CIDR="10.1.0.0/16" 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") + 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},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-peering}]' --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 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},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-peering}]' --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:" @@ -243,9 +263,8 @@ 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 -# 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/') +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/') # Sanitize subnet CIDR blocks VPC1_SUBNET_CIDR=$(sanitize_var "$VPC1_SUBNET_CIDR") || check_error 1 "Invalid VPC1_SUBNET_CIDR format" @@ -256,7 +275,7 @@ validate_cidr "$VPC2_SUBNET_CIDR" || check_error 1 "Invalid subnet CIDR for VPC2 # Create subnets in both VPCs echo "Creating subnet in VPC1..." -SUBNET1_ID=$(log_cmd "aws ec2 create-subnet --region '$AWS_REGION' --vpc-id '$VPC1_ID' --cidr-block '$VPC1_SUBNET_CIDR' --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=VPC1-Peering-Subnet}]' --query 'Subnet.SubnetId' --output text") +SUBNET1_ID=$(log_cmd "aws ec2 create-subnet --region '$AWS_REGION' --vpc-id '$VPC1_ID' --cidr-block '$VPC1_SUBNET_CIDR' --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=VPC1-Peering-Subnet},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-peering}]' --query 'Subnet.SubnetId' --output text") check_error $? "Failed to create subnet in VPC1" SUBNET1_ID=$(sanitize_var "$SUBNET1_ID") || check_error 1 "Invalid SUBNET1_ID returned" CREATED_RESOURCES+=("Subnet in VPC1: $SUBNET1_ID") @@ -264,7 +283,7 @@ CLEANUP_COMMANDS+=("aws ec2 delete-subnet --region '$AWS_REGION' --subnet-id '$S echo "Subnet created in VPC1 with ID: $SUBNET1_ID (CIDR: $VPC1_SUBNET_CIDR)" echo "Creating subnet in VPC2..." -SUBNET2_ID=$(log_cmd "aws ec2 create-subnet --region '$AWS_REGION' --vpc-id '$VPC2_ID' --cidr-block '$VPC2_SUBNET_CIDR' --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=VPC2-Peering-Subnet}]' --query 'Subnet.SubnetId' --output text") +SUBNET2_ID=$(log_cmd "aws ec2 create-subnet --region '$AWS_REGION' --vpc-id '$VPC2_ID' --cidr-block '$VPC2_SUBNET_CIDR' --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=VPC2-Peering-Subnet},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-peering}]' --query 'Subnet.SubnetId' --output text") check_error $? "Failed to create subnet in VPC2" SUBNET2_ID=$(sanitize_var "$SUBNET2_ID") || check_error 1 "Invalid SUBNET2_ID returned" CREATED_RESOURCES+=("Subnet in VPC2: $SUBNET2_ID") @@ -273,7 +292,7 @@ echo "Subnet created in VPC2 with ID: $SUBNET2_ID (CIDR: $VPC2_SUBNET_CIDR)" # Create a VPC peering connection echo "Creating VPC peering connection..." -PEERING_ID=$(log_cmd "aws ec2 create-vpc-peering-connection --region '$AWS_REGION' --vpc-id '$VPC1_ID' --peer-vpc-id '$VPC2_ID' --tag-specifications 'ResourceType=vpc-peering-connection,Tags=[{Key=Name,Value=VPC1-VPC2-Peering}]' --query 'VpcPeeringConnection.VpcPeeringConnectionId' --output text") +PEERING_ID=$(log_cmd "aws ec2 create-vpc-peering-connection --region '$AWS_REGION' --vpc-id '$VPC1_ID' --peer-vpc-id '$VPC2_ID' --tag-specifications 'ResourceType=vpc-peering-connection,Tags=[{Key=Name,Value=VPC1-VPC2-Peering},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-peering}]' --query 'VpcPeeringConnection.VpcPeeringConnectionId' --output text") check_error $? "Failed to create VPC peering connection" PEERING_ID=$(sanitize_var "$PEERING_ID") || check_error 1 "Invalid PEERING_ID returned" CREATED_RESOURCES+=("VPC Peering Connection: $PEERING_ID") @@ -293,7 +312,7 @@ check_error $? "Timeout waiting for peering connection to become active" # Create a route table for VPC1 echo "Creating route table for VPC1..." -RTB1_ID=$(log_cmd "aws ec2 create-route-table --region '$AWS_REGION' --vpc-id '$VPC1_ID' --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=VPC1-RouteTable}]' --query 'RouteTable.RouteTableId' --output text") +RTB1_ID=$(log_cmd "aws ec2 create-route-table --region '$AWS_REGION' --vpc-id '$VPC1_ID' --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=VPC1-RouteTable},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-peering}]' --query 'RouteTable.RouteTableId' --output text") check_error $? "Failed to create route table for VPC1" RTB1_ID=$(sanitize_var "$RTB1_ID") || check_error 1 "Invalid RTB1_ID returned" CREATED_RESOURCES+=("Route Table for VPC1: $RTB1_ID") @@ -317,7 +336,7 @@ echo "Route table associated with subnet in VPC1" # Create a route table for VPC2 echo "Creating route table for VPC2..." -RTB2_ID=$(log_cmd "aws ec2 create-route-table --region '$AWS_REGION' --vpc-id '$VPC2_ID' --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=VPC2-RouteTable}]' --query 'RouteTable.RouteTableId' --output text") +RTB2_ID=$(log_cmd "aws ec2 create-route-table --region '$AWS_REGION' --vpc-id '$VPC2_ID' --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=VPC2-RouteTable},{Key=project,Value=doc-smith},{Key=tutorial,Value=vpc-peering}]' --query 'RouteTable.RouteTableId' --output text") check_error $? "Failed to create route table for VPC2" RTB2_ID=$(sanitize_var "$RTB2_ID") || check_error 1 "Invalid RTB2_ID returned" CREATED_RESOURCES+=("Route Table for VPC2: $RTB2_ID") @@ -363,7 +382,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[@]+"${CREATED_RESOURCES[@]}"}"; do +for resource in "${CREATED_RESOURCES[@]}"; do echo "- $resource" done echo "==============================================" diff --git a/tuts/016-opensearch-service-gs/opensearch-service-gs.sh b/tuts/016-opensearch-service-gs/opensearch-service-gs.sh index bc6e4e2..e75a2a5 100644 --- a/tuts/016-opensearch-service-gs/opensearch-service-gs.sh +++ b/tuts/016-opensearch-service-gs/opensearch-service-gs.sh @@ -129,7 +129,7 @@ CREATE_OUTPUT=$(aws opensearch create-domain \ --domain-endpoint-options "EnforceHTTPS=true,TLSSecurityPolicy=Policy-Min-TLS-1-2-2019-07" \ --advanced-security-options "Enabled=true,InternalUserDatabaseEnabled=true,MasterUserOptions={MasterUserName=$MASTER_USER,MasterUserPassword=$MASTER_PASSWORD}" \ --access-policies "$ACCESS_POLICY" \ - --tags "Key=Environment,Value=Tutorial" "Key=Purpose,Value=OpenSearchGettingStarted" 2>&1) + --tags "Key=Environment,Value=Tutorial" "Key=Purpose,Value=OpenSearchGettingStarted" "Key=project,Value=doc-smith" "Key=tutorial,Value=opensearch-service-gs" 2>&1) # Check if domain creation was successful if [[ $? -ne 0 ]]; then diff --git a/tuts/018-ecs-ec2/ecs-ec2-getting-started.sh b/tuts/018-ecs-ec2/ecs-ec2-getting-started.sh index b87f109..f8fea40 100644 --- a/tuts/018-ecs-ec2/ecs-ec2-getting-started.sh +++ b/tuts/018-ecs-ec2/ecs-ec2-getting-started.sh @@ -18,6 +18,10 @@ SERVICE_NAME="nginx-service-$(openssl rand -hex 4)" KEY_PAIR_NAME="ecs-tutorial-key-$(openssl rand -hex 4)" SECURITY_GROUP_NAME="ecs-tutorial-sg-$(openssl rand -hex 4)" +# Tags +PROJECT_TAG="doc-smith" +TUTORIAL_TAG="ecs-ec2" + # Get current AWS region dynamically AWS_REGION=$(aws configure get region || echo "us-east-1") @@ -207,7 +211,7 @@ check_prerequisites() { create_cluster() { log "Creating ECS cluster: $CLUSTER_NAME" - CLUSTER_ARN=$(aws ecs create-cluster --cluster-name "$CLUSTER_NAME" --query 'cluster.clusterArn' --output text) + CLUSTER_ARN=$(aws ecs create-cluster --cluster-name "$CLUSTER_NAME" --tags key=project,value=$PROJECT_TAG key=tutorial,value=$TUTORIAL_TAG --query 'cluster.clusterArn' --output text) if [[ -z "$CLUSTER_ARN" ]]; then log "ERROR: Failed to create cluster" @@ -230,6 +234,8 @@ create_key_pair() { log "Created key pair: $KEY_PAIR_NAME" CREATED_RESOURCES+=("EC2 Key Pair: $KEY_PAIR_NAME") + + aws ec2 create-tags --resources "$KEY_PAIR_NAME" --tags Key=project,Value=$PROJECT_TAG Key=tutorial,Value=$TUTORIAL_TAG 2>>"$LOG_FILE" || log "WARNING: Failed to tag key pair" } # Function to create security group @@ -240,6 +246,7 @@ create_security_group() { --group-name "$SECURITY_GROUP_NAME" \ --description "ECS tutorial security group" \ --vpc-id "$DEFAULT_VPC" \ + --tag-specifications "ResourceType=security-group,Tags=[{Key=project,Value=$PROJECT_TAG},{Key=tutorial,Value=$TUTORIAL_TAG}]" \ --query 'GroupId' --output text) if [[ -z "$SECURITY_GROUP_ID" ]]; then @@ -312,6 +319,8 @@ EOF --role-name ecsInstanceRole \ --assume-role-policy-document file://ecs-instance-trust-policy.json + aws iam tag-role --role-name ecsInstanceRole --tags Key=project,Value=$PROJECT_TAG Key=tutorial,Value=$TUTORIAL_TAG + # Attach managed policy aws iam attach-role-policy \ --role-name ecsInstanceRole \ @@ -363,7 +372,7 @@ EOF --subnet-id "$DEFAULT_SUBNET" \ --iam-instance-profile Name=ecsInstanceRole \ --user-data file://ecs-user-data.sh \ - --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=ecs-tutorial-instance}]" \ + --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=ecs-tutorial-instance},{Key=project,Value=$PROJECT_TAG},{Key=tutorial,Value=$TUTORIAL_TAG}]" \ --monitoring Enabled=false \ --metadata-options HttpTokens=required,HttpPutResponseHopLimit=1 \ --query 'Instances[0].InstanceId' --output text) @@ -441,13 +450,25 @@ register_task_definition() { } ], "requiresCompatibilities": ["EC2"], - "networkMode": "bridge" + "networkMode": "bridge", + "tags": [ + { + "key": "project", + "value": "PROJECT_TAG_PLACEHOLDER" + }, + { + "key": "tutorial", + "value": "TUTORIAL_TAG_PLACEHOLDER" + } + ] } EOF # Replace placeholders securely sed -i "s|TASK_FAMILY_PLACEHOLDER|$TASK_FAMILY|g" task-definition.json sed -i "s|REGION_PLACEHOLDER|$AWS_REGION|g" task-definition.json + sed -i "s|PROJECT_TAG_PLACEHOLDER|$PROJECT_TAG|g" task-definition.json + sed -i "s|TUTORIAL_TAG_PLACEHOLDER|$TUTORIAL_TAG|g" task-definition.json # FIXED: Validate JSON before registration if ! jq empty task-definition.json 2>/dev/null; then @@ -482,6 +503,7 @@ create_service() { --service-name "$SERVICE_NAME" \ --task-definition "$TASK_FAMILY" \ --desired-count 1 \ + --tags key=project,value=$PROJECT_TAG key=tutorial,value=$TUTORIAL_TAG \ --query 'service.serviceArn' --output text) if [[ -z "$SERVICE_ARN" ]]; then diff --git a/tuts/019-lambda-gettingstarted/lambda-gettingstarted.sh b/tuts/019-lambda-gettingstarted/lambda-gettingstarted.sh index 2f3aded..bfe118f 100644 --- a/tuts/019-lambda-gettingstarted/lambda-gettingstarted.sh +++ b/tuts/019-lambda-gettingstarted/lambda-gettingstarted.sh @@ -265,6 +265,10 @@ ROLE_ARN="$ROLE_OUTPUT" CREATED_RESOURCES+=("iam-role:${ROLE_NAME}") echo "Role ARN: ${ROLE_ARN}" +aws iam tag-role \ + --role-name "$ROLE_NAME" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=lambda-gettingstarted + echo "" echo "Attaching AWSLambdaBasicExecutionRole policy..." aws iam attach-role-policy \ @@ -313,6 +317,7 @@ CREATE_OUTPUT=$(aws lambda create-function \ --handler "$HANDLER" \ --architectures x86_64 \ --zip-file "fileb://${TEMP_DIR}/function.zip" \ + --tags project=doc-smith,tutorial=lambda-gettingstarted \ --query '[FunctionName, FunctionArn, Runtime, State]' \ --output text 2>&1) @@ -419,6 +424,10 @@ fi CREATED_RESOURCES+=("log-group:${LOG_GROUP_NAME}") +aws logs tag-log-group \ + --log-group-name "$LOG_GROUP_NAME" \ + --tags project=doc-smith,tutorial=lambda-gettingstarted + ############################################################################### # Summary and cleanup ############################################################################### diff --git a/tuts/020-ebs-gs-volumes/ebs-gs-volumes.sh b/tuts/020-ebs-gs-volumes/ebs-gs-volumes.sh old mode 100755 new mode 100644 index 1834c81..b3c5fc7 --- a/tuts/020-ebs-gs-volumes/ebs-gs-volumes.sh +++ b/tuts/020-ebs-gs-volumes/ebs-gs-volumes.sh @@ -133,7 +133,7 @@ VOLUME_ID=$(aws ec2 create-volume \ --volume-type gp3 \ --size 10 \ --availability-zone "$AZ" \ - --tag-specifications 'ResourceType=volume,Tags=[{Key=Name,Value=EBSTutorialVolume},{Key=Purpose,Value=Tutorial}]' \ + --tag-specifications 'ResourceType=volume,Tags=[{Key=Name,Value=EBSTutorialVolume},{Key=Purpose,Value=Tutorial},{Key=project,Value=doc-smith},{Key=tutorial,Value=ebs-gs-volumes}]' \ --query 'VolumeId' \ --output text) @@ -233,6 +233,7 @@ if [[ "$ATTACH_CHOICE" =~ ^[Yy]$ ]]; then --group-name "$SG_NAME" \ --description "Security group for EBS tutorial" \ --vpc-id "$DEFAULT_VPC_ID" \ + --tag-specifications 'ResourceType=security-group,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=ebs-gs-volumes}]' \ --query "GroupId" \ --output text) @@ -258,7 +259,7 @@ if [[ "$ATTACH_CHOICE" =~ ^[Yy]$ ]]; then --instance-type "$INSTANCE_TYPE" \ --subnet-id "$SUBNET_ID" \ --security-group-ids "$SG_ID" \ - --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=EBSTutorialInstance},{Key=Purpose,Value=Tutorial}]' \ + --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=EBSTutorialInstance},{Key=Purpose,Value=Tutorial},{Key=project,Value=doc-smith},{Key=tutorial,Value=ebs-gs-volumes}]' \ --query "Instances[0].InstanceId" \ --output text) @@ -373,4 +374,4 @@ fi echo "" echo "Script completed at $(date)" -echo "==============================================" +echo "==============================================" \ No newline at end of file diff --git a/tuts/021-cloudformation-gs/cloudformation-gs.sh b/tuts/021-cloudformation-gs/cloudformation-gs.sh index 9270be9..61989b3 100644 --- a/tuts/021-cloudformation-gs/cloudformation-gs.sh +++ b/tuts/021-cloudformation-gs/cloudformation-gs.sh @@ -284,6 +284,7 @@ if ! CREATE_RESULT=$(aws cloudformation create-stack \ ParameterKey=InstanceType,ParameterValue=t2.micro \ ParameterKey=MyIP,ParameterValue="$MY_IP_CIDR" \ --capabilities CAPABILITY_IAM \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=cloudformation-gs \ --region "$AWS_REGION" \ --output text 2>&1); then handle_error "Stack creation failed: $CREATE_RESULT" diff --git a/tuts/022-ebs-intermediate/ebs-intermediate.sh b/tuts/022-ebs-intermediate/ebs-intermediate.sh index 2e7fe62..9efd0ca 100644 --- a/tuts/022-ebs-intermediate/ebs-intermediate.sh +++ b/tuts/022-ebs-intermediate/ebs-intermediate.sh @@ -192,7 +192,7 @@ fi # Step 2: Create a test volume for snapshot with minimal size for cost optimization echo "Step 2: Creating a test volume (1 GiB for testing)..." -VOLUME_ID=$(aws ec2 create-volume --region "$AWS_REGION" --availability-zone "$AVAILABILITY_ZONE" --size 1 --volume-type gp3 --iops 3000 --throughput 125 --tag-specifications 'ResourceType=volume,Tags=[{Key=Name,Value=ebs-tutorial-volume},{Key=ManagedBy,Value=ebs-intermediate-script}]' --query 'VolumeId' --output text) +VOLUME_ID=$(aws ec2 create-volume --region "$AWS_REGION" --availability-zone "$AVAILABILITY_ZONE" --size 1 --volume-type gp3 --iops 3000 --throughput 125 --tag-specifications 'ResourceType=volume,Tags=[{Key=Name,Value=ebs-tutorial-volume},{Key=ManagedBy,Value=ebs-intermediate-script},{Key=project,Value=doc-smith},{Key=tutorial,Value=ebs-intermediate}]' --query 'VolumeId' --output text) check_status "Creating test volume" # Security: Validate volume ID @@ -209,7 +209,7 @@ check_status "Waiting for volume" # Step 3: Create a snapshot of the volume echo "Step 3: Creating snapshot of the volume..." -SNAPSHOT_ID=$(aws ec2 create-snapshot --region "$AWS_REGION" --volume-id "$VOLUME_ID" --description "Snapshot for EBS tutorial - $(date +%Y-%m-%d)" --tag-specifications 'ResourceType=snapshot,Tags=[{Key=Name,Value=ebs-tutorial-snapshot},{Key=ManagedBy,Value=ebs-intermediate-script}]' --query 'SnapshotId' --output text) +SNAPSHOT_ID=$(aws ec2 create-snapshot --region "$AWS_REGION" --volume-id "$VOLUME_ID" --description "Snapshot for EBS tutorial - $(date +%Y-%m-%d)" --tag-specifications 'ResourceType=snapshot,Tags=[{Key=Name,Value=ebs-tutorial-snapshot},{Key=ManagedBy,Value=ebs-intermediate-script},{Key=project,Value=doc-smith},{Key=tutorial,Value=ebs-intermediate}]' --query 'SnapshotId' --output text) check_status "Creating snapshot" # Security: Validate snapshot ID @@ -227,7 +227,7 @@ echo "Snapshot completed." # Step 4: Create a new volume from the snapshot echo "Step 4: Creating a new volume from the snapshot..." -NEW_VOLUME_ID=$(aws ec2 create-volume --region "$AWS_REGION" --snapshot-id "$SNAPSHOT_ID" --availability-zone "$AVAILABILITY_ZONE" --volume-type gp3 --iops 3000 --throughput 125 --tag-specifications 'ResourceType=volume,Tags=[{Key=Name,Value=ebs-tutorial-volume-from-snapshot},{Key=ManagedBy,Value=ebs-intermediate-script}]' --query 'VolumeId' --output text) +NEW_VOLUME_ID=$(aws ec2 create-volume --region "$AWS_REGION" --snapshot-id "$SNAPSHOT_ID" --availability-zone "$AVAILABILITY_ZONE" --volume-type gp3 --iops 3000 --throughput 125 --tag-specifications 'ResourceType=volume,Tags=[{Key=Name,Value=ebs-tutorial-volume-from-snapshot},{Key=ManagedBy,Value=ebs-intermediate-script},{Key=project,Value=doc-smith},{Key=tutorial,Value=ebs-intermediate}]' --query 'VolumeId' --output text) check_status "Creating new volume from snapshot" # Security: Validate new volume ID diff --git a/tuts/024-glue-gs/glue-gs.sh b/tuts/024-glue-gs/glue-gs.sh index 4e660bf..eb523c3 100644 --- a/tuts/024-glue-gs/glue-gs.sh +++ b/tuts/024-glue-gs/glue-gs.sh @@ -137,6 +137,13 @@ create_database() { exit 1 fi + ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text) + aws glue tag-resource \ + --resource-arn "arn:aws:glue:${AWS_REGION}:${ACCOUNT_ID}:database/${DB_NAME}" \ + --tags-to-add Key=project,Value=doc-smith Key=tutorial,Value=glue-gs \ + --region "$AWS_REGION" \ + 2>/dev/null || true + DATABASE_CREATED=true CREATED_RESOURCES+=("database:$DB_NAME") echo "Database $DB_NAME created successfully." @@ -223,6 +230,7 @@ create_table() { prepare_table_input + local TABLE_ARN if ! aws glue create-table \ --database-name "$DB_NAME" \ --table-input "file://${TABLE_INPUT_FILE}" \ @@ -233,6 +241,12 @@ create_table() { exit 1 fi + aws glue tag-resource \ + --resource-arn "arn:aws:glue:${AWS_REGION}:${ACCOUNT_ID}:table/${DB_NAME}/${TABLE_NAME}" \ + --tags-to-add Key=project,Value=doc-smith Key=tutorial,Value=glue-gs \ + --region "$AWS_REGION" \ + 2>/dev/null || true + CREATED_RESOURCES+=("table:$TABLE_NAME") echo "Table $TABLE_NAME created successfully." } diff --git a/tuts/025-documentdb-gs/documentdb-gs.sh b/tuts/025-documentdb-gs/documentdb-gs.sh index d98a616..48d0524 100644 --- a/tuts/025-documentdb-gs/documentdb-gs.sh +++ b/tuts/025-documentdb-gs/documentdb-gs.sh @@ -233,6 +233,7 @@ SECRET_OUTPUT=$(aws secretsmanager create-secret \ --name "$SECRET_NAME" \ --description "DocumentDB master password for ${CLUSTER_ID}" \ --secret-string file://"$TEMP_PASS_FILE" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=documentdb-gs \ --output text --query "ARN" 2>&1) # Securely clear password from memory @@ -322,6 +323,7 @@ SUBNET_GROUP_OUTPUT=$(aws docdb create-db-subnet-group \ --db-subnet-group-name "$SUBNET_GROUP_NAME" \ --db-subnet-group-description "Subnet group for DocumentDB getting started" \ --subnet-ids $SUBNET_IDS \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=documentdb-gs \ --query "DBSubnetGroup.DBSubnetGroupName" --output text 2>&1) if echo "$SUBNET_GROUP_OUTPUT" | grep -iq "error"; then @@ -355,6 +357,7 @@ CLUSTER_OUTPUT=$(aws docdb create-db-cluster \ --kms-key-id "alias/aws/docdb" \ --no-deletion-protection \ --enable-cloudwatch-logs-exports '["audit","error","general","slowquery"]' \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=documentdb-gs \ --query "DBCluster.DBClusterIdentifier" --output text 2>&1) # Clear password immediately after use @@ -385,6 +388,7 @@ INSTANCE_OUTPUT=$(aws docdb create-db-instance \ --db-instance-class "$INSTANCE_CLASS" \ --db-cluster-identifier "$CLUSTER_ID" \ --engine docdb \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=documentdb-gs \ --query "DBInstance.DBInstanceIdentifier" --output text 2>&1) if echo "$INSTANCE_OUTPUT" | grep -iq "error"; then diff --git a/tuts/027-connect-gs/connect-gs.sh b/tuts/027-connect-gs/connect-gs.sh index 3f29b18..9dd0b15 100644 --- a/tuts/027-connect-gs/connect-gs.sh +++ b/tuts/027-connect-gs/connect-gs.sh @@ -176,6 +176,11 @@ fi echo "Successfully created Amazon Connect instance with ID: $INSTANCE_ID" | tee -a "$LOG_FILE" echo "Instance ARN: $INSTANCE_ARN" | tee -a "$LOG_FILE" +# Tag the instance +aws connect tag-resource --resource-arn "$INSTANCE_ARN" \ + --tags project=doc-smith,tutorial=connect-gs \ + --region "$AWS_REGION" + # Wait for the instance to be fully created and active if ! wait_for_instance "$INSTANCE_ID"; then echo "ERROR: Instance did not become fully active within the timeout period" | tee -a "$LOG_FILE" diff --git a/tuts/028-sagemaker-featurestore/sagemaker-featurestore.sh b/tuts/028-sagemaker-featurestore/sagemaker-featurestore.sh old mode 100755 new mode 100644 index 95bee00..5edf892 --- a/tuts/028-sagemaker-featurestore/sagemaker-featurestore.sh +++ b/tuts/028-sagemaker-featurestore/sagemaker-featurestore.sh @@ -121,6 +121,10 @@ create_sagemaker_role() { echo "Role created successfully" >&2 CREATED_RESOURCES+=("IAMRole:$role_name") + # Tag the role + echo "Tagging IAM role..." >&2 + aws iam tag-role --role-name "$role_name" --tags Key=project,Value=doc-smith Key=tutorial,Value=sagemaker-featurestore 2>&1 + # Attach necessary policies echo "Attaching policies to role..." >&2 @@ -235,6 +239,10 @@ fi echo "$BUCKET_RESULT" CREATED_RESOURCES+=("S3Bucket:$S3_BUCKET_NAME") +# Tag the S3 bucket +echo "Tagging S3 bucket: $S3_BUCKET_NAME" +aws s3api put-bucket-tagging --bucket "$S3_BUCKET_NAME" --tagging 'TagSet=[{Key=project,Value=doc-smith},{Key=tutorial,Value=sagemaker-featurestore}]' 2>&1 + # Block public access to the bucket BLOCK_RESULT=$(aws s3api put-public-access-block \ --bucket "$S3_BUCKET_NAME" \ @@ -275,7 +283,8 @@ CUSTOMERS_RESPONSE=$(aws sagemaker create-feature-group \ }, "DisableGlueTableCreation": false }' \ - --role-arn "$ROLE_ARN" 2>&1) + --role-arn "$ROLE_ARN" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=sagemaker-featurestore 2>&1) if echo "$CUSTOMERS_RESPONSE" | grep -i "error" > /dev/null; then echo "Failed to create customers feature group: $CUSTOMERS_RESPONSE" @@ -310,7 +319,8 @@ ORDERS_RESPONSE=$(aws sagemaker create-feature-group \ }, "DisableGlueTableCreation": false }' \ - --role-arn "$ROLE_ARN" 2>&1) + --role-arn "$ROLE_ARN" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=sagemaker-featurestore 2>&1) if echo "$ORDERS_RESPONSE" | grep -i "error" > /dev/null; then echo "Failed to create orders feature group: $ORDERS_RESPONSE" @@ -513,4 +523,4 @@ else fi fi -echo "Script completed at $(date)" +echo "Script completed at $(date)" \ No newline at end of file diff --git a/tuts/030-marketplace-buyer-gs/marketplace-buyer-getting-started.sh b/tuts/030-marketplace-buyer-gs/marketplace-buyer-getting-started.sh index a03456d..d08dcbe 100644 --- a/tuts/030-marketplace-buyer-gs/marketplace-buyer-getting-started.sh +++ b/tuts/030-marketplace-buyer-gs/marketplace-buyer-getting-started.sh @@ -256,6 +256,7 @@ INSTANCE_OUTPUT=$(aws ec2 run-instances \ --count 1 \ --region "$AWS_REGION" \ --monitoring Enabled=false \ + --tag-specifications 'ResourceType=instance,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=marketplace-buyer-gs}]' \ --output json) || { echo "ERROR: Failed to launch instance" >&2 exit 1 diff --git a/tuts/031-cloudwatch-dynamicdash/cloudwatch-dynamicdash.sh b/tuts/031-cloudwatch-dynamicdash/cloudwatch-dynamicdash.sh index 52e3aaf..4006533 100644 --- a/tuts/031-cloudwatch-dynamicdash/cloudwatch-dynamicdash.sh +++ b/tuts/031-cloudwatch-dynamicdash/cloudwatch-dynamicdash.sh @@ -11,7 +11,7 @@ exec > >(tee -a "$LOG_FILE") 2>&1 echo "$(date): Starting CloudWatch dashboard creation script" # Security: Set strict error handling -set -euo pipefail +set -uo pipefail trap 'handle_error "Script failed at line $LINENO"' ERR # Function to handle errors @@ -102,6 +102,7 @@ EOF if ! ROLE_ARN=$(aws iam create-role \ --role-name "$ROLE_NAME" \ --assume-role-policy-document "$TRUST_POLICY" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=cloudwatch-dynamicdash \ --query "Role.Arn" \ --output text 2>/dev/null); then handle_error "Failed to create IAM role for Lambda function" @@ -126,6 +127,7 @@ EOF --role "$ROLE_ARN" \ --handler index.handler \ --zip-file fileb://function.zip \ + --tags project=doc-smith,tutorial=cloudwatch-dynamicdash \ --region "$REGION" > /dev/null 2>&1; then aws iam detach-role-policy \ --role-name "$ROLE_NAME" \ @@ -247,8 +249,9 @@ if ! jq empty "$DASHBOARD_JSON" 2>/dev/null; then fi # Create the dashboard using the JSON file +DASHBOARD_NAME="LambdaMetricsDashboard-$(date +%s)" if ! DASHBOARD_RESULT=$(aws cloudwatch put-dashboard \ - --dashboard-name "LambdaMetricsDashboard-$(date +%s)" \ + --dashboard-name "$DASHBOARD_NAME" \ --dashboard-body file://"$DASHBOARD_JSON" \ --region "$REGION" 2>&1); then # If we created resources, clean them up @@ -271,9 +274,6 @@ else echo "Dashboard created successfully!" fi -# Extract dashboard name from result -DASHBOARD_NAME=$(echo "$DASHBOARD_RESULT" | grep -oP '"DashboardName"\s*:\s*"\K[^"]+' || echo "LambdaMetricsDashboard") - # Verify the dashboard was created echo "Verifying dashboard creation..." if ! DASHBOARD_INFO=$(aws cloudwatch get-dashboard --dashboard-name "$DASHBOARD_NAME" --region "$REGION" 2>&1); then diff --git a/tuts/032-cloudwatch-streams/cloudwatch-streams.sh b/tuts/032-cloudwatch-streams/cloudwatch-streams.sh index 14cf071..4a4e171 100644 --- a/tuts/032-cloudwatch-streams/cloudwatch-streams.sh +++ b/tuts/032-cloudwatch-streams/cloudwatch-streams.sh @@ -156,7 +156,7 @@ if ! python3 -m json.tool trust-policy.json > /dev/null 2>&1; then exit 1 fi -ROLE_OUTPUT=$(log_cmd "aws iam create-role --role-name '$ROLE_NAME' --assume-role-policy-document file://trust-policy.json --output json") +ROLE_OUTPUT=$(log_cmd "aws iam create-role --role-name '$ROLE_NAME' --assume-role-policy-document file://trust-policy.json --tags Key=project,Value=doc-smith Key=tutorial,Value=cloudwatch-streams --output json") check_error "$ROLE_OUTPUT" $? "Failed to create IAM role" ROLE_ARN=$(echo "$ROLE_OUTPUT" | python3 -c "import sys, json; print(json.load(sys.stdin)['Role']['Arn'])" 2>/dev/null) @@ -210,12 +210,12 @@ fi # Create first Lambda function echo "Creating first Lambda function: $LAMBDA_FUNCTION1..." | tee -a "$LOG_FILE" -LAMBDA1_OUTPUT=$(log_cmd "aws lambda create-function --function-name '$LAMBDA_FUNCTION1' --runtime python3.11 --role '$ROLE_ARN' --handler lambda_function.handler --zip-file fileb://lambda_function.zip --timeout 30 --memory-size 128") +LAMBDA1_OUTPUT=$(log_cmd "aws lambda create-function --function-name '$LAMBDA_FUNCTION1' --runtime python3.11 --role '$ROLE_ARN' --handler lambda_function.handler --zip-file fileb://lambda_function.zip --timeout 30 --memory-size 128 --tags project=doc-smith,tutorial=cloudwatch-streams") check_error "$LAMBDA1_OUTPUT" $? "Failed to create first Lambda function" # Create second Lambda function echo "Creating second Lambda function: $LAMBDA_FUNCTION2..." | tee -a "$LOG_FILE" -LAMBDA2_OUTPUT=$(log_cmd "aws lambda create-function --function-name '$LAMBDA_FUNCTION2' --runtime python3.11 --role '$ROLE_ARN' --handler lambda_function.handler --zip-file fileb://lambda_function.zip --timeout 30 --memory-size 128") +LAMBDA2_OUTPUT=$(log_cmd "aws lambda create-function --function-name '$LAMBDA_FUNCTION2' --runtime python3.11 --role '$ROLE_ARN' --handler lambda_function.handler --zip-file fileb://lambda_function.zip --timeout 30 --memory-size 128 --tags project=doc-smith,tutorial=cloudwatch-streams") check_error "$LAMBDA2_OUTPUT" $? "Failed to create second Lambda function" # Invoke Lambda functions to generate some metrics diff --git a/tuts/034-eks-gs/eks-gs.sh b/tuts/034-eks-gs/eks-gs.sh index d55964c..97b0275 100755 --- a/tuts/034-eks-gs/eks-gs.sh +++ b/tuts/034-eks-gs/eks-gs.sh @@ -153,7 +153,8 @@ echo "Creating CloudFormation stack: $STACK_NAME" # Create the CloudFormation stack CF_CREATE_OUTPUT=$(aws cloudformation create-stack \ --stack-name "$STACK_NAME" \ - --template-url https://s3.us-west-2.amazonaws.com/amazon-eks/cloudformation/2020-10-29/amazon-eks-vpc-private-subnets.yaml) + --template-url https://s3.us-west-2.amazonaws.com/amazon-eks/cloudformation/2020-10-29/amazon-eks-vpc-private-subnets.yaml \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=eks-gs) check_command "$CF_CREATE_OUTPUT" CREATED_RESOURCES+=("CloudFormation Stack: $STACK_NAME") @@ -190,6 +191,8 @@ CLUSTER_ROLE_OUTPUT=$(aws iam create-role \ --role-name "$CLUSTER_ROLE_NAME" \ --assume-role-policy-document file://"eks-cluster-role-trust-policy.json") check_command "$CLUSTER_ROLE_OUTPUT" +aws iam tag-role --role-name "$CLUSTER_ROLE_NAME" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=eks-gs CREATED_RESOURCES+=("IAM Role: $CLUSTER_ROLE_NAME") # Attach policy to cluster role @@ -222,6 +225,8 @@ NODE_ROLE_OUTPUT=$(aws iam create-role \ --role-name "$NODE_ROLE_NAME" \ --assume-role-policy-document file://"node-role-trust-policy.json") check_command "$NODE_ROLE_OUTPUT" +aws iam tag-role --role-name "$NODE_ROLE_NAME" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=eks-gs CREATED_RESOURCES+=("IAM Role: $NODE_ROLE_NAME") # Attach policies to node role @@ -283,7 +288,8 @@ echo "Creating EKS cluster (this will take 10-15 minutes)..." CREATE_CLUSTER_OUTPUT=$(aws eks create-cluster \ --name "$CLUSTER_NAME" \ --role-arn "$CLUSTER_ROLE_ARN" \ - --resources-vpc-config subnetIds="$SUBNET_IDS",securityGroupIds="$SECURITY_GROUP_ID") + --resources-vpc-config subnetIds="$SUBNET_IDS",securityGroupIds="$SECURITY_GROUP_ID" \ + --tags Key=project,Value=doc-smith,Key=tutorial,Value=eks-gs) check_command "$CREATE_CLUSTER_OUTPUT" CREATED_RESOURCES+=("EKS Cluster: $CLUSTER_NAME") @@ -335,7 +341,8 @@ CREATE_NODEGROUP_OUTPUT=$(aws eks create-nodegroup \ --cluster-name "$CLUSTER_NAME" \ --nodegroup-name "$NODEGROUP_NAME" \ --node-role "$NODE_ROLE_ARN" \ - --subnets "${SUBNET_IDS_ARRAY[@]}") + --subnets "${SUBNET_IDS_ARRAY[@]}" \ + --tags Key=project,Value=doc-smith,Key=tutorial,Value=eks-gs) check_command "$CREATE_NODEGROUP_OUTPUT" CREATED_RESOURCES+=("EKS Node Group: $NODEGROUP_NAME") diff --git a/tuts/035-workspaces-personal/workspaces-personal.sh b/tuts/035-workspaces-personal/workspaces-personal.sh index a23c3ce..f346b5e 100755 --- a/tuts/035-workspaces-personal/workspaces-personal.sh +++ b/tuts/035-workspaces-personal/workspaces-personal.sh @@ -309,9 +309,9 @@ WORKSPACE_JSON="{\"DirectoryId\":\"$DIRECTORY_ID\",\"UserName\":\"$USERNAME\",\" # Add tags if specified if [ -n "$TAGS_JSON" ]; then USER_TAGS=$(echo "$TAGS_JSON" | sed 's/^\[//;s/\]$//') - WORKSPACE_JSON="$WORKSPACE_JSON,\"Tags\":[$USER_TAGS,{\"Key\":\"project\",\"Value\":\"doc-smith\"},{\"Key\":\"tutorial\",\"Value\":\"035-workspaces-personal\"}]" + WORKSPACE_JSON="$WORKSPACE_JSON,\"Tags\":[$USER_TAGS,{\"Key\":\"project\",\"Value\":\"doc-smith\"},{\"Key\":\"tutorial\",\"Value\":\"workspaces-personal\"}]" else - WORKSPACE_JSON="$WORKSPACE_JSON,\"Tags\":[{\"Key\":\"project\",\"Value\":\"doc-smith\"},{\"Key\":\"tutorial\",\"Value\":\"035-workspaces-personal\"}]" + WORKSPACE_JSON="$WORKSPACE_JSON,\"Tags\":[{\"Key\":\"project\",\"Value\":\"doc-smith\"},{\"Key\":\"tutorial\",\"Value\":\"workspaces-personal\"}]" fi # Close the JSON object diff --git a/tuts/036-rds-gs/rds-gs.sh b/tuts/036-rds-gs/rds-gs.sh old mode 100755 new mode 100644 index f294ef7..5bf2f6e --- a/tuts/036-rds-gs/rds-gs.sh +++ b/tuts/036-rds-gs/rds-gs.sh @@ -93,7 +93,8 @@ echo "Step 3: Creating security group for RDS..." SG_OUTPUT=$(aws ec2 create-security-group \ --group-name "$SECURITY_GROUP_NAME" \ --description "Security group for RDS database access" \ - --vpc-id "$VPC_ID") + --vpc-id "$VPC_ID" \ + --tag-specifications 'ResourceType=security-group,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=rds-gs}]') check_error "$SG_OUTPUT" "aws ec2 create-security-group" SECURITY_GROUP_ID=$(echo "$SG_OUTPUT" | grep -o '"GroupId": "[^"]*' | cut -d'"' -f4) @@ -125,7 +126,8 @@ SUBNET2=${SUBNET_IDS[1]} SUBNET_GROUP_OUTPUT=$(aws rds create-db-subnet-group \ --db-subnet-group-name "$DB_SUBNET_GROUP_NAME" \ --db-subnet-group-description "Subnet group for RDS tutorial" \ - --subnet-ids "$SUBNET1" "$SUBNET2") + --subnet-ids "$SUBNET1" "$SUBNET2" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=rds-gs) check_error "$SUBNET_GROUP_OUTPUT" "aws rds create-db-subnet-group" CREATED_SUBNET_GROUP="true" @@ -138,7 +140,8 @@ SECRET_NAME="rds-db-credentials-${RANDOM_ID}" SECRET_OUTPUT=$(aws secretsmanager create-secret \ --name "$SECRET_NAME" \ --description "RDS DB credentials for $DB_INSTANCE_ID" \ - --secret-string '{"username":"adminuser","password":"'"$(openssl rand -base64 16)"'"}') + --secret-string '{"username":"adminuser","password":"'"$(openssl rand -base64 16)"'"}' \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=rds-gs) check_error "$SECRET_OUTPUT" "aws secretsmanager create-secret" SECRET_ARN=$(echo "$SECRET_OUTPUT" | grep -o '"ARN": "[^"]*' | cut -d'"' -f4) @@ -169,7 +172,8 @@ DB_OUTPUT=$(aws rds create-db-instance \ --db-subnet-group-name "$DB_SUBNET_GROUP_NAME" \ --backup-retention-period 7 \ --no-publicly-accessible \ - --no-multi-az) + --no-multi-az \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=rds-gs) check_error "$DB_OUTPUT" "aws rds create-db-instance" CREATED_RESOURCES+=("DB Instance: $DB_INSTANCE_ID") @@ -251,4 +255,4 @@ else echo "To clean up later, you'll need to delete these resources manually." fi -echo "Script completed successfully!" +echo "Script completed successfully!" \ No newline at end of file diff --git a/tuts/037-emr-gs/emr-gs.sh b/tuts/037-emr-gs/emr-gs.sh index 313b87b..9232184 100644 --- a/tuts/037-emr-gs/emr-gs.sh +++ b/tuts/037-emr-gs/emr-gs.sh @@ -96,6 +96,10 @@ echo "Using bucket name: $BUCKET_NAME" echo "Creating S3 bucket: $BUCKET_NAME" aws s3 mb "s3://$BUCKET_NAME" --region "${AWS_REGION:-us-east-1}" || handle_error "Failed to create S3 bucket" +# Tag the bucket +aws s3api put-bucket-tagging --bucket "$BUCKET_NAME" \ + --tagging 'TagSet=[{Key=project,Value=doc-smith},{Key=tutorial,Value=emr-gs}]' + # Enable bucket versioning for safety aws s3api put-bucket-versioning --bucket "$BUCKET_NAME" --versioning-configuration Status=Enabled || true @@ -204,7 +208,9 @@ if [ -z "$KEY_PAIRS" ]; then echo "No EC2 key pairs found. Creating a new key pair..." KEY_NAME="emr-tutorial-key-${RANDOM_ID}" KEY_NAME_FILE="${KEY_NAME}.pem" - aws ec2 create-key-pair --key-name "$KEY_NAME" --query "KeyMaterial" --output text > "$KEY_NAME_FILE" + aws ec2 create-key-pair --key-name "$KEY_NAME" \ + --tag-specifications 'ResourceType=key-pair,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=emr-gs}]' \ + --query "KeyMaterial" --output text > "$KEY_NAME_FILE" chmod 400 "$KEY_NAME_FILE" echo "Created new key pair: $KEY_NAME" else @@ -225,6 +231,7 @@ CLUSTER_RESPONSE=$(aws emr create-cluster \ --use-default-roles \ --log-uri "s3://$BUCKET_NAME/logs/" \ --ebs-root-volume-size 100 \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=emr-gs \ --security-configuration "EMR-Tutorial-SecurityConfig" 2>/dev/null || true) # Check for errors in the response diff --git a/tuts/038-redshift-serverless/redshift-serverless.sh b/tuts/038-redshift-serverless/redshift-serverless.sh index 93651d3..d007a8f 100755 --- a/tuts/038-redshift-serverless/redshift-serverless.sh +++ b/tuts/038-redshift-serverless/redshift-serverless.sh @@ -85,7 +85,8 @@ create_secret() { local secret_output=$(aws secretsmanager create-secret \ --name "$secret_name" \ --description "$description" \ - --secret-string "{\"username\":\"$username\",\"password\":\"$password\"}" 2>&1) + --secret-string "{\"username\":\"$username\",\"password\":\"$password\"}" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=redshift-serverless 2>&1) if echo "$secret_output" | grep -i "error\|exception\|fail" > /dev/null; then echo "ERROR: Failed to create secret: $secret_output" @@ -304,6 +305,8 @@ echo "Creating IAM role $ROLE_NAME..." ROLE_OUTPUT=$(aws iam create-role --role-name "$ROLE_NAME" --assume-role-policy-document file://redshift-trust-policy.json 2>&1) echo "$ROLE_OUTPUT" check_error "$ROLE_OUTPUT" "aws iam create-role" +aws iam tag-role --role-name "$ROLE_NAME" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=redshift-serverless CREATED_RESOURCES+=("IAM Role: $ROLE_NAME") # Attach S3 policy to the role @@ -322,7 +325,8 @@ NAMESPACE_OUTPUT=$(aws redshift-serverless create-namespace \ --namespace-name "$NAMESPACE_NAME" \ --admin-username "$ADMIN_USERNAME" \ --admin-user-password "$ADMIN_PASSWORD" \ - --db-name "$DB_NAME" 2>&1) + --db-name "$DB_NAME" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=redshift-serverless 2>&1) echo "$NAMESPACE_OUTPUT" check_error "$NAMESPACE_OUTPUT" "aws redshift-serverless create-namespace" CREATED_RESOURCES+=("Redshift Serverless Namespace: $NAMESPACE_NAME") @@ -343,7 +347,8 @@ echo "Creating Redshift Serverless workgroup $WORKGROUP_NAME..." WORKGROUP_OUTPUT=$(aws redshift-serverless create-workgroup \ --workgroup-name "$WORKGROUP_NAME" \ --namespace-name "$NAMESPACE_NAME" \ - --base-capacity 8 2>&1) + --base-capacity 8 \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=redshift-serverless 2>&1) echo "$WORKGROUP_OUTPUT" check_error "$WORKGROUP_OUTPUT" "aws redshift-serverless create-workgroup" CREATED_RESOURCES+=("Redshift Serverless Workgroup: $WORKGROUP_NAME") diff --git a/tuts/039-redshift-provisioned/redshift-provisioned.sh b/tuts/039-redshift-provisioned/redshift-provisioned.sh index 73173a6..ce3d283 100644 --- a/tuts/039-redshift-provisioned/redshift-provisioned.sh +++ b/tuts/039-redshift-provisioned/redshift-provisioned.sh @@ -163,6 +163,7 @@ CLUSTER_RESULT=$(aws redshift create-cluster \ --db-name "$DB_NAME" \ --port 5439 \ --encrypted \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=redshift-provisioned \ 2>&1) || handle_error "Failed to create Redshift cluster" echo "$CLUSTER_RESULT" @@ -212,6 +213,10 @@ echo "$ROLE_RESULT" ROLE_ARN=$(aws iam get-role --role-name "$ROLE_NAME" --query 'Role.Arn' --output text) echo "Role ARN: $ROLE_ARN" +# Tag the IAM role +echo "Tagging IAM role: $ROLE_NAME" +aws iam tag-role --role-name "$ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=redshift-provisioned + # Create policy document for S3 access with principle of least privilege echo "Creating S3 access policy" cat > redshift-s3-policy.json << 'EOF' diff --git a/tuts/040-qbusiness-ica/qbusiness-ica.sh b/tuts/040-qbusiness-ica/qbusiness-ica.sh index 7c89b41..77d4b00 100755 --- a/tuts/040-qbusiness-ica/qbusiness-ica.sh +++ b/tuts/040-qbusiness-ica/qbusiness-ica.sh @@ -246,6 +246,8 @@ echo "Creating IAM role: $ROLE_NAME" | tee -a "$LOG_FILE" ROLE_RESULT=$(log_cmd "aws iam create-role --role-name \"$ROLE_NAME\" --assume-role-policy-document file://qbusiness-trust-policy.json --query 'Role.Arn' --output text") check_error $? ROLE_ARN="$ROLE_RESULT" +aws iam tag-role --role-name "$ROLE_NAME" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=qbusiness-ica CREATED_RESOURCES+=("IAM Role: $ROLE_ARN") # Create and attach the policy to the role @@ -316,6 +318,7 @@ APP_RESULT=$(log_cmd "aws qbusiness create-application --region $AWS_REGION \ --role-arn \"$ROLE_ARN\" \ --description \"Amazon Q Business application created via script\" \ --attachments-configuration '{\"attachmentsControlMode\":\"ENABLED\"}' \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=qbusiness-ica \ --query 'applicationId' --output text") check_error $? APP_ID="$APP_RESULT" diff --git a/tuts/042-qbusiness-anon/qbusiness-anon.sh b/tuts/042-qbusiness-anon/qbusiness-anon.sh index 55b4ecd..434c2b7 100644 --- a/tuts/042-qbusiness-anon/qbusiness-anon.sh +++ b/tuts/042-qbusiness-anon/qbusiness-anon.sh @@ -122,6 +122,8 @@ chmod 600 "$TRUST_POLICY_FILE" ROLE_NAME="QBusinessServiceRole-${RANDOM_ID}" ROLE_OUTPUT=$(aws iam create-role --role-name "$ROLE_NAME" --assume-role-policy-document "file://$TRUST_POLICY_FILE" --output json 2>&1) check_error "$ROLE_OUTPUT" $? "Failed to create IAM role" +aws iam tag-role --role-name "$ROLE_NAME" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=qbusiness-anon # Extract role ARN using jq for safer JSON parsing if command -v jq &> /dev/null; then @@ -153,6 +155,7 @@ APP_OUTPUT=$(aws qbusiness create-application \ --identity-type ANONYMOUS \ --role-arn "$ROLE_ARN" \ --description "Amazon Q Business application with anonymous access" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=qbusiness-anon \ --output json 2>&1) check_error "$APP_OUTPUT" $? "Failed to create Amazon Q Business application" diff --git a/tuts/043-amazon-mq-gs/amazon-mq-gs.sh b/tuts/043-amazon-mq-gs/amazon-mq-gs.sh index 1d13736..680cc34 100644 --- a/tuts/043-amazon-mq-gs/amazon-mq-gs.sh +++ b/tuts/043-amazon-mq-gs/amazon-mq-gs.sh @@ -111,7 +111,8 @@ fi SECRET_RESULT=$(aws secretsmanager create-secret \ --name "$SECRET_NAME" \ --description "Amazon MQ broker credentials for $BROKER_NAME" \ - --secret-string "$CREDENTIALS_JSON" 2>&1) + --secret-string "$CREDENTIALS_JSON" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-mq-gs 2>&1) # Check for errors if echo "$SECRET_RESULT" | grep -i "error" > /dev/null; then @@ -142,6 +143,7 @@ BROKER_RESULT=$(aws mq create-broker \ --publicly-accessible \ --auto-minor-version-upgrade \ --storage-type EFS \ + --tags project=doc-smith,tutorial=amazon-mq-gs \ 2>&1) # Check for errors diff --git a/tuts/044-amazon-managed-grafana-gs/amazon-managed-grafana-gs.sh b/tuts/044-amazon-managed-grafana-gs/amazon-managed-grafana-gs.sh index 53fa704..d3bf238 100755 --- a/tuts/044-amazon-managed-grafana-gs/amazon-managed-grafana-gs.sh +++ b/tuts/044-amazon-managed-grafana-gs/amazon-managed-grafana-gs.sh @@ -93,6 +93,8 @@ ROLE_OUTPUT=$(aws iam create-role \ --description "Role for Amazon Managed Grafana workspace") check_error "$ROLE_OUTPUT" "create-role" +aws iam tag-role --role-name "$ROLE_NAME" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-managed-grafana-gs echo "IAM role created successfully" # Extract role ARN @@ -149,7 +151,7 @@ WORKSPACE_OUTPUT=$(aws grafana create-workspace \ --workspace-role-arn "$ROLE_ARN" \ --workspace-data-sources "CLOUDWATCH" "PROMETHEUS" "XRAY" \ --grafana-version "10.4" \ - --tags Environment=Development) + --tags Environment=Development,project=doc-smith,tutorial=amazon-managed-grafana-gs) check_error "$WORKSPACE_OUTPUT" "create-workspace" diff --git a/tuts/045-aws-iam-identity-center-gs/aws-iam-identity-center-gs.sh b/tuts/045-aws-iam-identity-center-gs/aws-iam-identity-center-gs.sh index 65d152e..3cabfef 100755 --- a/tuts/045-aws-iam-identity-center-gs/aws-iam-identity-center-gs.sh +++ b/tuts/045-aws-iam-identity-center-gs/aws-iam-identity-center-gs.sh @@ -274,7 +274,7 @@ else echo "Creating a new IAM Identity Center instance..." | tee -a "$LOG_FILE" # Create IAM Identity Center instance (only works for non-organization management accounts) - INSTANCE_OUTPUT=$(log_cmd "aws sso-admin create-instance --name \"MyIdentityCenter\" --tags Key=Purpose,Value=Tutorial") + INSTANCE_OUTPUT=$(log_cmd "aws sso-admin create-instance --name \"MyIdentityCenter\" --tags Key=Purpose,Value=Tutorial Key=project,Value=doc-smith Key=tutorial,Value=aws-iam-identity-center-gs") check_error "$INSTANCE_OUTPUT" $? "Failed to create IAM Identity Center instance" # Wait for instance to be created and get instance ARN @@ -402,6 +402,9 @@ if [[ "$IS_ORGANIZATION_INSTANCE" == "true" ]]; then cleanup_resources exit 1 fi + aws sso-admin tag-resource --instance-arn "$INSTANCE_ARN" \ + --resource-arn "$PERMISSION_SET_ARN" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-iam-identity-center-gs track_resource "permission_set" "$INSTANCE_ARN,$PERMISSION_SET_ARN" echo "Permission set created successfully with ARN: $PERMISSION_SET_ARN" | tee -a "$LOG_FILE" 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 3fac178..409ade3 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 @@ -383,6 +383,8 @@ fi # Track the created policy track_resource "IAM_POLICY" "$POLICY_ARN" +aws iam tag-policy --policy-arn "$POLICY_ARN" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-systems-manager-gs echo "Created policy: $POLICY_ARN" | tee -a "$LOG_FILE" @@ -450,6 +452,8 @@ fi # Track the created role track_resource "IAM_ROLE" "$ROLE_NAME" +aws iam tag-role --role-name "$ROLE_NAME" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-systems-manager-gs echo "Created IAM role: $ROLE_NAME" | tee -a "$LOG_FILE" echo "Role ARN: $ROLE_ARN" | tee -a "$LOG_FILE" diff --git a/tuts/047-aws-network-firewall-gs/aws-network-firewall-gs.sh b/tuts/047-aws-network-firewall-gs/aws-network-firewall-gs.sh index f9957ce..8298703 100755 --- a/tuts/047-aws-network-firewall-gs/aws-network-firewall-gs.sh +++ b/tuts/047-aws-network-firewall-gs/aws-network-firewall-gs.sh @@ -447,6 +447,7 @@ STATELESS_RULE_GROUP_ARN=$(aws network-firewall create-rule-group \ --capacity 10 \ --rule-group '{"RulesSource": {"StatelessRulesAndCustomActions": {"StatelessRules": [{"RuleDefinition": {"MatchAttributes": {"Sources": [{"AddressDefinition": "192.0.2.0/24"}], "Destinations": [], "SourcePorts": [], "DestinationPorts": [], "Protocols": []}, "Actions": ["aws:drop"]}, "Priority": 10}]}}}' \ --description "Stateless rule group example" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-network-firewall-gs \ --query 'RuleGroupResponse.RuleGroupArn' \ --output text) @@ -467,6 +468,7 @@ STATEFUL_RULE_GROUP_ARN=$(aws network-firewall create-rule-group \ --capacity 10 \ --rule-group '{"RulesSource": {"RulesString": "drop tls $HOME_NET any -> $EXTERNAL_NET any (ssl_state:client_hello; tls.sni; content:\"evil.com\"; startswith; nocase; endswith; msg:\"matching TLS denylisted FQDNs\"; priority:1; flow:to_server, established; sid:1; rev:1;)"}}' \ --description "Stateful rule group example" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-network-firewall-gs \ --query 'RuleGroupResponse.RuleGroupArn' \ --output text) @@ -512,6 +514,7 @@ FIREWALL_POLICY_ARN=$(aws network-firewall create-firewall-policy \ ] }' \ --description "Firewall policy example" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-network-firewall-gs \ --query 'FirewallPolicyResponse.FirewallPolicyArn' \ --output text) @@ -565,7 +568,8 @@ FIREWALL_OUTPUT=$(aws network-firewall create-firewall \ --firewall-name "$FIREWALL_NAME" \ --firewall-policy-arn "$FIREWALL_POLICY_ARN" \ --vpc-id "$VPC_ID" \ - --subnet-mappings "SubnetId=$SUBNET_ID") + --subnet-mappings "SubnetId=$SUBNET_ID" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-network-firewall-gs) check_error "$FIREWALL_OUTPUT" "Create firewall" echo "$FIREWALL_OUTPUT" @@ -662,7 +666,7 @@ echo "(auto-confirmed)" # Create a route table for the firewall endpoint echo "Creating route table for firewall endpoint..." -FIREWALL_ROUTE_TABLE_ID=$(aws ec2 create-route-table --vpc-id "$VPC_ID" --query 'RouteTable.RouteTableId' --output text) +FIREWALL_ROUTE_TABLE_ID=$(aws ec2 create-route-table --vpc-id "$VPC_ID" --tag-specifications 'ResourceType=route-table,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=aws-network-firewall-gs}]' --query 'RouteTable.RouteTableId' --output text) if [ $? -ne 0 ] || [ -z "$FIREWALL_ROUTE_TABLE_ID" ]; then echo "ERROR: Failed to create firewall route table" diff --git a/tuts/048-amazon-simple-notification-service-gs/amazon-simple-notification-service-gs.sh b/tuts/048-amazon-simple-notification-service-gs/amazon-simple-notification-service-gs.sh index e17ee0a..c908129 100644 --- a/tuts/048-amazon-simple-notification-service-gs/amazon-simple-notification-service-gs.sh +++ b/tuts/048-amazon-simple-notification-service-gs/amazon-simple-notification-service-gs.sh @@ -76,9 +76,13 @@ if [ ${#TOPIC_NAME} -gt 256 ]; then handle_error "Topic name exceeds maximum length of 256 characters" fi -# Step 1: Create an SNS topic with cost optimization: no tags +# Step 1: Create an SNS topic echo "Creating SNS topic: $TOPIC_NAME" -TOPIC_RESULT=$(aws sns create-topic --name "$TOPIC_NAME" --region "$AWS_REGION" --output json) || handle_error "Failed to create SNS topic" +TOPIC_RESULT=$(aws sns create-topic \ + --name "$TOPIC_NAME" \ + --region "$AWS_REGION" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-simple-notification-service-gs \ + --output json) || handle_error "Failed to create SNS topic" # Extract the topic ARN using jq for reliable parsing TOPIC_ARN=$(echo "$TOPIC_RESULT" | jq -r '.TopicArn // empty') || handle_error "Failed to parse topic result" @@ -121,6 +125,14 @@ echo "Subscription created: $SUBSCRIPTION_ARN" echo "A confirmation email has been sent to $EMAIL_ADDRESS" echo "" +# Tag the subscription +if [ "$SUBSCRIPTION_ARN" != "PendingConfirmation" ] && [ "$SUBSCRIPTION_ARN" != "pending confirmation" ]; then + aws sns tag-resource \ + --resource-arn "$SUBSCRIPTION_ARN" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-simple-notification-service-gs \ + --region "$AWS_REGION" 2>/dev/null || echo "Warning: Failed to tag subscription" +fi + # Step 3: List subscriptions to verify echo "Listing subscriptions for topic: $TOPIC_ARN" SUBSCRIPTIONS=$(aws sns list-subscriptions-by-topic --topic-arn "$TOPIC_ARN" --region "$AWS_REGION" --output json) || handle_error "Failed to list subscriptions" 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 83802ef..b5cf00e 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 @@ -205,7 +205,7 @@ echo "Creating application with name: $APP_NAME" # Step 1: Create an application echo "Executing: aws pinpoint create-app --create-application-request Name=${APP_NAME}" -CREATE_APP_OUTPUT=$(aws pinpoint create-app --create-application-request "Name=${APP_NAME}" 2>&1) +CREATE_APP_OUTPUT=$(aws pinpoint create-app --create-application-request "Name=${APP_NAME},tags={project=doc-smith,tutorial=aws-end-user-messaging-gs}" 2>&1) if ! check_error "$CREATE_APP_OUTPUT" "create-app"; then exit 1 diff --git a/tuts/051-aws-direct-connect-gs/aws-direct-connect-gs.sh b/tuts/051-aws-direct-connect-gs/aws-direct-connect-gs.sh index 666c9f2..6a50835 100755 --- a/tuts/051-aws-direct-connect-gs/aws-direct-connect-gs.sh +++ b/tuts/051-aws-direct-connect-gs/aws-direct-connect-gs.sh @@ -131,7 +131,8 @@ echo "Creating a dedicated connection at location $LOCATION_CODE with bandwidth CONNECTION_OUTPUT=$(aws directconnect create-connection \ --location "$LOCATION_CODE" \ --bandwidth "1Gbps" \ - --connection-name "$CONNECTION_NAME") + --connection-name "$CONNECTION_NAME" \ + --tags key=project,value=doc-smith key=tutorial,value=aws-direct-connect-gs) check_error "$CONNECTION_OUTPUT" "create-connection" echo "$CONNECTION_OUTPUT" @@ -173,7 +174,8 @@ fi # Step 6: Create a virtual private gateway (required for private virtual interface) echo "Creating a virtual private gateway..." -VGW_OUTPUT=$(aws ec2 create-vpn-gateway --type ipsec.1) +VGW_OUTPUT=$(aws ec2 create-vpn-gateway --type ipsec.1 \ + --tag-specifications 'ResourceType=vpn-gateway,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=aws-direct-connect-gs}]') check_error "$VGW_OUTPUT" "create-vpn-gateway" echo "$VGW_OUTPUT" diff --git a/tuts/052-aws-waf-gs/aws-waf-gs.sh b/tuts/052-aws-waf-gs/aws-waf-gs.sh index baec70c..9a485cb 100644 --- a/tuts/052-aws-waf-gs/aws-waf-gs.sh +++ b/tuts/052-aws-waf-gs/aws-waf-gs.sh @@ -147,6 +147,7 @@ create_result=$(aws wafv2 create-web-acl \ --scope "CLOUDFRONT" \ --default-action Allow={} \ --visibility-config "SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=$METRIC_NAME" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-waf-gs \ --region us-east-1 2>&1) || handle_error "Failed to create Web ACL" if ! validate_json "$create_result"; then diff --git a/tuts/053-aws-config-gs/aws-config-gs.sh b/tuts/053-aws-config-gs/aws-config-gs.sh old mode 100755 new mode 100644 index 06adb6b..b4fbb6f --- a/tuts/053-aws-config-gs/aws-config-gs.sh +++ b/tuts/053-aws-config-gs/aws-config-gs.sh @@ -149,6 +149,9 @@ if [ "$BUCKET_IS_SHARED" = "false" ]; then fi check_command "$BUCKET_RESULT" echo "S3 bucket created: $S3_BUCKET_NAME" + + aws s3api put-bucket-tagging --bucket "$S3_BUCKET_NAME" --tagging 'TagSet=[{Key=project,Value=doc-smith},{Key=tutorial,Value=aws-config-gs}]' + echo "Tags applied to S3 bucket" else echo "Using shared bucket: $S3_BUCKET_NAME (skipping creation)" fi @@ -162,7 +165,7 @@ echo "Public access blocked for bucket" # Step 2: Create an SNS topic TOPIC_NAME="config-topic-${RANDOM_ID}" echo "Creating SNS topic: $TOPIC_NAME" -SNS_RESULT=$(aws sns create-topic --name "$TOPIC_NAME") +SNS_RESULT=$(aws sns create-topic --name "$TOPIC_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-config-gs) check_command "$SNS_RESULT" SNS_TOPIC_ARN=$(echo "$SNS_RESULT" | grep -o 'arn:aws:sns:[^"]*') echo "SNS topic created: $SNS_TOPIC_ARN" @@ -194,6 +197,9 @@ check_command "$ROLE_RESULT" ROLE_ARN=$(echo "$ROLE_RESULT" | grep -o 'arn:aws:iam::[^"]*' | head -1) echo "IAM role created: $ROLE_ARN" +aws iam tag-role --role-name "$ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-config-gs +echo "Tags applied to IAM role" + echo "Attaching AWS managed policy to role..." ATTACH_RESULT=$(aws iam attach-role-policy --role-name "$ROLE_NAME" --policy-arn "$MANAGED_POLICY_ARN") check_command "$ATTACH_RESULT" @@ -283,6 +289,11 @@ RECORDER_RESULT=$(aws configservice put-configuration-recorder --configuration-r check_command "$RECORDER_RESULT" echo "Configuration recorder set up" +if [ "$CREATED_NEW_CONFIG_RECORDER" = "true" ]; then + aws configservice tag-resource --resource-arn "arn:aws:config:${AWS_REGION}:${ACCOUNT_ID}:config-recorder/${CONFIG_RECORDER_NAME}" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-config-gs + echo "Tags applied to configuration recorder" +fi + # Step 5: Check if delivery channel already exists DELIVERY_CHANNEL_NAME="default" CREATED_NEW_DELIVERY_CHANNEL="false" @@ -332,6 +343,9 @@ EOF CHANNEL_RESULT=$(aws configservice put-delivery-channel --delivery-channel file://deliveryChannel.json) check_command "$CHANNEL_RESULT" echo "Delivery channel created" + + aws configservice tag-resource --resource-arn "arn:aws:config:${AWS_REGION}:${ACCOUNT_ID}:delivery-channel/${DELIVERY_CHANNEL_NAME}" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-config-gs + echo "Tags applied to delivery channel" fi # Step 6: Start the configuration recorder @@ -381,4 +395,4 @@ else echo "Resources will not be cleaned up. You can manually clean them up later." fi -echo "Script completed successfully!" +echo "Script completed successfully!" \ No newline at end of file diff --git a/tuts/054-amazon-kinesis-video-streams-gs/amazon-kinesis-video-streams-gs.sh b/tuts/054-amazon-kinesis-video-streams-gs/amazon-kinesis-video-streams-gs.sh index ec0ddee..eef24b4 100644 --- a/tuts/054-amazon-kinesis-video-streams-gs/amazon-kinesis-video-streams-gs.sh +++ b/tuts/054-amazon-kinesis-video-streams-gs/amazon-kinesis-video-streams-gs.sh @@ -97,7 +97,7 @@ echo "==========================================" echo "Creating stream: $STREAM_NAME" # Create the Kinesis video stream -if ! CREATE_STREAM_OUTPUT=$(aws kinesisvideo create-stream --stream-name "$STREAM_NAME" --data-retention-in-hours 24 --output json 2>&1); then +if ! CREATE_STREAM_OUTPUT=$(aws kinesisvideo create-stream --stream-name "$STREAM_NAME" --data-retention-in-hours 24 --tags project=doc-smith,tutorial=amazon-kinesis-video-streams-gs --output json 2>&1); then handle_error "Failed to create stream: $CREATE_STREAM_OUTPUT" fi check_error "$CREATE_STREAM_OUTPUT" "create-stream" 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 61d0a68..bf9056d 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 @@ -135,7 +135,7 @@ echo "Random ID for this session: ${RANDOM_ID}" | tee -a "$LOG_FILE" echo -e "\n=== Step 1: Creating a VPC Lattice service network ===" | tee -a "$LOG_FILE" echo "Creating service network: $SERVICE_NETWORK_NAME" | tee -a "$LOG_FILE" -SERVICE_NETWORK_OUTPUT=$(log_command "aws vpc-lattice create-service-network --name '$SERVICE_NETWORK_NAME' --output json") +SERVICE_NETWORK_OUTPUT=$(log_command "aws vpc-lattice create-service-network --name '$SERVICE_NETWORK_NAME' --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-vpc-lattice-gs --output json") check_error $? # Extract the service network ID using jq for safety @@ -158,7 +158,7 @@ check_error $? echo -e "\n=== Step 2: Creating a VPC Lattice service ===" | tee -a "$LOG_FILE" echo "Creating service: $SERVICE_NAME" | tee -a "$LOG_FILE" -SERVICE_OUTPUT=$(log_command "aws vpc-lattice create-service --name '$SERVICE_NAME' --output json") +SERVICE_OUTPUT=$(log_command "aws vpc-lattice create-service --name '$SERVICE_NAME' --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-vpc-lattice-gs --output json") check_error $? # Extract the service ID using jq for safety diff --git a/tuts/057-amazon-managed-streaming-for-apache-kafka-gs/amazon-managed-streaming-for-apache-kafka-gs.sh b/tuts/057-amazon-managed-streaming-for-apache-kafka-gs/amazon-managed-streaming-for-apache-kafka-gs.sh index 4c8c7a6..78e302d 100755 --- a/tuts/057-amazon-managed-streaming-for-apache-kafka-gs/amazon-managed-streaming-for-apache-kafka-gs.sh +++ b/tuts/057-amazon-managed-streaming-for-apache-kafka-gs/amazon-managed-streaming-for-apache-kafka-gs.sh @@ -330,7 +330,8 @@ CLUSTER_RESPONSE=$(aws kafka create-cluster \ --broker-node-group-info "{\"InstanceType\": \"kafka.t3.small\", \"ClientSubnets\": [\"${SUBNET_ARRAY[0]}\", \"${SUBNET_ARRAY[1]}\", \"${SUBNET_ARRAY[2]}\"], \"SecurityGroups\": [\"$DEFAULT_SG\"]}" \ --kafka-version "3.6.0" \ --number-of-broker-nodes 3 \ - --encryption-info "{\"EncryptionInTransit\": {\"InCluster\": true, \"ClientBroker\": \"TLS\"}}" 2>&1) + --encryption-info "{\"EncryptionInTransit\": {\"InCluster\": true, \"ClientBroker\": \"TLS\"}}" \ + --tags project=doc-smith,tutorial=amazon-managed-streaming-for-apache-kafka-gs 2>&1) # Check if the command was successful if [ $? -ne 0 ]; then @@ -444,6 +445,9 @@ if [ -z "$POLICY_ARN" ]; then handle_error "Failed to extract policy ARN from response: $POLICY_RESPONSE" fi +aws iam tag-policy --policy-arn "$POLICY_ARN" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-managed-streaming-for-apache-kafka-gs + echo "IAM policy created. ARN: $POLICY_ARN" # Create IAM role for EC2 @@ -460,6 +464,8 @@ if [ $? -ne 0 ]; then fi echo "IAM role created: $ROLE_NAME" +aws iam tag-role --role-name "$ROLE_NAME" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-managed-streaming-for-apache-kafka-gs # Attach policy to role echo "Attaching policy to role" @@ -545,7 +551,8 @@ echo "Creating security group for client: $SG_NAME" CLIENT_SG_RESPONSE=$(aws ec2 create-security-group \ --group-name "$SG_NAME" \ --description "Security group for MSK client" \ - --vpc-id "$DEFAULT_VPC_ID" 2>&1) + --vpc-id "$DEFAULT_VPC_ID" \ + --tag-specifications 'ResourceType=security-group,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-managed-streaming-for-apache-kafka-gs}]' 2>&1) # Check if the command was successful if [ $? -ne 0 ]; then @@ -606,7 +613,7 @@ echo "Ingress rule added to MSK security group" # Create key pair KEY_NAME="MSKKeyPair-${RANDOM_SUFFIX}" echo "Creating key pair: $KEY_NAME" -KEY_RESPONSE=$(aws ec2 create-key-pair --key-name "$KEY_NAME" --query 'KeyMaterial' --output text 2>&1) +KEY_RESPONSE=$(aws ec2 create-key-pair --key-name "$KEY_NAME" --tag-specifications 'ResourceType=key-pair,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-managed-streaming-for-apache-kafka-gs}]' --query 'KeyMaterial' --output text 2>&1) # Check if the command was successful if [ $? -ne 0 ]; then @@ -646,7 +653,7 @@ INSTANCE_RESPONSE=$(aws ec2 run-instances \ --security-group-ids "$CLIENT_SG_ID" \ --subnet-id "$SELECTED_SUBNET_ID" \ --iam-instance-profile "Name=$INSTANCE_PROFILE_NAME" \ - --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=MSKTutorialClient-${RANDOM_SUFFIX}}]" 2>&1) + --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=MSKTutorialClient-${RANDOM_SUFFIX}},{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-managed-streaming-for-apache-kafka-gs}]" 2>&1) # Check if the command was successful if [ $? -ne 0 ]; then diff --git a/tuts/058-elastic-load-balancing-gs/elastic-load-balancing-gs.sh b/tuts/058-elastic-load-balancing-gs/elastic-load-balancing-gs.sh index 48523c3..289d240 100644 --- a/tuts/058-elastic-load-balancing-gs/elastic-load-balancing-gs.sh +++ b/tuts/058-elastic-load-balancing-gs/elastic-load-balancing-gs.sh @@ -143,6 +143,7 @@ SG_INFO=$(aws ec2 create-security-group \ --group-name "${RESOURCE_PREFIX}-sg" \ --description "Security group for ELB demo" \ --vpc-id "$VPC_ID" \ + --tag-specifications 'ResourceType=security-group,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=elastic-load-balancing-gs}]' \ --query "GroupId" --output text 2>/dev/null || echo "") check_command "$SG_INFO" SECURITY_GROUP_ID="$SG_INFO" @@ -165,6 +166,7 @@ LB_INFO=$(aws elbv2 create-load-balancer \ --name "${RESOURCE_PREFIX}-lb" \ --subnets "${SUBNETS[0]}" "${SUBNETS[1]}" \ --security-groups "$SECURITY_GROUP_ID" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=elastic-load-balancing-gs \ --query "LoadBalancers[0].LoadBalancerArn" --output text 2>/dev/null || echo "") check_command "$LB_INFO" LOAD_BALANCER_ARN="$LB_INFO" @@ -185,6 +187,7 @@ TG_INFO=$(aws elbv2 create-target-group \ --port 80 \ --vpc-id "$VPC_ID" \ --target-type instance \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=elastic-load-balancing-gs \ --query "TargetGroups[0].TargetGroupArn" --output text 2>/dev/null || echo "") check_command "$TG_INFO" TARGET_GROUP_ARN="$TG_INFO" @@ -231,6 +234,7 @@ LISTENER_INFO=$(aws elbv2 create-listener \ --protocol HTTP \ --port 80 \ --default-actions Type=forward,TargetGroupArn="$TARGET_GROUP_ARN" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=elastic-load-balancing-gs \ --query "Listeners[0].ListenerArn" --output text 2>/dev/null || echo "") check_command "$LISTENER_INFO" LISTENER_ARN="$LISTENER_INFO" diff --git a/tuts/059-amazon-datazone-gs/amazon-datazone-gs.sh b/tuts/059-amazon-datazone-gs/amazon-datazone-gs.sh index 78ac00c..f183ec3 100755 --- a/tuts/059-amazon-datazone-gs/amazon-datazone-gs.sh +++ b/tuts/059-amazon-datazone-gs/amazon-datazone-gs.sh @@ -188,6 +188,8 @@ EOF # Create the role ROLE_CREATE=$(aws iam create-role --role-name "$ROLE_NAME" --assume-role-policy-document file://trust-policy.json) check_error "$ROLE_CREATE" "create-role" + aws iam tag-role --role-name "$ROLE_NAME" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-datazone-gs # FIX: Enhanced IAM role permissions for DataZone domain execution # Attach necessary policies with more comprehensive permissions @@ -232,6 +234,7 @@ DOMAIN_RESULT=$(aws datazone create-domain \ --name "$DOMAIN_NAME" \ --description "My first DataZone domain" \ --domain-execution-role "arn:aws:iam::$ACCOUNT_ID:role/$ROLE_NAME" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-datazone-gs \ --region "$REGION") check_error "$DOMAIN_RESULT" "create-domain" @@ -444,6 +447,8 @@ EOF # Create the role GLUE_ROLE_CREATE=$(aws iam create-role --role-name "$GLUE_ROLE_NAME" --assume-role-policy-document file://glue-trust-policy.json) check_error "$GLUE_ROLE_CREATE" "create-glue-role" + aws iam tag-role --role-name "$GLUE_ROLE_NAME" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-datazone-gs # Create policy document cat > glue-policy.json << EOF diff --git a/tuts/061-amazon-athena-gs/amazon-athena-gs.sh b/tuts/061-amazon-athena-gs/amazon-athena-gs.sh index 0a95ce9..7e4291d 100644 --- a/tuts/061-amazon-athena-gs/amazon-athena-gs.sh +++ b/tuts/061-amazon-athena-gs/amazon-athena-gs.sh @@ -129,6 +129,10 @@ if [ "$BUCKET_IS_SHARED" = false ]; then handle_error "Failed to create S3 bucket: $CREATE_BUCKET_RESULT" fi + aws s3api put-bucket-tagging \ + --bucket "$S3_BUCKET" \ + --tagging 'TagSet=[{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-athena-gs}]' + # Security: Enable S3 bucket encryption with KMS validation echo "Enabling default encryption on S3 bucket..." if ! aws s3api put-bucket-encryption \ diff --git a/tuts/063-aws-iot-core-gs/aws-iot-core-gs.sh b/tuts/063-aws-iot-core-gs/aws-iot-core-gs.sh index 5b7856a..91dd712 100755 --- a/tuts/063-aws-iot-core-gs/aws-iot-core-gs.sh +++ b/tuts/063-aws-iot-core-gs/aws-iot-core-gs.sh @@ -135,7 +135,7 @@ cat > iot-policy.json << EOF EOF echo "Creating IoT policy: $POLICY_NAME..." | tee -a $LOG_FILE -log_cmd "aws iot create-policy --policy-name $POLICY_NAME --policy-document file://iot-policy.json" +log_cmd "aws iot create-policy --policy-name $POLICY_NAME --policy-document file://iot-policy.json --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-iot-core-gs" check_error $? # Create IoT thing @@ -143,6 +143,9 @@ echo "Creating IoT thing: $THING_NAME..." | tee -a $LOG_FILE log_cmd "aws iot create-thing --thing-name $THING_NAME" check_error $? +THING_ARN=$(aws iot describe-thing --thing-name "$THING_NAME" --query 'thingArn' --output text) +aws iot tag-resource --resource-arn "$THING_ARN" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-iot-core-gs + # Create directory for certificates echo "Creating certificates directory..." | tee -a $LOG_FILE log_cmd "mkdir -p $CERTS_DIR" @@ -265,7 +268,7 @@ cat > shared-sub-policy.json << EOF } EOF -log_cmd "aws iot create-policy --policy-name $SHARED_POLICY_NAME --policy-document file://shared-sub-policy.json" +log_cmd "aws iot create-policy --policy-name $SHARED_POLICY_NAME --policy-document file://shared-sub-policy.json --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-iot-core-gs" check_error $? log_cmd "aws iot attach-policy --policy-name $SHARED_POLICY_NAME --target $CERTIFICATE_ARN" diff --git a/tuts/064-amazon-neptune-gs/amazon-neptune-gs.sh b/tuts/064-amazon-neptune-gs/amazon-neptune-gs.sh index 4872d2f..00f83b4 100755 --- a/tuts/064-amazon-neptune-gs/amazon-neptune-gs.sh +++ b/tuts/064-amazon-neptune-gs/amazon-neptune-gs.sh @@ -83,7 +83,7 @@ echo "Security Group Name: $SG_NAME" | tee -a "$LOG_FILE" # Step 1: Create VPC echo "Creating VPC..." | tee -a "$LOG_FILE" -VPC_OUTPUT=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications "ResourceType=vpc,Tags=[{Key=Name,Value=$VPC_NAME}]" --output json) +VPC_OUTPUT=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications "ResourceType=vpc,Tags=[{Key=Name,Value=$VPC_NAME},{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-neptune-gs}]" --output json) check_error "$VPC_OUTPUT" $? "Failed to create VPC" VPC_ID=$(echo "$VPC_OUTPUT" | grep -o '"VpcId": "[^"]*' | cut -d'"' -f4) @@ -95,7 +95,7 @@ log_cmd "aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-hostnames" # Step 2: Create Internet Gateway and attach to VPC echo "Creating Internet Gateway..." | tee -a "$LOG_FILE" -IGW_OUTPUT=$(aws ec2 create-internet-gateway --output json) +IGW_OUTPUT=$(aws ec2 create-internet-gateway --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-neptune-gs}]' --output json) check_error "$IGW_OUTPUT" $? "Failed to create Internet Gateway" IGW_ID=$(echo "$IGW_OUTPUT" | grep -o '"InternetGatewayId": "[^"]*' | cut -d'"' -f4) @@ -116,15 +116,15 @@ AZ2=$(echo "$AZ_OUTPUT" | grep -o '"ZoneName": "[^"]*' | cut -d'"' -f4 | head -2 AZ3=$(echo "$AZ_OUTPUT" | grep -o '"ZoneName": "[^"]*' | cut -d'"' -f4 | head -3 | tail -1) # Create 3 subnets in different AZs -SUBNET1_OUTPUT=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.1.0/24 --availability-zone $AZ1 --output json) +SUBNET1_OUTPUT=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.1.0/24 --availability-zone $AZ1 --tag-specifications 'ResourceType=subnet,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-neptune-gs}]' --output json) check_error "$SUBNET1_OUTPUT" $? "Failed to create subnet 1" SUBNET1_ID=$(echo "$SUBNET1_OUTPUT" | grep -o '"SubnetId": "[^"]*' | cut -d'"' -f4) -SUBNET2_OUTPUT=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.2.0/24 --availability-zone $AZ2 --output json) +SUBNET2_OUTPUT=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.2.0/24 --availability-zone $AZ2 --tag-specifications 'ResourceType=subnet,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-neptune-gs}]' --output json) check_error "$SUBNET2_OUTPUT" $? "Failed to create subnet 2" SUBNET2_ID=$(echo "$SUBNET2_OUTPUT" | grep -o '"SubnetId": "[^"]*' | cut -d'"' -f4) -SUBNET3_OUTPUT=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.3.0/24 --availability-zone $AZ3 --output json) +SUBNET3_OUTPUT=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.3.0/24 --availability-zone $AZ3 --tag-specifications 'ResourceType=subnet,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-neptune-gs}]' --output json) check_error "$SUBNET3_OUTPUT" $? "Failed to create subnet 3" SUBNET3_ID=$(echo "$SUBNET3_OUTPUT" | grep -o '"SubnetId": "[^"]*' | cut -d'"' -f4) @@ -133,7 +133,7 @@ echo "Created subnets: $SUBNET1_ID, $SUBNET2_ID, $SUBNET3_ID" | tee -a "$LOG_FIL # Step 4: Create route table and add route to Internet Gateway echo "Creating route table..." | tee -a "$LOG_FILE" -ROUTE_TABLE_OUTPUT=$(aws ec2 create-route-table --vpc-id $VPC_ID --output json) +ROUTE_TABLE_OUTPUT=$(aws ec2 create-route-table --vpc-id $VPC_ID --tag-specifications 'ResourceType=route-table,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-neptune-gs}]' --output json) check_error "$ROUTE_TABLE_OUTPUT" $? "Failed to create route table" ROUTE_TABLE_ID=$(echo "$ROUTE_TABLE_OUTPUT" | grep -o '"RouteTableId": "[^"]*' | cut -d'"' -f4) @@ -149,7 +149,7 @@ log_cmd "aws ec2 associate-route-table --route-table-id $ROUTE_TABLE_ID --subnet # Step 5: Create security group echo "Creating security group..." | tee -a "$LOG_FILE" -SG_OUTPUT=$(aws ec2 create-security-group --group-name $SG_NAME --description "Security group for Neptune" --vpc-id $VPC_ID --output json) +SG_OUTPUT=$(aws ec2 create-security-group --group-name $SG_NAME --description "Security group for Neptune" --vpc-id $VPC_ID --tag-specifications 'ResourceType=security-group,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-neptune-gs}]' --output json) check_error "$SG_OUTPUT" $? "Failed to create security group" SECURITY_GROUP_ID=$(echo "$SG_OUTPUT" | grep -o '"GroupId": "[^"]*' | cut -d'"' -f4) @@ -162,19 +162,19 @@ log_cmd "aws ec2 authorize-security-group-ingress --group-id $SECURITY_GROUP_ID # Step 6: Create DB subnet group echo "Creating DB subnet group..." | tee -a "$LOG_FILE" -DB_SUBNET_GROUP_OUTPUT=$(aws neptune create-db-subnet-group --db-subnet-group-name $DB_SUBNET_GROUP --db-subnet-group-description "Subnet group for Neptune" --subnet-ids $SUBNET1_ID $SUBNET2_ID $SUBNET3_ID --output json) +DB_SUBNET_GROUP_OUTPUT=$(aws neptune create-db-subnet-group --db-subnet-group-name $DB_SUBNET_GROUP --db-subnet-group-description "Subnet group for Neptune" --subnet-ids $SUBNET1_ID $SUBNET2_ID $SUBNET3_ID --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-neptune-gs --output json) check_error "$DB_SUBNET_GROUP_OUTPUT" $? "Failed to create DB subnet group" echo "DB subnet group created: $DB_SUBNET_GROUP" | tee -a "$LOG_FILE" # Step 7: Create Neptune DB cluster echo "Creating Neptune DB cluster..." | tee -a "$LOG_FILE" -DB_CLUSTER_OUTPUT=$(aws neptune create-db-cluster --db-cluster-identifier $DB_CLUSTER_ID --engine neptune --vpc-security-group-ids $SECURITY_GROUP_ID --db-subnet-group-name $DB_SUBNET_GROUP --output json) +DB_CLUSTER_OUTPUT=$(aws neptune create-db-cluster --db-cluster-identifier $DB_CLUSTER_ID --engine neptune --vpc-security-group-ids $SECURITY_GROUP_ID --db-subnet-group-name $DB_SUBNET_GROUP --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-neptune-gs --output json) check_error "$DB_CLUSTER_OUTPUT" $? "Failed to create Neptune DB cluster" echo "Neptune DB cluster created: $DB_CLUSTER_ID" | tee -a "$LOG_FILE" # Step 8: Create Neptune DB instance echo "Creating Neptune DB instance..." | tee -a "$LOG_FILE" -DB_INSTANCE_OUTPUT=$(aws neptune create-db-instance --db-instance-identifier $DB_INSTANCE_ID --db-instance-class db.r5.large --engine neptune --db-cluster-identifier $DB_CLUSTER_ID --output json) +DB_INSTANCE_OUTPUT=$(aws neptune create-db-instance --db-instance-identifier $DB_INSTANCE_ID --db-instance-class db.r5.large --engine neptune --db-cluster-identifier $DB_CLUSTER_ID --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-neptune-gs --output json) check_error "$DB_INSTANCE_OUTPUT" $? "Failed to create Neptune DB instance" echo "Neptune DB instance created: $DB_INSTANCE_ID" | tee -a "$LOG_FILE" diff --git a/tuts/065-amazon-elasticache-gs/amazon-elasticache-gs.sh b/tuts/065-amazon-elasticache-gs/amazon-elasticache-gs.sh index 2d37cac..cf79280 100644 --- a/tuts/065-amazon-elasticache-gs/amazon-elasticache-gs.sh +++ b/tuts/065-amazon-elasticache-gs/amazon-elasticache-gs.sh @@ -4,7 +4,7 @@ # This script creates a Valkey serverless cache, configures security groups, # and demonstrates how to connect to and use the cache. -set -euo pipefail +set -uo pipefail # Set up logging LOG_FILE="elasticache_tutorial_$(date +%Y%m%d_%H%M%S).log" @@ -117,7 +117,8 @@ echo "" echo "Step 2: Creating Valkey serverless cache..." if ! CREATE_RESULT=$(aws elasticache create-serverless-cache \ --serverless-cache-name "$CACHE_NAME" \ - --engine valkey 2>&1); then + --engine valkey \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-elasticache-gs 2>&1); then handle_error "Failed to create serverless cache: $CREATE_RESULT" fi diff --git a/tuts/066-amazon-cognito-gs/amazon-cognito-gs.sh b/tuts/066-amazon-cognito-gs/amazon-cognito-gs.sh index 34c1964..4d82847 100644 --- a/tuts/066-amazon-cognito-gs/amazon-cognito-gs.sh +++ b/tuts/066-amazon-cognito-gs/amazon-cognito-gs.sh @@ -157,6 +157,11 @@ fi echo "User Pool created with ID: $USER_POOL_ID" +USER_POOL_ARN=$(echo "$USER_POOL_OUTPUT" | jq -r '.UserPool.Arn // empty') +aws cognito-idp tag-resource \ + --resource-arn "$USER_POOL_ARN" \ + --tags project=doc-smith,tutorial=amazon-cognito-gs + # Wait for user pool to be ready echo "Waiting for user pool to be ready..." sleep 5 diff --git a/tuts/067-aws-payment-cryptography-gs/aws-payment-cryptography-gs.sh b/tuts/067-aws-payment-cryptography-gs/aws-payment-cryptography-gs.sh index ff4c663..19ece20 100644 --- a/tuts/067-aws-payment-cryptography-gs/aws-payment-cryptography-gs.sh +++ b/tuts/067-aws-payment-cryptography-gs/aws-payment-cryptography-gs.sh @@ -64,7 +64,8 @@ log "Starting AWS Payment Cryptography tutorial" log "Step 1: Creating a card verification key (CVK)" if ! KEY_OUTPUT=$(aws payment-cryptography create-key \ --exportable \ - --key-attributes KeyAlgorithm=TDES_2KEY,KeyUsage=TR31_C0_CARD_VERIFICATION_KEY,KeyClass=SYMMETRIC_KEY,KeyModesOfUse='{Generate=true,Verify=true}' 2>&1); then + --key-attributes KeyAlgorithm=TDES_2KEY,KeyUsage=TR31_C0_CARD_VERIFICATION_KEY,KeyClass=SYMMETRIC_KEY,KeyModesOfUse='{Generate=true,Verify=true}' \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-payment-cryptography-gs 2>&1); then handle_error "Failed to create key" fi diff --git a/tuts/069-aws-fault-injection-service-gs/aws-fault-injection-service-getting-started.sh b/tuts/069-aws-fault-injection-service-gs/aws-fault-injection-service-getting-started.sh index 6eab00b..e16187a 100644 --- a/tuts/069-aws-fault-injection-service-gs/aws-fault-injection-service-getting-started.sh +++ b/tuts/069-aws-fault-injection-service-gs/aws-fault-injection-service-getting-started.sh @@ -117,6 +117,7 @@ FIS_ROLE_OUTPUT=$(aws iam create-role \ --role-name "$FIS_ROLE_NAME" \ --assume-role-policy-document file://fis-trust-policy.json) check_error "$FIS_ROLE_OUTPUT" "aws iam create-role" +aws iam tag-role --role-name "$FIS_ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-fault-injection-service-gs CREATED_RESOURCES+=("IAM Role: $FIS_ROLE_NAME") # Create policy document for SSM actions @@ -169,6 +170,7 @@ EC2_ROLE_OUTPUT=$(aws iam create-role \ --role-name "$EC2_ROLE_NAME" \ --assume-role-policy-document file://ec2-trust-policy.json) check_error "$EC2_ROLE_OUTPUT" "aws iam create-role" +aws iam tag-role --role-name "$EC2_ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-fault-injection-service-gs CREATED_RESOURCES+=("IAM Role: $EC2_ROLE_NAME") # Attach SSM policy to the EC2 role @@ -214,7 +216,7 @@ INSTANCE_OUTPUT=$(aws ec2 run-instances \ --image-id "$AMI_ID" \ --instance-type t2.micro \ --iam-instance-profile Name="$INSTANCE_PROFILE_NAME" \ - --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=FIS-Test-Instance}]') + --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=FIS-Test-Instance},{Key=project,Value=doc-smith},{Key=tutorial,Value=aws-fault-injection-service-gs}]') check_error "$INSTANCE_OUTPUT" "aws ec2 run-instances" # Get instance ID @@ -350,7 +352,9 @@ cat > experiment-template.json << EOF ], "roleArn": "$ROLE_ARN", "tags": { - "Name": "FIS-CPU-Stress-Experiment" + "Name": "FIS-CPU-Stress-Experiment", + "project": "doc-smith", + "tutorial": "aws-fault-injection-service-gs" } } EOF diff --git a/tuts/070-amazon-dynamodb-gs/amazon-dynamodb-gs.sh b/tuts/070-amazon-dynamodb-gs/amazon-dynamodb-gs.sh index 967dfca..de19e5f 100644 --- a/tuts/070-amazon-dynamodb-gs/amazon-dynamodb-gs.sh +++ b/tuts/070-amazon-dynamodb-gs/amazon-dynamodb-gs.sh @@ -9,7 +9,7 @@ # - Querying data in the table # - Deleting the table (cleanup) -set -euo pipefail +set -uo pipefail # Set up logging with secure permissions LOG_DIR="${XDG_STATE_HOME:-.}/dynamodb-tutorial-logs" @@ -141,7 +141,8 @@ CREATE_TABLE_OUTPUT=$(aws dynamodb create-table \ AttributeName=SongTitle,AttributeType=S \ --key-schema AttributeName=Artist,KeyType=HASH AttributeName=SongTitle,KeyType=RANGE \ --billing-mode PAY_PER_REQUEST \ - --table-class STANDARD 2>&1) || { + --table-class STANDARD \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-dynamodb-gs 2>&1) || { echo "ERROR: Failed to create table" >&2 exit 1 } diff --git a/tuts/073-aws-secrets-manager-gs/aws-secrets-manager-gs.sh b/tuts/073-aws-secrets-manager-gs/aws-secrets-manager-gs.sh index 136fb81..9c17941 100644 --- a/tuts/073-aws-secrets-manager-gs/aws-secrets-manager-gs.sh +++ b/tuts/073-aws-secrets-manager-gs/aws-secrets-manager-gs.sh @@ -130,6 +130,7 @@ ADMIN_ROLE_OUTPUT=$(aws iam create-role \ check_error "$ADMIN_ROLE_OUTPUT" "create-role for admin" echo "$ADMIN_ROLE_OUTPUT" +aws iam tag-role --role-name "$ADMIN_ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-secrets-manager-gs # Attach the SecretsManagerReadWrite policy to the admin role echo "Attaching SecretsManagerReadWrite policy to admin role" @@ -148,6 +149,7 @@ RUNTIME_ROLE_OUTPUT=$(aws iam create-role \ check_error "$RUNTIME_ROLE_OUTPUT" "create-role for runtime" echo "$RUNTIME_ROLE_OUTPUT" +aws iam tag-role --role-name "$RUNTIME_ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-secrets-manager-gs # Wait for roles to be fully created echo "Waiting for IAM roles to be fully created..." @@ -168,7 +170,8 @@ CREATE_SECRET_OUTPUT=$(aws secretsmanager create-secret \ --name "$SECRET_NAME" \ --description "API key for my application" \ --secret-string "$SECRET_VALUE" \ - --add-replica-regions 'Region=us-east-1' 2>&1) + --add-replica-regions 'Region=us-east-1' \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-secrets-manager-gs 2>&1) check_error "$CREATE_SECRET_OUTPUT" "create-secret" echo "$CREATE_SECRET_OUTPUT" diff --git a/tuts/074-amazon-textract-gs/amazon-textract-getting-started.sh b/tuts/074-amazon-textract-gs/amazon-textract-getting-started.sh index f28d80b..ca1d3ef 100644 --- a/tuts/074-amazon-textract-gs/amazon-textract-getting-started.sh +++ b/tuts/074-amazon-textract-gs/amazon-textract-getting-started.sh @@ -113,6 +113,10 @@ if [ "$BUCKET_IS_SHARED" = false ]; then echo "$CREATE_BUCKET_OUTPUT" check_error $CREATE_BUCKET_STATUS "$CREATE_BUCKET_OUTPUT" "aws s3 mb s3://$BUCKET_NAME" + aws s3api put-bucket-tagging \ + --bucket "$BUCKET_NAME" \ + --tagging 'TagSet=[{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-textract-gs}]' + # Apply security settings to bucket aws s3api put-bucket-versioning --bucket "$BUCKET_NAME" --versioning-configuration Status=Enabled 2>&1 || true aws s3api put-bucket-encryption --bucket "$BUCKET_NAME" --server-side-encryption-configuration '{"Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "AES256"}}]}' 2>&1 || true diff --git a/tuts/075-aws-database-migration-service-gs/aws-database-migration-service-gs.sh b/tuts/075-aws-database-migration-service-gs/aws-database-migration-service-gs.sh old mode 100755 new mode 100644 index b267971..7f06f32 --- a/tuts/075-aws-database-migration-service-gs/aws-database-migration-service-gs.sh +++ b/tuts/075-aws-database-migration-service-gs/aws-database-migration-service-gs.sh @@ -278,7 +278,6 @@ cleanup_resources() { aws rds delete-db-parameter-group --db-parameter-group-name "$DB_PARAM_GROUP_POSTGRES" fi - # FIX: Added cleanup for DB subnet group if [ -n "$DB_SUBNET_GROUP" ]; then echo "Deleting DB subnet group..." aws rds delete-db-subnet-group --db-subnet-group-name "$DB_SUBNET_GROUP" @@ -341,7 +340,7 @@ DB_PASSWORD=$(generate_password) # Store password in AWS Secrets Manager echo "Creating secret for database password..." SECRET_NAME="dms-tutorial-db-password-$RANDOM_ID" -SECRET_ARN=$(aws secretsmanager create-secret --name "$SECRET_NAME" --secret-string "$DB_PASSWORD" --query 'ARN' --output text) +SECRET_ARN=$(aws secretsmanager create-secret --name "$SECRET_NAME" --secret-string "$DB_PASSWORD" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-database-migration-service-gs --query 'ARN' --output text) check_status echo "Database password stored in Secrets Manager with ARN: $SECRET_ARN" @@ -497,7 +496,7 @@ fi if [ "$USING_EXISTING_VPC" = false ]; then echo "Creating new VPC..." - VPC_ID=$(aws ec2 create-vpc --cidr-block 10.0.1.0/24 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=DMSVPC}]' --query 'Vpc.VpcId' --output text) + VPC_ID=$(aws ec2 create-vpc --cidr-block 10.0.1.0/24 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=DMSVPC},{Key=project,Value=doc-smith},{Key=tutorial,Value=aws-database-migration-service-gs}]' --query 'Vpc.VpcId' --output text) check_status echo "VPC created with ID: $VPC_ID" @@ -506,24 +505,24 @@ if [ "$USING_EXISTING_VPC" = false ]; then check_status echo "Creating subnets..." - PUBLIC_SUBNET_1_ID=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.1.0/26 --availability-zone "$AZ1" --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=DMSVPC-public-subnet-1}]' --query 'Subnet.SubnetId' --output text) + PUBLIC_SUBNET_1_ID=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.1.0/26 --availability-zone "$AZ1" --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=DMSVPC-public-subnet-1},{Key=project,Value=doc-smith},{Key=tutorial,Value=aws-database-migration-service-gs}]' --query 'Subnet.SubnetId' --output text) check_status echo "Public subnet 1 created with ID: $PUBLIC_SUBNET_1_ID" - PUBLIC_SUBNET_2_ID=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.1.64/26 --availability-zone "$AZ2" --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=DMSVPC-public-subnet-2}]' --query 'Subnet.SubnetId' --output text) + PUBLIC_SUBNET_2_ID=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.1.64/26 --availability-zone "$AZ2" --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=DMSVPC-public-subnet-2},{Key=project,Value=doc-smith},{Key=tutorial,Value=aws-database-migration-service-gs}]' --query 'Subnet.SubnetId' --output text) check_status echo "Public subnet 2 created with ID: $PUBLIC_SUBNET_2_ID" - PRIVATE_SUBNET_1_ID=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.1.128/26 --availability-zone "$AZ1" --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=DMSVPC-private-subnet-1}]' --query 'Subnet.SubnetId' --output text) + PRIVATE_SUBNET_1_ID=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.1.128/26 --availability-zone "$AZ1" --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=DMSVPC-private-subnet-1},{Key=project,Value=doc-smith},{Key=tutorial,Value=aws-database-migration-service-gs}]' --query 'Subnet.SubnetId' --output text) check_status echo "Private subnet 1 created with ID: $PRIVATE_SUBNET_1_ID" - PRIVATE_SUBNET_2_ID=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.1.192/26 --availability-zone "$AZ2" --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=DMSVPC-private-subnet-2}]' --query 'Subnet.SubnetId' --output text) + PRIVATE_SUBNET_2_ID=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.1.192/26 --availability-zone "$AZ2" --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=DMSVPC-private-subnet-2},{Key=project,Value=doc-smith},{Key=tutorial,Value=aws-database-migration-service-gs}]' --query 'Subnet.SubnetId' --output text) check_status echo "Private subnet 2 created with ID: $PRIVATE_SUBNET_2_ID" echo "Creating internet gateway..." - IGW_ID=$(aws ec2 create-internet-gateway --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=DMSVPC-igw}]' --query 'InternetGateway.InternetGatewayId' --output text) + IGW_ID=$(aws ec2 create-internet-gateway --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=DMSVPC-igw},{Key=project,Value=doc-smith},{Key=tutorial,Value=aws-database-migration-service-gs}]' --query 'InternetGateway.InternetGatewayId' --output text) check_status echo "Internet gateway created with ID: $IGW_ID" @@ -532,7 +531,7 @@ if [ "$USING_EXISTING_VPC" = false ]; then check_status echo "Creating route table..." - PUBLIC_RT_ID=$(aws ec2 create-route-table --vpc-id "$VPC_ID" --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=DMSVPC-public-rt}]' --query 'RouteTable.RouteTableId' --output text) + PUBLIC_RT_ID=$(aws ec2 create-route-table --vpc-id "$VPC_ID" --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=DMSVPC-public-rt},{Key=project,Value=doc-smith},{Key=tutorial,Value=aws-database-migration-service-gs}]' --query 'RouteTable.RouteTableId' --output text) check_status echo "Route table created with ID: $PUBLIC_RT_ID" @@ -569,7 +568,8 @@ echo "Creating MariaDB parameter group: $DB_PARAM_GROUP_MARIADB" aws rds create-db-parameter-group \ --db-parameter-group-name "$DB_PARAM_GROUP_MARIADB" \ --db-parameter-group-family mariadb10.6 \ - --description "Group for specifying binary log settings for replication" + --description "Group for specifying binary log settings for replication" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-database-migration-service-gs check_status echo "Modifying MariaDB parameters..." @@ -584,7 +584,8 @@ echo "Creating PostgreSQL parameter group: $DB_PARAM_GROUP_POSTGRES" aws rds create-db-parameter-group \ --db-parameter-group-name "$DB_PARAM_GROUP_POSTGRES" \ --db-parameter-group-family postgres16 \ - --description "Group for specifying role setting for replication" + --description "Group for specifying role setting for replication" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-database-migration-service-gs check_status echo "Modifying PostgreSQL parameters..." @@ -593,13 +594,13 @@ aws rds modify-db-parameter-group \ --parameters "ParameterName=session_replication_role,ParameterValue=replica,ApplyMethod=immediate" check_status -# FIX: Create a custom DB subnet group instead of using the default one echo "Creating DB subnet group..." DB_SUBNET_GROUP="dms-db-subnet-group-$RANDOM_ID" aws rds create-db-subnet-group \ --db-subnet-group-name "$DB_SUBNET_GROUP" \ --db-subnet-group-description "DB subnet group for DMS tutorial" \ - --subnet-ids "$PUBLIC_SUBNET_1_ID" "$PUBLIC_SUBNET_2_ID" + --subnet-ids "$PUBLIC_SUBNET_1_ID" "$PUBLIC_SUBNET_2_ID" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-database-migration-service-gs check_status echo "DB subnet group created: $DB_SUBNET_GROUP" # Step 3: Create Your Source Amazon RDS Database (MariaDB) @@ -623,7 +624,8 @@ aws rds create-db-instance \ --db-name dms_sample \ --backup-retention-period 1 \ --no-auto-minor-version-upgrade \ - --publicly-accessible + --publicly-accessible \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-database-migration-service-gs check_status echo "Waiting for MariaDB instance to be available..." @@ -650,7 +652,8 @@ aws rds create-db-instance \ --db-name dms_sample \ --backup-retention-period 0 \ --no-auto-minor-version-upgrade \ - --publicly-accessible + --publicly-accessible \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-database-migration-service-gs check_status echo "Waiting for PostgreSQL instance to be available..." @@ -672,7 +675,7 @@ echo "Using AMI: $AMI_ID" # Create a key pair KEY_NAME="DMSKeyPair-$RANDOM_ID" echo "Creating key pair: $KEY_NAME" -aws ec2 create-key-pair --key-name "$KEY_NAME" --query 'KeyMaterial' --output text > "${KEY_NAME}.pem" +aws ec2 create-key-pair --key-name "$KEY_NAME" --tag-specifications 'ResourceType=key-pair,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=aws-database-migration-service-gs}]' --query 'KeyMaterial' --output text > "${KEY_NAME}.pem" check_status chmod 400 "${KEY_NAME}.pem" echo "Key pair created and saved to ${KEY_NAME}.pem" @@ -695,7 +698,7 @@ EC2_INSTANCE_ID=$(aws ec2 run-instances \ --key-name "$KEY_NAME" \ --subnet-id "$PUBLIC_SUBNET_1_ID" \ --security-group-ids "$SG_ID" \ - --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=DMSClient}]' \ + --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=DMSClient},{Key=project,Value=doc-smith},{Key=tutorial,Value=aws-database-migration-service-gs}]' \ --associate-public-ip-address \ --query 'Instances[0].InstanceId' \ --output text) @@ -801,7 +804,8 @@ if [[ "${RUN_MIGRATION,,}" == "y" ]]; then aws dms create-replication-subnet-group \ --replication-subnet-group-identifier "$DMS_SUBNET_GROUP" \ --replication-subnet-group-description "DMS subnet group" \ - --subnet-ids "$PUBLIC_SUBNET_1_ID" "$PUBLIC_SUBNET_2_ID" + --subnet-ids "$PUBLIC_SUBNET_1_ID" "$PUBLIC_SUBNET_2_ID" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-database-migration-service-gs check_status # Create a replication instance @@ -814,7 +818,8 @@ if [[ "${RUN_MIGRATION,,}" == "y" ]]; then --vpc-security-group-ids "$SG_ID" \ --replication-subnet-group-identifier "$DMS_SUBNET_GROUP" \ --availability-zone "$AZ1" \ - --no-publicly-accessible + --no-publicly-accessible \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-database-migration-service-gs check_status echo "Waiting for DMS replication instance to be available..." @@ -865,6 +870,7 @@ if [[ "${RUN_MIGRATION,,}" == "y" ]]; then --server-name "$MARIADB_ENDPOINT" \ --port 3306 \ --database-name dms_sample \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-database-migration-service-gs \ --query 'Endpoint.EndpointArn' \ --output text) check_status @@ -882,6 +888,7 @@ if [[ "${RUN_MIGRATION,,}" == "y" ]]; then --server-name "$POSTGRES_ENDPOINT" \ --port 5432 \ --database-name dms_sample \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-database-migration-service-gs \ --query 'Endpoint.EndpointArn' \ --output text) check_status @@ -964,6 +971,7 @@ if [[ "${RUN_MIGRATION,,}" == "y" ]]; then --migration-type full-load-and-cdc \ --table-mappings "$TABLE_MAPPINGS" \ --replication-task-settings "$TASK_SETTINGS" \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-database-migration-service-gs \ --query 'ReplicationTask.ReplicationTaskArn' \ --output text) check_status @@ -995,9 +1003,10 @@ if [[ "${RUN_MIGRATION,,}" == "y" ]]; then echo "Migration task started. Initial replication will take some time to complete." else - echo "Step 10: Skipping DMS migration task creation (as requested)" - echo "========================================================" - echo "Infrastructure is ready. You can create migration tasks later as needed." + echo "" + echo "✗ Skipping DMS migration setup" + echo "==============================" + echo "Infrastructure is ready. You can create DMS resources later as needed." echo "" fi @@ -1066,4 +1075,4 @@ print_resources cleanup_resources echo "Script completed successfully." -exit 0 +exit 0 \ No newline at end of file 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 1d5166c..d596131 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 @@ -44,6 +44,9 @@ MAX_RETRIES=3 RETRY_DELAY=2 API_CALL_DELAY=0.5 +# Tagging configuration +TAGS_KEY_VALUE="Key=project,Value=doc-smith Key=tutorial,Value=aws-account-management-gs" + # Function to handle errors safely handle_error() { local message="${1:-Error encountered}" @@ -310,6 +313,10 @@ done echo "- Sequential API execution to prevent rate limit errors" echo "- Applied rate limiting between API calls" echo "" + echo "Tagging configuration applied:" + echo "- Project tag: doc-smith" + echo "- Tutorial tag: aws-account-management-gs" + echo "" echo "See $LOG_FILE for detailed logs." } | tee -a "$LOG_FILE" diff --git a/tuts/078-amazon-elastic-container-registry-gs/amazon-elastic-container-registry-gs.sh b/tuts/078-amazon-elastic-container-registry-gs/amazon-elastic-container-registry-gs.sh index aad9bb9..5655c0c 100755 --- a/tuts/078-amazon-elastic-container-registry-gs/amazon-elastic-container-registry-gs.sh +++ b/tuts/078-amazon-elastic-container-registry-gs/amazon-elastic-container-registry-gs.sh @@ -159,7 +159,7 @@ fi # Create ECR repository echo "Creating ECR repository..." -REPO_RESULT=$(aws ecr create-repository --repository-name hello-repository) +REPO_RESULT=$(aws ecr create-repository --repository-name hello-repository --tags key=project,value=doc-smith key=tutorial,value=amazon-elastic-container-registry-gs) if [[ -z "$REPO_RESULT" || "$REPO_RESULT" == *"error"* ]]; then handle_error "Failed to create ECR repository" fi 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 b9fea7d..33d31c5 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 @@ -114,6 +114,8 @@ create_iam_role() { return 1 fi + aws iam tag-role --role-name "$ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-iot-device-defender-gs 2>&1 || true + # For IoT logging role, create an inline policy instead of using a managed policy if [[ "$ROLE_NAME" == "AWSIoTLoggingRole" ]]; then local LOGGING_POLICY @@ -377,6 +379,9 @@ fi echo "Audit task started with ID: $TASK_ID" CREATED_RESOURCES+=("Audit Task: $TASK_ID") +# Tag the audit task via IoT service +aws iot tag-resource --resource-arn "arn:aws:iot:$(aws configure get region):${ACCOUNT_ID}:audittask/${TASK_ID}" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-iot-device-defender-gs 2>&1 || true + # Wait for the audit task to complete echo "Waiting for audit task to complete (this may take a few minutes)..." TASK_STATUS="IN_PROGRESS" @@ -486,6 +491,7 @@ if validate_json "$MITIGATION_RESULT"; then MITIGATION_ACTION_ARN=$(extract_json_value "$MITIGATION_RESULT" "actionArn") if [ -n "$MITIGATION_ACTION_ARN" ]; then echo "Mitigation Action ARN: $MITIGATION_ACTION_ARN" + aws iot tag-resource --resource-arn "$MITIGATION_ACTION_ARN" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-iot-device-defender-gs 2>&1 || true fi else echo "WARNING: Could not validate mitigation action response, but action may have been created" @@ -565,7 +571,7 @@ if [ -n "$TOPIC_ARN" ]; then echo "Topic ARN: $TOPIC_ARN" else echo "Creating SNS topic for notifications..." - SNS_RESULT=$(aws sns create-topic --name "IoTDDNotifications" --output json 2>&1) || true + SNS_RESULT=$(aws sns create-topic --name "IoTDDNotifications" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-iot-device-defender-gs --output json 2>&1) || true if ! check_error "$SNS_RESULT"; then echo "WARNING: Failed to create SNS topic, continuing..." diff --git a/tuts/080-aws-step-functions-gs/aws-step-functions-gs.sh b/tuts/080-aws-step-functions-gs/aws-step-functions-gs.sh index b18e79d..532c213 100644 --- a/tuts/080-aws-step-functions-gs/aws-step-functions-gs.sh +++ b/tuts/080-aws-step-functions-gs/aws-step-functions-gs.sh @@ -371,6 +371,7 @@ ROLE_RESULT=$(aws iam create-role \ check_api_error "$ROLE_RESULT" "Create IAM role" echo "Role created successfully" +aws iam tag-role --role-name "$ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-step-functions-gs # Get the role ARN ROLE_ARN=$(extract_json_field "$ROLE_RESULT" ".Role.Arn") @@ -435,7 +436,8 @@ SM_RESULT=$(aws stepfunctions create-state-machine \ --name "$STATE_MACHINE_NAME" \ --definition file://hello-world.json \ --role-arn "$ROLE_ARN" \ - --type STANDARD 2>&1) + --type STANDARD \ + --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-step-functions-gs 2>&1) check_api_error "$SM_RESULT" "Create state machine" echo "State machine created successfully" diff --git a/tuts/081-aws-elemental-mediaconnect-gs/aws-elemental-mediaconnect-gs.sh b/tuts/081-aws-elemental-mediaconnect-gs/aws-elemental-mediaconnect-gs.sh index b7806f7..1d2c058 100644 --- a/tuts/081-aws-elemental-mediaconnect-gs/aws-elemental-mediaconnect-gs.sh +++ b/tuts/081-aws-elemental-mediaconnect-gs/aws-elemental-mediaconnect-gs.sh @@ -18,6 +18,9 @@ exec > >(tee -a "$LOG_FILE") 2>&1 echo "Starting AWS Elemental MediaConnect tutorial script at $(date)" echo "All commands and outputs will be logged to $LOG_FILE" +# Tags for all resources +TAGS_ARRAY=("Key=project,Value=doc-smith" "Key=tutorial,Value=aws-elemental-mediaconnect-gs") + # Function to handle errors handle_error() { echo "ERROR: $1" >&2 @@ -71,6 +74,18 @@ extract_json_value() { fi } +# Function to tag MediaConnect resource +tag_mediaconnect_resource() { + local resource_arn="$1" + echo "Tagging resource: $resource_arn" + + for tag in "${TAGS_ARRAY[@]}"; do + if ! aws mediaconnect tag-resource --resource-arn "$resource_arn" --tags "$tag" 2>&1; then + echo "WARNING: Failed to apply tag $tag to resource" + fi + done +} + # Function to clean up resources cleanup_resources() { echo "Cleaning up resources..." @@ -206,6 +221,9 @@ if [[ ! "$FLOW_ARN" =~ ^arn:aws:mediaconnect:[a-z0-9-]+:[0-9]+:flow:[a-zA-Z0-9:- handle_error "Invalid Flow ARN format: $FLOW_ARN" fi +# Tag the flow +tag_mediaconnect_resource "$FLOW_ARN" + # Step 3: Add an output echo "Step 3: Adding an output to the flow..." add_output_output="" @@ -226,6 +244,7 @@ if [ -z "$output_arn" ]; then else OUTPUT_ARN="$output_arn" echo "Output ARN: $OUTPUT_ARN" + tag_mediaconnect_resource "$OUTPUT_ARN" fi # Step 4: Grant an entitlement @@ -250,6 +269,7 @@ if [ -z "$entitlement_arn" ]; then else ENTITLEMENT_ARN="$entitlement_arn" echo "Entitlement ARN: $ENTITLEMENT_ARN" + tag_mediaconnect_resource "$ENTITLEMENT_ARN" fi # Step 5: List entitlements to share with affiliates diff --git a/tuts/082-amazon-polly-gs/amazon-polly-getting-started.sh b/tuts/082-amazon-polly-gs/amazon-polly-getting-started.sh index 9a4b12f..8025dba 100644 --- a/tuts/082-amazon-polly-gs/amazon-polly-getting-started.sh +++ b/tuts/082-amazon-polly-gs/amazon-polly-getting-started.sh @@ -161,7 +161,13 @@ EOF # Upload the lexicon echo "Uploading lexicon..." | tee -a "$LOG_FILE" -log_cmd "aws polly put-lexicon --name '$LEXICON_NAME' --content file://'$LEXICON_FILE'" || true +LEXICON_ARN=$(aws polly put-lexicon --name "$LEXICON_NAME" --content file://"$LEXICON_FILE" --query 'LexiconArn' --output text 2>&1) || true +if [[ -n "$LEXICON_ARN" && "$LEXICON_ARN" != "" && ! "$LEXICON_ARN" =~ error ]]; then + echo "Lexicon uploaded with ARN: $LEXICON_ARN" | tee -a "$LOG_FILE" + aws polly tag-resource --resource-arn "$LEXICON_ARN" --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-polly-gs 2>&1 | tee -a "$LOG_FILE" || true +else + echo "Lexicon uploaded." | tee -a "$LOG_FILE" +fi # List available lexicons echo "Listing available lexicons..." | tee -a "$LOG_FILE" diff --git a/tuts/085-amazon-ecs-service-connect/amazon-ecs-service-connect.sh b/tuts/085-amazon-ecs-service-connect/amazon-ecs-service-connect.sh index 813ab62..a15c99a 100755 --- a/tuts/085-amazon-ecs-service-connect/amazon-ecs-service-connect.sh +++ b/tuts/085-amazon-ecs-service-connect/amazon-ecs-service-connect.sh @@ -130,7 +130,8 @@ setup_default_vpc_infrastructure() { SG_OUTPUT=$(aws ec2 create-security-group \ --group-name "${ENV_PREFIX}-ecs-sg-${RANDOM_SUFFIX}" \ --description "Security group for ECS Service Connect tutorial" \ - --vpc-id "$VPC_ID" 2>&1) + --vpc-id "$VPC_ID" \ + --tag-specifications 'ResourceType=security-group,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-ecs-service-connect}]' 2>&1) check_for_errors "$SG_OUTPUT" "create-security-group" SECURITY_GROUP_ID=$(echo "$SG_OUTPUT" | grep -o '"GroupId": "[^"]*"' | cut -d'"' -f4) track_resource "SG:$SECURITY_GROUP_ID" @@ -157,7 +158,7 @@ create_log_groups() { log "Creating CloudWatch log groups..." # Create log group for nginx container - aws logs create-log-group --log-group-name "/ecs/service-connect-nginx" 2>&1 | grep -v "ResourceAlreadyExistsException" || { + aws logs create-log-group --log-group-name "/ecs/service-connect-nginx" --tags project=doc-smith,tutorial=amazon-ecs-service-connect 2>&1 | grep -v "ResourceAlreadyExistsException" || { if [ ${PIPESTATUS[0]} -eq 0 ]; then log "Log group /ecs/service-connect-nginx created" track_resource "LOG_GROUP:/ecs/service-connect-nginx" @@ -167,7 +168,7 @@ create_log_groups() { } # Create log group for service connect proxy - aws logs create-log-group --log-group-name "/ecs/service-connect-proxy" 2>&1 | grep -v "ResourceAlreadyExistsException" || { + aws logs create-log-group --log-group-name "/ecs/service-connect-proxy" --tags project=doc-smith,tutorial=amazon-ecs-service-connect 2>&1 | grep -v "ResourceAlreadyExistsException" || { if [ ${PIPESTATUS[0]} -eq 0 ]; then log "Log group /ecs/service-connect-proxy created" track_resource "LOG_GROUP:/ecs/service-connect-proxy" @@ -184,7 +185,7 @@ create_ecs_cluster() { CLUSTER_OUTPUT=$(aws ecs create-cluster \ --cluster-name "$CLUSTER_NAME" \ --service-connect-defaults namespace="$NAMESPACE_NAME" \ - --tags key=Environment,value=tutorial 2>&1) + --tags key=Environment,value=tutorial key=project,value=doc-smith key=tutorial,value=amazon-ecs-service-connect 2>&1) check_for_errors "$CLUSTER_OUTPUT" "create-cluster" track_resource "CLUSTER:$CLUSTER_NAME" @@ -228,6 +229,7 @@ create_iam_roles() { --role-name ecsTaskExecutionRole \ --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy >/dev/null 2>&1 track_resource "ROLE:ecsTaskExecutionRole" + aws iam tag-role --role-name ecsTaskExecutionRole --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-ecs-service-connect log "Created ecsTaskExecutionRole" sleep 10 fi @@ -259,6 +261,7 @@ EOF --assume-role-policy-document file:///tmp/ecs-task-trust-policy.json >/dev/null track_resource "IAM_ROLE:ecsTaskRole" + aws iam tag-role --role-name ecsTaskRole --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-ecs-service-connect log "Created ecsTaskRole" # Wait for role to be available @@ -363,6 +366,14 @@ create_ecs_service() { { "key": "Environment", "value": "tutorial" + }, + { + "key": "project", + "value": "doc-smith" + }, + { + "key": "tutorial", + "value": "amazon-ecs-service-connect" } ] } diff --git a/tuts/086-amazon-ecs-fargate-linux/amazon-ecs-fargate-linux.sh b/tuts/086-amazon-ecs-fargate-linux/amazon-ecs-fargate-linux.sh index 49ca735..03cb4ab 100644 --- a/tuts/086-amazon-ecs-fargate-linux/amazon-ecs-fargate-linux.sh +++ b/tuts/086-amazon-ecs-fargate-linux/amazon-ecs-fargate-linux.sh @@ -311,6 +311,8 @@ EOF execute_command "aws iam create-role --role-name ecsTaskExecutionRole --assume-role-policy-document file://trust-policy.json" "Create ECS task execution role" + aws iam tag-role --role-name ecsTaskExecutionRole --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-ecs-fargate-linux + execute_command "aws iam attach-role-policy --role-name ecsTaskExecutionRole --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" "Attach ECS task execution policy" # Clean up temporary file securely @@ -325,7 +327,7 @@ echo "===========================================" echo "STEP 2: CREATE ECS CLUSTER" echo "===========================================" -CLUSTER_OUTPUT=$(execute_command "aws ecs create-cluster --cluster-name '$CLUSTER_NAME'" "Create ECS cluster") +CLUSTER_OUTPUT=$(execute_command "aws ecs create-cluster --cluster-name '$CLUSTER_NAME' --tags key=project,value=doc-smith key=tutorial,value=amazon-ecs-fargate-linux" "Create ECS cluster") check_for_aws_errors "$CLUSTER_OUTPUT" "Create ECS cluster" CREATED_RESOURCES+=("ECS Cluster: $CLUSTER_NAME") @@ -413,7 +415,7 @@ echo "Using default VPC: $VPC_ID" # Create security group with restricted access # Note: This allows HTTP access from anywhere for demo purposes # In production, restrict source to specific IP ranges or security groups -SECURITY_GROUP_OUTPUT=$(execute_command "aws ec2 create-security-group --group-name '$SECURITY_GROUP_NAME' --description 'Security group for ECS Fargate tutorial - HTTP access' --vpc-id '$VPC_ID'" "Create security group") +SECURITY_GROUP_OUTPUT=$(execute_command "aws ec2 create-security-group --group-name '$SECURITY_GROUP_NAME' --description 'Security group for ECS Fargate tutorial - HTTP access' --vpc-id '$VPC_ID' --tag-specifications 'ResourceType=security-group,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-ecs-fargate-linux}]'" "Create security group") check_for_aws_errors "$SECURITY_GROUP_OUTPUT" "Create security group" SECURITY_GROUP_ID=$(echo "$SECURITY_GROUP_OUTPUT" | grep -o '"GroupId": "[^"]*"' | head -1 | cut -d'"' -f4) @@ -461,7 +463,7 @@ echo "STEP 5: CREATE ECS SERVICE" echo "===========================================" # Create the service with proper JSON formatting for network configuration -SERVICE_CMD="aws ecs create-service --cluster '$CLUSTER_NAME' --service-name '$SERVICE_NAME' --task-definition '$TASK_FAMILY' --desired-count 1 --launch-type FARGATE --network-configuration '{\"awsvpcConfiguration\":{\"subnets\":[\"$(echo "$SUBNET_IDS_COMMA" | sed 's/,/","/g')\"],\"securityGroups\":[\"$SECURITY_GROUP_ID\"],\"assignPublicIp\":\"ENABLED\"}}'" +SERVICE_CMD="aws ecs create-service --cluster '$CLUSTER_NAME' --service-name '$SERVICE_NAME' --task-definition '$TASK_FAMILY' --desired-count 1 --launch-type FARGATE --network-configuration '{\"awsvpcConfiguration\":{\"subnets\":[\"$(echo "$SUBNET_IDS_COMMA" | sed 's/,/","/g')\"],\"securityGroups\":[\"$SECURITY_GROUP_ID\"],\"assignPublicIp\":\"ENABLED\"}}' --tags key=project,value=doc-smith key=tutorial,value=amazon-ecs-fargate-linux" echo "Service creation command: $SERVICE_CMD" diff --git a/tuts/087-apigateway-lambda-integration/apigateway-lambda-integration.sh b/tuts/087-apigateway-lambda-integration/apigateway-lambda-integration.sh index 1c9f3f7..bf2daf0 100644 --- a/tuts/087-apigateway-lambda-integration/apigateway-lambda-integration.sh +++ b/tuts/087-apigateway-lambda-integration/apigateway-lambda-integration.sh @@ -127,6 +127,8 @@ aws iam create-role \ exit 1 } +aws iam tag-role --role-name "$ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=apigateway-lambda-integration + # Attach execution policy aws iam attach-role-policy \ --role-name "$ROLE_NAME" \ @@ -149,7 +151,8 @@ aws lambda create-function \ --zip-file fileb://function.zip \ --timeout 30 \ --memory-size 128 \ - --environment "Variables={LOG_LEVEL=INFO}" || { + --environment "Variables={LOG_LEVEL=INFO}" \ + --tags project=doc-smith,tutorial=apigateway-lambda-integration || { echo "Error: Failed to create Lambda function" >&2 exit 1 } @@ -161,6 +164,7 @@ API_RESPONSE=$(aws apigateway create-rest-api \ --name "$API_NAME" \ --endpoint-configuration types=REGIONAL \ --description "API for Lambda proxy integration tutorial" \ + --tags project=doc-smith,tutorial=apigateway-lambda-integration \ --output json) API_ID=$(echo "$API_RESPONSE" | grep -o '"id": "[^"]*"' | head -1 | cut -d'"' -f4)