View a markdown version of this page

Getting Started with IoT Device Defender - Amazon Identity and Access Management
Services or capabilities described in Amazon Web Services documentation might vary by Region. To see the differences applicable to the China Regions, see Getting Started with Amazon Web Services in China (PDF).

Getting Started with IoT Device Defender

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

Bash
Amazon 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 -euo pipefail # 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 -iE "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 safely extract JSON values using jq extract_json_value() { local json="$1" local key="$2" echo "$json" | jq -r ".${key} // empty" 2>/dev/null || echo "" } # Function to validate JSON validate_json() { local json="$1" echo "$json" | jq empty 2>/dev/null } # Function to check AWS CLI availability check_aws_cli() { if ! command -v aws &> /dev/null; then echo "ERROR: AWS CLI is not installed or not in PATH" return 1 fi if ! command -v jq &> /dev/null; then echo "ERROR: jq is not installed or not in PATH" return 1 fi return 0 } # Function to get AWS account ID get_account_id() { local account_id account_id=$(aws sts get-caller-identity --query 'Account' --output text 2>/dev/null) || true if [ -z "$account_id" ]; then echo "ERROR: Could not retrieve AWS account ID" return 1 fi echo "$account_id" return 0 } # Function to create IAM roles with retry logic create_iam_role() { local ROLE_NAME=$1 local TRUST_POLICY=$2 local MANAGED_POLICY=$3 local RETRY_COUNT=0 local MAX_RETRIES=3 echo "Creating IAM role: $ROLE_NAME" # Validate trust policy JSON if ! validate_json "$TRUST_POLICY"; then echo "ERROR: Invalid trust policy JSON for role $ROLE_NAME" return 1 fi # Check if role already exists if aws iam get-role --role-name "$ROLE_NAME" >/dev/null 2>&1; then echo "Role $ROLE_NAME already exists, skipping creation" ROLE_ARN=$(aws iam get-role --role-name "$ROLE_NAME" --query 'Role.Arn' --output text 2>/dev/null) || true if [ -z "$ROLE_ARN" ]; then echo "ERROR: Could not retrieve ARN for existing role $ROLE_NAME" return 1 fi echo "Role ARN: $ROLE_ARN" return 0 fi # Create the role with trust policy and retry logic while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do ROLE_RESULT=$(aws iam create-role \ --role-name "$ROLE_NAME" \ --assume-role-policy-document "$TRUST_POLICY" 2>&1) || true if check_error "$ROLE_RESULT"; then break fi RETRY_COUNT=$((RETRY_COUNT + 1)) if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then echo "Retrying role creation (attempt $((RETRY_COUNT + 1))/$MAX_RETRIES)..." sleep $((RETRY_COUNT * 2)) fi done if ! check_error "$ROLE_RESULT"; then echo "Failed to create role $ROLE_NAME after $MAX_RETRIES attempts" 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 LOGGING_POLICY=$(cat <<'EOF' { "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:*:*:*" } ] } EOF ) if ! validate_json "$LOGGING_POLICY"; then echo "ERROR: Invalid logging policy JSON" return 1 fi POLICY_RESULT=$(aws iam put-role-policy \ --role-name "$ROLE_NAME" \ --policy-name "${ROLE_NAME}Policy" \ --policy-document "$LOGGING_POLICY" 2>&1) || true if ! check_error "$POLICY_RESULT"; then echo "Failed to attach inline policy to role $ROLE_NAME" return 1 fi elif [[ "$ROLE_NAME" == "IoTMitigationActionErrorLoggingRole" ]]; then local MITIGATION_POLICY MITIGATION_POLICY=$(cat <<'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "iot:UpdateCACertificate", "iot:UpdateCertificate", "iot:SetV2LoggingOptions", "iot:SetLoggingOptions", "iot:AddThingToThingGroup" ], "Resource": "arn:aws:iot:*:*:*" }, { "Effect": "Allow", "Action": "iam:PassRole", "Resource": "*", "Condition": { "StringEquals": { "iam:PassedToService": "iot.amazonaws.com" } } } ] } EOF ) if ! validate_json "$MITIGATION_POLICY"; then echo "ERROR: Invalid mitigation policy JSON" return 1 fi POLICY_RESULT=$(aws iam put-role-policy \ --role-name "$ROLE_NAME" \ --policy-name "${ROLE_NAME}Policy" \ --policy-document "$MITIGATION_POLICY" 2>&1) || true 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) || true 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" # Get the role ARN with error handling ROLE_ARN=$(aws iam get-role --role-name "$ROLE_NAME" --query 'Role.Arn' --output text 2>/dev/null) || true if [ -z "$ROLE_ARN" ]; then echo "ERROR: Could not retrieve ARN for newly created role $ROLE_NAME" return 1 fi echo "Role ARN: $ROLE_ARN" return 0 } # Array to store created resources for cleanup declare -a CREATED_RESOURCES # Validate prerequisites echo "Validating prerequisites..." if ! check_aws_cli; then echo "ERROR: Prerequisites not met" exit 1 fi ACCOUNT_ID=$(get_account_id) || exit 1 echo "AWS Account ID: $ACCOUNT_ID" echo "" # 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=$(cat <<'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "iot.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF ) if ! create_iam_role "AWSIoTDeviceDefenderAuditRole" "$IOT_DEFENDER_AUDIT_TRUST_POLICY" "arn:aws:iam::aws:policy/service-role/AWSIoTDeviceDefenderAudit"; then echo "ERROR: Failed to create audit role" exit 1 fi AUDIT_ROLE_ARN=$ROLE_ARN CREATED_RESOURCES+=("IAM Role: AWSIoTDeviceDefenderAuditRole") # Create IoT Logging role IOT_LOGGING_TRUST_POLICY=$(cat <<'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "iot.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF ) if ! create_iam_role "AWSIoTLoggingRole" "$IOT_LOGGING_TRUST_POLICY" ""; then echo "ERROR: Failed to create logging role" exit 1 fi LOGGING_ROLE_ARN=$ROLE_ARN CREATED_RESOURCES+=("IAM Role: AWSIoTLoggingRole") # Create IoT Mitigation Action role IOT_MITIGATION_TRUST_POLICY=$(cat <<'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "iot.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF ) if ! create_iam_role "IoTMitigationActionErrorLoggingRole" "$IOT_MITIGATION_TRUST_POLICY" ""; then echo "ERROR: Failed to create mitigation role" exit 1 fi MITIGATION_ROLE_ARN=$ROLE_ARN CREATED_RESOURCES+=("IAM Role: IoTMitigationActionErrorLoggingRole") # Wait for IAM role propagation echo "Waiting for IAM role propagation..." sleep 5 # 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 --output json 2>&1) || true if validate_json "$CURRENT_CONFIG"; then echo "$CURRENT_CONFIG" | jq '.' 2>/dev/null || echo "Could not parse current configuration" fi # Enable specific audit checks with proper JSON escaping echo "Enabling audit checks..." AUDIT_CONFIG='{"LOGGING_DISABLED_CHECK":{"enabled":true}}' if ! validate_json "$AUDIT_CONFIG"; then echo "ERROR: Invalid audit configuration JSON" exit 1 fi UPDATE_RESULT=$(aws iot update-account-audit-configuration \ --role-arn "$AUDIT_ROLE_ARN" \ --audit-check-configurations "$AUDIT_CONFIG" 2>&1) || 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 --output json 2>&1) || true if ! check_error "$AUDIT_TASK_RESULT"; then echo "Failed to start on-demand audit task" exit 1 fi TASK_ID=$(extract_json_value "$AUDIT_TASK_RESULT" "taskId") if [ -z "$TASK_ID" ]; then echo "ERROR: Could not extract task ID from response" exit 1 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" TIMEOUT=0 MAX_TIMEOUT=600 POLL_INTERVAL=15 while [ "$TASK_STATUS" != "COMPLETED" ]; do if [ $TIMEOUT -ge $MAX_TIMEOUT ]; then echo "WARNING: Audit task did not complete within ${MAX_TIMEOUT} seconds, continuing..." break fi sleep "$POLL_INTERVAL" TIMEOUT=$((TIMEOUT + POLL_INTERVAL)) TASK_DETAILS=$(aws iot describe-audit-task --task-id "$TASK_ID" --output json 2>&1) || true if validate_json "$TASK_DETAILS"; then TASK_STATUS=$(extract_json_value "$TASK_DETAILS" "taskStatus") echo "Current task status: $TASK_STATUS (elapsed: ${TIMEOUT}s)" if [ "$TASK_STATUS" = "FAILED" ]; then echo "WARNING: Audit task failed, continuing with script..." FAILURE_REASON=$(extract_json_value "$TASK_DETAILS" "taskStatistics.failedChecksNotApplicable") if [ -n "$FAILURE_REASON" ]; then echo "Reason: $FAILURE_REASON" fi break fi else echo "WARNING: Could not parse task details, retrying..." fi done echo "Audit task processing completed" # Get audit findings (non-blocking) echo "Getting audit findings..." FINDINGS=$(aws iot list-audit-findings \ --task-id "$TASK_ID" --output json 2>&1) || true if validate_json "$FINDINGS"; then FINDING_COUNT=$(echo "$FINDINGS" | jq '.findings | length' 2>/dev/null || echo "0") echo "Audit findings count: $FINDING_COUNT" if [ "$FINDING_COUNT" -gt 0 ]; then echo "Sample finding:" echo "$FINDINGS" | jq '.findings[0]' 2>/dev/null || echo "Could not parse finding" fi else echo "WARNING: Could not parse audit findings response" FINDINGS='{"findings":[]}' fi # Check if we have any non-compliant findings FINDING_ID=$(extract_json_value "$FINDINGS" "findings[0].findingId") if [ -n "$FINDING_ID" ]; then 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 and delete it if aws iot describe-mitigation-action --action-name "EnableErrorLoggingAction" >/dev/null 2>&1; then echo "Mitigation action 'EnableErrorLoggingAction' already exists, deleting it first..." aws iot delete-mitigation-action --action-name "EnableErrorLoggingAction" 2>&1 || true sleep 2 fi echo "Creating mitigation action to enable AWS IoT logging..." # Build mitigation action parameters JSON MITIGATION_PARAMS=$(cat <<EOF { "enableIoTLoggingParams": { "roleArnForLogging": "$LOGGING_ROLE_ARN", "logLevel": "ERROR" } } EOF ) if ! validate_json "$MITIGATION_PARAMS"; then echo "ERROR: Invalid mitigation parameters JSON" exit 1 fi MITIGATION_RESULT=$(aws iot create-mitigation-action \ --action-name "EnableErrorLoggingAction" \ --role-arn "$MITIGATION_ROLE_ARN" \ --action-params "$MITIGATION_PARAMS" --output json 2>&1) || true if ! check_error "$MITIGATION_RESULT"; then echo "Failed to create mitigation action" exit 1 fi if validate_json "$MITIGATION_RESULT"; then echo "Mitigation action created successfully" 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" fi 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" # Build target JSON TARGET_JSON=$(cat <<EOF { "findingIds": ["$FINDING_ID"] } EOF ) if ! validate_json "$TARGET_JSON"; then echo "ERROR: Invalid target JSON" exit 1 fi # Build audit check to actions mapping JSON AUDIT_CHECK_MAPPING=$(cat <<EOF { "LOGGING_DISABLED_CHECK": ["EnableErrorLoggingAction"] } EOF ) if ! validate_json "$AUDIT_CHECK_MAPPING"; then echo "ERROR: Invalid audit check mapping JSON" exit 1 fi MITIGATION_TASK_RESULT=$(aws iot start-audit-mitigation-actions-task \ --task-id "$MITIGATION_TASK_ID" \ --target "$TARGET_JSON" \ --audit-check-to-actions-mapping "$AUDIT_CHECK_MAPPING" --output json 2>&1) || true if ! check_error "$MITIGATION_TASK_RESULT"; then echo "WARNING: Failed to start mitigation actions task, continuing..." else echo "Mitigation actions task started successfully" CREATED_RESOURCES+=("Mitigation Task: $MITIGATION_TASK_ID") 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 --output json 2>&1) || true TOPIC_ARN="" if validate_json "$SNS_TOPICS"; then TOPIC_ARN=$(echo "$SNS_TOPICS" | jq -r '.Topics[] | select(.TopicArn | contains("IoTDDNotifications")) | .TopicArn' 2>/dev/null | head -1 || echo "") fi if [ -n "$TOPIC_ARN" ]; then echo "SNS topic 'IoTDDNotifications' already exists, using existing topic..." echo "Topic ARN: $TOPIC_ARN" else echo "Creating SNS topic for notifications..." 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..." SNS_RESULT="" else TOPIC_ARN=$(extract_json_value "$SNS_RESULT" "TopicArn") if [ -n "$TOPIC_ARN" ]; then echo "SNS topic created with ARN: $TOPIC_ARN" CREATED_RESOURCES+=("SNS Topic: IoTDDNotifications") fi fi fi if [ -n "$TOPIC_ARN" ]; then echo "Updating audit configuration to enable SNS notifications..." # Build SNS notification configuration JSON SNS_CONFIG=$(cat <<EOF { "SNS": { "targetArn": "$TOPIC_ARN", "roleArn": "$AUDIT_ROLE_ARN", "enabled": true } } EOF ) if ! validate_json "$SNS_CONFIG"; then echo "ERROR: Invalid SNS configuration JSON" exit 1 fi SNS_UPDATE_RESULT=$(aws iot update-account-audit-configuration \ --audit-notification-target-configurations "$SNS_CONFIG" 2>&1) || true if ! check_error "$SNS_UPDATE_RESULT"; then echo "WARNING: Failed to update audit configuration for SNS notifications" else echo "SNS notifications enabled successfully" fi else echo "Skipping SNS configuration due to topic creation failure" fi # Step 7: Enable AWS IoT logging echo "" echo "===================================================" echo "Step 7: Enabling AWS IoT logging" echo "===================================================" echo "Setting up AWS IoT logging options..." LOGGING_RESULT=$(aws iot set-v2-logging-options \ --role-arn "$LOGGING_ROLE_ARN" \ --default-log-level "ERROR" 2>&1) || true if ! check_error "$LOGGING_RESULT"; then echo "V2 logging setup failed, trying v1 logging..." V1_LOGGING_CONFIG=$(cat <<EOF { "roleArn": "$LOGGING_ROLE_ARN", "logLevel": "ERROR" } EOF ) if ! validate_json "$V1_LOGGING_CONFIG"; then echo "ERROR: Invalid v1 logging configuration JSON" exit 1 fi LOGGING_RESULT_V1=$(aws iot set-logging-options \ --logging-options-payload "$V1_LOGGING_CONFIG" 2>&1) || true if ! check_error "$LOGGING_RESULT_V1"; then echo "WARNING: Failed to set up AWS IoT logging with both v1 and v2 methods, continuing..." 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 --output json 2>&1) || true if [ -n "$LOGGING_CONFIG" ] && ! check_error "$LOGGING_CONFIG" && validate_json "$LOGGING_CONFIG"; then echo "Logging configuration verified" echo "$LOGGING_CONFIG" | jq '.' 2>/dev/null || echo "Configuration retrieved but could not display details" else echo "Could not verify logging configuration, but setup completed" 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[@]+"${CREATED_RESOURCES[@]}"}"; do echo "- $resource" done echo "" # Cleanup phase echo "===========================================" echo "CLEANUP" echo "===========================================" echo "Starting automatic cleanup of resources..." echo "Waiting 10 seconds before cleanup to allow resource stabilization..." sleep 10 # Disable AWS IoT logging echo "Disabling AWS IoT logging..." DISABLE_V2_RESULT=$(aws iot set-v2-logging-options \ --default-log-level "DISABLED" 2>&1) || true if check_error "$DISABLE_V2_RESULT"; then echo "V2 logging disabled successfully" else echo "Attempting v1 logging disable..." V1_DISABLE_CONFIG=$(cat <<'EOF' { "logLevel": "DISABLED" } EOF ) DISABLE_V1_RESULT=$(aws iot set-logging-options \ --logging-options-payload "$V1_DISABLE_CONFIG" 2>&1) || true if check_error "$DISABLE_V1_RESULT"; then echo "V1 logging disabled successfully" else echo "WARNING: Could not disable logging" fi fi # Delete mitigation action echo "Deleting mitigation action..." aws iot delete-mitigation-action --action-name "EnableErrorLoggingAction" 2>&1 || true # Reset audit configuration echo "Resetting IoT Device Defender audit configuration..." RESET_AUDIT_CONFIG='{"LOGGING_DISABLED_CHECK":{"enabled":false}}' aws iot update-account-audit-configuration \ --audit-check-configurations "$RESET_AUDIT_CONFIG" 2>&1 || true # Delete SNS topic echo "Deleting SNS topic..." if [ -n "${TOPIC_ARN:-}" ] && [ "$TOPIC_ARN" != "null" ]; then aws sns delete-topic --topic-arn "$TOPIC_ARN" 2>&1 || true fi # Clean up IAM roles with improved error handling echo "Cleaning up IAM roles..." cleanup_role() { local role_name=$1 echo "Cleaning up role: $role_name" if aws iam get-role --role-name "$role_name" >/dev/null 2>&1; then ROLE_POLICIES=$(aws iam list-role-policies --role-name "$role_name" --output json 2>&1 || echo '{"PolicyNames":[]}') if validate_json "$ROLE_POLICIES"; then while IFS= read -r policy_name; do if [ -n "$policy_name" ] && [ "$policy_name" != "null" ]; then echo " Deleting inline policy: $policy_name" aws iam delete-role-policy \ --role-name "$role_name" \ --policy-name "$policy_name" 2>&1 || true fi done < <(echo "$ROLE_POLICIES" | jq -r '.PolicyNames[]' 2>/dev/null || echo "") fi ATTACHED_POLICIES=$(aws iam list-attached-role-policies --role-name "$role_name" --output json 2>&1 || echo '{"AttachedPolicies":[]}') if validate_json "$ATTACHED_POLICIES"; then while IFS= read -r policy_arn; do if [ -n "$policy_arn" ] && [ "$policy_arn" != "null" ]; then echo " Detaching managed policy: $policy_arn" aws iam detach-role-policy \ --role-name "$role_name" \ --policy-arn "$policy_arn" 2>&1 || true fi done < <(echo "$ATTACHED_POLICIES" | jq -r '.AttachedPolicies[].PolicyArn' 2>/dev/null || echo "") fi echo " Deleting role: $role_name" aws iam delete-role --role-name "$role_name" 2>&1 || true else echo " Role $role_name does not exist or already deleted" fi } cleanup_role "AWSIoTDeviceDefenderAuditRole" cleanup_role "AWSIoTLoggingRole" cleanup_role "IoTMitigationActionErrorLoggingRole" echo "Cleanup completed successfully" echo "" echo "Script execution completed at $(date)" echo "Log file: $LOG_FILE"

For a complete list of Amazon SDK developer guides and code examples, see Using this service with an Amazon SDK. This topic also includes information about getting started and details about previous SDK versions.