구성 관리 시작하기

구성 관리 시작하기

이 코드 예제에서는 AWS CLI로 AWS Config를 설정하는 방법을 보여드려요.

출처: 문서

본문

다음 작업을 다루어요.

  • Amazon S3 버킷 생성
  • Amazon SNS 주제 생성
  • Config용 IAM 역할 생성
  • Config 구성 레코더(configuration recorder) 설정
  • Config 전달 채널(delivery channel) 설정
  • 구성 레코더 시작
  • Config 설정 검증

Bash (AWS CLI with Bash script)

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

이 스크립트는 S3 버킷과 SNS 주제, IAM 역할을 만들고 AWS Config의 구성 레코더·전달 채널을 설정해요.

#!/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..."
		if [ "$BUCKET_IS_SHARED" = "false" ]; then
			aws s3api delete-bucket --bucket "$S3_BUCKET_NAME" 2>/dev/null || true
		fi
	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
# Check for shared prereq bucket
PREREQ_BUCKET=$(aws cloudformation describe-stacks --stack-name tutorial-prereqs-bucket \
	--query 'Stacks[0].Outputs[?OutputKey==`BucketName`].OutputValue' --output text 2>/dev/null)
if [ -n "$PREREQ_BUCKET" ] && [ "$PREREQ_BUCKET" != "None" ]; then
	S3_BUCKET_NAME="$PREREQ_BUCKET"
	BUCKET_IS_SHARED=true
	echo "Using shared bucket: $S3_BUCKET_NAME"
else
	BUCKET_IS_SHARED=false
	S3_BUCKET_NAME="configservice-${RANDOM_ID}"
	echo "Creating S3 bucket: $S3_BUCKET_NAME"
fi

# 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 [ "$BUCKET_IS_SHARED" = "false" ]; then
	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"

	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

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

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

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"
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"

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"

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"

	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
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): "
CLEANUP_CHOICE='y'

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

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

  • AttachRolePolicy, CreateBucket, CreateRole, CreateTopic, DeleteBucket, DeleteConfigurationRecorder, DeleteDeliveryChannel, DeleteRole, DeleteRolePolicy, DeleteTopic, DescribeConfigurationRecorderStatus, DescribeConfigurationRecorders, DescribeDeliveryChannels, DetachRolePolicy, GetCallerIdentity, PutConfigurationRecorder, PutDeliveryChannel, PutPublicAccessBlock, PutRolePolicy, Rm, StartConfigurationRecorder, StopConfigurationRecorder

더 알아보기 (Learn more)