There are more AWS SDK examples available in the AWS Doc SDK Examples
Amazon SNS examples using AWS CLI with Bash script
The following code examples show you how to perform actions and implement common scenarios by using the AWS Command Line Interface with Bash script with Amazon SNS.
Scenarios are code examples that show you how to accomplish specific tasks by calling multiple functions within a service or combined with other AWS services.
Each example includes a link to the complete source code, where you can find instructions on how to set up and run the code in context.
Topics
Scenarios
The following code example shows how to:
Create an Amazon SNS topic
Subscribe an email endpoint to the topic
Verify your subscription
Publish a message to the topic
Clean up resources
- AWS CLI with Bash script
-
Note
There's more on GitHub. Find the complete example and learn how to set up and run in the Sample developer tutorials
repository. #!/bin/bash # Amazon SNS Getting Started Script # This script demonstrates how to create an SNS topic, subscribe to it, publish a message, # and clean up resources. # Set up logging LOG_FILE="sns-tutorial.log" exec > >(tee -a "$LOG_FILE") 2>&1 echo "Starting Amazon SNS Getting Started Tutorial..." echo "$(date)" echo "==============================================" # Function to handle errors handle_error() { echo "ERROR: $1" echo "Attempting to clean up resources..." cleanup_resources exit 1 } # Function to clean up resources cleanup_resources() { if [ -n "$SUBSCRIPTION_ARN" ] && [ "$SUBSCRIPTION_ARN" != "pending confirmation" ]; then echo "Deleting subscription: $SUBSCRIPTION_ARN" aws sns unsubscribe --subscription-arn "$SUBSCRIPTION_ARN" fi if [ -n "$TOPIC_ARN" ]; then echo "Deleting topic: $TOPIC_ARN" aws sns delete-topic --topic-arn "$TOPIC_ARN" fi } # Generate a random topic name suffix RANDOM_SUFFIX=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 8 | head -n 1) TOPIC_NAME="my-topic-${RANDOM_SUFFIX}" # Step 1: Create an SNS topic echo "Creating SNS topic: $TOPIC_NAME" TOPIC_RESULT=$(aws sns create-topic --name "$TOPIC_NAME") # Check for errors if echo "$TOPIC_RESULT" | grep -i "error" > /dev/null; then handle_error "Failed to create SNS topic: $TOPIC_RESULT" fi # Extract the topic ARN TOPIC_ARN=$(echo "$TOPIC_RESULT" | grep -o '"TopicArn": "[^"]*' | cut -d'"' -f4) if [ -z "$TOPIC_ARN" ]; then handle_error "Failed to extract topic ARN from result: $TOPIC_RESULT" fi echo "Successfully created topic with ARN: $TOPIC_ARN" # Step 2: Subscribe to the topic echo "" echo "==============================================" echo "EMAIL SUBSCRIPTION" echo "==============================================" echo "Please enter your email address to subscribe to the topic:" read -r EMAIL_ADDRESS echo "Subscribing email: $EMAIL_ADDRESS to topic" SUBSCRIPTION_RESULT=$(aws sns subscribe \ --topic-arn "$TOPIC_ARN" \ --protocol email \ --notification-endpoint "$EMAIL_ADDRESS") # Check for errors if echo "$SUBSCRIPTION_RESULT" | grep -i "error" > /dev/null; then handle_error "Failed to create subscription: $SUBSCRIPTION_RESULT" fi # Extract the subscription ARN (will be "pending confirmation") SUBSCRIPTION_ARN=$(echo "$SUBSCRIPTION_RESULT" | grep -o '"SubscriptionArn": "[^"]*' | cut -d'"' -f4) echo "Subscription created: $SUBSCRIPTION_ARN" echo "A confirmation email has been sent to $EMAIL_ADDRESS" echo "Please check your email and confirm the subscription." echo "" echo "Waiting for you to confirm the subscription..." echo "Press Enter after you have confirmed the subscription to continue:" read -r # Step 3: List subscriptions to verify echo "Listing subscriptions for topic: $TOPIC_ARN" SUBSCRIPTIONS=$(aws sns list-subscriptions-by-topic --topic-arn "$TOPIC_ARN") # Check for errors if echo "$SUBSCRIPTIONS" | grep -i "error" > /dev/null; then handle_error "Failed to list subscriptions: $SUBSCRIPTIONS" fi echo "Current subscriptions:" echo "$SUBSCRIPTIONS" # Get the confirmed subscription ARN SUBSCRIPTION_ARN=$(echo "$SUBSCRIPTIONS" | grep -o '"SubscriptionArn": "[^"]*' | grep -v "pending confirmation" | head -1 | cut -d'"' -f4) if [ -z "$SUBSCRIPTION_ARN" ] || [ "$SUBSCRIPTION_ARN" == "pending confirmation" ]; then echo "Warning: No confirmed subscription found. You may not have confirmed the subscription yet." echo "The script will continue, but you may not receive the test message." fi # Step 4: Publish a message to the topic echo "" echo "Publishing a test message to the topic" MESSAGE="Hello from Amazon SNS! This is a test message sent at $(date)." PUBLISH_RESULT=$(aws sns publish \ --topic-arn "$TOPIC_ARN" \ --message "$MESSAGE") # Check for errors if echo "$PUBLISH_RESULT" | grep -i "error" > /dev/null; then handle_error "Failed to publish message: $PUBLISH_RESULT" fi MESSAGE_ID=$(echo "$PUBLISH_RESULT" | grep -o '"MessageId": "[^"]*' | cut -d'"' -f4) echo "Message published successfully with ID: $MESSAGE_ID" echo "Check your email for the message." # Pause to allow the user to check their email echo "" echo "Pausing for 10 seconds to allow message delivery..." sleep 10 # Step 5: Clean up resources echo "" echo "==============================================" echo "CLEANUP CONFIRMATION" echo "==============================================" echo "Resources created:" echo "- SNS Topic: $TOPIC_ARN" echo "- Subscription: $SUBSCRIPTION_ARN" echo "" echo "Do you want to clean up all created resources? (y/n):" read -r CLEANUP_CHOICE if [[ "$CLEANUP_CHOICE" =~ ^[Yy]$ ]]; then echo "Cleaning up resources..." cleanup_resources echo "Cleanup completed successfully." else echo "Skipping cleanup. Resources will remain in your AWS account." echo "To clean up later, use the following commands:" echo "aws sns unsubscribe --subscription-arn $SUBSCRIPTION_ARN" echo "aws sns delete-topic --topic-arn $TOPIC_ARN" fi echo "" echo "Tutorial completed successfully!" echo "$(date)" echo "=============================================="-
For API details, see the following topics in AWS CLI Command Reference.
-
The following code example shows how to:
Create Required IAM Roles
Enable IoT Device Defender Audit Checks
Run an On-Demand Audit
Create a Mitigation Action
Apply Mitigation Actions to Findings
Set Up SNS Notifications (Optional)
Enable IoT Logging
- AWS CLI with Bash script
-
Note
There's more on GitHub. Find the complete example and learn how to set up and run in the Sample developer tutorials
repository. #!/bin/bash # AWS IoT Device Defender Getting Started Script # This script demonstrates how to use AWS IoT Device Defender to enable audit checks, # view audit results, create mitigation actions, and apply them to findings. # Set up logging LOG_FILE="iot-device-defender-script-$(date +%Y%m%d%H%M%S).log" exec > >(tee -a "$LOG_FILE") 2>&1 echo "===================================================" echo "AWS IoT Device Defender Getting Started Script" echo "===================================================" echo "Starting script execution at $(date)" echo "" # Function to check for errors in command output check_error() { if echo "$1" | grep -i "An error occurred\|Exception\|Failed\|usage: aws" > /dev/null; then echo "ERROR: Command failed with the following output:" echo "$1" return 1 fi return 0 } # Function to create IAM roles create_iam_role() { local ROLE_NAME=$1 local TRUST_POLICY=$2 local MANAGED_POLICY=$3 echo "Creating IAM role: $ROLE_NAME" # Check if role already exists ROLE_EXISTS=$(aws iam get-role --role-name "$ROLE_NAME" 2>&1 || echo "NOT_EXISTS") if echo "$ROLE_EXISTS" | grep -i "NoSuchEntity" > /dev/null; then # Create the role with trust policy ROLE_RESULT=$(aws iam create-role \ --role-name "$ROLE_NAME" \ --assume-role-policy-document "$TRUST_POLICY" 2>&1) if ! check_error "$ROLE_RESULT"; then echo "Failed to create role $ROLE_NAME" return 1 fi # For IoT logging role, create an inline policy instead of using a managed policy if [[ "$ROLE_NAME" == "AWSIoTLoggingRole" ]]; then LOGGING_POLICY='{ "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", "logs:PutMetricFilter", "logs:PutRetentionPolicy", "logs:GetLogEvents", "logs:DescribeLogStreams" ], "Resource": [ "arn:aws:logs:*:*:*" ] } ] }' POLICY_RESULT=$(aws iam put-role-policy \ --role-name "$ROLE_NAME" \ --policy-name "${ROLE_NAME}Policy" \ --policy-document "$LOGGING_POLICY" 2>&1) if ! check_error "$POLICY_RESULT"; then echo "Failed to attach inline policy to role $ROLE_NAME" return 1 fi elif [[ "$ROLE_NAME" == "IoTMitigationActionErrorLoggingRole" ]]; then MITIGATION_POLICY='{ "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "iot:UpdateCACertificate", "iot:UpdateCertificate", "iot:SetV2LoggingOptions", "iot:SetLoggingOptions", "iot:AddThingToThingGroup", "iot:PublishToTopic" ], "Resource": "*" }, { "Effect": "Allow", "Action": "iam:PassRole", "Resource": "*", "Condition": { "StringEquals": { "iam:PassedToService": "iot.amazonaws.com" } } } ] }' POLICY_RESULT=$(aws iam put-role-policy \ --role-name "$ROLE_NAME" \ --policy-name "${ROLE_NAME}Policy" \ --policy-document "$MITIGATION_POLICY" 2>&1) if ! check_error "$POLICY_RESULT"; then echo "Failed to attach inline policy to role $ROLE_NAME" return 1 fi else # Attach managed policy to role if provided if [ -n "$MANAGED_POLICY" ]; then ATTACH_RESULT=$(aws iam attach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "$MANAGED_POLICY" 2>&1) if ! check_error "$ATTACH_RESULT"; then echo "Failed to attach policy to role $ROLE_NAME" return 1 fi fi fi echo "Role $ROLE_NAME created successfully" else echo "Role $ROLE_NAME already exists, skipping creation" fi # Get the role ARN ROLE_ARN=$(aws iam get-role --role-name "$ROLE_NAME" --query 'Role.Arn' --output text) echo "Role ARN: $ROLE_ARN" return 0 } # Array to store created resources for cleanup declare -a CREATED_RESOURCES # Step 1: Create IAM roles needed for the tutorial echo "===================================================" echo "Step 1: Creating required IAM roles" echo "===================================================" # Create IoT Device Defender Audit role IOT_DEFENDER_AUDIT_TRUST_POLICY='{ "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "iot.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }' create_iam_role "AWSIoTDeviceDefenderAuditRole" "$IOT_DEFENDER_AUDIT_TRUST_POLICY" "arn:aws:iam::aws:policy/service-role/AWSIoTDeviceDefenderAudit" AUDIT_ROLE_ARN=$ROLE_ARN CREATED_RESOURCES+=("IAM Role: AWSIoTDeviceDefenderAuditRole") # Create IoT Logging role IOT_LOGGING_TRUST_POLICY='{ "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "iot.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }' create_iam_role "AWSIoTLoggingRole" "$IOT_LOGGING_TRUST_POLICY" "" LOGGING_ROLE_ARN=$ROLE_ARN CREATED_RESOURCES+=("IAM Role: AWSIoTLoggingRole") # Create IoT Mitigation Action role IOT_MITIGATION_TRUST_POLICY='{ "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "iot.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }' create_iam_role "IoTMitigationActionErrorLoggingRole" "$IOT_MITIGATION_TRUST_POLICY" "" MITIGATION_ROLE_ARN=$ROLE_ARN CREATED_RESOURCES+=("IAM Role: IoTMitigationActionErrorLoggingRole") # Step 2: Enable audit checks echo "" echo "===================================================" echo "Step 2: Enabling AWS IoT Device Defender audit checks" echo "===================================================" # Get current audit configuration echo "Getting current audit configuration..." CURRENT_CONFIG=$(aws iot describe-account-audit-configuration) echo "$CURRENT_CONFIG" # Enable specific audit checks echo "Enabling audit checks..." UPDATE_RESULT=$(aws iot update-account-audit-configuration \ --role-arn "$AUDIT_ROLE_ARN" \ --audit-check-configurations '{"LOGGING_DISABLED_CHECK":{"enabled":true}}') if ! check_error "$UPDATE_RESULT"; then echo "Failed to update audit configuration" exit 1 fi echo "Audit checks enabled successfully" # Step 3: Run an on-demand audit echo "" echo "===================================================" echo "Step 3: Running an on-demand audit" echo "===================================================" echo "Starting on-demand audit task..." AUDIT_TASK_RESULT=$(aws iot start-on-demand-audit-task \ --target-check-names LOGGING_DISABLED_CHECK) if ! check_error "$AUDIT_TASK_RESULT"; then echo "Failed to start on-demand audit task" exit 1 fi TASK_ID=$(echo "$AUDIT_TASK_RESULT" | grep -o '"taskId": "[^"]*' | cut -d'"' -f4) echo "Audit task started with ID: $TASK_ID" CREATED_RESOURCES+=("Audit Task: $TASK_ID") # Wait for the audit task to complete echo "Waiting for audit task to complete (this may take a few minutes)..." TASK_STATUS="IN_PROGRESS" while [ "$TASK_STATUS" != "COMPLETED" ]; do sleep 10 TASK_DETAILS=$(aws iot describe-audit-task --task-id "$TASK_ID") TASK_STATUS=$(echo "$TASK_DETAILS" | grep -o '"taskStatus": "[^"]*' | cut -d'"' -f4) echo "Current task status: $TASK_STATUS" if [ "$TASK_STATUS" == "FAILED" ]; then echo "Audit task failed" exit 1 fi done echo "Audit task completed successfully" # Get audit findings echo "Getting audit findings..." FINDINGS=$(aws iot list-audit-findings \ --task-id "$TASK_ID") echo "Audit findings:" echo "$FINDINGS" # Check if we have any non-compliant findings if echo "$FINDINGS" | grep -q '"findingId"'; then FINDING_ID=$(echo "$FINDINGS" | grep -o '"findingId": "[^"]*' | head -1 | cut -d'"' -f4) echo "Found non-compliant finding with ID: $FINDING_ID" HAS_FINDINGS=true else echo "No non-compliant findings detected" HAS_FINDINGS=false fi # Step 4: Create a mitigation action echo "" echo "===================================================" echo "Step 4: Creating a mitigation action" echo "===================================================" # Check if mitigation action already exists MITIGATION_EXISTS=$(aws iot list-mitigation-actions --action-name "EnableErrorLoggingAction" 2>&1) if echo "$MITIGATION_EXISTS" | grep -q "EnableErrorLoggingAction"; then echo "Mitigation action 'EnableErrorLoggingAction' already exists, deleting it first..." aws iot delete-mitigation-action --action-name "EnableErrorLoggingAction" # Wait a moment for deletion to complete sleep 5 fi echo "Creating mitigation action to enable AWS IoT logging..." MITIGATION_RESULT=$(aws iot create-mitigation-action \ --action-name "EnableErrorLoggingAction" \ --role-arn "$MITIGATION_ROLE_ARN" \ --action-params "{\"enableIoTLoggingParams\":{\"roleArnForLogging\":\"$LOGGING_ROLE_ARN\",\"logLevel\":\"ERROR\"}}") echo "$MITIGATION_RESULT" if ! check_error "$MITIGATION_RESULT"; then echo "Failed to create mitigation action" exit 1 fi echo "Mitigation action created successfully" CREATED_RESOURCES+=("Mitigation Action: EnableErrorLoggingAction") # Step 5: Apply mitigation action to findings (if any) if [ "$HAS_FINDINGS" = true ]; then echo "" echo "===================================================" echo "Step 5: Applying mitigation action to findings" echo "===================================================" MITIGATION_TASK_ID="MitigationTask-$(date +%s)" echo "Starting mitigation actions task with ID: $MITIGATION_TASK_ID" MITIGATION_TASK_RESULT=$(aws iot start-audit-mitigation-actions-task \ --task-id "$MITIGATION_TASK_ID" \ --target "{\"findingIds\":[\"$FINDING_ID\"]}" \ --audit-check-to-actions-mapping "{\"LOGGING_DISABLED_CHECK\":[\"EnableErrorLoggingAction\"]}") if ! check_error "$MITIGATION_TASK_RESULT"; then echo "Failed to start mitigation actions task" exit 1 fi echo "Mitigation actions task started successfully" CREATED_RESOURCES+=("Mitigation Task: $MITIGATION_TASK_ID") # Wait for the mitigation task to complete echo "Waiting for mitigation task to complete..." sleep 10 # Use a more reliable date format for the API call START_TIME=$(date -u -d 'today' '+%Y-%m-%dT%H:%M:%S.000Z') END_TIME=$(date -u '+%Y-%m-%dT%H:%M:%S.000Z') MITIGATION_TASKS=$(aws iot list-audit-mitigation-actions-tasks \ --start-time "$START_TIME" \ --end-time "$END_TIME" 2>&1) if check_error "$MITIGATION_TASKS"; then echo "Mitigation tasks:" echo "$MITIGATION_TASKS" else echo "Could not retrieve mitigation task status, but task was started successfully" fi else echo "" echo "===================================================" echo "Step 5: Skipping mitigation action application (no findings)" echo "===================================================" fi # Step 6: Set up SNS notifications (optional) echo "" echo "===================================================" echo "Step 6: Setting up SNS notifications" echo "===================================================" # Check if SNS topic already exists SNS_TOPICS=$(aws sns list-topics) if echo "$SNS_TOPICS" | grep -q "IoTDDNotifications"; then echo "SNS topic 'IoTDDNotifications' already exists, using existing topic..." TOPIC_ARN=$(echo "$SNS_TOPICS" | grep -o '"TopicArn": "[^"]*IoTDDNotifications' | cut -d'"' -f4) else echo "Creating SNS topic for notifications..." SNS_RESULT=$(aws sns create-topic --name "IoTDDNotifications") if ! check_error "$SNS_RESULT"; then echo "Failed to create SNS topic" exit 1 fi TOPIC_ARN=$(echo "$SNS_RESULT" | grep -o '"TopicArn": "[^"]*' | cut -d'"' -f4) echo "SNS topic created with ARN: $TOPIC_ARN" CREATED_RESOURCES+=("SNS Topic: IoTDDNotifications") fi echo "Updating audit configuration to enable SNS notifications..." SNS_UPDATE_RESULT=$(aws iot update-account-audit-configuration \ --audit-notification-target-configurations "{\"SNS\":{\"targetArn\":\"$TOPIC_ARN\",\"roleArn\":\"$AUDIT_ROLE_ARN\",\"enabled\":true}}") if ! check_error "$SNS_UPDATE_RESULT"; then echo "Failed to update audit configuration for SNS notifications" exit 1 fi echo "SNS notifications enabled successfully" # Step 7: Enable AWS IoT logging echo "" echo "===================================================" echo "Step 7: Enabling AWS IoT logging" echo "===================================================" echo "Setting up AWS IoT logging options..." # Create the logging options payload LOGGING_OPTIONS_PAYLOAD="{\"roleArn\":\"$LOGGING_ROLE_ARN\",\"logLevel\":\"ERROR\"}" LOGGING_RESULT=$(aws iot set-v2-logging-options \ --role-arn "$LOGGING_ROLE_ARN" \ --default-log-level "ERROR" 2>&1) if ! check_error "$LOGGING_RESULT"; then echo "Failed to set up AWS IoT v2 logging, trying v1 logging..." # Try the older set-logging-options command with proper payload format LOGGING_RESULT_V1=$(aws iot set-logging-options \ --logging-options-payload "$LOGGING_OPTIONS_PAYLOAD" 2>&1) if ! check_error "$LOGGING_RESULT_V1"; then echo "Failed to set up AWS IoT logging with both v1 and v2 methods" echo "V2 result: $LOGGING_RESULT" echo "V1 result: $LOGGING_RESULT_V1" exit 1 else echo "AWS IoT v1 logging enabled successfully" fi else echo "AWS IoT v2 logging enabled successfully" fi # Verify logging is enabled echo "Verifying logging configuration..." LOGGING_CONFIG=$(aws iot get-v2-logging-options 2>&1) if check_error "$LOGGING_CONFIG"; then echo "V2 Logging configuration:" echo "$LOGGING_CONFIG" else echo "Checking v1 logging configuration..." LOGGING_CONFIG_V1=$(aws iot get-logging-options 2>&1) if check_error "$LOGGING_CONFIG_V1"; then echo "V1 Logging configuration:" echo "$LOGGING_CONFIG_V1" else echo "Could not retrieve logging configuration" fi fi # Script completed successfully echo "" echo "===================================================" echo "AWS IoT Device Defender setup completed successfully!" echo "===================================================" echo "The following resources were created:" for resource in "${CREATED_RESOURCES[@]}"; do echo "- $resource" done echo "" # Ask if user wants to clean up resources echo "===========================================" echo "CLEANUP CONFIRMATION" echo "===========================================" echo "Do you want to clean up all created resources? (y/n): " read -r CLEANUP_CHOICE if [[ $CLEANUP_CHOICE =~ ^[Yy]$ ]]; then echo "Starting cleanup process..." # Disable AWS IoT logging echo "Disabling AWS IoT logging..." # Try to disable v2 logging first DISABLE_V2_RESULT=$(aws iot set-v2-logging-options \ --default-log-level "DISABLED" 2>&1) if ! check_error "$DISABLE_V2_RESULT"; then echo "Failed to disable v2 logging, trying v1..." # Try v1 logging disable DISABLE_V1_RESULT=$(aws iot set-logging-options \ --logging-options-payload "{\"logLevel\":\"DISABLED\"}" 2>&1) if ! check_error "$DISABLE_V1_RESULT"; then echo "Warning: Could not disable logging through either v1 or v2 methods" else echo "V1 logging disabled successfully" fi else echo "V2 logging disabled successfully" fi # Delete mitigation action echo "Deleting mitigation action..." aws iot delete-mitigation-action --action-name "EnableErrorLoggingAction" # Reset audit configuration echo "Resetting IoT Device Defender audit configuration..." aws iot update-account-audit-configuration \ --audit-check-configurations '{"LOGGING_DISABLED_CHECK":{"enabled":false}}' 2>&1 | grep -qi "error" && echo "Warning: Failed to disable audit check" aws iot delete-account-audit-configuration --delete-scheduled-audits 2>&1 | grep -qi "error" && echo "Warning: Failed to delete audit configuration" # Delete SNS topic echo "Deleting SNS topic..." aws sns delete-topic --topic-arn "$TOPIC_ARN" # Detach policies from roles and delete roles (in reverse order) echo "Cleaning up IAM roles..." # Check if policies exist before trying to delete them ROLE_POLICIES=$(aws iam list-role-policies --role-name "IoTMitigationActionErrorLoggingRole" 2>&1) if ! echo "$ROLE_POLICIES" | grep -q "NoSuchEntity"; then if echo "$ROLE_POLICIES" | grep -q "IoTMitigationActionErrorLoggingRolePolicy"; then aws iam delete-role-policy \ --role-name "IoTMitigationActionErrorLoggingRole" \ --policy-name "IoTMitigationActionErrorLoggingRolePolicy" fi fi aws iam delete-role --role-name "IoTMitigationActionErrorLoggingRole" ROLE_POLICIES=$(aws iam list-role-policies --role-name "AWSIoTLoggingRole" 2>&1) if ! echo "$ROLE_POLICIES" | grep -q "NoSuchEntity"; then if echo "$ROLE_POLICIES" | grep -q "AWSIoTLoggingRolePolicy"; then aws iam delete-role-policy \ --role-name "AWSIoTLoggingRole" \ --policy-name "AWSIoTLoggingRolePolicy" fi fi aws iam delete-role --role-name "AWSIoTLoggingRole" aws iam detach-role-policy \ --role-name "AWSIoTDeviceDefenderAuditRole" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSIoTDeviceDefenderAudit" aws iam delete-role --role-name "AWSIoTDeviceDefenderAuditRole" echo "Cleanup completed successfully" else echo "Skipping cleanup. Resources will remain in your AWS account." fi echo "" echo "Script execution completed at $(date)" echo "Log file: $LOG_FILE"-
For API details, see the following topics in AWS CLI Command Reference.
-
The following code example shows how to:
Create an Amazon S3 bucket
Create an Amazon SNS topic
Create an IAM role for Config
Set up the Config configuration recorder
Set up the Config delivery channel
Start the configuration recorder
Verify the Config setup
- AWS CLI with Bash script
-
Note
There's more on GitHub. Find the complete example and learn how to set up and run in the Sample developer tutorials
repository. #!/bin/bash # AWS Config Setup Script (v2) # This script sets up AWS Config with the AWS CLI # Error handling set -e LOGFILE="aws-config-setup-v2.log" touch $LOGFILE exec > >(tee -a $LOGFILE) exec 2>&1 # Function to handle errors handle_error() { echo "ERROR: An error occurred at line $1" echo "Attempting to clean up resources..." cleanup_resources exit 1 } # Set trap for error handling trap 'handle_error $LINENO' ERR # Function to generate random identifier generate_random_id() { echo $(openssl rand -hex 6) } # Function to check if command was successful check_command() { if echo "$1" | grep -i "error" > /dev/null; then echo "ERROR: $1" return 1 fi return 0 } # Function to clean up resources cleanup_resources() { if [ -n "$CONFIG_RECORDER_NAME" ]; then echo "Stopping configuration recorder..." aws configservice stop-configuration-recorder --configuration-recorder-name "$CONFIG_RECORDER_NAME" 2>/dev/null || true fi # Check if we created a new delivery channel before trying to delete it if [ -n "$DELIVERY_CHANNEL_NAME" ] && [ "$CREATED_NEW_DELIVERY_CHANNEL" = "true" ]; then echo "Deleting delivery channel..." aws configservice delete-delivery-channel --delivery-channel-name "$DELIVERY_CHANNEL_NAME" 2>/dev/null || true fi if [ -n "$CONFIG_RECORDER_NAME" ] && [ "$CREATED_NEW_CONFIG_RECORDER" = "true" ]; then echo "Deleting configuration recorder..." aws configservice delete-configuration-recorder --configuration-recorder-name "$CONFIG_RECORDER_NAME" 2>/dev/null || true fi if [ -n "$ROLE_NAME" ]; then if [ -n "$POLICY_NAME" ]; then echo "Detaching custom policy from role..." aws iam delete-role-policy --role-name "$ROLE_NAME" --policy-name "$POLICY_NAME" 2>/dev/null || true fi if [ -n "$MANAGED_POLICY_ARN" ]; then echo "Detaching managed policy from role..." aws iam detach-role-policy --role-name "$ROLE_NAME" --policy-arn "$MANAGED_POLICY_ARN" 2>/dev/null || true fi echo "Deleting IAM role..." aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null || true fi if [ -n "$SNS_TOPIC_ARN" ]; then echo "Deleting SNS topic..." aws sns delete-topic --topic-arn "$SNS_TOPIC_ARN" 2>/dev/null || true fi if [ -n "$S3_BUCKET_NAME" ]; then echo "Emptying S3 bucket..." aws s3 rm "s3://$S3_BUCKET_NAME" --recursive 2>/dev/null || true echo "Deleting S3 bucket..." aws s3api delete-bucket --bucket "$S3_BUCKET_NAME" 2>/dev/null || true fi } # Function to display created resources display_resources() { echo "" echo "===========================================" echo "CREATED RESOURCES" echo "===========================================" echo "S3 Bucket: $S3_BUCKET_NAME" echo "SNS Topic ARN: $SNS_TOPIC_ARN" echo "IAM Role: $ROLE_NAME" if [ "$CREATED_NEW_CONFIG_RECORDER" = "true" ]; then echo "Configuration Recorder: $CONFIG_RECORDER_NAME (newly created)" else echo "Configuration Recorder: $CONFIG_RECORDER_NAME (existing)" fi if [ "$CREATED_NEW_DELIVERY_CHANNEL" = "true" ]; then echo "Delivery Channel: $DELIVERY_CHANNEL_NAME (newly created)" else echo "Delivery Channel: $DELIVERY_CHANNEL_NAME (existing)" fi echo "===========================================" } # Get AWS account ID echo "Getting AWS account ID..." ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) if [ -z "$ACCOUNT_ID" ]; then echo "ERROR: Failed to get AWS account ID" exit 1 fi echo "AWS Account ID: $ACCOUNT_ID" # Generate random identifier for resources RANDOM_ID=$(generate_random_id) echo "Generated random identifier: $RANDOM_ID" # Step 1: Create an S3 bucket S3_BUCKET_NAME="configservice-${RANDOM_ID}" echo "Creating S3 bucket: $S3_BUCKET_NAME" # Get the current region AWS_REGION=$(aws configure get region) if [ -z "$AWS_REGION" ]; then AWS_REGION="us-east-1" # Default to us-east-1 if no region is configured fi echo "Using AWS Region: $AWS_REGION" # Create bucket with appropriate command based on region if [ "$AWS_REGION" = "us-east-1" ]; then BUCKET_RESULT=$(aws s3api create-bucket --bucket "$S3_BUCKET_NAME") else BUCKET_RESULT=$(aws s3api create-bucket --bucket "$S3_BUCKET_NAME" --create-bucket-configuration LocationConstraint="$AWS_REGION") fi check_command "$BUCKET_RESULT" echo "S3 bucket created: $S3_BUCKET_NAME" # Block public access for the bucket aws s3api put-public-access-block \ --bucket "$S3_BUCKET_NAME" \ --public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true" 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") check_command "$SNS_RESULT" SNS_TOPIC_ARN=$(echo "$SNS_RESULT" | grep -o 'arn:aws:sns:[^"]*') echo "SNS topic created: $SNS_TOPIC_ARN" # Step 3: Create an IAM role for AWS Config ROLE_NAME="config-role-${RANDOM_ID}" POLICY_NAME="config-delivery-permissions" MANAGED_POLICY_ARN="arn:aws:iam::aws:policy/service-role/AWS_ConfigRole" echo "Creating trust policy document..." cat > config-trust-policy.json << EOF { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "config.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF echo "Creating IAM role: $ROLE_NAME" ROLE_RESULT=$(aws iam create-role --role-name "$ROLE_NAME" --assume-role-policy-document file://config-trust-policy.json) check_command "$ROLE_RESULT" ROLE_ARN=$(echo "$ROLE_RESULT" | grep -o 'arn:aws:iam::[^"]*' | head -1) echo "IAM role created: $ROLE_ARN" 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" echo "AWS managed policy attached" echo "Creating custom policy document for S3 and SNS access..." cat > config-delivery-permissions.json << EOF { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject" ], "Resource": "arn:aws:s3:::${S3_BUCKET_NAME}/AWSLogs/${ACCOUNT_ID}/*", "Condition": { "StringLike": { "s3:x-amz-acl": "bucket-owner-full-control" } } }, { "Effect": "Allow", "Action": [ "s3:GetBucketAcl" ], "Resource": "arn:aws:s3:::${S3_BUCKET_NAME}" }, { "Effect": "Allow", "Action": [ "sns:Publish" ], "Resource": "${SNS_TOPIC_ARN}" } ] } EOF echo "Attaching custom policy to role..." POLICY_RESULT=$(aws iam put-role-policy --role-name "$ROLE_NAME" --policy-name "$POLICY_NAME" --policy-document file://config-delivery-permissions.json) check_command "$POLICY_RESULT" echo "Custom policy attached" # Wait for IAM role to propagate echo "Waiting for IAM role to propagate (15 seconds)..." sleep 15 # Step 4: Check if configuration recorder already exists CONFIG_RECORDER_NAME="default" CREATED_NEW_CONFIG_RECORDER="false" echo "Checking for existing configuration recorder..." EXISTING_RECORDERS=$(aws configservice describe-configuration-recorders 2>/dev/null || echo "") if echo "$EXISTING_RECORDERS" | grep -q "name"; then echo "Configuration recorder already exists. Will update it." # Get the name of the existing recorder CONFIG_RECORDER_NAME=$(echo "$EXISTING_RECORDERS" | grep -o '"name": "[^"]*"' | head -1 | cut -d'"' -f4) echo "Using existing configuration recorder: $CONFIG_RECORDER_NAME" else echo "No existing configuration recorder found. Will create a new one." CREATED_NEW_CONFIG_RECORDER="true" fi echo "Creating configuration recorder configuration..." cat > configurationRecorder.json << EOF { "name": "${CONFIG_RECORDER_NAME}", "roleARN": "${ROLE_ARN}", "recordingMode": { "recordingFrequency": "CONTINUOUS" } } EOF echo "Creating recording group configuration..." cat > recordingGroup.json << EOF { "allSupported": true, "includeGlobalResourceTypes": true } EOF echo "Setting up configuration recorder..." RECORDER_RESULT=$(aws configservice put-configuration-recorder --configuration-recorder file://configurationRecorder.json --recording-group file://recordingGroup.json) check_command "$RECORDER_RESULT" echo "Configuration recorder set up" # Step 5: Check if delivery channel already exists DELIVERY_CHANNEL_NAME="default" CREATED_NEW_DELIVERY_CHANNEL="false" echo "Checking for existing delivery channel..." EXISTING_CHANNELS=$(aws configservice describe-delivery-channels 2>/dev/null || echo "") if echo "$EXISTING_CHANNELS" | grep -q "name"; then echo "Delivery channel already exists." # Get the name of the existing channel DELIVERY_CHANNEL_NAME=$(echo "$EXISTING_CHANNELS" | grep -o '"name": "[^"]*"' | head -1 | cut -d'"' -f4) echo "Using existing delivery channel: $DELIVERY_CHANNEL_NAME" # Update the existing delivery channel echo "Creating delivery channel configuration for update..." cat > deliveryChannel.json << EOF { "name": "${DELIVERY_CHANNEL_NAME}", "s3BucketName": "${S3_BUCKET_NAME}", "snsTopicARN": "${SNS_TOPIC_ARN}", "configSnapshotDeliveryProperties": { "deliveryFrequency": "Six_Hours" } } EOF echo "Updating delivery channel..." CHANNEL_RESULT=$(aws configservice put-delivery-channel --delivery-channel file://deliveryChannel.json) check_command "$CHANNEL_RESULT" echo "Delivery channel updated" else echo "No existing delivery channel found. Will create a new one." CREATED_NEW_DELIVERY_CHANNEL="true" echo "Creating delivery channel configuration..." cat > deliveryChannel.json << EOF { "name": "${DELIVERY_CHANNEL_NAME}", "s3BucketName": "${S3_BUCKET_NAME}", "snsTopicARN": "${SNS_TOPIC_ARN}", "configSnapshotDeliveryProperties": { "deliveryFrequency": "Six_Hours" } } EOF echo "Creating delivery channel..." CHANNEL_RESULT=$(aws configservice put-delivery-channel --delivery-channel file://deliveryChannel.json) check_command "$CHANNEL_RESULT" echo "Delivery channel created" fi # Step 6: Start the configuration recorder echo "Checking configuration recorder status..." RECORDER_STATUS=$(aws configservice describe-configuration-recorder-status 2>/dev/null || echo "") if echo "$RECORDER_STATUS" | grep -q '"recording": true'; then echo "Configuration recorder is already running." else echo "Starting configuration recorder..." START_RESULT=$(aws configservice start-configuration-recorder --configuration-recorder-name "$CONFIG_RECORDER_NAME") check_command "$START_RESULT" echo "Configuration recorder started" fi # Step 7: Verify the AWS Config setup echo "Verifying delivery channel..." VERIFY_CHANNEL=$(aws configservice describe-delivery-channels) check_command "$VERIFY_CHANNEL" echo "$VERIFY_CHANNEL" echo "Verifying configuration recorder..." VERIFY_RECORDER=$(aws configservice describe-configuration-recorders) check_command "$VERIFY_RECORDER" echo "$VERIFY_RECORDER" echo "Verifying configuration recorder status..." VERIFY_STATUS=$(aws configservice describe-configuration-recorder-status) check_command "$VERIFY_STATUS" echo "$VERIFY_STATUS" # Display created resources display_resources # Ask if user wants to clean up resources echo "" echo "===========================================" echo "CLEANUP CONFIRMATION" echo "===========================================" echo "Do you want to clean up all created resources? (y/n): " read -r CLEANUP_CHOICE if [[ "$CLEANUP_CHOICE" =~ ^[Yy]$ ]]; then echo "Cleaning up resources..." cleanup_resources echo "Cleanup completed." else echo "Resources will not be cleaned up. You can manually clean them up later." fi echo "Script completed successfully!"-
For API details, see the following topics in AWS CLI Command Reference.
-