사물 인터넷(IoT) 장치 보호 시작하기

사물 인터넷(IoT) 장치 보호 시작하기

이 코드 예제에서는 AWS IoT Device Defender를 사용해 감사(audit) 검사를 활성화하고, 결과를 확인하며, 완화(mitigation) 조치를 만드는 방법을 보여드려요.

출처: 문서

본문

다음 작업을 다루어요.

  • 필요한 IAM 역할 생성
  • IoT Device Defender 감사 검사 활성화
  • 주문형(On-Demand) 감사 실행
  • 완화 조치(Mitigation Action) 생성
  • 감사 결과(finding)에 완화 조치 적용
  • SNS 알림 설정(선택)
  • IoT 로깅 활성화

Bash (AWS CLI with Bash script)

Note GitHub에 더 많은 내용이 있어요. 전체 예제와 설정·실행 방법은 Sample developer tutorials 저장소에서 확인할 수 있어요.

이 스크립트는 IoT 감사·로깅·완화 작업에 필요한 IAM 역할을 만들고, 감사 검사를 활성화한 뒤 주문형 감사를 실행하고 완화 조치를 적용해요.

#!/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"

API 상세는 AWS CLI Command Reference의 다음 항목을 참고해요.

  • AttachRolePolicy, CreateMitigationAction, CreateRole, CreateTopic, DeleteAccountAuditConfiguration, DeleteMitigationAction, DeleteRole, DeleteRolePolicy, DeleteTopic, DescribeAccountAuditConfiguration, DescribeAuditTask, DetachRolePolicy, GetLoggingOptions, GetRole, GetV2LoggingOptions, ListAuditFindings, ListAuditMitigationActionsTasks, ListMitigationActions, ListRolePolicies, ListTopics, PutRolePolicy, SetLoggingOptions, SetV2LoggingOptions, StartAuditMitigationActionsTask, StartOnDemandAuditTask, UpdateAccountAuditConfiguration

더 알아보기 (Learn more)