View a markdown version of this page

Contoh IAM menggunakan skrip AWS CLI Bash - AWS Command Line Interface

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

Contoh IAM menggunakan skrip AWS CLI Bash

Contoh kode berikut menunjukkan cara melakukan tindakan dan mengimplementasikan skenario umum dengan menggunakan skrip AWS Command Line Interface with Bash dengan IAM.

Dasar-dasar adalah contoh kode yang menunjukkan kepada Anda bagaimana melakukan operasi penting dalam suatu layanan.

Tindakan merupakan kutipan kode dari program yang lebih besar dan harus dijalankan dalam konteks. Sementara tindakan menunjukkan cara memanggil fungsi layanan individual, Anda dapat melihat tindakan dalam konteks dalam skenario terkait.

Skenario adalah contoh kode yang menunjukkan kepada Anda bagaimana menyelesaikan tugas tertentu dengan memanggil beberapa fungsi dalam layanan atau dikombinasikan dengan yang lain Layanan AWS.

Setiap contoh menyertakan tautan ke kode sumber lengkap, di mana Anda dapat menemukan instruksi tentang cara mengatur dan menjalankan kode dalam konteks.

Hal-hal mendasar

Contoh kode berikut menunjukkan cara membuat pengguna dan mengambil peran.

Awas

Untuk menghindari risiko keamanan, jangan gunakan pengguna IAM untuk otentikasi saat mengembangkan perangkat lunak yang dibuat khusus atau bekerja dengan data nyata. Sebaliknya, gunakan federasi dengan penyedia identitas seperti AWS IAM Identity Center.

  • Buat pengguna tanpa izin.

  • Buat peran yang memberikan izin untuk mencantumkan bucket Amazon S3 untuk akun tersebut.

  • Tambahkan kebijakan agar pengguna dapat mengambil peran tersebut.

  • Asumsikan peran dan daftar bucket S3 menggunakan kredenal sementara, lalu bersihkan sumber daya.

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankannya di Repositori Contoh Kode AWS.

############################################################################### # function iam_create_user_assume_role # # Scenario to create an IAM user, create an IAM role, and apply the role to the user. # # "IAM access" permissions are needed to run this code. # "STS assume role" permissions are needed to run this code. (Note: It might be necessary to # create a custom policy). # # Returns: # 0 - If successful. # 1 - If an error occurred. ############################################################################### function iam_create_user_assume_role() { { if [ "$IAM_OPERATIONS_SOURCED" != "True" ]; then source ./iam_operations.sh fi } echo_repeat "*" 88 echo "Welcome to the IAM create user and assume role demo." echo echo "This demo will create an IAM user, create an IAM role, and apply the role to the user." echo_repeat "*" 88 echo echo -n "Enter a name for a new IAM user: " get_input user_name=$get_input_result local user_arn user_arn=$(iam_create_user -u "$user_name") # shellcheck disable=SC2181 if [[ ${?} == 0 ]]; then echo "Created demo IAM user named $user_name" else errecho "$user_arn" errecho "The user failed to create. This demo will exit." return 1 fi local access_key_response access_key_response=$(iam_create_user_access_key -u "$user_name") # shellcheck disable=SC2181 if [[ ${?} != 0 ]]; then errecho "The access key failed to create. This demo will exit." clean_up "$user_name" return 1 fi IFS=$'\t ' read -r -a access_key_values <<<"$access_key_response" local key_name=${access_key_values[0]} local key_secret=${access_key_values[1]} echo "Created access key named $key_name" echo "Wait 10 seconds for the user to be ready." sleep 10 echo_repeat "*" 88 echo local iam_role_name iam_role_name=$(generate_random_name "test-role") echo "Creating a role named $iam_role_name with user $user_name as the principal." local assume_role_policy_document="{ \"Version\": \"2012-10-17\", \"Statement\": [{ \"Effect\": \"Allow\", \"Principal\": {\"AWS\": \"$user_arn\"}, \"Action\": \"sts:AssumeRole\" }] }" local role_arn role_arn=$(iam_create_role -n "$iam_role_name" -p "$assume_role_policy_document") # shellcheck disable=SC2181 if [ ${?} == 0 ]; then echo "Created IAM role named $iam_role_name" else errecho "The role failed to create. This demo will exit." clean_up "$user_name" "$key_name" return 1 fi local policy_name policy_name=$(generate_random_name "test-policy") local policy_document="{ \"Version\": \"2012-10-17\", \"Statement\": [{ \"Effect\": \"Allow\", \"Action\": \"s3:ListAllMyBuckets\", \"Resource\": \"arn:aws:s3:::*\"}]}" local policy_arn policy_arn=$(iam_create_policy -n "$policy_name" -p "$policy_document") # shellcheck disable=SC2181 if [[ ${?} == 0 ]]; then echo "Created IAM policy named $policy_name" else errecho "The policy failed to create." clean_up "$user_name" "$key_name" "$iam_role_name" return 1 fi if (iam_attach_role_policy -n "$iam_role_name" -p "$policy_arn"); then echo "Attached policy $policy_arn to role $iam_role_name" else errecho "The policy failed to attach." clean_up "$user_name" "$key_name" "$iam_role_name" "$policy_arn" return 1 fi local assume_role_policy_document="{ \"Version\": \"2012-10-17\", \"Statement\": [{ \"Effect\": \"Allow\", \"Action\": \"sts:AssumeRole\", \"Resource\": \"$role_arn\"}]}" local assume_role_policy_name assume_role_policy_name=$(generate_random_name "test-assume-role-") # shellcheck disable=SC2181 local assume_role_policy_arn assume_role_policy_arn=$(iam_create_policy -n "$assume_role_policy_name" -p "$assume_role_policy_document") # shellcheck disable=SC2181 if [ ${?} == 0 ]; then echo "Created IAM policy named $assume_role_policy_name for sts assume role" else errecho "The policy failed to create." clean_up "$user_name" "$key_name" "$iam_role_name" "$policy_arn" "$policy_arn" return 1 fi echo "Wait 10 seconds to give AWS time to propagate these new resources and connections." sleep 10 echo_repeat "*" 88 echo echo "Try to list buckets without the new user assuming the role." echo_repeat "*" 88 echo # Set the environment variables for the created user. # bashsupport disable=BP2001 export AWS_ACCESS_KEY_ID=$key_name # bashsupport disable=BP2001 export AWS_SECRET_ACCESS_KEY=$key_secret local buckets buckets=$(s3_list_buckets) # shellcheck disable=SC2181 if [ ${?} == 0 ]; then local bucket_count bucket_count=$(echo "$buckets" | wc -w | xargs) echo "There are $bucket_count buckets in the account. This should not have happened." else errecho "Because the role with permissions has not been assumed, listing buckets failed." fi echo echo_repeat "*" 88 echo "Now assume the role $iam_role_name and list the buckets." echo_repeat "*" 88 echo local credentials credentials=$(sts_assume_role -r "$role_arn" -n "AssumeRoleDemoSession") # shellcheck disable=SC2181 if [ ${?} == 0 ]; then echo "Assumed role $iam_role_name" else errecho "Failed to assume role." export AWS_ACCESS_KEY_ID="" export AWS_SECRET_ACCESS_KEY="" clean_up "$user_name" "$key_name" "$iam_role_name" "$policy_arn" "$policy_arn" "$assume_role_policy_arn" return 1 fi IFS=$'\t ' read -r -a credentials <<<"$credentials" export AWS_ACCESS_KEY_ID=${credentials[0]} export AWS_SECRET_ACCESS_KEY=${credentials[1]} # bashsupport disable=BP2001 export AWS_SESSION_TOKEN=${credentials[2]} buckets=$(s3_list_buckets) # shellcheck disable=SC2181 if [ ${?} == 0 ]; then local bucket_count bucket_count=$(echo "$buckets" | wc -w | xargs) echo "There are $bucket_count buckets in the account. Listing buckets succeeded because of " echo "the assumed role." else errecho "Failed to list buckets. This should not happen." export AWS_ACCESS_KEY_ID="" export AWS_SECRET_ACCESS_KEY="" export AWS_SESSION_TOKEN="" clean_up "$user_name" "$key_name" "$iam_role_name" "$policy_arn" "$policy_arn" "$assume_role_policy_arn" return 1 fi local result=0 export AWS_ACCESS_KEY_ID="" export AWS_SECRET_ACCESS_KEY="" echo echo_repeat "*" 88 echo "The created resources will now be deleted." echo_repeat "*" 88 echo clean_up "$user_name" "$key_name" "$iam_role_name" "$policy_arn" "$policy_arn" "$assume_role_policy_arn" # shellcheck disable=SC2181 if [[ ${?} -ne 0 ]]; then result=1 fi return $result }

Fungsi IAM yang digunakan dalam skenario ini.

############################################################################### # function iam_user_exists # # This function checks to see if the specified AWS Identity and Access Management (IAM) user already exists. # # Parameters: # $1 - The name of the IAM user to check. # # Returns: # 0 - If the user already exists. # 1 - If the user doesn't exist. ############################################################################### function iam_user_exists() { local user_name user_name=$1 # Check whether the IAM user already exists. # We suppress all output - we're interested only in the return code. local errors errors=$(aws iam get-user \ --user-name "$user_name" 2>&1 >/dev/null) local error_code=${?} if [[ $error_code -eq 0 ]]; then return 0 # 0 in Bash script means true. else if [[ $errors != *"error"*"(NoSuchEntity)"* ]]; then aws_cli_error_log $error_code errecho "Error calling iam get-user $errors" fi return 1 # 1 in Bash script means false. fi } ############################################################################### # function iam_create_user # # This function creates the specified IAM user, unless # it already exists. # # Parameters: # -u user_name -- The name of the user to create. # # Returns: # The ARN of the user. # And: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_create_user() { local user_name response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_create_user" echo "Creates an AWS Identity and Access Management (IAM) user. You must supply a username:" echo " -u user_name The name of the user. It must be unique within the account." echo "" } # Retrieve the calling parameters. while getopts "u:h" option; do case "${option}" in u) user_name="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$user_name" ]]; then errecho "ERROR: You must provide a username with the -u parameter." usage return 1 fi iecho "Parameters:\n" iecho " User name: $user_name" iecho "" # If the user already exists, we don't want to try to create it. if (iam_user_exists "$user_name"); then errecho "ERROR: A user with that name already exists in the account." return 1 fi response=$(aws iam create-user --user-name "$user_name" \ --output text \ --query 'User.Arn') local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports create-user operation failed.$response" return 1 fi echo "$response" return 0 } ############################################################################### # function iam_create_user_access_key # # This function creates an IAM access key for the specified user. # # Parameters: # -u user_name -- The name of the IAM user. # [-f file_name] -- The optional file name for the access key output. # # Returns: # [access_key_id access_key_secret] # And: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_create_user_access_key() { local user_name file_name response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_create_user_access_key" echo "Creates an AWS Identity and Access Management (IAM) key pair." echo " -u user_name The name of the IAM user." echo " [-f file_name] Optional file name for the access key output." echo "" } # Retrieve the calling parameters. while getopts "u:f:h" option; do case "${option}" in u) user_name="${OPTARG}" ;; f) file_name="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$user_name" ]]; then errecho "ERROR: You must provide a username with the -u parameter." usage return 1 fi response=$(aws iam create-access-key \ --user-name "$user_name" \ --output text) local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports create-access-key operation failed.$response" return 1 fi if [[ -n "$file_name" ]]; then echo "$response" >"$file_name" fi local key_id key_secret # shellcheck disable=SC2086 key_id=$(echo $response | cut -f 2 -d ' ') # shellcheck disable=SC2086 key_secret=$(echo $response | cut -f 4 -d ' ') echo "$key_id $key_secret" return 0 } ############################################################################### # function iam_create_role # # This function creates an IAM role. # # Parameters: # -n role_name -- The name of the IAM role. # -p policy_json -- The assume role policy document. # # Returns: # The ARN of the role. # And: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_create_role() { local role_name policy_document response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_create_user_access_key" echo "Creates an AWS Identity and Access Management (IAM) role." echo " -n role_name The name of the IAM role." echo " -p policy_json -- The assume role policy document." echo "" } # Retrieve the calling parameters. while getopts "n:p:h" option; do case "${option}" in n) role_name="${OPTARG}" ;; p) policy_document="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$role_name" ]]; then errecho "ERROR: You must provide a role name with the -n parameter." usage return 1 fi if [[ -z "$policy_document" ]]; then errecho "ERROR: You must provide a policy document with the -p parameter." usage return 1 fi response=$(aws iam create-role \ --role-name "$role_name" \ --assume-role-policy-document "$policy_document" \ --output text \ --query Role.Arn) local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports create-role operation failed.\n$response" return 1 fi echo "$response" return 0 } ############################################################################### # function iam_create_policy # # This function creates an IAM policy. # # Parameters: # -n policy_name -- The name of the IAM policy. # -p policy_json -- The policy document. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_create_policy() { local policy_name policy_document response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_create_policy" echo "Creates an AWS Identity and Access Management (IAM) policy." echo " -n policy_name The name of the IAM policy." echo " -p policy_json -- The policy document." echo "" } # Retrieve the calling parameters. while getopts "n:p:h" option; do case "${option}" in n) policy_name="${OPTARG}" ;; p) policy_document="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$policy_name" ]]; then errecho "ERROR: You must provide a policy name with the -n parameter." usage return 1 fi if [[ -z "$policy_document" ]]; then errecho "ERROR: You must provide a policy document with the -p parameter." usage return 1 fi response=$(aws iam create-policy \ --policy-name "$policy_name" \ --policy-document "$policy_document" \ --output text \ --query Policy.Arn) local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports create-policy operation failed.\n$response" return 1 fi echo "$response" } ############################################################################### # function iam_attach_role_policy # # This function attaches an IAM policy to a tole. # # Parameters: # -n role_name -- The name of the IAM role. # -p policy_ARN -- The IAM policy document ARN.. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_attach_role_policy() { local role_name policy_arn response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_attach_role_policy" echo "Attaches an AWS Identity and Access Management (IAM) policy to an IAM role." echo " -n role_name The name of the IAM role." echo " -p policy_ARN -- The IAM policy document ARN." echo "" } # Retrieve the calling parameters. while getopts "n:p:h" option; do case "${option}" in n) role_name="${OPTARG}" ;; p) policy_arn="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$role_name" ]]; then errecho "ERROR: You must provide a role name with the -n parameter." usage return 1 fi if [[ -z "$policy_arn" ]]; then errecho "ERROR: You must provide a policy ARN with the -p parameter." usage return 1 fi response=$(aws iam attach-role-policy \ --role-name "$role_name" \ --policy-arn "$policy_arn") local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports attach-role-policy operation failed.\n$response" return 1 fi echo "$response" return 0 } ############################################################################### # function iam_detach_role_policy # # This function detaches an IAM policy to a tole. # # Parameters: # -n role_name -- The name of the IAM role. # -p policy_ARN -- The IAM policy document ARN.. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_detach_role_policy() { local role_name policy_arn response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_detach_role_policy" echo "Detaches an AWS Identity and Access Management (IAM) policy to an IAM role." echo " -n role_name The name of the IAM role." echo " -p policy_ARN -- The IAM policy document ARN." echo "" } # Retrieve the calling parameters. while getopts "n:p:h" option; do case "${option}" in n) role_name="${OPTARG}" ;; p) policy_arn="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$role_name" ]]; then errecho "ERROR: You must provide a role name with the -n parameter." usage return 1 fi if [[ -z "$policy_arn" ]]; then errecho "ERROR: You must provide a policy ARN with the -p parameter." usage return 1 fi response=$(aws iam detach-role-policy \ --role-name "$role_name" \ --policy-arn "$policy_arn") local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports detach-role-policy operation failed.\n$response" return 1 fi echo "$response" return 0 } ############################################################################### # function iam_delete_policy # # This function deletes an IAM policy. # # Parameters: # -n policy_arn -- The name of the IAM policy arn. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_delete_policy() { local policy_arn response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_delete_policy" echo "Deletes an AWS Identity and Access Management (IAM) policy" echo " -n policy_arn -- The name of the IAM policy arn." echo "" } # Retrieve the calling parameters. while getopts "n:h" option; do case "${option}" in n) policy_arn="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$policy_arn" ]]; then errecho "ERROR: You must provide a policy arn with the -n parameter." usage return 1 fi iecho "Parameters:\n" iecho " Policy arn: $policy_arn" iecho "" response=$(aws iam delete-policy \ --policy-arn "$policy_arn") local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports delete-policy operation failed.\n$response" return 1 fi iecho "delete-policy response:$response" iecho return 0 } ############################################################################### # function iam_delete_role # # This function deletes an IAM role. # # Parameters: # -n role_name -- The name of the IAM role. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_delete_role() { local role_name response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_delete_role" echo "Deletes an AWS Identity and Access Management (IAM) role" echo " -n role_name -- The name of the IAM role." echo "" } # Retrieve the calling parameters. while getopts "n:h" option; do case "${option}" in n) role_name="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 echo "role_name:$role_name" if [[ -z "$role_name" ]]; then errecho "ERROR: You must provide a role name with the -n parameter." usage return 1 fi iecho "Parameters:\n" iecho " Role name: $role_name" iecho "" response=$(aws iam delete-role \ --role-name "$role_name") local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports delete-role operation failed.\n$response" return 1 fi iecho "delete-role response:$response" iecho return 0 } ############################################################################### # function iam_delete_access_key # # This function deletes an IAM access key for the specified IAM user. # # Parameters: # -u user_name -- The name of the user. # -k access_key -- The access key to delete. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_delete_access_key() { local user_name access_key response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_delete_access_key" echo "Deletes an AWS Identity and Access Management (IAM) access key for the specified IAM user" echo " -u user_name The name of the user." echo " -k access_key The access key to delete." echo "" } # Retrieve the calling parameters. while getopts "u:k:h" option; do case "${option}" in u) user_name="${OPTARG}" ;; k) access_key="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$user_name" ]]; then errecho "ERROR: You must provide a username with the -u parameter." usage return 1 fi if [[ -z "$access_key" ]]; then errecho "ERROR: You must provide an access key with the -k parameter." usage return 1 fi iecho "Parameters:\n" iecho " Username: $user_name" iecho " Access key: $access_key" iecho "" response=$(aws iam delete-access-key \ --user-name "$user_name" \ --access-key-id "$access_key") local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports delete-access-key operation failed.\n$response" return 1 fi iecho "delete-access-key response:$response" iecho return 0 } ############################################################################### # function iam_delete_user # # This function deletes the specified IAM user. # # Parameters: # -u user_name -- The name of the user to create. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_delete_user() { local user_name response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_delete_user" echo "Deletes an AWS Identity and Access Management (IAM) user. You must supply a username:" echo " -u user_name The name of the user." echo "" } # Retrieve the calling parameters. while getopts "u:h" option; do case "${option}" in u) user_name="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$user_name" ]]; then errecho "ERROR: You must provide a username with the -u parameter." usage return 1 fi iecho "Parameters:\n" iecho " User name: $user_name" iecho "" # If the user does not exist, we don't want to try to delete it. if (! iam_user_exists "$user_name"); then errecho "ERROR: A user with that name does not exist in the account." return 1 fi response=$(aws iam delete-user \ --user-name "$user_name") local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports delete-user operation failed.$response" return 1 fi iecho "delete-user response:$response" iecho return 0 }

Tindakan

Contoh kode berikut menunjukkan cara menggunakanAttachRolePolicy.

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankannya di Repositori Contoh Kode AWS.

############################################################################### # function errecho # # This function outputs everything sent to it to STDERR (standard error output). ############################################################################### function errecho() { printf "%s\n" "$*" 1>&2 } ############################################################################### # function iam_attach_role_policy # # This function attaches an IAM policy to a tole. # # Parameters: # -n role_name -- The name of the IAM role. # -p policy_ARN -- The IAM policy document ARN.. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_attach_role_policy() { local role_name policy_arn response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_attach_role_policy" echo "Attaches an AWS Identity and Access Management (IAM) policy to an IAM role." echo " -n role_name The name of the IAM role." echo " -p policy_ARN -- The IAM policy document ARN." echo "" } # Retrieve the calling parameters. while getopts "n:p:h" option; do case "${option}" in n) role_name="${OPTARG}" ;; p) policy_arn="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$role_name" ]]; then errecho "ERROR: You must provide a role name with the -n parameter." usage return 1 fi if [[ -z "$policy_arn" ]]; then errecho "ERROR: You must provide a policy ARN with the -p parameter." usage return 1 fi response=$(aws iam attach-role-policy \ --role-name "$role_name" \ --policy-arn "$policy_arn") local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports attach-role-policy operation failed.\n$response" return 1 fi echo "$response" return 0 }

Contoh kode berikut menunjukkan cara menggunakanCreateAccessKey.

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankannya di Repositori Contoh Kode AWS.

############################################################################### # function errecho # # This function outputs everything sent to it to STDERR (standard error output). ############################################################################### function errecho() { printf "%s\n" "$*" 1>&2 } ############################################################################### # function iam_create_user_access_key # # This function creates an IAM access key for the specified user. # # Parameters: # -u user_name -- The name of the IAM user. # [-f file_name] -- The optional file name for the access key output. # # Returns: # [access_key_id access_key_secret] # And: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_create_user_access_key() { local user_name file_name response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_create_user_access_key" echo "Creates an AWS Identity and Access Management (IAM) key pair." echo " -u user_name The name of the IAM user." echo " [-f file_name] Optional file name for the access key output." echo "" } # Retrieve the calling parameters. while getopts "u:f:h" option; do case "${option}" in u) user_name="${OPTARG}" ;; f) file_name="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$user_name" ]]; then errecho "ERROR: You must provide a username with the -u parameter." usage return 1 fi response=$(aws iam create-access-key \ --user-name "$user_name" \ --output text) local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports create-access-key operation failed.$response" return 1 fi if [[ -n "$file_name" ]]; then echo "$response" >"$file_name" fi local key_id key_secret # shellcheck disable=SC2086 key_id=$(echo $response | cut -f 2 -d ' ') # shellcheck disable=SC2086 key_secret=$(echo $response | cut -f 4 -d ' ') echo "$key_id $key_secret" return 0 }

Contoh kode berikut menunjukkan cara menggunakanCreatePolicy.

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankannya di Repositori Contoh Kode AWS.

############################################################################### # function errecho # # This function outputs everything sent to it to STDERR (standard error output). ############################################################################### function errecho() { printf "%s\n" "$*" 1>&2 } ############################################################################### # function iam_create_policy # # This function creates an IAM policy. # # Parameters: # -n policy_name -- The name of the IAM policy. # -p policy_json -- The policy document. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_create_policy() { local policy_name policy_document response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_create_policy" echo "Creates an AWS Identity and Access Management (IAM) policy." echo " -n policy_name The name of the IAM policy." echo " -p policy_json -- The policy document." echo "" } # Retrieve the calling parameters. while getopts "n:p:h" option; do case "${option}" in n) policy_name="${OPTARG}" ;; p) policy_document="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$policy_name" ]]; then errecho "ERROR: You must provide a policy name with the -n parameter." usage return 1 fi if [[ -z "$policy_document" ]]; then errecho "ERROR: You must provide a policy document with the -p parameter." usage return 1 fi response=$(aws iam create-policy \ --policy-name "$policy_name" \ --policy-document "$policy_document" \ --output text \ --query Policy.Arn) local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports create-policy operation failed.\n$response" return 1 fi echo "$response" }
  • Untuk detail API, lihat CreatePolicydi Referensi AWS CLI Perintah.

Contoh kode berikut menunjukkan cara menggunakanCreateRole.

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankannya di Repositori Contoh Kode AWS.

############################################################################### # function errecho # # This function outputs everything sent to it to STDERR (standard error output). ############################################################################### function errecho() { printf "%s\n" "$*" 1>&2 } ############################################################################### # function iam_create_role # # This function creates an IAM role. # # Parameters: # -n role_name -- The name of the IAM role. # -p policy_json -- The assume role policy document. # # Returns: # The ARN of the role. # And: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_create_role() { local role_name policy_document response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_create_user_access_key" echo "Creates an AWS Identity and Access Management (IAM) role." echo " -n role_name The name of the IAM role." echo " -p policy_json -- The assume role policy document." echo "" } # Retrieve the calling parameters. while getopts "n:p:h" option; do case "${option}" in n) role_name="${OPTARG}" ;; p) policy_document="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$role_name" ]]; then errecho "ERROR: You must provide a role name with the -n parameter." usage return 1 fi if [[ -z "$policy_document" ]]; then errecho "ERROR: You must provide a policy document with the -p parameter." usage return 1 fi response=$(aws iam create-role \ --role-name "$role_name" \ --assume-role-policy-document "$policy_document" \ --output text \ --query Role.Arn) local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports create-role operation failed.\n$response" return 1 fi echo "$response" return 0 }
  • Untuk detail API, lihat CreateRoledi Referensi AWS CLI Perintah.

Contoh kode berikut menunjukkan cara menggunakanCreateUser.

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankannya di Repositori Contoh Kode AWS.

############################################################################### # function iecho # # This function enables the script to display the specified text only if # the global variable $VERBOSE is set to true. ############################################################################### function iecho() { if [[ $VERBOSE == true ]]; then echo "$@" fi } ############################################################################### # function errecho # # This function outputs everything sent to it to STDERR (standard error output). ############################################################################### function errecho() { printf "%s\n" "$*" 1>&2 } ############################################################################### # function iam_create_user # # This function creates the specified IAM user, unless # it already exists. # # Parameters: # -u user_name -- The name of the user to create. # # Returns: # The ARN of the user. # And: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_create_user() { local user_name response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_create_user" echo "Creates an AWS Identity and Access Management (IAM) user. You must supply a username:" echo " -u user_name The name of the user. It must be unique within the account." echo "" } # Retrieve the calling parameters. while getopts "u:h" option; do case "${option}" in u) user_name="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$user_name" ]]; then errecho "ERROR: You must provide a username with the -u parameter." usage return 1 fi iecho "Parameters:\n" iecho " User name: $user_name" iecho "" # If the user already exists, we don't want to try to create it. if (iam_user_exists "$user_name"); then errecho "ERROR: A user with that name already exists in the account." return 1 fi response=$(aws iam create-user --user-name "$user_name" \ --output text \ --query 'User.Arn') local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports create-user operation failed.$response" return 1 fi echo "$response" return 0 }
  • Untuk detail API, lihat CreateUserdi Referensi AWS CLI Perintah.

Contoh kode berikut menunjukkan cara menggunakanDeleteAccessKey.

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankannya di Repositori Contoh Kode AWS.

############################################################################### # function errecho # # This function outputs everything sent to it to STDERR (standard error output). ############################################################################### function errecho() { printf "%s\n" "$*" 1>&2 } ############################################################################### # function iam_delete_access_key # # This function deletes an IAM access key for the specified IAM user. # # Parameters: # -u user_name -- The name of the user. # -k access_key -- The access key to delete. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_delete_access_key() { local user_name access_key response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_delete_access_key" echo "Deletes an AWS Identity and Access Management (IAM) access key for the specified IAM user" echo " -u user_name The name of the user." echo " -k access_key The access key to delete." echo "" } # Retrieve the calling parameters. while getopts "u:k:h" option; do case "${option}" in u) user_name="${OPTARG}" ;; k) access_key="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$user_name" ]]; then errecho "ERROR: You must provide a username with the -u parameter." usage return 1 fi if [[ -z "$access_key" ]]; then errecho "ERROR: You must provide an access key with the -k parameter." usage return 1 fi iecho "Parameters:\n" iecho " Username: $user_name" iecho " Access key: $access_key" iecho "" response=$(aws iam delete-access-key \ --user-name "$user_name" \ --access-key-id "$access_key") local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports delete-access-key operation failed.\n$response" return 1 fi iecho "delete-access-key response:$response" iecho return 0 }

Contoh kode berikut menunjukkan cara menggunakanDeletePolicy.

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankannya di Repositori Contoh Kode AWS.

############################################################################### # function iecho # # This function enables the script to display the specified text only if # the global variable $VERBOSE is set to true. ############################################################################### function iecho() { if [[ $VERBOSE == true ]]; then echo "$@" fi } ############################################################################### # function errecho # # This function outputs everything sent to it to STDERR (standard error output). ############################################################################### function errecho() { printf "%s\n" "$*" 1>&2 } ############################################################################### # function iam_delete_policy # # This function deletes an IAM policy. # # Parameters: # -n policy_arn -- The name of the IAM policy arn. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_delete_policy() { local policy_arn response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_delete_policy" echo "Deletes an AWS Identity and Access Management (IAM) policy" echo " -n policy_arn -- The name of the IAM policy arn." echo "" } # Retrieve the calling parameters. while getopts "n:h" option; do case "${option}" in n) policy_arn="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$policy_arn" ]]; then errecho "ERROR: You must provide a policy arn with the -n parameter." usage return 1 fi iecho "Parameters:\n" iecho " Policy arn: $policy_arn" iecho "" response=$(aws iam delete-policy \ --policy-arn "$policy_arn") local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports delete-policy operation failed.\n$response" return 1 fi iecho "delete-policy response:$response" iecho return 0 }
  • Untuk detail API, lihat DeletePolicydi Referensi AWS CLI Perintah.

Contoh kode berikut menunjukkan cara menggunakanDeleteRole.

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankannya di Repositori Contoh Kode AWS.

############################################################################### # function iecho # # This function enables the script to display the specified text only if # the global variable $VERBOSE is set to true. ############################################################################### function iecho() { if [[ $VERBOSE == true ]]; then echo "$@" fi } ############################################################################### # function errecho # # This function outputs everything sent to it to STDERR (standard error output). ############################################################################### function errecho() { printf "%s\n" "$*" 1>&2 } ############################################################################### # function iam_delete_role # # This function deletes an IAM role. # # Parameters: # -n role_name -- The name of the IAM role. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_delete_role() { local role_name response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_delete_role" echo "Deletes an AWS Identity and Access Management (IAM) role" echo " -n role_name -- The name of the IAM role." echo "" } # Retrieve the calling parameters. while getopts "n:h" option; do case "${option}" in n) role_name="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 echo "role_name:$role_name" if [[ -z "$role_name" ]]; then errecho "ERROR: You must provide a role name with the -n parameter." usage return 1 fi iecho "Parameters:\n" iecho " Role name: $role_name" iecho "" response=$(aws iam delete-role \ --role-name "$role_name") local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports delete-role operation failed.\n$response" return 1 fi iecho "delete-role response:$response" iecho return 0 }
  • Untuk detail API, lihat DeleteRoledi Referensi AWS CLI Perintah.

Contoh kode berikut menunjukkan cara menggunakanDeleteUser.

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankannya di Repositori Contoh Kode AWS.

############################################################################### # function iecho # # This function enables the script to display the specified text only if # the global variable $VERBOSE is set to true. ############################################################################### function iecho() { if [[ $VERBOSE == true ]]; then echo "$@" fi } ############################################################################### # function errecho # # This function outputs everything sent to it to STDERR (standard error output). ############################################################################### function errecho() { printf "%s\n" "$*" 1>&2 } ############################################################################### # function iam_delete_user # # This function deletes the specified IAM user. # # Parameters: # -u user_name -- The name of the user to create. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_delete_user() { local user_name response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_delete_user" echo "Deletes an AWS Identity and Access Management (IAM) user. You must supply a username:" echo " -u user_name The name of the user." echo "" } # Retrieve the calling parameters. while getopts "u:h" option; do case "${option}" in u) user_name="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$user_name" ]]; then errecho "ERROR: You must provide a username with the -u parameter." usage return 1 fi iecho "Parameters:\n" iecho " User name: $user_name" iecho "" # If the user does not exist, we don't want to try to delete it. if (! iam_user_exists "$user_name"); then errecho "ERROR: A user with that name does not exist in the account." return 1 fi response=$(aws iam delete-user \ --user-name "$user_name") local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports delete-user operation failed.$response" return 1 fi iecho "delete-user response:$response" iecho return 0 }
  • Untuk detail API, lihat DeleteUserdi Referensi AWS CLI Perintah.

Contoh kode berikut menunjukkan cara menggunakanDetachRolePolicy.

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankannya di Repositori Contoh Kode AWS.

############################################################################### # function errecho # # This function outputs everything sent to it to STDERR (standard error output). ############################################################################### function errecho() { printf "%s\n" "$*" 1>&2 } ############################################################################### # function iam_detach_role_policy # # This function detaches an IAM policy to a tole. # # Parameters: # -n role_name -- The name of the IAM role. # -p policy_ARN -- The IAM policy document ARN.. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_detach_role_policy() { local role_name policy_arn response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function iam_detach_role_policy" echo "Detaches an AWS Identity and Access Management (IAM) policy to an IAM role." echo " -n role_name The name of the IAM role." echo " -p policy_ARN -- The IAM policy document ARN." echo "" } # Retrieve the calling parameters. while getopts "n:p:h" option; do case "${option}" in n) role_name="${OPTARG}" ;; p) policy_arn="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$role_name" ]]; then errecho "ERROR: You must provide a role name with the -n parameter." usage return 1 fi if [[ -z "$policy_arn" ]]; then errecho "ERROR: You must provide a policy ARN with the -p parameter." usage return 1 fi response=$(aws iam detach-role-policy \ --role-name "$role_name" \ --policy-arn "$policy_arn") local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports detach-role-policy operation failed.\n$response" return 1 fi echo "$response" return 0 }

Contoh kode berikut menunjukkan cara menggunakanGetUser.

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankannya di Repositori Contoh Kode AWS.

############################################################################### # function errecho # # This function outputs everything sent to it to STDERR (standard error output). ############################################################################### function errecho() { printf "%s\n" "$*" 1>&2 } ############################################################################### # function iam_user_exists # # This function checks to see if the specified AWS Identity and Access Management (IAM) user already exists. # # Parameters: # $1 - The name of the IAM user to check. # # Returns: # 0 - If the user already exists. # 1 - If the user doesn't exist. ############################################################################### function iam_user_exists() { local user_name user_name=$1 # Check whether the IAM user already exists. # We suppress all output - we're interested only in the return code. local errors errors=$(aws iam get-user \ --user-name "$user_name" 2>&1 >/dev/null) local error_code=${?} if [[ $error_code -eq 0 ]]; then return 0 # 0 in Bash script means true. else if [[ $errors != *"error"*"(NoSuchEntity)"* ]]; then aws_cli_error_log $error_code errecho "Error calling iam get-user $errors" fi return 1 # 1 in Bash script means false. fi }
  • Untuk detail API, lihat GetUserdi Referensi AWS CLI Perintah.

Contoh kode berikut menunjukkan cara menggunakanListAccessKeys.

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankannya di Repositori Contoh Kode AWS.

############################################################################### # function errecho # # This function outputs everything sent to it to STDERR (standard error output). ############################################################################### function errecho() { printf "%s\n" "$*" 1>&2 } ############################################################################### # function iam_list_access_keys # # This function lists the access keys for the specified user. # # Parameters: # -u user_name -- The name of the IAM user. # # Returns: # access_key_ids # And: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_list_access_keys() { # bashsupport disable=BP5008 function usage() { echo "function iam_list_access_keys" echo "Lists the AWS Identity and Access Management (IAM) access key IDs for the specified user." echo " -u user_name The name of the IAM user." echo "" } local user_name response local option OPTARG # Required to use getopts command in a function. # Retrieve the calling parameters. while getopts "u:h" option; do case "${option}" in u) user_name="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$user_name" ]]; then errecho "ERROR: You must provide a username with the -u parameter." usage return 1 fi response=$(aws iam list-access-keys \ --user-name "$user_name" \ --output text \ --query 'AccessKeyMetadata[].AccessKeyId') local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports list-access-keys operation failed.$response" return 1 fi echo "$response" return 0 }
  • Untuk detail API, lihat ListAccessKeysdi Referensi AWS CLI Perintah.

Contoh kode berikut menunjukkan cara menggunakanListUsers.

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankannya di Repositori Contoh Kode AWS.

############################################################################### # function errecho # # This function outputs everything sent to it to STDERR (standard error output). ############################################################################### function errecho() { printf "%s\n" "$*" 1>&2 } ############################################################################### # function iam_list_users # # List the IAM users in the account. # # Returns: # The list of users names # And: # 0 - If the user already exists. # 1 - If the user doesn't exist. ############################################################################### function iam_list_users() { local option OPTARG # Required to use getopts command in a function. local error_code # bashsupport disable=BP5008 function usage() { echo "function iam_list_users" echo "Lists the AWS Identity and Access Management (IAM) user in the account." echo "" } # Retrieve the calling parameters. while getopts "h" option; do case "${option}" in h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 local response response=$(aws iam list-users \ --output text \ --query "Users[].UserName") error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports list-users operation failed.$response" return 1 fi echo "$response" return 0 }
  • Untuk detail API, lihat ListUsersdi Referensi AWS CLI Perintah.

Contoh kode berikut menunjukkan cara menggunakanUpdateAccessKey.

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankannya di Repositori Contoh Kode AWS.

############################################################################### # function iam_update_access_key # # This function can activate or deactivate an IAM access key for the specified IAM user. # # Parameters: # -u user_name -- The name of the user. # -k access_key -- The access key to update. # -a -- Activate the selected access key. # -d -- Deactivate the selected access key. # # Example: # # To deactivate the selected access key for IAM user Bob # iam_update_access_key -u Bob -k AKIAIOSFODNN7EXAMPLE -d # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function iam_update_access_key() { local user_name access_key status response local option OPTARG # Required to use getopts command in a function. local activate_flag=false deactivate_flag=false # bashsupport disable=BP5008 function usage() { echo "function iam_update_access_key" echo "Updates the status of an AWS Identity and Access Management (IAM) access key for the specified IAM user" echo " -u user_name The name of the user." echo " -k access_key The access key to update." echo " -a Activate the access key." echo " -d Deactivate the access key." echo "" } # Retrieve the calling parameters. while getopts "u:k:adh" option; do case "${option}" in u) user_name="${OPTARG}" ;; k) access_key="${OPTARG}" ;; a) activate_flag=true ;; d) deactivate_flag=true ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 # Validate input parameters if [[ -z "$user_name" ]]; then errecho "ERROR: You must provide a username with the -u parameter." usage return 1 fi if [[ -z "$access_key" ]]; then errecho "ERROR: You must provide an access key with the -k parameter." usage return 1 fi # Ensure that only -a or -d is specified if [[ "$activate_flag" == true && "$deactivate_flag" == true ]]; then errecho "ERROR: You cannot specify both -a (activate) and -d (deactivate) at the same time." usage return 1 fi # If neither -a nor -d is provided, return an error if [[ "$activate_flag" == false && "$deactivate_flag" == false ]]; then errecho "ERROR: You must specify either -a (activate) or -d (deactivate)." usage return 1 fi # Determine the status based on the flag if [[ "$activate_flag" == true ]]; then status="Active" elif [[ "$deactivate_flag" == true ]]; then status="Inactive" fi iecho "Parameters:\n" iecho " Username: $user_name" iecho " Access key: $access_key" iecho " New status: $status" iecho "" # Update the access key status response=$(aws iam update-access-key \ --user-name "$user_name" \ --access-key-id "$access_key" \ --status "$status" 2>&1) local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports update-access-key operation failed.\n$response" return 1 fi iecho "update-access-key response: $response" iecho return 0 }

Skenario

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Buat infrastruktur VPC

  • Mengatur pencatatan

  • Buat cluster ECS

  • Konfigurasikan peran IAM

  • Buat layanan dengan Service Connect

  • Verifikasi penyebaran

  • Pembersihan sumber daya

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash # ECS Service Connect Tutorial Script v4 - Modified to use Default VPC # This script creates an ECS cluster with Service Connect and deploys an nginx service # Uses the default VPC to avoid VPC limits set -e # Exit on any error # Configuration SCRIPT_NAME="ECS Service Connect Tutorial" LOG_FILE="ecs-service-connect-tutorial-v4-default-vpc.log" REGION=${AWS_DEFAULT_REGION:-${AWS_REGION:-$(aws configure get region 2>/dev/null)}} if [ -z "$REGION" ]; then echo "ERROR: No AWS region configured." echo "Set one with: aws configure set region us-east-1" exit 1 fi ENV_PREFIX="tutorial" CLUSTER_NAME="${ENV_PREFIX}-cluster" NAMESPACE_NAME="service-connect" # Generate random suffix for unique resource names RANDOM_SUFFIX=$(openssl rand -hex 6) # Arrays to track created resources for cleanup declare -a CREATED_RESOURCES=() # Logging function log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } # Error handling function handle_error() { log "ERROR: Script failed at line $1" log "Attempting to clean up resources..." cleanup_resources exit 1 } # Set up error handling trap 'handle_error $LINENO' ERR # Function to add resource to tracking array track_resource() { CREATED_RESOURCES+=("$1") log "Tracking resource: $1" } # Function to check if command output contains actual errors check_for_errors() { local output="$1" local command_name="$2" # Check for specific AWS CLI error patterns, not just any occurrence of "error" if echo "$output" | grep -qi "An error occurred\|InvalidParameterException\|AccessDenied\|ValidationException\|ResourceNotFoundException"; then log "ERROR in $command_name: $output" return 1 fi return 0 } # Function to get AWS account ID get_account_id() { ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) log "Using AWS Account ID: $ACCOUNT_ID" } # Function to wait for resources to be ready wait_for_resource() { local resource_type="$1" local resource_id="$2" case "$resource_type" in "cluster") log "Waiting for cluster $resource_id to be active..." local attempt=1 local max_attempts=30 while [ $attempt -le $max_attempts ]; do local status=$(aws ecs describe-clusters --clusters "$resource_id" --query 'clusters[0].status' --output text) if [ "$status" = "ACTIVE" ]; then log "Cluster is now active" return 0 fi log "Cluster status: $status (attempt $attempt/$max_attempts)" sleep 10 ((attempt++)) done log "ERROR: Cluster did not become active within expected time" return 1 ;; "service") log "Waiting for service $resource_id to be stable..." aws ecs wait services-stable --cluster "$CLUSTER_NAME" --services "$resource_id" ;; "nat-gateway") log "Waiting for NAT Gateway $resource_id to be available..." aws ec2 wait nat-gateway-available --nat-gateway-ids "$resource_id" ;; esac } # Function to use default VPC infrastructure setup_default_vpc_infrastructure() { log "Using default VPC infrastructure..." # Get default VPC VPC_ID=$(aws ec2 describe-vpcs --filters "Name=isDefault,Values=true" --query 'Vpcs[0].VpcId' --output text) if [[ "$VPC_ID" == "None" || -z "$VPC_ID" ]]; then log "ERROR: No default VPC found. Please create a default VPC first." exit 1 fi log "Using default VPC: $VPC_ID" # Get default subnets SUBNETS=$(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPC_ID" "Name=default-for-az,Values=true" --query 'Subnets[].SubnetId' --output text) SUBNET_ARRAY=($SUBNETS) if [ ${#SUBNET_ARRAY[@]} -lt 2 ]; then log "ERROR: Need at least 2 subnets for ECS Service Connect. Found: ${#SUBNET_ARRAY[@]}" exit 1 fi PUBLIC_SUBNET1=${SUBNET_ARRAY[0]} PUBLIC_SUBNET2=${SUBNET_ARRAY[1]} log "Using subnets: $PUBLIC_SUBNET1, $PUBLIC_SUBNET2" # Create security group for ECS tasks SG_OUTPUT=$(aws ec2 create-security-group \ --group-name "${ENV_PREFIX}-ecs-sg-${RANDOM_SUFFIX}" \ --description "Security group for ECS Service Connect tutorial" \ --vpc-id "$VPC_ID" \ --tag-specifications 'ResourceType=security-group,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-ecs-service-connect}]' 2>&1) check_for_errors "$SG_OUTPUT" "create-security-group" SECURITY_GROUP_ID=$(echo "$SG_OUTPUT" | grep -o '"GroupId": "[^"]*"' | cut -d'"' -f4) track_resource "SG:$SECURITY_GROUP_ID" log "Created security group: $SECURITY_GROUP_ID" # Add inbound rules to security group aws ec2 authorize-security-group-ingress \ --group-id "$SECURITY_GROUP_ID" \ --protocol tcp \ --port 80 \ --cidr 0.0.0.0/0 >/dev/null 2>&1 || true aws ec2 authorize-security-group-ingress \ --group-id "$SECURITY_GROUP_ID" \ --protocol tcp \ --port 443 \ --cidr 0.0.0.0/0 >/dev/null 2>&1 || true log "Default VPC infrastructure setup completed" } # Function to create CloudWatch log groups create_log_groups() { log "Creating CloudWatch log groups..." # Create log group for nginx container aws logs create-log-group --log-group-name "/ecs/service-connect-nginx" --tags project=doc-smith,tutorial=amazon-ecs-service-connect 2>&1 | grep -v "ResourceAlreadyExistsException" || { if [ ${PIPESTATUS[0]} -eq 0 ]; then log "Log group /ecs/service-connect-nginx created" track_resource "LOG_GROUP:/ecs/service-connect-nginx" else log "Log group /ecs/service-connect-nginx already exists" fi } # Create log group for service connect proxy aws logs create-log-group --log-group-name "/ecs/service-connect-proxy" --tags project=doc-smith,tutorial=amazon-ecs-service-connect 2>&1 | grep -v "ResourceAlreadyExistsException" || { if [ ${PIPESTATUS[0]} -eq 0 ]; then log "Log group /ecs/service-connect-proxy created" track_resource "LOG_GROUP:/ecs/service-connect-proxy" else log "Log group /ecs/service-connect-proxy already exists" fi } } # Function to create ECS cluster with Service Connect create_ecs_cluster() { log "Creating ECS cluster with Service Connect..." CLUSTER_OUTPUT=$(aws ecs create-cluster \ --cluster-name "$CLUSTER_NAME" \ --service-connect-defaults namespace="$NAMESPACE_NAME" \ --tags key=Environment,value=tutorial key=project,value=doc-smith key=tutorial,value=amazon-ecs-service-connect 2>&1) check_for_errors "$CLUSTER_OUTPUT" "create-cluster" track_resource "CLUSTER:$CLUSTER_NAME" log "Created ECS cluster: $CLUSTER_NAME" wait_for_resource "cluster" "$CLUSTER_NAME" # Track the Service Connect namespace that gets created # Wait a moment for the namespace to be created sleep 5 NAMESPACE_ID=$(aws servicediscovery list-namespaces \ --filters Name=TYPE,Values=HTTP \ --query "Namespaces[?Name=='$NAMESPACE_NAME'].Id" --output text 2>/dev/null || echo "") if [[ -n "$NAMESPACE_ID" && "$NAMESPACE_ID" != "None" ]]; then track_resource "NAMESPACE:$NAMESPACE_ID" log "Service Connect namespace created: $NAMESPACE_ID" fi } # Function to create IAM roles create_iam_roles() { log "Creating IAM roles..." # Check if ecsTaskExecutionRole exists if aws iam get-role --role-name ecsTaskExecutionRole >/dev/null 2>&1; then log "IAM role ecsTaskExecutionRole exists" else log "Creating ecsTaskExecutionRole..." aws iam create-role \ --role-name ecsTaskExecutionRole \ --assume-role-policy-document '{ "Version":"2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"Service": "ecs-tasks.amazonaws.com"}, "Action": "sts:AssumeRole" }] }' >/dev/null 2>&1 aws iam attach-role-policy \ --role-name ecsTaskExecutionRole \ --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy >/dev/null 2>&1 track_resource "ROLE:ecsTaskExecutionRole" aws iam tag-role --role-name ecsTaskExecutionRole --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-ecs-service-connect log "Created ecsTaskExecutionRole" sleep 10 fi # Check if ecsTaskRole exists, create if not if aws iam get-role --role-name ecsTaskRole >/dev/null 2>&1; then log "IAM role ecsTaskRole exists" else log "IAM role ecsTaskRole does not exist, will create it" # Create trust policy for ECS tasks cat > /tmp/ecs-task-trust-policy.json << EOF { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF aws iam create-role \ --role-name ecsTaskRole \ --assume-role-policy-document file:///tmp/ecs-task-trust-policy.json >/dev/null track_resource "IAM_ROLE:ecsTaskRole" aws iam tag-role --role-name ecsTaskRole --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-ecs-service-connect log "Created ecsTaskRole" # Wait for role to be available sleep 10 fi } # Function to create task definition create_task_definition() { log "Creating task definition..." # Create task definition JSON cat > /tmp/task-definition.json << EOF { "family": "service-connect-nginx", "networkMode": "awsvpc", "requiresCompatibilities": ["FARGATE"], "cpu": "256", "memory": "512", "executionRoleArn": "arn:aws:iam::${ACCOUNT_ID}:role/ecsTaskExecutionRole", "taskRoleArn": "arn:aws:iam::${ACCOUNT_ID}:role/ecsTaskRole", "containerDefinitions": [ { "name": "nginx", "image": "public.ecr.aws/docker/library/nginx:latest", "portMappings": [ { "containerPort": 80, "protocol": "tcp", "name": "nginx-port" } ], "essential": true, "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/service-connect-nginx", "awslogs-region": "${REGION}", "awslogs-stream-prefix": "ecs" } } } ] } EOF TASK_DEF_OUTPUT=$(aws ecs register-task-definition --cli-input-json file:///tmp/task-definition.json 2>&1) check_for_errors "$TASK_DEF_OUTPUT" "register-task-definition" TASK_DEF_ARN=$(echo "$TASK_DEF_OUTPUT" | grep -o '"taskDefinitionArn": "[^"]*"' | cut -d'"' -f4) track_resource "TASK_DEF:service-connect-nginx" log "Created task definition: $TASK_DEF_ARN" # Clean up temporary file rm -f /tmp/task-definition.json } # Function to create ECS service with Service Connect create_ecs_service() { log "Creating ECS service with Service Connect..." # Create service definition JSON cat > /tmp/service-definition.json << EOF { "serviceName": "service-connect-nginx-service", "cluster": "${CLUSTER_NAME}", "taskDefinition": "service-connect-nginx", "desiredCount": 1, "launchType": "FARGATE", "networkConfiguration": { "awsvpcConfiguration": { "subnets": ["${PUBLIC_SUBNET1}", "${PUBLIC_SUBNET2}"], "securityGroups": ["${SECURITY_GROUP_ID}"], "assignPublicIp": "ENABLED" } }, "serviceConnectConfiguration": { "enabled": true, "namespace": "${NAMESPACE_NAME}", "services": [ { "portName": "nginx-port", "discoveryName": "nginx", "clientAliases": [ { "port": 80, "dnsName": "nginx" } ] } ], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/service-connect-proxy", "awslogs-region": "${REGION}", "awslogs-stream-prefix": "ecs-service-connect" } } }, "tags": [ { "key": "Environment", "value": "tutorial" }, { "key": "project", "value": "doc-smith" }, { "key": "tutorial", "value": "amazon-ecs-service-connect" } ] } EOF SERVICE_OUTPUT=$(aws ecs create-service --cli-input-json file:///tmp/service-definition.json 2>&1) check_for_errors "$SERVICE_OUTPUT" "create-service" track_resource "SERVICE:service-connect-nginx-service" log "Created ECS service: service-connect-nginx-service" wait_for_resource "service" "service-connect-nginx-service" # Clean up temporary file rm -f /tmp/service-definition.json } # Function to verify deployment verify_deployment() { log "Verifying deployment..." # Check service status SERVICE_STATUS=$(aws ecs describe-services \ --cluster "$CLUSTER_NAME" \ --services "service-connect-nginx-service" \ --query 'services[0].status' --output text) log "Service status: $SERVICE_STATUS" # Check running tasks RUNNING_COUNT=$(aws ecs describe-services \ --cluster "$CLUSTER_NAME" \ --services "service-connect-nginx-service" \ --query 'services[0].runningCount' --output text) log "Running tasks: $RUNNING_COUNT" # Get task ARN TASK_ARN=$(aws ecs list-tasks \ --cluster "$CLUSTER_NAME" \ --service-name "service-connect-nginx-service" \ --query 'taskArns[0]' --output text) if [[ "$TASK_ARN" != "None" && -n "$TASK_ARN" ]]; then log "Task ARN: $TASK_ARN" # Try to get task IP address TASK_IP=$(aws ecs describe-tasks \ --cluster "$CLUSTER_NAME" \ --tasks "$TASK_ARN" \ --query 'tasks[0].attachments[0].details[?name==`privateIPv4Address`].value' \ --output text 2>/dev/null || echo "") if [[ -n "$TASK_IP" && "$TASK_IP" != "None" ]]; then log "Task IP address: $TASK_IP" else log "Could not retrieve task IP address" fi fi # Check Service Connect namespace NAMESPACE_STATUS=$(aws servicediscovery list-namespaces \ --filters Name=TYPE,Values=HTTP \ --query "Namespaces[?Name=='$NAMESPACE_NAME'].Id" --output text 2>/dev/null || echo "") if [[ -n "$NAMESPACE_STATUS" && "$NAMESPACE_STATUS" != "None" ]]; then log "Service Connect namespace '$NAMESPACE_NAME' is active" else log "Service Connect namespace '$NAMESPACE_NAME' not found or not active" fi # Display Service Connect configuration log "Service Connect configuration:" aws ecs describe-services \ --cluster "$CLUSTER_NAME" \ --services "service-connect-nginx-service" \ --query 'services[0].serviceConnectConfiguration' 2>/dev/null || true } # Function to display created resources display_resources() { echo "" echo "===========================================" echo "CREATED RESOURCES" echo "===========================================" for resource in "${CREATED_RESOURCES[@]}"; do echo "- $resource" done echo "===========================================" echo "" } # Function to cleanup resources cleanup_resources() { log "Starting cleanup process..." # Delete resources in reverse order of creation for ((i=${#CREATED_RESOURCES[@]}-1; i>=0; i--)); do resource="${CREATED_RESOURCES[i]}" resource_type=$(echo "$resource" | cut -d':' -f1) resource_id=$(echo "$resource" | cut -d':' -f2) log "Cleaning up $resource_type: $resource_id" case "$resource_type" in "SERVICE") aws ecs update-service --cluster "$CLUSTER_NAME" --service "$resource_id" --desired-count 0 2>&1 | grep -qi "error" && log "Warning: Failed to scale down service $resource_id" aws ecs wait services-stable --cluster "$CLUSTER_NAME" --services "$resource_id" 2>/dev/null || true aws ecs delete-service --cluster "$CLUSTER_NAME" --service "$resource_id" --force 2>&1 | grep -qi "error" && log "Warning: Failed to delete service $resource_id" ;; "TASK_DEF") TASK_DEF_ARNS=$(aws ecs list-task-definitions --family-prefix "$resource_id" --query 'taskDefinitionArns' --output text 2>/dev/null) for arn in $TASK_DEF_ARNS; do aws ecs deregister-task-definition --task-definition "$arn" >/dev/null 2>&1 || true done ;; "ROLE") aws iam detach-role-policy --role-name "$resource_id" --policy-arn "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" 2>/dev/null || true aws iam delete-role --role-name "$resource_id" 2>&1 | grep -qi "error" && log "Warning: Failed to delete role $resource_id" ;; "IAM_ROLE") aws iam detach-role-policy --role-name "$resource_id" --policy-arn "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" 2>/dev/null || true aws iam delete-role --role-name "$resource_id" 2>&1 | grep -qi "error" && log "Warning: Failed to delete role $resource_id" ;; "CLUSTER") aws ecs delete-cluster --cluster "$resource_id" 2>&1 | grep -qi "error" && log "Warning: Failed to delete cluster $resource_id" ;; "SG") for attempt in 1 2 3 4 5; do if aws ec2 delete-security-group --group-id "$resource_id" 2>/dev/null; then break fi log "Security group $resource_id still has dependencies, retrying in 30s ($attempt/5)..." sleep 30 done ;; "LOG_GROUP") aws logs delete-log-group --log-group-name "$resource_id" 2>&1 | grep -qi "error" && log "Warning: Failed to delete log group $resource_id" ;; "NAMESPACE") # First, delete any services in the namespace NAMESPACE_SERVICES=$(aws servicediscovery list-services \ --filters Name=NAMESPACE_ID,Values="$resource_id" \ --query 'Services[].Id' --output text 2>/dev/null || echo "") if [[ -n "$NAMESPACE_SERVICES" && "$NAMESPACE_SERVICES" != "None" ]]; then for service_id in $NAMESPACE_SERVICES; do aws servicediscovery delete-service --id "$service_id" >/dev/null 2>&1 || true sleep 2 done fi # Then delete the namespace aws servicediscovery delete-namespace --id "$resource_id" >/dev/null 2>&1 || true ;; esac sleep 2 # Brief pause between deletions done # Clean up temporary files rm -f /tmp/ecs-task-trust-policy.json rm -f /tmp/task-definition.json rm -f /tmp/service-definition.json log "Cleanup completed" } # Main execution main() { log "Starting $SCRIPT_NAME v4 (Default VPC)" log "Region: $REGION" log "Log file: $LOG_FILE" # Get AWS account ID get_account_id # Setup infrastructure using default VPC setup_default_vpc_infrastructure # Create CloudWatch log groups create_log_groups # Create ECS cluster create_ecs_cluster # Create IAM roles create_iam_roles # Create task definition create_task_definition # Create ECS service create_ecs_service # Verify deployment verify_deployment log "Tutorial completed successfully!" # Display created resources display_resources # Ask user if they want to clean up 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 cleanup_resources log "All resources have been cleaned up" else log "Resources left intact. You can clean them up later by running the cleanup function." echo "" echo "To clean up resources later, you can use the AWS CLI commands or the AWS Management Console." echo "Remember to delete resources in the correct order to avoid dependency issues." fi } # Make script executable and run chmod +x "$0" main "$@"

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Buat peran IAM untuk eksekusi Lambda

  • Buat dan terapkan fungsi Lambda

  • Buat REST API

  • Konfigurasikan integrasi proxy Lambda

  • Menerapkan dan menguji API

  • Pembersihan sumber daya

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash set -euo pipefail # Simple API Gateway Lambda Integration Script # This script creates a REST API with Lambda proxy integration # Generate random identifiers FUNCTION_NAME="GetStartedLambdaProxyIntegration-$(openssl rand -hex 4)" ROLE_NAME="GetStartedLambdaBasicExecutionRole-$(openssl rand -hex 4)" API_NAME="LambdaProxyAPI-$(openssl rand -hex 4)" # Get AWS account info ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) REGION=$(aws configure get region || echo "us-east-1") # Validate inputs if [[ -z "$ACCOUNT_ID" ]] || [[ -z "$REGION" ]]; then echo "Error: Failed to retrieve AWS account information" >&2 exit 1 fi echo "Creating Lambda function code..." # Create Lambda function code with input validation cat > lambda_function.py << 'EOF' import json import logging logger = logging.getLogger() logger.setLevel(logging.INFO) def lambda_handler(event, context): try: logger.info("Received event: %s", json.dumps(event)) greeter = 'World' # Safely retrieve greeter from query string parameters query_params = event.get('queryStringParameters') or {} if isinstance(query_params, dict) and 'greeter' in query_params: greeter_value = query_params.get('greeter') if isinstance(greeter_value, str) and greeter_value: greeter = greeter_value # Safely retrieve greeter from multi-value headers multi_headers = event.get('multiValueHeaders') or {} if isinstance(multi_headers, dict) and 'greeter' in multi_headers: greeter_list = multi_headers.get('greeter', []) if isinstance(greeter_list, list) and greeter_list: greeter = " and ".join(str(g) for g in greeter_list if g) # Safely retrieve greeter from headers headers = event.get('headers') or {} if isinstance(headers, dict) and 'greeter' in headers: greeter_value = headers.get('greeter') if isinstance(greeter_value, str) and greeter_value: greeter = greeter_value # Safely retrieve greeter from body body = event.get('body') if body and isinstance(body, str): try: body_dict = json.loads(body) if isinstance(body_dict, dict) and 'greeter' in body_dict: greeter_value = body_dict.get('greeter') if isinstance(greeter_value, str) and greeter_value: greeter = greeter_value except (json.JSONDecodeError, ValueError) as e: logger.warning("Failed to parse body: %s", str(e)) # Sanitize greeter to prevent injection greeter = greeter.replace('"', '\\"').replace("'", "\\'") response = { "statusCode": 200, "headers": { "Content-Type": "application/json" }, "body": json.dumps({"message": f"Hello, {greeter}!"}) } logger.info("Response: %s", json.dumps(response)) return response except Exception as e: logger.error("Unexpected error: %s", str(e), exc_info=True) return { "statusCode": 500, "headers": { "Content-Type": "application/json" }, "body": json.dumps({"error": "Internal server error"}) } EOF # Create deployment package zip -q function.zip lambda_function.py || { echo "Error: Failed to create function.zip" >&2 exit 1 } echo "Creating IAM role..." # Create IAM trust policy cat > trust-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF # Create IAM role with error handling aws iam create-role \ --role-name "$ROLE_NAME" \ --assume-role-policy-document file://trust-policy.json \ --description "Temporary role for Lambda execution" || { echo "Error: Failed to create IAM role" >&2 exit 1 } aws iam tag-role --role-name "$ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=apigateway-lambda-integration # Attach execution policy aws iam attach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" || { echo "Error: Failed to attach IAM policy" >&2 exit 1 } # Wait for role propagation sleep 15 echo "Creating Lambda function..." # Create Lambda function with Python 3.11 (more recent runtime) aws lambda create-function \ --function-name "$FUNCTION_NAME" \ --runtime python3.11 \ --role "arn:aws:iam::$ACCOUNT_ID:role/$ROLE_NAME" \ --handler lambda_function.lambda_handler \ --zip-file fileb://function.zip \ --timeout 30 \ --memory-size 128 \ --environment "Variables={LOG_LEVEL=INFO}" \ --tags project=doc-smith,tutorial=apigateway-lambda-integration || { echo "Error: Failed to create Lambda function" >&2 exit 1 } echo "Creating API Gateway..." # Create REST API with minimum logging API_RESPONSE=$(aws apigateway create-rest-api \ --name "$API_NAME" \ --endpoint-configuration types=REGIONAL \ --description "API for Lambda proxy integration tutorial" \ --tags project=doc-smith,tutorial=apigateway-lambda-integration \ --output json) API_ID=$(echo "$API_RESPONSE" | grep -o '"id": "[^"]*"' | head -1 | cut -d'"' -f4) if [[ -z "$API_ID" ]]; then echo "Error: Failed to create API Gateway" >&2 exit 1 fi # Get root resource ID ROOT_RESOURCE_ID=$(aws apigateway get-resources --rest-api-id "$API_ID" --query 'items[?path==`/`].id' --output text) # Create helloworld resource aws apigateway create-resource \ --rest-api-id "$API_ID" \ --parent-id "$ROOT_RESOURCE_ID" \ --path-part helloworld || { echo "Error: Failed to create resource" >&2 exit 1 } # Get resource ID RESOURCE_ID=$(aws apigateway get-resources --rest-api-id "$API_ID" --query "items[?pathPart=='helloworld'].id" --output text) # Create ANY method with no authorization (intentional for tutorial) aws apigateway put-method \ --rest-api-id "$API_ID" \ --resource-id "$RESOURCE_ID" \ --http-method ANY \ --authorization-type NONE || { echo "Error: Failed to create method" >&2 exit 1 } # Set up Lambda proxy integration LAMBDA_URI="arn:aws:apigateway:$REGION:lambda:path/2015-03-31/functions/arn:aws:lambda:$REGION:$ACCOUNT_ID:function:$FUNCTION_NAME/invocations" aws apigateway put-integration \ --rest-api-id "$API_ID" \ --resource-id "$RESOURCE_ID" \ --http-method ANY \ --type AWS_PROXY \ --integration-http-method POST \ --uri "$LAMBDA_URI" || { echo "Error: Failed to create integration" >&2 exit 1 } # Grant API Gateway permission to invoke Lambda STATEMENT_ID="apigateway-invoke-$(openssl rand -hex 4)" SOURCE_ARN="arn:aws:execute-api:$REGION:$ACCOUNT_ID:$API_ID/*/*" aws lambda add-permission \ --function-name "$FUNCTION_NAME" \ --statement-id "$STATEMENT_ID" \ --action lambda:InvokeFunction \ --principal apigateway.amazonaws.com \ --source-arn "$SOURCE_ARN" || { echo "Error: Failed to add Lambda permission" >&2 exit 1 } # Deploy API aws apigateway create-deployment \ --rest-api-id "$API_ID" \ --stage-name test \ --description "Test deployment" || { echo "Error: Failed to deploy API" >&2 exit 1 } echo "Testing API..." # Test the API INVOKE_URL="https://$API_ID.execute-api.$REGION.amazonaws.com/test/helloworld" echo "API URL: $INVOKE_URL" # Test with query parameter (with proper URL encoding) echo "Testing with query parameter:" curl -s -X GET "$INVOKE_URL?greeter=John" | jq . 2>/dev/null || curl -s -X GET "$INVOKE_URL?greeter=John" echo "" # Test with header echo "Testing with header:" curl -s -X GET "$INVOKE_URL" \ -H 'content-type: application/json' \ -H 'greeter: John' | jq . 2>/dev/null || curl -s -X GET "$INVOKE_URL" \ -H 'content-type: application/json' \ -H 'greeter: John' echo "" # Test with body echo "Testing with POST body:" curl -s -X POST "$INVOKE_URL" \ -H 'content-type: application/json' \ -d '{"greeter": "John"}' | jq . 2>/dev/null || curl -s -X POST "$INVOKE_URL" \ -H 'content-type: application/json' \ -d '{"greeter": "John"}' echo "" echo "Tutorial completed! API is available at: $INVOKE_URL" # Cleanup echo "Cleaning up resources..." # Delete API aws apigateway delete-rest-api --rest-api-id "$API_ID" || echo "Warning: Failed to delete API" >&2 # Delete Lambda function aws lambda delete-function --function-name "$FUNCTION_NAME" || echo "Warning: Failed to delete Lambda function" >&2 # Detach policy and delete role aws iam detach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" || echo "Warning: Failed to detach policy" >&2 aws iam delete-role --role-name "$ROLE_NAME" || echo "Warning: Failed to delete role" >&2 # Clean up local files securely rm -f lambda_function.py function.zip trust-policy.json echo "Cleanup completed!"

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Buat cluster

  • Buat definisi tugas

  • Buat layanan

  • Bersihkan

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash # Amazon ECS Fargate Tutorial Script - Version 5 # This script creates an ECS cluster, task definition, and service using Fargate launch type # Fixed version with proper resource dependency handling during cleanup # Security improvements applied set -e # Exit on any error # Initialize logging LOG_FILE="ecs-fargate-tutorial-v5.log" exec > >(tee -a "$LOG_FILE") 2>&1 echo "Starting Amazon ECS Fargate tutorial at $(date)" echo "Log file: $LOG_FILE" # Generate random identifier for unique resource names RANDOM_ID=$(openssl rand -hex 6) CLUSTER_NAME="fargate-cluster-${RANDOM_ID}" SERVICE_NAME="fargate-service-${RANDOM_ID}" TASK_FAMILY="sample-fargate-${RANDOM_ID}" SECURITY_GROUP_NAME="ecs-fargate-sg-${RANDOM_ID}" # Array to track created resources for cleanup CREATED_RESOURCES=() # Function to log and execute commands with input validation execute_command() { local cmd="$1" local description="$2" # Validate that cmd is not empty if [[ -z "$cmd" ]]; then echo "ERROR: Command is empty" return 1 fi echo "" echo "==========================================" echo "EXECUTING: $description" echo "COMMAND: $cmd" echo "==========================================" local output local exit_code set +e # Temporarily disable exit on error output=$(eval "$cmd" 2>&1) exit_code=$? set -e # Re-enable exit on error if [[ $exit_code -eq 0 ]]; then echo "SUCCESS: $description" echo "OUTPUT: $output" return 0 else echo "FAILED: $description" echo "EXIT CODE: $exit_code" echo "OUTPUT: $output" return 1 fi } # Function to check for actual AWS API errors in command output check_for_aws_errors() { local output="$1" local description="$2" # Look for specific AWS error patterns, not just the word "error" if echo "$output" | grep -qi "An error occurred\|InvalidParameter\|AccessDenied\|ResourceNotFound\|ValidationException"; then echo "AWS API ERROR detected in output for: $description" echo "Output: $output" return 1 fi return 0 } # Function to safely extract JSON values safe_json_extract() { local json="$1" local key="$2" local value value=$(echo "$json" | grep -o "\"$key\": \"[^\"]*\"" | cut -d'"' -f4 2>/dev/null || echo "") echo "$value" } # Function to wait for network interfaces to be cleaned up wait_for_network_interfaces_cleanup() { local security_group_id="$1" local max_attempts=30 local attempt=1 # Validate security group ID format if [[ ! "$security_group_id" =~ ^sg-[a-z0-9]{8,17}$ ]]; then echo "ERROR: Invalid security group ID format: $security_group_id" return 1 fi echo "Waiting for network interfaces to be cleaned up..." while [[ $attempt -le $max_attempts ]]; do echo "Attempt $attempt/$max_attempts: Checking for dependent network interfaces..." # Check if there are any network interfaces still using this security group local eni_count eni_count=$(aws ec2 describe-network-interfaces \ --filters "Name=group-id,Values=$security_group_id" \ --query "length(NetworkInterfaces)" \ --output text 2>/dev/null || echo "0") if [[ "$eni_count" == "0" ]]; then echo "No network interfaces found using security group $security_group_id" return 0 else echo "Found $eni_count network interface(s) still using security group $security_group_id" echo "Waiting 10 seconds before next check..." sleep 10 ((attempt++)) fi done echo "WARNING: Network interfaces may still be attached after $max_attempts attempts" echo "This is normal and the security group deletion will be retried" return 1 } # Function to retry security group deletion with exponential backoff retry_security_group_deletion() { local security_group_id="$1" local max_attempts=10 local attempt=1 local wait_time=5 # Validate security group ID format if [[ ! "$security_group_id" =~ ^sg-[a-z0-9]{8,17}$ ]]; then echo "ERROR: Invalid security group ID format: $security_group_id" return 1 fi while [[ $attempt -le $max_attempts ]]; do echo "Attempt $attempt/$max_attempts: Trying to delete security group $security_group_id" if execute_command "aws ec2 delete-security-group --group-id '$security_group_id'" "Delete security group (attempt $attempt)"; then echo "Successfully deleted security group $security_group_id" return 0 else if [[ $attempt -eq $max_attempts ]]; then echo "FAILED: Could not delete security group $security_group_id after $max_attempts attempts" echo "This may be due to network interfaces that are still being cleaned up by AWS" echo "You can manually delete it later using: aws ec2 delete-security-group --group-id $security_group_id" return 1 else echo "Waiting $wait_time seconds before retry..." sleep $wait_time wait_time=$((wait_time * 2)) # Exponential backoff ((attempt++)) fi fi done } # Function to cleanup resources with proper dependency handling cleanup_resources() { echo "" echo "===========================================" echo "CLEANUP PROCESS" echo "===========================================" echo "The following resources were created:" for resource in "${CREATED_RESOURCES[@]}"; do echo " - $resource" done echo "" echo "Auto-confirming cleanup of all created resources..." CLEANUP_CHOICE="y" if [[ "$CLEANUP_CHOICE" =~ ^[Yy]$ ]]; then echo "Starting cleanup process..." # Step 1: Scale service to 0 tasks first, then delete service if [[ " ${CREATED_RESOURCES[*]} " =~ " ECS Service: $SERVICE_NAME " ]]; then echo "" echo "Step 1: Scaling service to 0 tasks..." if execute_command "aws ecs update-service --cluster '$CLUSTER_NAME' --service '$SERVICE_NAME' --desired-count 0" "Scale service to 0 tasks"; then echo "Waiting for service to stabilize after scaling to 0..." execute_command "aws ecs wait services-stable --cluster '$CLUSTER_NAME' --services '$SERVICE_NAME'" "Wait for service to stabilize" echo "Deleting service..." execute_command "aws ecs delete-service --cluster '$CLUSTER_NAME' --service '$SERVICE_NAME'" "Delete ECS service" else echo "WARNING: Failed to scale service. Attempting to delete anyway..." execute_command "aws ecs delete-service --cluster '$CLUSTER_NAME' --service '$SERVICE_NAME' --force" "Force delete ECS service" fi fi # Step 2: Wait a bit for tasks to fully terminate echo "" echo "Step 2: Waiting for tasks to fully terminate..." sleep 15 # Step 3: Delete cluster if [[ " ${CREATED_RESOURCES[*]} " =~ " ECS Cluster: $CLUSTER_NAME " ]]; then echo "" echo "Step 3: Deleting cluster..." execute_command "aws ecs delete-cluster --cluster '$CLUSTER_NAME'" "Delete ECS cluster" fi # Step 4: Wait for network interfaces to be cleaned up, then delete security group if [[ -n "$SECURITY_GROUP_ID" && "$SECURITY_GROUP_ID" != "None" ]]; then echo "" echo "Step 4: Cleaning up security group..." # First, wait for network interfaces to be cleaned up wait_for_network_interfaces_cleanup "$SECURITY_GROUP_ID" # Then retry security group deletion with backoff retry_security_group_deletion "$SECURITY_GROUP_ID" fi # Step 5: Clean up task definition (deregister all revisions) if [[ " ${CREATED_RESOURCES[*]} " =~ " Task Definition: $TASK_FAMILY " ]]; then echo "" echo "Step 5: Deregistering task definition revisions..." # Get all revisions of the task definition local revisions revisions=$(aws ecs list-task-definitions --family-prefix "$TASK_FAMILY" --query "taskDefinitionArns" --output text 2>/dev/null || echo "") if [[ -n "$revisions" && "$revisions" != "None" ]]; then for revision_arn in $revisions; do echo "Deregistering task definition: $revision_arn" execute_command "aws ecs deregister-task-definition --task-definition '$revision_arn'" "Deregister task definition $revision_arn" || true done else echo "No task definition revisions found to deregister" fi fi echo "" echo "===========================================" echo "CLEANUP COMPLETED" echo "===========================================" echo "All resources have been cleaned up successfully!" else echo "Cleanup skipped. Resources remain active." echo "" echo "To clean up manually later, use the following commands in order:" echo "1. Scale service to 0: aws ecs update-service --cluster $CLUSTER_NAME --service $SERVICE_NAME --desired-count 0" echo "2. Wait for stability: aws ecs wait services-stable --cluster $CLUSTER_NAME --services $SERVICE_NAME" echo "3. Delete service: aws ecs delete-service --cluster $CLUSTER_NAME --service $SERVICE_NAME" echo "4. Delete cluster: aws ecs delete-cluster --cluster $CLUSTER_NAME" echo "5. Wait 2-3 minutes, then delete security group: aws ec2 delete-security-group --group-id $SECURITY_GROUP_ID" if [[ " ${CREATED_RESOURCES[*]} " =~ " Task Definition: $TASK_FAMILY " ]]; then echo "6. Deregister task definitions: aws ecs list-task-definitions --family-prefix $TASK_FAMILY" echo " Then for each ARN: aws ecs deregister-task-definition --task-definition <ARN>" fi fi } # Trap to handle script interruption trap cleanup_resources EXIT echo "Using random identifier: $RANDOM_ID" echo "Cluster name: $CLUSTER_NAME" echo "Service name: $SERVICE_NAME" echo "Task family: $TASK_FAMILY" # Step 1: Ensure ECS task execution role exists echo "" echo "===========================================" echo "STEP 1: VERIFY ECS TASK EXECUTION ROLE" echo "===========================================" ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) if [[ ! "$ACCOUNT_ID" =~ ^[0-9]{12}$ ]]; then echo "ERROR: Invalid AWS Account ID retrieved: $ACCOUNT_ID" exit 1 fi EXECUTION_ROLE_ARN="arn:aws:iam::${ACCOUNT_ID}:role/ecsTaskExecutionRole" # Check if role exists if aws iam get-role --role-name ecsTaskExecutionRole >/dev/null 2>&1; then echo "ECS task execution role already exists" else echo "Creating ECS task execution role..." # Create trust policy with strict validation cat > trust-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF # Validate JSON before using if ! jq empty trust-policy.json 2>/dev/null; then echo "ERROR: Invalid JSON in trust policy" rm -f trust-policy.json exit 1 fi execute_command "aws iam create-role --role-name ecsTaskExecutionRole --assume-role-policy-document file://trust-policy.json" "Create ECS task execution role" aws iam tag-role --role-name ecsTaskExecutionRole --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-ecs-fargate-linux execute_command "aws iam attach-role-policy --role-name ecsTaskExecutionRole --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" "Attach ECS task execution policy" # Clean up temporary file securely shred -vfz -n 3 trust-policy.json 2>/dev/null || rm -f trust-policy.json CREATED_RESOURCES+=("IAM Role: ecsTaskExecutionRole") fi # Step 2: Create ECS cluster echo "" echo "===========================================" echo "STEP 2: CREATE ECS CLUSTER" echo "===========================================" CLUSTER_OUTPUT=$(execute_command "aws ecs create-cluster --cluster-name '$CLUSTER_NAME' --tags key=project,value=doc-smith key=tutorial,value=amazon-ecs-fargate-linux" "Create ECS cluster") check_for_aws_errors "$CLUSTER_OUTPUT" "Create ECS cluster" CREATED_RESOURCES+=("ECS Cluster: $CLUSTER_NAME") # Step 3: Create task definition echo "" echo "===========================================" echo "STEP 3: CREATE TASK DEFINITION" echo "===========================================" # Create task definition JSON with validated inputs cat > task-definition.json << EOF { "family": "$TASK_FAMILY", "networkMode": "awsvpc", "requiresCompatibilities": ["FARGATE"], "cpu": "256", "memory": "512", "executionRoleArn": "$EXECUTION_ROLE_ARN", "containerDefinitions": [ { "name": "fargate-app", "image": "public.ecr.aws/docker/library/httpd:2.4-alpine", "portMappings": [ { "containerPort": 80, "hostPort": 80, "protocol": "tcp" } ], "essential": true, "entryPoint": ["sh", "-c"], "command": [ "/bin/sh -c \"echo '<html> <head> <title>Amazon ECS Sample App</title> <style>body {margin-top: 40px; background-color: #333;} </style> </head><body> <div style=color:white;text-align:center> <h1>Amazon ECS Sample App</h1> <h2>Congratulations!</h2> <p>Your application is now running on a container in Amazon ECS.</p> </div></body></html>' > /usr/local/apache2/htdocs/index.html && httpd-foreground\"" ], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/fargate-sample", "awslogs-region": "us-east-1", "awslogs-stream-prefix": "ecs" } } } ] } EOF # Validate JSON before using if ! jq empty task-definition.json 2>/dev/null; then echo "ERROR: Invalid JSON in task definition" rm -f task-definition.json exit 1 fi TASK_DEF_OUTPUT=$(execute_command "aws ecs register-task-definition --cli-input-json file://task-definition.json" "Register task definition") check_for_aws_errors "$TASK_DEF_OUTPUT" "Register task definition" # Clean up temporary file securely shred -vfz -n 3 task-definition.json 2>/dev/null || rm -f task-definition.json CREATED_RESOURCES+=("Task Definition: $TASK_FAMILY") # Step 4: Set up networking echo "" echo "===========================================" echo "STEP 4: SET UP NETWORKING" echo "===========================================" # Get default VPC ID VPC_ID=$(aws ec2 describe-vpcs --filters "Name=is-default,Values=true" --query "Vpcs[0].VpcId" --output text) if [[ "$VPC_ID" == "None" || -z "$VPC_ID" ]]; then echo "ERROR: No default VPC found. Please create a default VPC or specify a custom VPC." exit 1 fi # Validate VPC ID format if [[ ! "$VPC_ID" =~ ^vpc-[a-z0-9]{8,17}$ ]]; then echo "ERROR: Invalid VPC ID format: $VPC_ID" exit 1 fi echo "Using default VPC: $VPC_ID" # Create security group with restricted access # Note: This allows HTTP access from anywhere for demo purposes # In production, restrict source to specific IP ranges or security groups SECURITY_GROUP_OUTPUT=$(execute_command "aws ec2 create-security-group --group-name '$SECURITY_GROUP_NAME' --description 'Security group for ECS Fargate tutorial - HTTP access' --vpc-id '$VPC_ID' --tag-specifications 'ResourceType=security-group,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-ecs-fargate-linux}]'" "Create security group") check_for_aws_errors "$SECURITY_GROUP_OUTPUT" "Create security group" SECURITY_GROUP_ID=$(echo "$SECURITY_GROUP_OUTPUT" | grep -o '"GroupId": "[^"]*"' | head -1 | cut -d'"' -f4) if [[ -z "$SECURITY_GROUP_ID" || "$SECURITY_GROUP_ID" == "None" ]]; then SECURITY_GROUP_ID=$(aws ec2 describe-security-groups --group-names "$SECURITY_GROUP_NAME" --filters "Name=vpc-id,Values=$VPC_ID" --query "SecurityGroups[0].GroupId" --output text) fi # Validate security group ID format if [[ ! "$SECURITY_GROUP_ID" =~ ^sg-[a-z0-9]{8,17}$ ]]; then echo "ERROR: Invalid security group ID format: $SECURITY_GROUP_ID" exit 1 fi echo "Created security group: $SECURITY_GROUP_ID" CREATED_RESOURCES+=("Security Group: $SECURITY_GROUP_ID") # Add HTTP inbound rule # WARNING: This allows HTTP access from anywhere (0.0.0.0/0) # In production environments, restrict this to specific IP ranges execute_command "aws ec2 authorize-security-group-ingress --group-id '$SECURITY_GROUP_ID' --protocol tcp --port 80 --cidr 0.0.0.0/0" "Add HTTP inbound rule to security group" # Get subnet IDs from default VPC echo "Getting subnet IDs from default VPC..." SUBNET_IDS_RAW=$(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPC_ID" --query "Subnets[*].SubnetId" --output text) if [[ -z "$SUBNET_IDS_RAW" ]]; then echo "ERROR: No subnets found in default VPC" exit 1 fi # Convert to proper comma-separated format, handling both spaces and tabs SUBNET_IDS_COMMA=$(echo "$SUBNET_IDS_RAW" | tr -s '[:space:]' ',' | sed 's/,$//') echo "Raw subnet IDs: $SUBNET_IDS_RAW" echo "Formatted subnet IDs: $SUBNET_IDS_COMMA" # Validate subnet IDs format if [[ ! "$SUBNET_IDS_COMMA" =~ ^subnet-[a-z0-9]+(,subnet-[a-z0-9]+)*$ ]]; then echo "ERROR: Invalid subnet ID format: $SUBNET_IDS_COMMA" exit 1 fi # Step 5: Create ECS service echo "" echo "===========================================" echo "STEP 5: CREATE ECS SERVICE" echo "===========================================" # Create the service with proper JSON formatting for network configuration SERVICE_CMD="aws ecs create-service --cluster '$CLUSTER_NAME' --service-name '$SERVICE_NAME' --task-definition '$TASK_FAMILY' --desired-count 1 --launch-type FARGATE --network-configuration '{\"awsvpcConfiguration\":{\"subnets\":[\"$(echo "$SUBNET_IDS_COMMA" | sed 's/,/","/g')\"],\"securityGroups\":[\"$SECURITY_GROUP_ID\"],\"assignPublicIp\":\"ENABLED\"}}' --tags key=project,value=doc-smith key=tutorial,value=amazon-ecs-fargate-linux" echo "Service creation command: $SERVICE_CMD" SERVICE_OUTPUT=$(execute_command "$SERVICE_CMD" "Create ECS service") check_for_aws_errors "$SERVICE_OUTPUT" "Create ECS service" CREATED_RESOURCES+=("ECS Service: $SERVICE_NAME") # Step 6: Wait for service to stabilize and get public IP echo "" echo "===========================================" echo "STEP 6: WAIT FOR SERVICE AND GET PUBLIC IP" echo "===========================================" echo "Waiting for service to stabilize (this may take a few minutes)..." execute_command "aws ecs wait services-stable --cluster '$CLUSTER_NAME' --services '$SERVICE_NAME'" "Wait for service to stabilize" # Get task ARN TASK_ARN=$(aws ecs list-tasks --cluster "$CLUSTER_NAME" --service-name "$SERVICE_NAME" --query "taskArns[0]" --output text) if [[ "$TASK_ARN" == "None" || -z "$TASK_ARN" ]]; then echo "ERROR: No running tasks found for service" exit 1 fi echo "Task ARN: $TASK_ARN" # Get network interface ID ENI_ID=$(aws ecs describe-tasks --cluster "$CLUSTER_NAME" --tasks "$TASK_ARN" --query "tasks[0].attachments[0].details[?name=='networkInterfaceId'].value" --output text) if [[ "$ENI_ID" == "None" || -z "$ENI_ID" ]]; then echo "ERROR: Could not retrieve network interface ID" exit 1 fi # Validate ENI ID format if [[ ! "$ENI_ID" =~ ^eni-[a-z0-9]{8,17}$ ]]; then echo "ERROR: Invalid network interface ID format: $ENI_ID" exit 1 fi echo "Network Interface ID: $ENI_ID" # Get public IP PUBLIC_IP=$(aws ec2 describe-network-interfaces --network-interface-ids "$ENI_ID" --query "NetworkInterfaces[0].Association.PublicIp" --output text) if [[ "$PUBLIC_IP" == "None" || -z "$PUBLIC_IP" ]]; then echo "WARNING: No public IP assigned to the task" echo "The task may be in a private subnet or public IP assignment failed" else # Validate IP format if [[ ! "$PUBLIC_IP" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then echo "ERROR: Invalid IP address format: $PUBLIC_IP" PUBLIC_IP="" else echo "" echo "===========================================" echo "SUCCESS! APPLICATION IS RUNNING" echo "===========================================" echo "Your application is available at: http://$PUBLIC_IP" echo "You can test it by opening this URL in your browser" echo "" fi fi # Display service information echo "" echo "===========================================" echo "SERVICE INFORMATION" echo "===========================================" execute_command "aws ecs describe-services --cluster '$CLUSTER_NAME' --services '$SERVICE_NAME'" "Get service details" echo "" echo "===========================================" echo "TUTORIAL COMPLETED SUCCESSFULLY" echo "===========================================" echo "Resources created:" for resource in "${CREATED_RESOURCES[@]}"; do echo " - $resource" done if [[ -n "$PUBLIC_IP" && "$PUBLIC_IP" != "None" ]]; then echo "" echo "Application URL: http://$PUBLIC_IP" fi echo "" echo "Script completed at $(date)"

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Buat CloudWatch dasbor

  • Tambahkan widget metrik Lambda dengan variabel nama fungsi

  • Verifikasi dasbor

  • Pembersihan sumber daya

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash # Script to create a CloudWatch dashboard with Lambda function name as a variable # This script creates a CloudWatch dashboard that allows you to switch between different Lambda functions # Set up logging with secure permissions LOG_FILE="${HOME}/.cloudwatch-dashboard-script.log" touch "$LOG_FILE" && chmod 600 "$LOG_FILE" exec > >(tee -a "$LOG_FILE") 2>&1 echo "$(date): Starting CloudWatch dashboard creation script" # Security: Set strict error handling set -uo pipefail trap 'handle_error "Script failed at line $LINENO"' ERR # Function to handle errors handle_error() { local error_msg="${1:-Unknown error}" echo "ERROR: $error_msg" >&2 echo "Resources created:" echo "- CloudWatch Dashboard: LambdaMetricsDashboard" echo "" echo "===========================================" echo "CLEANUP CONFIRMATION" echo "===========================================" echo "An error occurred. Proceeding with automatic cleanup..." echo "Cleaning up resources..." aws cloudwatch delete-dashboards --dashboard-names LambdaMetricsDashboard 2>/dev/null || true # Clean up temporary files if [ -n "${TEMP_DIR:-}" ] && [ -d "$TEMP_DIR" ]; then rm -rf "$TEMP_DIR" fi rm -f dashboard-body.json echo "Cleanup complete." exit 1 } # Security: Validate AWS CLI is installed if ! command -v aws &> /dev/null; then handle_error "AWS CLI is not installed. Please install it and try again." fi # Check if AWS CLI is installed and configured echo "Checking AWS CLI configuration..." if ! aws sts get-caller-identity > /dev/null 2>&1; then handle_error "AWS CLI is not properly configured. Please configure it with 'aws configure' and try again." fi # Get the current region securely REGION=$(aws configure get region 2>/dev/null || echo "") if [ -z "$REGION" ]; then REGION="us-east-1" echo "No region found in AWS config, defaulting to $REGION" fi echo "Using region: $REGION" # Validate region format if ! [[ "$REGION" =~ ^[a-z]{2}-[a-z]+-[0-9]{1}$ ]]; then handle_error "Invalid AWS region format: $REGION" fi # Check if there are any Lambda functions in the account echo "Checking for Lambda functions..." LAMBDA_FUNCTIONS=$(aws lambda list-functions --region "$REGION" --query "Functions[*].FunctionName" --output text 2>/dev/null || echo "") if [ -z "$LAMBDA_FUNCTIONS" ]; then echo "No Lambda functions found in your account. Creating a simple test function..." # Create a temporary directory for Lambda function code with secure permissions TEMP_DIR=$(mktemp -d) chmod 700 "$TEMP_DIR" trap 'rm -rf "$TEMP_DIR"' EXIT # Create a simple Lambda function cat > "$TEMP_DIR/index.js" << 'EOF' exports.handler = async (event) => { console.log('Event:', JSON.stringify(event, null, 2)); return { statusCode: 200, body: JSON.stringify('Hello from Lambda!'), }; }; EOF # Zip the function code if ! cd "$TEMP_DIR"; then handle_error "Failed to change to temporary directory" fi if ! zip -q function.zip index.js; then handle_error "Failed to create zip file" fi # Create a role for the Lambda function with restricted trust policy ROLE_NAME="LambdaDashboardTestRole-$(date +%s)" TRUST_POLICY='{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]}' if ! ROLE_ARN=$(aws iam create-role \ --role-name "$ROLE_NAME" \ --assume-role-policy-document "$TRUST_POLICY" \ --tags Key=project,Value=doc-smith Key=tutorial,Value=cloudwatch-dynamicdash \ --query "Role.Arn" \ --output text 2>/dev/null); then handle_error "Failed to create IAM role for Lambda function" fi echo "Waiting for role to be available..." sleep 10 # Attach basic Lambda execution policy if ! aws iam attach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"; then aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null || true handle_error "Failed to attach policy to IAM role" fi # Create the Lambda function FUNCTION_NAME="DashboardTestFunction-$(date +%s)" if ! aws lambda create-function \ --function-name "$FUNCTION_NAME" \ --runtime nodejs18.x \ --role "$ROLE_ARN" \ --handler index.handler \ --zip-file fileb://function.zip \ --tags project=doc-smith,tutorial=cloudwatch-dynamicdash \ --region "$REGION" > /dev/null 2>&1; then aws iam detach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 2>/dev/null || true aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null || true handle_error "Failed to create Lambda function" fi # Invoke the function to generate some metrics echo "Invoking Lambda function to generate metrics..." for i in {1..5}; do aws lambda invoke --function-name "$FUNCTION_NAME" --payload '{}' /dev/null --region "$REGION" > /dev/null 2>&1 || true sleep 1 done # Go back to original directory cd - > /dev/null # Set the function name for the dashboard DEFAULT_FUNCTION="$FUNCTION_NAME" else # Use the first Lambda function as default DEFAULT_FUNCTION=$(echo "$LAMBDA_FUNCTIONS" | awk '{print $1}') echo "Found Lambda functions. Using $DEFAULT_FUNCTION as default." FUNCTION_NAME="" ROLE_NAME="" fi # Create a dashboard with Lambda metrics and a function name variable echo "Creating CloudWatch dashboard with Lambda function name variable..." # Create a JSON file for the dashboard body with secure permissions DASHBOARD_JSON="dashboard-body-$$.json" touch "$DASHBOARD_JSON" && chmod 600 "$DASHBOARD_JSON" # Escape special characters in region and function name for JSON REGION_ESCAPED=$(printf '%s\n' "$REGION" | sed 's:[\/&]:\\&:g') FUNCTION_ESCAPED=$(printf '%s\n' "$DEFAULT_FUNCTION" | sed 's:[\/&]:\\&:g') cat > "$DASHBOARD_JSON" << EOF { "widgets": [ { "type": "metric", "x": 0, "y": 0, "width": 12, "height": 6, "properties": { "metrics": [ [ "AWS/Lambda", "Invocations", "FunctionName", "\${FunctionName}" ], [ ".", "Errors", ".", "." ], [ ".", "Throttles", ".", "." ] ], "view": "timeSeries", "stacked": false, "region": "$REGION_ESCAPED", "title": "Lambda Function Metrics for \${FunctionName}", "period": 300 } }, { "type": "metric", "x": 0, "y": 6, "width": 12, "height": 6, "properties": { "metrics": [ [ "AWS/Lambda", "Duration", "FunctionName", "\${FunctionName}", { "stat": "Average" } ] ], "view": "timeSeries", "stacked": false, "region": "$REGION_ESCAPED", "title": "Duration for \${FunctionName}", "period": 300 } }, { "type": "metric", "x": 12, "y": 0, "width": 12, "height": 6, "properties": { "metrics": [ [ "AWS/Lambda", "ConcurrentExecutions", "FunctionName", "\${FunctionName}" ] ], "view": "timeSeries", "stacked": false, "region": "$REGION_ESCAPED", "title": "Concurrent Executions for \${FunctionName}", "period": 300 } } ], "periodOverride": "auto", "variables": [ { "type": "property", "id": "FunctionName", "property": "FunctionName", "label": "Lambda Function", "inputType": "select", "values": [ { "value": "$FUNCTION_ESCAPED", "label": "$FUNCTION_ESCAPED" } ] } ] } EOF # Validate JSON before sending if ! jq empty "$DASHBOARD_JSON" 2>/dev/null; then handle_error "Invalid JSON generated for dashboard" fi # Create the dashboard using the JSON file DASHBOARD_NAME="LambdaMetricsDashboard-$(date +%s)" if ! DASHBOARD_RESULT=$(aws cloudwatch put-dashboard \ --dashboard-name "$DASHBOARD_NAME" \ --dashboard-body file://"$DASHBOARD_JSON" \ --region "$REGION" 2>&1); then # If we created resources, clean them up if [ -n "${FUNCTION_NAME:-}" ]; then aws lambda delete-function --function-name "$FUNCTION_NAME" --region "$REGION" 2>/dev/null || true aws iam detach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 2>/dev/null || true aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null || true fi handle_error "Failed to create CloudWatch dashboard." fi # Display any validation messages but continue if echo "$DASHBOARD_RESULT" | grep -q "DashboardValidationMessages"; then echo "Dashboard created with validation messages:" echo "$DASHBOARD_RESULT" echo "These validation messages are warnings and the dashboard should still function." else echo "Dashboard created successfully!" fi # Verify the dashboard was created echo "Verifying dashboard creation..." if ! DASHBOARD_INFO=$(aws cloudwatch get-dashboard --dashboard-name "$DASHBOARD_NAME" --region "$REGION" 2>&1); then # If we created resources, clean them up if [ -n "${FUNCTION_NAME:-}" ]; then aws lambda delete-function --function-name "$FUNCTION_NAME" --region "$REGION" 2>/dev/null || true aws iam detach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 2>/dev/null || true aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null || true fi handle_error "Failed to verify dashboard creation." fi echo "Dashboard verification successful!" echo "Dashboard details:" echo "$DASHBOARD_INFO" # List all dashboards to confirm echo "Listing all dashboards:" if ! DASHBOARDS=$(aws cloudwatch list-dashboards --region "$REGION" 2>&1); then # If we created resources, clean them up if [ -n "${FUNCTION_NAME:-}" ]; then aws lambda delete-function --function-name "$FUNCTION_NAME" --region "$REGION" 2>/dev/null || true aws iam detach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 2>/dev/null || true aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null || true fi handle_error "Failed to list dashboards." fi echo "$DASHBOARDS" # Show instructions for accessing the dashboard echo "" echo "Dashboard created successfully! To access it:" echo "1. Open the CloudWatch console at https://console.aws.amazon.com/cloudwatch/" echo "2. In the navigation pane, choose Dashboards" echo "3. Select $DASHBOARD_NAME" echo "4. You should see a dropdown menu labeled 'Lambda Function' at the top of the dashboard" echo "5. Use this dropdown to select different Lambda functions and see their metrics" echo "" # Create a list of resources for cleanup RESOURCES=("- CloudWatch Dashboard: $DASHBOARD_NAME") if [ -n "${FUNCTION_NAME:-}" ]; then RESOURCES+=("- Lambda Function: $FUNCTION_NAME") RESOURCES+=("- IAM Role: $ROLE_NAME") fi # Prompt for cleanup with automatic yes echo "===========================================" echo "CLEANUP CONFIRMATION" echo "===========================================" echo "Resources created:" for resource in "${RESOURCES[@]}"; do echo "$resource" done echo "" echo "Proceeding with automatic cleanup..." CLEANUP_CHOICE="y" if [[ "${CLEANUP_CHOICE,,}" == "y" ]]; then echo "Cleaning up resources..." # Delete the dashboard if aws cloudwatch delete-dashboards --dashboard-names "$DASHBOARD_NAME" --region "$REGION" 2>/dev/null; then echo "Dashboard deleted successfully." else echo "WARNING: Failed to delete dashboard. You may need to delete it manually." fi # If we created a Lambda function, delete it and its role if [ -n "${FUNCTION_NAME:-}" ]; then echo "Deleting Lambda function..." if aws lambda delete-function --function-name "$FUNCTION_NAME" --region "$REGION" 2>/dev/null; then echo "Lambda function deleted successfully." else echo "WARNING: Failed to delete Lambda function. You may need to delete it manually." fi echo "Detaching role policy..." if aws iam detach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 2>/dev/null; then echo "Role policy detached successfully." else echo "WARNING: Failed to detach role policy. You may need to detach it manually." fi echo "Deleting IAM role..." if aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null; then echo "IAM role deleted successfully." else echo "WARNING: Failed to delete IAM role. You may need to delete it manually." fi fi # Clean up the JSON file rm -f "$DASHBOARD_JSON" echo "Cleanup complete." fi echo "Script completed successfully!"

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Buat cluster ECS

  • Buat dan pantau layanan

  • Pembersihan sumber daya

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash # ECS EC2 Launch Type Tutorial Script - UPDATED VERSION # This script demonstrates creating an ECS cluster, launching a container instance, # registering a task definition, and creating a service using the EC2 launch type. # Updated to match the tutorial draft with nginx web server and service creation. # # - UPDATED: Changed from sleep task to nginx web server with service set -e # Exit on any error # Configuration SCRIPT_NAME="ecs-ec2-tutorial" LOG_FILE="${SCRIPT_NAME}-$(date +%Y%m%d-%H%M%S).log" CLUSTER_NAME="tutorial-cluster-$(openssl rand -hex 4)" TASK_FAMILY="nginx-task-$(openssl rand -hex 4)" SERVICE_NAME="nginx-service-$(openssl rand -hex 4)" KEY_PAIR_NAME="ecs-tutorial-key-$(openssl rand -hex 4)" SECURITY_GROUP_NAME="ecs-tutorial-sg-$(openssl rand -hex 4)" # Tags PROJECT_TAG="doc-smith" TUTORIAL_TAG="ecs-ec2" # Get current AWS region dynamically AWS_REGION=$(aws configure get region || echo "us-east-1") # Resource tracking arrays CREATED_RESOURCES=() CLEANUP_ORDER=() # Logging function log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } # Error handling function handle_error() { local exit_code=$? log "ERROR: Script failed with exit code $exit_code" log "ERROR: Last command: $BASH_COMMAND" echo "" echo "===========================================" echo "ERROR OCCURRED - ATTEMPTING CLEANUP" echo "===========================================" echo "Resources created before error:" for resource in "${CREATED_RESOURCES[@]}"; do echo " - $resource" done cleanup_resources exit $exit_code } # Set error trap trap handle_error ERR # FIXED: Enhanced cleanup function with proper error handling and logging cleanup_resources() { log "Starting cleanup process..." local cleanup_errors=0 # Delete service first (this will stop tasks automatically) if [[ -n "${SERVICE_ARN:-}" ]]; then log "Updating service to desired count 0: $SERVICE_NAME" if ! aws ecs update-service --cluster "$CLUSTER_NAME" --service "$SERVICE_NAME" --desired-count 0 2>>"$LOG_FILE"; then log "WARNING: Failed to update service desired count to 0" ((cleanup_errors++)) else log "Waiting for service tasks to stop..." sleep 30 # Give time for tasks to stop fi log "Deleting service: $SERVICE_NAME" if ! aws ecs delete-service --cluster "$CLUSTER_NAME" --service "$SERVICE_NAME" 2>>"$LOG_FILE"; then log "WARNING: Failed to delete service $SERVICE_NAME" ((cleanup_errors++)) fi fi # Stop and delete any remaining tasks if [[ -n "${TASK_ARN:-}" ]]; then log "Stopping task: $TASK_ARN" if ! aws ecs stop-task --cluster "$CLUSTER_NAME" --task "$TASK_ARN" --reason "Tutorial cleanup" 2>>"$LOG_FILE"; then log "WARNING: Failed to stop task $TASK_ARN" ((cleanup_errors++)) else log "Waiting for task to stop..." if ! aws ecs wait tasks-stopped --cluster "$CLUSTER_NAME" --tasks "$TASK_ARN" 2>>"$LOG_FILE"; then log "WARNING: Task stop wait failed for $TASK_ARN" ((cleanup_errors++)) fi fi fi # Deregister task definition if [[ -n "${TASK_DEFINITION_ARN:-}" ]]; then log "Deregistering task definition: $TASK_DEFINITION_ARN" if ! aws ecs deregister-task-definition --task-definition "$TASK_DEFINITION_ARN" 2>>"$LOG_FILE"; then log "WARNING: Failed to deregister task definition $TASK_DEFINITION_ARN" ((cleanup_errors++)) fi fi # Terminate EC2 instance if [[ -n "${INSTANCE_ID:-}" ]]; then log "Terminating EC2 instance: $INSTANCE_ID" if ! aws ec2 terminate-instances --instance-ids "$INSTANCE_ID" 2>>"$LOG_FILE"; then log "WARNING: Failed to terminate instance $INSTANCE_ID" ((cleanup_errors++)) else log "Waiting for instance to terminate..." if ! aws ec2 wait instance-terminated --instance-ids "$INSTANCE_ID" 2>>"$LOG_FILE"; then log "WARNING: Instance termination wait failed for $INSTANCE_ID" ((cleanup_errors++)) fi fi fi # Delete security group with retry logic if [[ -n "${SECURITY_GROUP_ID:-}" ]]; then log "Deleting security group: $SECURITY_GROUP_ID" local retry_count=0 local max_retries=3 while [[ $retry_count -lt $max_retries ]]; do if aws ec2 delete-security-group --group-id "$SECURITY_GROUP_ID" 2>>"$LOG_FILE"; then log "Successfully deleted security group" break else ((retry_count++)) if [[ $retry_count -lt $max_retries ]]; then log "Retry $retry_count/$max_retries: Waiting 10 seconds before retrying security group deletion..." sleep 10 else log "ERROR: Failed to delete security group after $max_retries attempts" ((cleanup_errors++)) fi fi done fi # Delete key pair if [[ -n "${KEY_PAIR_NAME:-}" ]]; then log "Deleting key pair: $KEY_PAIR_NAME" if ! aws ec2 delete-key-pair --key-name "$KEY_PAIR_NAME" 2>>"$LOG_FILE"; then log "WARNING: Failed to delete key pair $KEY_PAIR_NAME" ((cleanup_errors++)) fi rm -f "${KEY_PAIR_NAME}.pem" 2>>"$LOG_FILE" || log "WARNING: Failed to remove local key file" fi # Delete ECS cluster if [[ -n "${CLUSTER_NAME:-}" ]]; then log "Deleting ECS cluster: $CLUSTER_NAME" if ! aws ecs delete-cluster --cluster "$CLUSTER_NAME" 2>>"$LOG_FILE"; then log "WARNING: Failed to delete cluster $CLUSTER_NAME" ((cleanup_errors++)) fi fi if [[ $cleanup_errors -eq 0 ]]; then log "Cleanup completed successfully" else log "Cleanup completed with $cleanup_errors warnings/errors. Check log file for details." fi } # Function to check prerequisites check_prerequisites() { log "Checking prerequisites..." # Check AWS CLI if ! command -v aws &> /dev/null; then log "ERROR: AWS CLI is not installed" exit 1 fi # Check AWS credentials if ! aws sts get-caller-identity &> /dev/null; then log "ERROR: AWS credentials not configured" exit 1 fi # Get caller identity CALLER_IDENTITY=$(aws sts get-caller-identity --output text --query 'Account') log "AWS Account: $CALLER_IDENTITY" log "AWS Region: $AWS_REGION" # Check for default VPC DEFAULT_VPC=$(aws ec2 describe-vpcs --filters "Name=is-default,Values=true" --query 'Vpcs[0].VpcId' --output text) if [[ "$DEFAULT_VPC" == "None" ]]; then log "ERROR: No default VPC found. Please create a VPC first." exit 1 fi log "Using default VPC: $DEFAULT_VPC" # Get default subnet DEFAULT_SUBNET=$(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$DEFAULT_VPC" "Name=default-for-az,Values=true" --query 'Subnets[0].SubnetId' --output text) if [[ "$DEFAULT_SUBNET" == "None" ]]; then log "ERROR: No default subnet found" exit 1 fi log "Using default subnet: $DEFAULT_SUBNET" log "Prerequisites check completed successfully" } # Function to create ECS cluster create_cluster() { log "Creating ECS cluster: $CLUSTER_NAME" CLUSTER_ARN=$(aws ecs create-cluster --cluster-name "$CLUSTER_NAME" --tags key=project,value=$PROJECT_TAG key=tutorial,value=$TUTORIAL_TAG --query 'cluster.clusterArn' --output text) if [[ -z "$CLUSTER_ARN" ]]; then log "ERROR: Failed to create cluster" exit 1 fi log "Created cluster: $CLUSTER_ARN" CREATED_RESOURCES+=("ECS Cluster: $CLUSTER_NAME") } # Function to create key pair create_key_pair() { log "Creating EC2 key pair: $KEY_PAIR_NAME" # FIXED: Set secure umask before key creation umask 077 aws ec2 create-key-pair --key-name "$KEY_PAIR_NAME" --query 'KeyMaterial' --output text > "${KEY_PAIR_NAME}.pem" chmod 400 "${KEY_PAIR_NAME}.pem" umask 022 # Reset umask log "Created key pair: $KEY_PAIR_NAME" CREATED_RESOURCES+=("EC2 Key Pair: $KEY_PAIR_NAME") aws ec2 create-tags --resources "$KEY_PAIR_NAME" --tags Key=project,Value=$PROJECT_TAG Key=tutorial,Value=$TUTORIAL_TAG 2>>"$LOG_FILE" || log "WARNING: Failed to tag key pair" } # Function to create security group create_security_group() { log "Creating security group: $SECURITY_GROUP_NAME" SECURITY_GROUP_ID=$(aws ec2 create-security-group \ --group-name "$SECURITY_GROUP_NAME" \ --description "ECS tutorial security group" \ --vpc-id "$DEFAULT_VPC" \ --tag-specifications "ResourceType=security-group,Tags=[{Key=project,Value=$PROJECT_TAG},{Key=tutorial,Value=$TUTORIAL_TAG}]" \ --query 'GroupId' --output text) if [[ -z "$SECURITY_GROUP_ID" ]]; then log "ERROR: Failed to create security group" exit 1 fi # Add HTTP access rule for nginx web server with restricted CIDR # SECURITY FIX: Restrict access to specific CIDR if available, otherwise document the risk log "WARNING: Security group allows HTTP (port 80) from 0.0.0.0/0 - restrict this in production" aws ec2 authorize-security-group-ingress \ --group-id "$SECURITY_GROUP_ID" \ --protocol tcp \ --port 80 \ --cidr "0.0.0.0/0" log "Created security group: $SECURITY_GROUP_ID" log "Added HTTP access on port 80" CREATED_RESOURCES+=("Security Group: $SECURITY_GROUP_ID") } # Function to get ECS optimized AMI get_ecs_ami() { log "Getting ECS-optimized AMI ID..." ECS_AMI_ID=$(aws ssm get-parameters \ --names /aws/service/ecs/optimized-ami/amazon-linux-2/recommended \ --query 'Parameters[0].Value' --output text | jq -r '.image_id') if [[ -z "$ECS_AMI_ID" ]]; then log "ERROR: Failed to get ECS-optimized AMI ID" exit 1 fi log "ECS-optimized AMI ID: $ECS_AMI_ID" } # Function to create IAM role for ECS instance (if it doesn't exist) ensure_ecs_instance_role() { log "Checking for ecsInstanceRole..." if ! aws iam get-role --role-name ecsInstanceRole &> /dev/null; then log "Creating ecsInstanceRole..." # Create trust policy cat > ecs-instance-trust-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF # SECURITY FIX: Validate JSON before using if ! jq empty ecs-instance-trust-policy.json 2>/dev/null; then log "ERROR: Invalid JSON in trust policy" rm -f ecs-instance-trust-policy.json exit 1 fi # Create role aws iam create-role \ --role-name ecsInstanceRole \ --assume-role-policy-document file://ecs-instance-trust-policy.json aws iam tag-role --role-name ecsInstanceRole --tags Key=project,Value=$PROJECT_TAG Key=tutorial,Value=$TUTORIAL_TAG # Attach managed policy aws iam attach-role-policy \ --role-name ecsInstanceRole \ --policy-arn arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role # Create instance profile aws iam create-instance-profile --instance-profile-name ecsInstanceRole # Add role to instance profile aws iam add-role-to-instance-profile \ --instance-profile-name ecsInstanceRole \ --role-name ecsInstanceRole # FIXED: Enhanced wait for role to be ready log "Waiting for IAM role to be ready..." aws iam wait role-exists --role-name ecsInstanceRole sleep 30 # Additional buffer for eventual consistency rm -f ecs-instance-trust-policy.json log "Created ecsInstanceRole" CREATED_RESOURCES+=("IAM Role: ecsInstanceRole") else log "ecsInstanceRole already exists" fi } # Function to launch container instance launch_container_instance() { log "Launching ECS container instance..." # Create user data script cat > ecs-user-data.sh << EOF #!/bin/bash echo ECS_CLUSTER=$CLUSTER_NAME >> /etc/ecs/ecs.config EOF # SECURITY FIX: Validate user data script before use if ! bash -n ecs-user-data.sh 2>/dev/null; then log "ERROR: Invalid user data script" rm -f ecs-user-data.sh exit 1 fi INSTANCE_ID=$(aws ec2 run-instances \ --image-id "$ECS_AMI_ID" \ --instance-type t3.micro \ --key-name "$KEY_PAIR_NAME" \ --security-group-ids "$SECURITY_GROUP_ID" \ --subnet-id "$DEFAULT_SUBNET" \ --iam-instance-profile Name=ecsInstanceRole \ --user-data file://ecs-user-data.sh \ --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=ecs-tutorial-instance},{Key=project,Value=$PROJECT_TAG},{Key=tutorial,Value=$TUTORIAL_TAG}]" \ --monitoring Enabled=false \ --metadata-options HttpTokens=required,HttpPutResponseHopLimit=1 \ --query 'Instances[0].InstanceId' --output text) if [[ -z "$INSTANCE_ID" ]]; then log "ERROR: Failed to launch EC2 instance" rm -f ecs-user-data.sh exit 1 fi log "Launched EC2 instance: $INSTANCE_ID" log "Instance metadata options: IMDSv2 enforced with hop limit 1" CREATED_RESOURCES+=("EC2 Instance: $INSTANCE_ID") # Wait for instance to be running log "Waiting for instance to be running..." aws ec2 wait instance-running --instance-ids "$INSTANCE_ID" # Wait for ECS agent to register log "Waiting for ECS agent to register with cluster..." local max_attempts=30 local attempt=0 while [[ $attempt -lt $max_attempts ]]; do CONTAINER_INSTANCES=$(aws ecs list-container-instances --cluster "$CLUSTER_NAME" --query 'containerInstanceArns' --output text) if [[ -n "$CONTAINER_INSTANCES" && "$CONTAINER_INSTANCES" != "None" ]]; then log "Container instance registered successfully" break fi attempt=$((attempt + 1)) log "Waiting for container instance registration... (attempt $attempt/$max_attempts)" sleep 10 done if [[ $attempt -eq $max_attempts ]]; then log "ERROR: Container instance failed to register within expected time" rm -f ecs-user-data.sh exit 1 fi rm -f ecs-user-data.sh } # Function to register task definition register_task_definition() { log "Creating task definition..." # Create nginx task definition JSON matching the tutorial cat > task-definition.json << 'EOF' { "family": "TASK_FAMILY_PLACEHOLDER", "containerDefinitions": [ { "name": "nginx", "image": "public.ecr.aws/docker/library/nginx:latest", "cpu": 256, "memory": 512, "essential": true, "portMappings": [ { "containerPort": 80, "hostPort": 80, "protocol": "tcp" } ], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/nginx-task", "awslogs-region": "REGION_PLACEHOLDER", "awslogs-stream-prefix": "ecs" } } } ], "requiresCompatibilities": ["EC2"], "networkMode": "bridge", "tags": [ { "key": "project", "value": "PROJECT_TAG_PLACEHOLDER" }, { "key": "tutorial", "value": "TUTORIAL_TAG_PLACEHOLDER" } ] } EOF # Replace placeholders securely sed -i "s|TASK_FAMILY_PLACEHOLDER|$TASK_FAMILY|g" task-definition.json sed -i "s|REGION_PLACEHOLDER|$AWS_REGION|g" task-definition.json sed -i "s|PROJECT_TAG_PLACEHOLDER|$PROJECT_TAG|g" task-definition.json sed -i "s|TUTORIAL_TAG_PLACEHOLDER|$TUTORIAL_TAG|g" task-definition.json # FIXED: Validate JSON before registration if ! jq empty task-definition.json 2>/dev/null; then log "ERROR: Invalid JSON in task definition" rm -f task-definition.json exit 1 fi TASK_DEFINITION_ARN=$(aws ecs register-task-definition \ --cli-input-json file://task-definition.json \ --query 'taskDefinition.taskDefinitionArn' --output text) if [[ -z "$TASK_DEFINITION_ARN" ]]; then log "ERROR: Failed to register task definition" rm -f task-definition.json exit 1 fi log "Registered task definition: $TASK_DEFINITION_ARN" log "Task definition includes CloudWatch Logs configuration for monitoring" CREATED_RESOURCES+=("Task Definition: $TASK_DEFINITION_ARN") rm -f task-definition.json } # Function to create service create_service() { log "Creating ECS service..." SERVICE_ARN=$(aws ecs create-service \ --cluster "$CLUSTER_NAME" \ --service-name "$SERVICE_NAME" \ --task-definition "$TASK_FAMILY" \ --desired-count 1 \ --tags key=project,value=$PROJECT_TAG key=tutorial,value=$TUTORIAL_TAG \ --query 'service.serviceArn' --output text) if [[ -z "$SERVICE_ARN" ]]; then log "ERROR: Failed to create service" exit 1 fi log "Created service: $SERVICE_ARN" CREATED_RESOURCES+=("ECS Service: $SERVICE_NAME") # Wait for service to be stable log "Waiting for service to be stable..." aws ecs wait services-stable --cluster "$CLUSTER_NAME" --services "$SERVICE_NAME" log "Service is now stable and running" # Get the task ARN for monitoring TASK_ARN=$(aws ecs list-tasks --cluster "$CLUSTER_NAME" --service-name "$SERVICE_NAME" --query 'taskArns[0]' --output text) if [[ -n "$TASK_ARN" && "$TASK_ARN" != "None" ]]; then log "Service task: $TASK_ARN" CREATED_RESOURCES+=("ECS Task: $TASK_ARN") fi } # Function to demonstrate monitoring and testing demonstrate_monitoring() { log "Demonstrating monitoring capabilities..." # List services log "Listing services in cluster:" aws ecs list-services --cluster "$CLUSTER_NAME" --output table # Describe service log "Service details:" aws ecs describe-services --cluster "$CLUSTER_NAME" --services "$SERVICE_NAME" --output table --query 'services[0].{ServiceName:serviceName,Status:status,RunningCount:runningCount,DesiredCount:desiredCount,TaskDefinition:taskDefinition}' # List tasks log "Listing tasks in service:" aws ecs list-tasks --cluster "$CLUSTER_NAME" --service-name "$SERVICE_NAME" --output table # Describe task if [[ -n "$TASK_ARN" && "$TASK_ARN" != "None" ]]; then log "Task details:" aws ecs describe-tasks --cluster "$CLUSTER_NAME" --tasks "$TASK_ARN" --output table --query 'tasks[0].{TaskArn:taskArn,LastStatus:lastStatus,DesiredStatus:desiredStatus,CreatedAt:createdAt}' fi # List container instances log "Container instances in cluster:" aws ecs list-container-instances --cluster "$CLUSTER_NAME" --output table # Describe container instance CONTAINER_INSTANCE_ARN=$(aws ecs list-container-instances --cluster "$CLUSTER_NAME" --query 'containerInstanceArns[0]' --output text) if [[ -n "$CONTAINER_INSTANCE_ARN" && "$CONTAINER_INSTANCE_ARN" != "None" ]]; then log "Container instance details:" aws ecs describe-container-instances --cluster "$CLUSTER_NAME" --container-instances "$CONTAINER_INSTANCE_ARN" --output table --query 'containerInstances[0].{Arn:containerInstanceArn,Status:status,RunningTasks:runningTasksCount,PendingTasks:pendingTasksCount}' fi # Test the nginx web server log "Testing nginx web server..." PUBLIC_IP=$(aws ec2 describe-instances --instance-ids "$INSTANCE_ID" --query 'Reservations[0].Instances[0].PublicIpAddress' --output text) if [[ -n "$PUBLIC_IP" && "$PUBLIC_IP" != "None" ]]; then log "Container instance public IP: $PUBLIC_IP" log "Testing HTTP connection to nginx..." # Wait a moment for nginx to be fully ready sleep 10 if curl -s --connect-timeout 10 "http://$PUBLIC_IP" | grep -q "Welcome to nginx"; then log "SUCCESS: Nginx web server is responding correctly" echo "" echo "===========================================" echo "WEB SERVER TEST SUCCESSFUL" echo "===========================================" echo "You can access your nginx web server at: http://$PUBLIC_IP" echo "The nginx welcome page should be visible in your browser." else log "WARNING: Nginx web server may not be fully ready yet. Try accessing http://$PUBLIC_IP in a few minutes." fi else log "WARNING: Could not retrieve public IP address" fi } # Main execution main() { log "Starting ECS EC2 Launch Type Tutorial (UPDATED VERSION)" log "Log file: $LOG_FILE" log "Security improvements: IMDSv2 enforced, JSON validation, input sanitization, CloudWatch Logs configured" check_prerequisites create_cluster create_key_pair create_security_group get_ecs_ami ensure_ecs_instance_role launch_container_instance register_task_definition create_service demonstrate_monitoring log "Tutorial completed successfully!" echo "" echo "===========================================" echo "TUTORIAL COMPLETED SUCCESSFULLY" echo "===========================================" echo "Resources created:" for resource in "${CREATED_RESOURCES[@]}"; do echo " - $resource" done echo "" echo "The nginx service will continue running and maintain the desired task count." echo "You can monitor the service status using:" echo " aws ecs describe-services --cluster $CLUSTER_NAME --services $SERVICE_NAME" echo "" if [[ -n "${PUBLIC_IP:-}" ]]; then echo "Access your web server at: http://$PUBLIC_IP" echo "" fi echo "===========================================" echo "CLEANUP CONFIRMATION" echo "===========================================" log "Auto-confirming cleanup - proceeding with resource cleanup" cleanup_resources log "All resources have been cleaned up" } # Run main function main "$@"

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Buat peran IAM untuk ruang kerja Anda

  • Buat ruang kerja Grafana

  • Konfigurasikan otentikasi

  • Konfigurasikan pengaturan opsional

  • Pembersihan sumber daya

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash # Amazon Managed Grafana Workspace Creation Script # This script creates an Amazon Managed Grafana workspace and configures it # Set up logging LOG_FILE="grafana-workspace-creation.log" exec > >(tee -a "$LOG_FILE") 2>&1 echo "Starting Amazon Managed Grafana workspace creation script at $(date)" echo "All commands and outputs will be logged to $LOG_FILE" # Function to check for errors in command output check_error() { local output=$1 local cmd=$2 if echo "$output" | grep -i "error\|exception\|fail" > /dev/null; then echo "ERROR: Command '$cmd' failed with output:" echo "$output" cleanup_on_error exit 1 fi } # Function to clean up resources on error cleanup_on_error() { echo "Error encountered. Attempting to clean up resources..." if [ -n "$WORKSPACE_ID" ]; then echo "Deleting workspace $WORKSPACE_ID..." aws grafana delete-workspace --workspace-id "$WORKSPACE_ID" fi if [ -n "$ROLE_NAME" ]; then echo "Detaching policies from role $ROLE_NAME..." if [ -n "$POLICY_ARN" ]; then aws iam detach-role-policy --role-name "$ROLE_NAME" --policy-arn "$POLICY_ARN" fi echo "Deleting role $ROLE_NAME..." aws iam delete-role --role-name "$ROLE_NAME" fi if [ -n "$POLICY_ARN" ]; then echo "Deleting policy..." aws iam delete-policy --policy-arn "$POLICY_ARN" fi # Clean up JSON files rm -f trust-policy.json cloudwatch-policy.json echo "Cleanup completed. See $LOG_FILE for details." } # Generate a random identifier for resource names RANDOM_ID=$(openssl rand -hex 4) WORKSPACE_NAME="GrafanaWorkspace-${RANDOM_ID}" ROLE_NAME="GrafanaWorkspaceRole-${RANDOM_ID}" echo "Using workspace name: $WORKSPACE_NAME" echo "Using role name: $ROLE_NAME" # Step 1: Get AWS account ID echo "Getting AWS account ID..." ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) check_error "$ACCOUNT_ID" "get-caller-identity" echo "AWS Account ID: $ACCOUNT_ID" # Step 2: Create IAM role for Grafana workspace echo "Creating IAM role for Grafana workspace..." # Create trust policy document cat > trust-policy.json << EOF { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "grafana.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF # Create IAM role ROLE_OUTPUT=$(aws iam create-role \ --role-name "$ROLE_NAME" \ --assume-role-policy-document file://trust-policy.json \ --description "Role for Amazon Managed Grafana workspace") check_error "$ROLE_OUTPUT" "create-role" aws iam tag-role --role-name "$ROLE_NAME" \ --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-managed-grafana-gs echo "IAM role created successfully" # Extract role ARN ROLE_ARN=$(echo "$ROLE_OUTPUT" | grep -o '"Arn": "[^"]*' | cut -d'"' -f4) echo "Role ARN: $ROLE_ARN" # Attach policies to the role echo "Attaching policies to the role..." # CloudWatch policy cat > cloudwatch-policy.json << EOF { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "cloudwatch:DescribeAlarmsForMetric", "cloudwatch:DescribeAlarmHistory", "cloudwatch:DescribeAlarms", "cloudwatch:ListMetrics", "cloudwatch:GetMetricStatistics", "cloudwatch:GetMetricData" ], "Resource": "*" } ] } EOF POLICY_OUTPUT=$(aws iam create-policy \ --policy-name "GrafanaCloudWatchPolicy-${RANDOM_ID}" \ --policy-document file://cloudwatch-policy.json) check_error "$POLICY_OUTPUT" "create-policy" POLICY_ARN=$(echo "$POLICY_OUTPUT" | grep -o '"Arn": "[^"]*' | cut -d'"' -f4) echo "CloudWatch policy ARN: $POLICY_ARN" ATTACH_OUTPUT=$(aws iam attach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "$POLICY_ARN") check_error "$ATTACH_OUTPUT" "attach-role-policy" echo "CloudWatch policy attached to role" # Step 3: Create the Grafana workspace echo "Creating Amazon Managed Grafana workspace..." WORKSPACE_OUTPUT=$(aws grafana create-workspace \ --workspace-name "$WORKSPACE_NAME" \ --authentication-providers "SAML" \ --permission-type "CUSTOMER_MANAGED" \ --account-access-type "CURRENT_ACCOUNT" \ --workspace-role-arn "$ROLE_ARN" \ --workspace-data-sources "CLOUDWATCH" "PROMETHEUS" "XRAY" \ --grafana-version "10.4" \ --tags Environment=Development,project=doc-smith,tutorial=amazon-managed-grafana-gs) check_error "$WORKSPACE_OUTPUT" "create-workspace" echo "Workspace creation initiated:" echo "$WORKSPACE_OUTPUT" # Extract workspace ID WORKSPACE_ID=$(echo "$WORKSPACE_OUTPUT" | grep -o '"id": "[^"]*' | cut -d'"' -f4) if [ -z "$WORKSPACE_ID" ]; then echo "ERROR: Failed to extract workspace ID from output" exit 1 fi echo "Workspace ID: $WORKSPACE_ID" # Step 4: Wait for workspace to become active echo "Waiting for workspace to become active. This may take several minutes..." ACTIVE=false MAX_ATTEMPTS=30 ATTEMPT=0 while [ $ACTIVE = false ] && [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do ATTEMPT=$((ATTEMPT+1)) echo "Checking workspace status (attempt $ATTEMPT of $MAX_ATTEMPTS)..." DESCRIBE_OUTPUT=$(aws grafana describe-workspace --workspace-id "$WORKSPACE_ID") check_error "$DESCRIBE_OUTPUT" "describe-workspace" STATUS=$(echo "$DESCRIBE_OUTPUT" | grep -o '"status": "[^"]*' | cut -d'"' -f4) echo "Current status: $STATUS" if [ "$STATUS" = "ACTIVE" ]; then ACTIVE=true echo "Workspace is now ACTIVE" elif [ "$STATUS" = "FAILED" ]; then echo "ERROR: Workspace creation failed" cleanup_on_error exit 1 else echo "Workspace is still being created. Waiting 30 seconds..." sleep 30 fi done if [ $ACTIVE = false ]; then echo "ERROR: Workspace did not become active within the expected time" cleanup_on_error exit 1 fi # Extract workspace endpoint URL WORKSPACE_URL=$(echo "$DESCRIBE_OUTPUT" | grep -o '"endpoint": "[^"]*' | cut -d'"' -f4) echo "Workspace URL: https://$WORKSPACE_URL" # Step 5: Display workspace information echo "" echo "===========================================" echo "WORKSPACE INFORMATION" echo "===========================================" echo "Workspace ID: $WORKSPACE_ID" echo "Workspace URL: https://$WORKSPACE_URL" echo "Workspace Name: $WORKSPACE_NAME" echo "IAM Role: $ROLE_NAME" echo "" echo "Note: Since SAML authentication is used, you need to configure SAML settings" echo "using the AWS Management Console or the update-workspace-authentication command." echo "===========================================" # Step 6: Prompt for cleanup echo "" echo "===========================================" echo "CLEANUP CONFIRMATION" echo "===========================================" echo "Resources created:" echo "- Amazon Managed Grafana workspace: $WORKSPACE_ID" echo "- IAM Role: $ROLE_NAME" echo "- IAM Policy: GrafanaCloudWatchPolicy-${RANDOM_ID}" 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..." echo "Deleting workspace $WORKSPACE_ID..." DELETE_OUTPUT=$(aws grafana delete-workspace --workspace-id "$WORKSPACE_ID") check_error "$DELETE_OUTPUT" "delete-workspace" echo "Waiting for workspace to be deleted..." DELETED=false ATTEMPT=0 while [ $DELETED = false ] && [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do ATTEMPT=$((ATTEMPT+1)) echo "Checking deletion status (attempt $ATTEMPT of $MAX_ATTEMPTS)..." if aws grafana describe-workspace --workspace-id "$WORKSPACE_ID" 2>&1 | grep -i "not found\|does not exist" > /dev/null; then DELETED=true echo "Workspace has been deleted" else echo "Workspace is still being deleted. Waiting 30 seconds..." sleep 30 fi done if [ $DELETED = false ]; then echo "WARNING: Workspace deletion is taking longer than expected. It may still be in progress." fi # Detach policy from role echo "Detaching policy from role..." aws iam detach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "$POLICY_ARN" # Delete policy echo "Deleting IAM policy..." aws iam delete-policy \ --policy-arn "$POLICY_ARN" # Delete role echo "Deleting IAM role..." aws iam delete-role \ --role-name "$ROLE_NAME" # Clean up JSON files rm -f trust-policy.json cloudwatch-policy.json echo "Cleanup completed" else echo "Skipping cleanup. Resources will remain in your AWS account." fi echo "Script completed at $(date)"

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Buat peran IAM untuk Lambda

  • Buat kode fungsi

  • Buat fungsi Lambda

  • Uji fungsi Lambda Anda

  • Pembersihan sumber daya

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash # AWS Lambda - Create Your First Function # This script creates a Lambda function, invokes it with a test event, # views CloudWatch logs, and cleans up all resources. # # Source: https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html # # Resources created: # - IAM role (Lambda execution role with basic logging permissions) # - Lambda function (Python 3.13 or Node.js 22.x runtime) # - CloudWatch log group (created automatically by Lambda on invocation) set -eE -o pipefail ############################################################################### # Setup ############################################################################### UNIQUE_ID=$(head -c 8 /dev/urandom | od -An -tx1 | tr -d ' ') FUNCTION_NAME="my-lambda-function-${UNIQUE_ID}" ROLE_NAME="lambda-execution-role-${UNIQUE_ID}" LOG_GROUP_NAME="/aws/lambda/${FUNCTION_NAME}" TEMP_DIR=$(mktemp -d) readonly TEMP_DIR LOG_FILE="${TEMP_DIR}/lambda-gettingstarted.log" exec > >(tee -a "$LOG_FILE") 2>&1 declare -a CREATED_RESOURCES ############################################################################### # Helper functions ############################################################################### cleanup_resources() { # Disable error trap to prevent recursion during cleanup trap - ERR set +eE echo "" echo "Cleaning up resources..." echo "" for ((i=${#CREATED_RESOURCES[@]}-1; i>=0; i--)); do local RESOURCE="${CREATED_RESOURCES[$i]}" local TYPE="${RESOURCE%%:*}" local NAME="${RESOURCE#*:}" case "$TYPE" in log-group) echo "Deleting CloudWatch log group: ${NAME}" aws logs delete-log-group \ --log-group-name "$NAME" 2>&1 || echo " WARNING: Could not delete log group ${NAME}." ;; lambda-function) echo "Deleting Lambda function: ${NAME}" aws lambda delete-function \ --function-name "$NAME" 2>&1 || echo " WARNING: Could not delete Lambda function ${NAME}." echo " Waiting for function deletion to complete..." local DELETE_WAIT=0 while aws lambda get-function --function-name "$NAME" > /dev/null 2>&1; do sleep 2 DELETE_WAIT=$((DELETE_WAIT + 2)) if [ "$DELETE_WAIT" -ge 60 ]; then echo " WARNING: Timed out waiting for function deletion." break fi done ;; iam-role-policy) local ROLE_PART="${NAME%%|*}" local POLICY_PART="${NAME#*|}" echo "Detaching policy from role: ${ROLE_PART}" aws iam detach-role-policy \ --role-name "$ROLE_PART" \ --policy-arn "$POLICY_PART" 2>&1 || echo " WARNING: Could not detach policy from role ${ROLE_PART}." ;; iam-role) echo "Deleting IAM role: ${NAME}" aws iam delete-role \ --role-name "$NAME" 2>&1 || echo " WARNING: Could not delete IAM role ${NAME}." ;; esac done if [ -d "$TEMP_DIR" ]; then rm -rf "$TEMP_DIR" fi echo "" echo "Cleanup complete." } handle_error() { echo "" echo "===========================================" echo "ERROR: Script failed at $1" echo "===========================================" echo "" if [ ${#CREATED_RESOURCES[@]} -gt 0 ]; then echo "Attempting to clean up ${#CREATED_RESOURCES[@]} resource(s)..." cleanup_resources fi exit 1 } trap 'handle_error "line $LINENO"' ERR wait_for_resource() { local DESCRIPTION="$1" local COMMAND="$2" local TARGET_VALUE="$3" local TIMEOUT=300 local ELAPSED=0 local INTERVAL=5 echo "Waiting for ${DESCRIPTION}..." while true; do local RESULT RESULT=$(eval "$COMMAND" 2>&1) || true if echo "$RESULT" | grep -q "$TARGET_VALUE"; then echo " ${DESCRIPTION} is ready." return 0 fi if [ "$ELAPSED" -ge "$TIMEOUT" ]; then echo "ERROR: Timed out waiting for ${DESCRIPTION} after ${TIMEOUT} seconds." return 1 fi sleep "$INTERVAL" ELAPSED=$((ELAPSED + INTERVAL)) done } validate_input() { local input="$1" local pattern="$2" if ! [[ "$input" =~ $pattern ]]; then echo "ERROR: Invalid input: $input" return 1 fi return 0 } ############################################################################### # Region pre-check ############################################################################### CONFIGURED_REGION=$(aws configure get region 2>/dev/null || true) if [ -z "$CONFIGURED_REGION" ] && [ -z "$AWS_DEFAULT_REGION" ] && [ -z "$AWS_REGION" ]; then echo "ERROR: No AWS region configured." echo "Run 'aws configure set region <region>' or export AWS_DEFAULT_REGION." exit 1 fi ############################################################################### # Runtime selection ############################################################################### echo "" echo "===========================================" echo "AWS Lambda - Create Your First Function" echo "===========================================" echo "" echo "Select a runtime for your Lambda function:" echo " 1) Python 3.13" echo " 2) Node.js 22.x" echo "" echo "Using default: Python 3.13" RUNTIME_CHOICE="1" case "$RUNTIME_CHOICE" in 1) RUNTIME="python3.13" HANDLER="lambda_function.lambda_handler" CODE_FILE="lambda_function.py" cat > "${TEMP_DIR}/${CODE_FILE}" << 'PYTHON_EOF' import json import logging logger = logging.getLogger() logger.setLevel(logging.INFO) def lambda_handler(event, context): if not isinstance(event, dict) or 'length' not in event or 'width' not in event: raise ValueError('Event must contain length and width') try: length = float(event['length']) width = float(event['width']) if length < 0 or width < 0: raise ValueError('Length and width must be non-negative') area = calculate_area(length, width) print(f'The area is {area}') logger.info(f'CloudWatch logs group: {context.log_group_name}') return json.dumps({'area': area}) except (TypeError, ValueError) as e: logger.error(f'Error processing input: {str(e)}') raise def calculate_area(length, width): return length * width PYTHON_EOF echo "Selected runtime: Python 3.13" ;; 2) RUNTIME="nodejs22.x" HANDLER="index.handler" CODE_FILE="index.mjs" cat > "${TEMP_DIR}/${CODE_FILE}" << 'NODEJS_EOF' export const handler = async (event, context) => { if (!event || typeof event.length !== 'number' || typeof event.width !== 'number') { throw new Error('Event must contain numeric length and width'); } if (event.length < 0 || event.width < 0) { throw new Error('Length and width must be non-negative'); } const area = event.length * event.width; console.log(`The area is ${area}`); console.log('CloudWatch log group: ', context.logGroupName); return JSON.stringify({area}); }; NODEJS_EOF echo "Selected runtime: Node.js 22.x" ;; *) echo "ERROR: Invalid choice. Please enter 1 or 2." exit 1 ;; esac ############################################################################### # Step 1: Create IAM execution role ############################################################################### echo "" echo "===========================================" echo "Step 1: Create IAM execution role" echo "===========================================" echo "" TRUST_POLICY='{ "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }' echo "Creating IAM role: ${ROLE_NAME}" ROLE_OUTPUT=$(aws iam create-role \ --role-name "$ROLE_NAME" \ --assume-role-policy-document "$TRUST_POLICY" \ --query 'Role.Arn' \ --output text 2>&1) if ! validate_input "$ROLE_OUTPUT" "^arn:aws:iam::[0-9]+:role/"; then echo "ERROR: Failed to create IAM role" exit 1 fi echo "$ROLE_OUTPUT" ROLE_ARN="$ROLE_OUTPUT" CREATED_RESOURCES+=("iam-role:${ROLE_NAME}") echo "Role ARN: ${ROLE_ARN}" aws iam tag-role \ --role-name "$ROLE_NAME" \ --tags Key=project,Value=doc-smith Key=tutorial,Value=lambda-gettingstarted echo "" echo "Attaching AWSLambdaBasicExecutionRole policy..." aws iam attach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 2>&1 CREATED_RESOURCES+=("iam-role-policy:${ROLE_NAME}|arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole") echo "Policy attached." # IAM roles can take a few seconds to propagate echo "Waiting for IAM role to propagate..." sleep 10 ############################################################################### # Step 2: Create Lambda function ############################################################################### echo "" echo "===========================================" echo "Step 2: Create Lambda function" echo "===========================================" echo "" echo "Creating deployment package..." ORIGINAL_DIR=$(pwd) cd "$TEMP_DIR" || exit 1 zip -j function.zip "$CODE_FILE" > /dev/null 2>&1 || { echo "ERROR: Failed to create deployment package" exit 1 } cd "$ORIGINAL_DIR" || exit 1 if [ ! -f "${TEMP_DIR}/function.zip" ]; then echo "ERROR: Deployment package creation failed" exit 1 fi echo "Creating Lambda function: ${FUNCTION_NAME}" echo " Runtime: ${RUNTIME}" echo " Handler: ${HANDLER}" echo "" CREATE_OUTPUT=$(aws lambda create-function \ --function-name "$FUNCTION_NAME" \ --runtime "$RUNTIME" \ --role "$ROLE_ARN" \ --handler "$HANDLER" \ --architectures x86_64 \ --zip-file "fileb://${TEMP_DIR}/function.zip" \ --tags project=doc-smith,tutorial=lambda-gettingstarted \ --query '[FunctionName, FunctionArn, Runtime, State]' \ --output text 2>&1) if [ -z "$CREATE_OUTPUT" ]; then echo "ERROR: Failed to create Lambda function" exit 1 fi echo "$CREATE_OUTPUT" CREATED_RESOURCES+=("lambda-function:${FUNCTION_NAME}") wait_for_resource "Lambda function to become Active" \ "aws lambda get-function-configuration --function-name ${FUNCTION_NAME} --query State --output text" \ "Active" ############################################################################### # Step 3: Invoke the function ############################################################################### echo "" echo "===========================================" echo "Step 3: Invoke the function" echo "===========================================" echo "" TEST_EVENT='{"length": 6, "width": 7}' echo "Invoking function with test event: ${TEST_EVENT}" echo "" echo "$TEST_EVENT" > "${TEMP_DIR}/test-event.json" if ! validate_input "$TEST_EVENT" '"length": [0-9]+, "width": [0-9]+'; then echo "ERROR: Invalid test event format" exit 1 fi INVOKE_OUTPUT=$(aws lambda invoke \ --function-name "$FUNCTION_NAME" \ --payload "fileb://${TEMP_DIR}/test-event.json" \ --cli-read-timeout 30 \ "${TEMP_DIR}/response.json" 2>&1) echo "$INVOKE_OUTPUT" if [ ! -f "${TEMP_DIR}/response.json" ]; then echo "ERROR: No response file generated" exit 1 fi RESPONSE=$(cat "${TEMP_DIR}/response.json") echo "" echo "Function response: ${RESPONSE}" echo "" if echo "$INVOKE_OUTPUT" | grep -qi "functionerror"; then echo "WARNING: Function returned an error." fi ############################################################################### # Step 4: View CloudWatch logs ############################################################################### echo "" echo "===========================================" echo "Step 4: View CloudWatch Logs" echo "===========================================" echo "" echo "Log group: ${LOG_GROUP_NAME}" echo "" echo "Waiting for CloudWatch logs to be available..." LOG_STREAMS="" for i in $(seq 1 6); do LOG_STREAMS=$(aws logs describe-log-streams \ --log-group-name "$LOG_GROUP_NAME" \ --order-by LastEventTime \ --descending \ --query 'logStreams[0].logStreamName' \ --output text 2>/dev/null) || true if [ -n "$LOG_STREAMS" ] && [ "$LOG_STREAMS" != "None" ]; then break fi LOG_STREAMS="" sleep 5 done if [ -n "$LOG_STREAMS" ] && [ "$LOG_STREAMS" != "None" ]; then echo "Latest log stream: ${LOG_STREAMS}" echo "" echo "--- Log events ---" LOG_EVENTS=$(aws logs get-log-events \ --log-group-name "$LOG_GROUP_NAME" \ --log-stream-name "$LOG_STREAMS" \ --query 'events[].message' \ --output text 2>&1) || true echo "$LOG_EVENTS" echo "--- End of log events ---" else echo "No log streams found yet. Logs may take a moment to appear." echo "You can view them in the CloudWatch console:" echo " Log group: ${LOG_GROUP_NAME}" fi CREATED_RESOURCES+=("log-group:${LOG_GROUP_NAME}") aws logs tag-log-group \ --log-group-name "$LOG_GROUP_NAME" \ --tags project=doc-smith,tutorial=lambda-gettingstarted ############################################################################### # Summary and cleanup ############################################################################### echo "" echo "===========================================" echo "SUMMARY" echo "===========================================" echo "" echo "Resources created:" echo " IAM role: ${ROLE_NAME}" echo " Lambda function: ${FUNCTION_NAME}" echo " CloudWatch logs: ${LOG_GROUP_NAME}" echo "" echo "===========================================" echo "CLEANUP" echo "===========================================" echo "" echo "Cleaning up all created resources..." cleanup_resources echo "" echo "Done."

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Gunakan rahasia-manajer CreateSecret

  • Gunakan rahasia-manajer DeleteSecret

  • Gunakan rahasia-manajer GetSecretValue

  • Gunakan pergeseran merah CreateNamespace

  • Gunakan pergeseran merah CreateWorkgroup

  • Gunakan pergeseran merah DeleteNamespace

  • Gunakan iam CreateRole

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash # Amazon Redshift Serverless Tutorial Script with Secrets Manager (No jq dependency) # This script creates a Redshift Serverless environment, loads sample data, and runs queries # Uses AWS Secrets Manager for secure password management without requiring jq # Set up logging LOG_FILE="redshift-serverless-tutorial-v4.log" exec > >(tee -a "$LOG_FILE") 2>&1 echo "Starting Amazon Redshift Serverless tutorial script at $(date)" echo "All commands and outputs will be logged to $LOG_FILE" # Function to check for errors in command output check_error() { local output=$1 local cmd=$2 if echo "$output" | grep -i "error\|exception\|fail" > /dev/null; then echo "ERROR: Command failed: $cmd" echo "Output: $output" cleanup_resources exit 1 fi } # Function to generate a secure password that meets Redshift requirements generate_secure_password() { # Redshift password requirements: # - 8-64 characters # - At least one uppercase letter # - At least one lowercase letter # - At least one decimal digit # - Can contain printable ASCII characters except /, ", ', \, @, space local password="" local valid=false local attempts=0 local max_attempts=10 while [[ "$valid" == false && $attempts -lt $max_attempts ]]; do # Generate base password with safe characters local base=$(openssl rand -base64 12 | tr -d '/+=' | head -c 12) # Ensure we have at least one of each required character type local upper=$(echo "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | fold -w1 | shuf -n1) local lower=$(echo "abcdefghijklmnopqrstuvwxyz" | fold -w1 | shuf -n1) local digit=$(echo "0123456789" | fold -w1 | shuf -n1) local special=$(echo "!#$%&*()_+-=[]{}|;:,.<>?" | fold -w1 | shuf -n1) # Combine and shuffle password="${base}${upper}${lower}${digit}${special}" password=$(echo "$password" | fold -w1 | shuf | tr -d '\n') # Validate password meets requirements if [[ ${#password} -ge 8 && ${#password} -le 64 ]] && \ [[ "$password" =~ [A-Z] ]] && \ [[ "$password" =~ [a-z] ]] && \ [[ "$password" =~ [0-9] ]] && \ [[ ! "$password" =~ [/\"\'\\@[:space:]] ]]; then valid=true fi ((attempts++)) done if [[ "$valid" == false ]]; then echo "ERROR: Failed to generate valid password after $max_attempts attempts" exit 1 fi echo "$password" } # Function to create secret in AWS Secrets Manager create_secret() { local secret_name=$1 local username=$2 local password=$3 local description=$4 echo "Creating secret in AWS Secrets Manager: $secret_name" # Create the secret using AWS CLI without jq local secret_output=$(aws secretsmanager create-secret \ --name "$secret_name" \ --description "$description" \ --secret-string "{\"username\":\"$username\",\"password\":\"$password\"}" \ --tags Key=project,Value=doc-smith Key=tutorial,Value=redshift-serverless 2>&1) if echo "$secret_output" | grep -i "error\|exception\|fail" > /dev/null; then echo "ERROR: Failed to create secret: $secret_output" return 1 fi echo "Secret created successfully: $secret_name" return 0 } # Function to retrieve password from AWS Secrets Manager get_password_from_secret() { local secret_name=$1 # Get the secret value and extract password using sed/grep instead of jq local secret_value=$(aws secretsmanager get-secret-value \ --secret-id "$secret_name" \ --query 'SecretString' \ --output text 2>/dev/null) if [[ $? -eq 0 ]]; then # Extract password from JSON using sed echo "$secret_value" | sed -n 's/.*"password":"\([^"]*\)".*/\1/p' else echo "" fi } # Function to wait for a resource to be available wait_for_resource() { local resource_type=$1 local resource_name=$2 local max_attempts=$3 local wait_seconds=$4 local check_cmd=$5 echo "Waiting for $resource_type $resource_name to be available..." for ((i=1; i<=$max_attempts; i++)); do local output=$($check_cmd 2>/dev/null) local status=$(echo "$output" | grep -o '"Status": "[^"]*' | cut -d'"' -f4 || echo "") if [[ "$status" == "AVAILABLE" ]]; then echo "$resource_type $resource_name is now available" return 0 fi echo "Attempt $i/$max_attempts: $resource_type $resource_name status: $status. Waiting $wait_seconds seconds..." sleep $wait_seconds done echo "ERROR: Timed out waiting for $resource_type $resource_name to be available" return 1 } # Function to wait for a resource to be deleted wait_for_resource_deletion() { local resource_type=$1 local resource_name=$2 local max_attempts=$3 local wait_seconds=$4 local check_cmd=$5 echo "Waiting for $resource_type $resource_name to be deleted..." for ((i=1; i<=$max_attempts; i++)); do local output=$($check_cmd 2>&1) if echo "$output" | grep -i "not found\|does not exist" > /dev/null; then echo "$resource_type $resource_name has been deleted" return 0 fi echo "Attempt $i/$max_attempts: $resource_type $resource_name is still being deleted. Waiting $wait_seconds seconds..." sleep $wait_seconds done echo "ERROR: Timed out waiting for $resource_type $resource_name to be deleted" return 1 } # Function to clean up resources cleanup_resources() { echo "" echo "===========================================" echo "CLEANUP CONFIRMATION" echo "===========================================" echo "The following resources were created:" echo "- Redshift Serverless Workgroup: $WORKGROUP_NAME" echo "- Redshift Serverless Namespace: $NAMESPACE_NAME" echo "- IAM Role: $ROLE_NAME" echo "- Secrets Manager Secret: $SECRET_NAME" echo "" echo "Do you want to clean up all created resources? (y/n): " read -r CLEANUP_CHOICE if [[ "${CLEANUP_CHOICE,,}" == "y" ]]; then echo "Cleaning up resources..." # Delete the workgroup echo "Deleting Redshift Serverless workgroup $WORKGROUP_NAME..." WORKGROUP_DELETE_OUTPUT=$(aws redshift-serverless delete-workgroup --workgroup-name "$WORKGROUP_NAME" 2>&1) echo "$WORKGROUP_DELETE_OUTPUT" # Wait for workgroup to be deleted before deleting namespace wait_for_resource_deletion "workgroup" "$WORKGROUP_NAME" 20 30 "aws redshift-serverless get-workgroup --workgroup-name $WORKGROUP_NAME" # Delete the namespace echo "Deleting Redshift Serverless namespace $NAMESPACE_NAME..." NAMESPACE_DELETE_OUTPUT=$(aws redshift-serverless delete-namespace --namespace-name "$NAMESPACE_NAME" 2>&1) echo "$NAMESPACE_DELETE_OUTPUT" # Wait for namespace to be deleted wait_for_resource_deletion "namespace" "$NAMESPACE_NAME" 20 30 "aws redshift-serverless get-namespace --namespace-name $NAMESPACE_NAME" # Delete the IAM role policy echo "Deleting IAM role policy..." POLICY_DELETE_OUTPUT=$(aws iam delete-role-policy --role-name "$ROLE_NAME" --policy-name S3Access 2>&1) echo "$POLICY_DELETE_OUTPUT" # Delete the IAM role echo "Deleting IAM role $ROLE_NAME..." ROLE_DELETE_OUTPUT=$(aws iam delete-role --role-name "$ROLE_NAME" 2>&1) echo "$ROLE_DELETE_OUTPUT" # Delete the secret echo "Deleting Secrets Manager secret $SECRET_NAME..." SECRET_DELETE_OUTPUT=$(aws secretsmanager delete-secret --secret-id "$SECRET_NAME" --force-delete-without-recovery 2>&1) echo "$SECRET_DELETE_OUTPUT" echo "Cleanup completed." else echo "Cleanup skipped. Resources will remain in your AWS account." fi } # Check if required tools are available if ! command -v openssl &> /dev/null; then echo "ERROR: openssl is required but not installed. Please install openssl to continue." exit 1 fi # Generate unique names for resources RANDOM_SUFFIX=$(cat /dev/urandom | tr -dc 'a-z0-9' | head -c 6) NAMESPACE_NAME="rs-namespace-${RANDOM_SUFFIX}" WORKGROUP_NAME="rs-workgroup-${RANDOM_SUFFIX}" ROLE_NAME="RedshiftServerlessS3Role-${RANDOM_SUFFIX}" SECRET_NAME="redshift-serverless-admin-${RANDOM_SUFFIX}" DB_NAME="dev" ADMIN_USERNAME="admin" # Generate secure password echo "Generating secure password..." ADMIN_PASSWORD=$(generate_secure_password) # Create secret in AWS Secrets Manager create_secret "$SECRET_NAME" "$ADMIN_USERNAME" "$ADMIN_PASSWORD" "Admin credentials for Redshift Serverless namespace $NAMESPACE_NAME" if [[ $? -ne 0 ]]; then echo "ERROR: Failed to create secret in AWS Secrets Manager" exit 1 fi # Track created resources CREATED_RESOURCES=() echo "Using the following resource names:" echo "- Namespace: $NAMESPACE_NAME" echo "- Workgroup: $WORKGROUP_NAME" echo "- IAM Role: $ROLE_NAME" echo "- Secret: $SECRET_NAME" echo "- Database: $DB_NAME" echo "- Admin Username: $ADMIN_USERNAME" echo "- Admin Password: [STORED IN SECRETS MANAGER]" # Step 1: Create IAM role for S3 access echo "Creating IAM role for Redshift Serverless S3 access..." # Create trust policy document cat > redshift-trust-policy.json << EOF { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "redshift-serverless.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF # Create S3 access policy document cat > redshift-s3-policy.json << EOF { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::redshift-downloads", "arn:aws:s3:::redshift-downloads/*" ] } ] } EOF # Create IAM role echo "Creating IAM role $ROLE_NAME..." ROLE_OUTPUT=$(aws iam create-role --role-name "$ROLE_NAME" --assume-role-policy-document file://redshift-trust-policy.json 2>&1) echo "$ROLE_OUTPUT" check_error "$ROLE_OUTPUT" "aws iam create-role" aws iam tag-role --role-name "$ROLE_NAME" \ --tags Key=project,Value=doc-smith Key=tutorial,Value=redshift-serverless CREATED_RESOURCES+=("IAM Role: $ROLE_NAME") # Attach S3 policy to the role echo "Attaching S3 access policy to role $ROLE_NAME..." POLICY_OUTPUT=$(aws iam put-role-policy --role-name "$ROLE_NAME" --policy-name S3Access --policy-document file://redshift-s3-policy.json 2>&1) echo "$POLICY_OUTPUT" check_error "$POLICY_OUTPUT" "aws iam put-role-policy" # Get the role ARN ROLE_ARN=$(aws iam get-role --role-name "$ROLE_NAME" --query 'Role.Arn' --output text) echo "Role ARN: $ROLE_ARN" # Step 2: Create a namespace echo "Creating Redshift Serverless namespace $NAMESPACE_NAME..." NAMESPACE_OUTPUT=$(aws redshift-serverless create-namespace \ --namespace-name "$NAMESPACE_NAME" \ --admin-username "$ADMIN_USERNAME" \ --admin-user-password "$ADMIN_PASSWORD" \ --db-name "$DB_NAME" \ --tags Key=project,Value=doc-smith Key=tutorial,Value=redshift-serverless 2>&1) echo "$NAMESPACE_OUTPUT" check_error "$NAMESPACE_OUTPUT" "aws redshift-serverless create-namespace" CREATED_RESOURCES+=("Redshift Serverless Namespace: $NAMESPACE_NAME") # Wait for namespace to be available wait_for_resource "namespace" "$NAMESPACE_NAME" 10 30 "aws redshift-serverless get-namespace --namespace-name $NAMESPACE_NAME" # Associate IAM role with namespace echo "Associating IAM role with namespace..." UPDATE_NAMESPACE_OUTPUT=$(aws redshift-serverless update-namespace \ --namespace-name "$NAMESPACE_NAME" \ --iam-roles "$ROLE_ARN" 2>&1) echo "$UPDATE_NAMESPACE_OUTPUT" check_error "$UPDATE_NAMESPACE_OUTPUT" "aws redshift-serverless update-namespace" # Step 3: Create a workgroup echo "Creating Redshift Serverless workgroup $WORKGROUP_NAME..." WORKGROUP_OUTPUT=$(aws redshift-serverless create-workgroup \ --workgroup-name "$WORKGROUP_NAME" \ --namespace-name "$NAMESPACE_NAME" \ --base-capacity 8 \ --tags Key=project,Value=doc-smith Key=tutorial,Value=redshift-serverless 2>&1) echo "$WORKGROUP_OUTPUT" check_error "$WORKGROUP_OUTPUT" "aws redshift-serverless create-workgroup" CREATED_RESOURCES+=("Redshift Serverless Workgroup: $WORKGROUP_NAME") # Wait for workgroup to be available wait_for_resource "workgroup" "$WORKGROUP_NAME" 20 30 "aws redshift-serverless get-workgroup --workgroup-name $WORKGROUP_NAME" # Get workgroup endpoint WORKGROUP_ENDPOINT=$(aws redshift-serverless get-workgroup \ --workgroup-name "$WORKGROUP_NAME" \ --query 'workgroup.endpoint.address' \ --output text) echo "Workgroup endpoint: $WORKGROUP_ENDPOINT" # Wait additional time for the endpoint to be fully operational echo "Waiting for endpoint to be fully operational..." sleep 60 # Step 4: Create tables for sample data echo "Creating tables for sample data..." # Create users table echo "Creating users table..." USERS_TABLE_OUTPUT=$(aws redshift-data execute-statement \ --database "$DB_NAME" \ --workgroup-name "$WORKGROUP_NAME" \ --sql "CREATE TABLE users( userid INTEGER NOT NULL DISTKEY SORTKEY, username CHAR(8), firstname VARCHAR(30), lastname VARCHAR(30), city VARCHAR(30), state CHAR(2), email VARCHAR(100), phone CHAR(14), likesports BOOLEAN, liketheatre BOOLEAN, likeconcerts BOOLEAN, likejazz BOOLEAN, likeclassical BOOLEAN, likeopera BOOLEAN, likerock BOOLEAN, likevegas BOOLEAN, likebroadway BOOLEAN, likemusicals BOOLEAN );" 2>&1) echo "$USERS_TABLE_OUTPUT" check_error "$USERS_TABLE_OUTPUT" "aws redshift-data execute-statement (users table)" USERS_QUERY_ID=$(echo "$USERS_TABLE_OUTPUT" | grep -o '"Id": "[^"]*' | cut -d'"' -f4) # Wait for query to complete echo "Waiting for users table creation to complete..." sleep 5 # Create event table echo "Creating event table..." EVENT_TABLE_OUTPUT=$(aws redshift-data execute-statement \ --database "$DB_NAME" \ --workgroup-name "$WORKGROUP_NAME" \ --sql "CREATE TABLE event( eventid INTEGER NOT NULL DISTKEY, venueid SMALLINT NOT NULL, catid SMALLINT NOT NULL, dateid SMALLINT NOT NULL SORTKEY, eventname VARCHAR(200), starttime TIMESTAMP );" 2>&1) echo "$EVENT_TABLE_OUTPUT" check_error "$EVENT_TABLE_OUTPUT" "aws redshift-data execute-statement (event table)" EVENT_QUERY_ID=$(echo "$EVENT_TABLE_OUTPUT" | grep -o '"Id": "[^"]*' | cut -d'"' -f4) # Wait for query to complete echo "Waiting for event table creation to complete..." sleep 5 # Create sales table echo "Creating sales table..." SALES_TABLE_OUTPUT=$(aws redshift-data execute-statement \ --database "$DB_NAME" \ --workgroup-name "$WORKGROUP_NAME" \ --sql "CREATE TABLE sales( salesid INTEGER NOT NULL, listid INTEGER NOT NULL DISTKEY, sellerid INTEGER NOT NULL, buyerid INTEGER NOT NULL, eventid INTEGER NOT NULL, dateid SMALLINT NOT NULL SORTKEY, qtysold SMALLINT NOT NULL, pricepaid DECIMAL(8,2), commission DECIMAL(8,2), saletime TIMESTAMP );" 2>&1) echo "$SALES_TABLE_OUTPUT" check_error "$SALES_TABLE_OUTPUT" "aws redshift-data execute-statement (sales table)" SALES_QUERY_ID=$(echo "$SALES_TABLE_OUTPUT" | grep -o '"Id": "[^"]*' | cut -d'"' -f4) # Wait for tables to be created echo "Waiting for tables to be created..." sleep 10 # Step 5: Load sample data from Amazon S3 echo "Loading sample data from Amazon S3..." # Load data into users table echo "Loading data into users table..." USERS_LOAD_OUTPUT=$(aws redshift-data execute-statement \ --database "$DB_NAME" \ --workgroup-name "$WORKGROUP_NAME" \ --sql "COPY users FROM 's3://redshift-downloads/tickit/allusers_pipe.txt' DELIMITER '|' TIMEFORMAT 'YYYY-MM-DD HH:MI:SS' IGNOREHEADER 1 IAM_ROLE '$ROLE_ARN';" 2>&1) echo "$USERS_LOAD_OUTPUT" check_error "$USERS_LOAD_OUTPUT" "aws redshift-data execute-statement (load users)" USERS_LOAD_QUERY_ID=$(echo "$USERS_LOAD_OUTPUT" | grep -o '"Id": "[^"]*' | cut -d'"' -f4) # Wait for data loading to complete echo "Waiting for users data loading to complete..." sleep 10 # Load data into event table echo "Loading data into event table..." EVENT_LOAD_OUTPUT=$(aws redshift-data execute-statement \ --database "$DB_NAME" \ --workgroup-name "$WORKGROUP_NAME" \ --sql "COPY event FROM 's3://redshift-downloads/tickit/allevents_pipe.txt' DELIMITER '|' TIMEFORMAT 'YYYY-MM-DD HH:MI:SS' IGNOREHEADER 1 IAM_ROLE '$ROLE_ARN';" 2>&1) echo "$EVENT_LOAD_OUTPUT" check_error "$EVENT_LOAD_OUTPUT" "aws redshift-data execute-statement (load event)" EVENT_LOAD_QUERY_ID=$(echo "$EVENT_LOAD_OUTPUT" | grep -o '"Id": "[^"]*' | cut -d'"' -f4) # Wait for data loading to complete echo "Waiting for event data loading to complete..." sleep 10 # Load data into sales table echo "Loading data into sales table..." SALES_LOAD_OUTPUT=$(aws redshift-data execute-statement \ --database "$DB_NAME" \ --workgroup-name "$WORKGROUP_NAME" \ --sql "COPY sales FROM 's3://redshift-downloads/tickit/sales_tab.txt' DELIMITER '\t' TIMEFORMAT 'MM/DD/YYYY HH:MI:SS' IGNOREHEADER 1 IAM_ROLE '$ROLE_ARN';" 2>&1) echo "$SALES_LOAD_OUTPUT" check_error "$SALES_LOAD_OUTPUT" "aws redshift-data execute-statement (load sales)" SALES_LOAD_QUERY_ID=$(echo "$SALES_LOAD_OUTPUT" | grep -o '"Id": "[^"]*' | cut -d'"' -f4) # Wait for data loading to complete echo "Waiting for sales data loading to complete..." sleep 30 # Step 6: Run sample queries echo "Running sample queries..." # Query 1: Find top 10 buyers by quantity echo "Running query: Find top 10 buyers by quantity..." QUERY1_OUTPUT=$(aws redshift-data execute-statement \ --database "$DB_NAME" \ --workgroup-name "$WORKGROUP_NAME" \ --sql "SELECT firstname, lastname, total_quantity FROM (SELECT buyerid, sum(qtysold) total_quantity FROM sales GROUP BY buyerid ORDER BY total_quantity desc limit 10) Q, users WHERE Q.buyerid = userid ORDER BY Q.total_quantity desc;" 2>&1) echo "$QUERY1_OUTPUT" check_error "$QUERY1_OUTPUT" "aws redshift-data execute-statement (query 1)" QUERY1_ID=$(echo "$QUERY1_OUTPUT" | grep -o '"Id": "[^"]*' | cut -d'"' -f4) # Wait for query to complete echo "Waiting for query 1 to complete..." sleep 10 # Get query 1 results echo "Getting results for query 1..." QUERY1_STATUS_OUTPUT=$(aws redshift-data describe-statement --id "$QUERY1_ID" 2>&1) echo "$QUERY1_STATUS_OUTPUT" check_error "$QUERY1_STATUS_OUTPUT" "aws redshift-data describe-statement (query 1)" QUERY1_STATUS=$(echo "$QUERY1_STATUS_OUTPUT" | grep -o '"Status": "[^"]*' | cut -d'"' -f4) if [ "$QUERY1_STATUS" == "FINISHED" ]; then QUERY1_RESULTS=$(aws redshift-data get-statement-result --id "$QUERY1_ID" 2>&1) echo "Query 1 Results:" echo "$QUERY1_RESULTS" else echo "Query 1 is not yet complete. Status: $QUERY1_STATUS" echo "Waiting additional time for query to complete..." sleep 20 # Check again QUERY1_STATUS_OUTPUT=$(aws redshift-data describe-statement --id "$QUERY1_ID" 2>&1) QUERY1_STATUS=$(echo "$QUERY1_STATUS_OUTPUT" | grep -o '"Status": "[^"]*' | cut -d'"' -f4) if [ "$QUERY1_STATUS" == "FINISHED" ]; then QUERY1_RESULTS=$(aws redshift-data get-statement-result --id "$QUERY1_ID" 2>&1) echo "Query 1 Results:" echo "$QUERY1_RESULTS" else echo "Query 1 is still not complete. Status: $QUERY1_STATUS" fi fi # Query 2: Find events in the 99.9 percentile in terms of all time total sales echo "Running query: Find events in the 99.9 percentile in terms of all time total sales..." QUERY2_OUTPUT=$(aws redshift-data execute-statement \ --database "$DB_NAME" \ --workgroup-name "$WORKGROUP_NAME" \ --sql "SELECT eventname, total_price FROM (SELECT eventid, total_price, ntile(1000) over(order by total_price desc) as percentile FROM (SELECT eventid, sum(pricepaid) total_price FROM sales GROUP BY eventid)) Q, event E WHERE Q.eventid = E.eventid AND percentile = 1 ORDER BY total_price desc;" 2>&1) echo "$QUERY2_OUTPUT" check_error "$QUERY2_OUTPUT" "aws redshift-data execute-statement (query 2)" QUERY2_ID=$(echo "$QUERY2_OUTPUT" | grep -o '"Id": "[^"]*' | cut -d'"' -f4) # Wait for query to complete echo "Waiting for query 2 to complete..." sleep 10 # Get query 2 results echo "Getting results for query 2..." QUERY2_STATUS_OUTPUT=$(aws redshift-data describe-statement --id "$QUERY2_ID" 2>&1) echo "$QUERY2_STATUS_OUTPUT" check_error "$QUERY2_STATUS_OUTPUT" "aws redshift-data describe-statement (query 2)" QUERY2_STATUS=$(echo "$QUERY2_STATUS_OUTPUT" | grep -o '"Status": "[^"]*' | cut -d'"' -f4) if [ "$QUERY2_STATUS" == "FINISHED" ]; then QUERY2_RESULTS=$(aws redshift-data get-statement-result --id "$QUERY2_ID" 2>&1) echo "Query 2 Results:" echo "$QUERY2_RESULTS" else echo "Query 2 is not yet complete. Status: $QUERY2_STATUS" echo "Waiting additional time for query to complete..." sleep 20 # Check again QUERY2_STATUS_OUTPUT=$(aws redshift-data describe-statement --id "$QUERY2_ID" 2>&1) QUERY2_STATUS=$(echo "$QUERY2_STATUS_OUTPUT" | grep -o '"Status": "[^"]*' | cut -d'"' -f4) if [ "$QUERY2_STATUS" == "FINISHED" ]; then QUERY2_RESULTS=$(aws redshift-data get-statement-result --id "$QUERY2_ID" 2>&1) echo "Query 2 Results:" echo "$QUERY2_RESULTS" else echo "Query 2 is still not complete. Status: $QUERY2_STATUS" fi fi # Summary echo "" echo "===========================================" echo "TUTORIAL SUMMARY" echo "===========================================" echo "You have successfully:" echo "1. Created a Redshift Serverless namespace and workgroup" echo "2. Created an IAM role with S3 access permissions" echo "3. Stored admin credentials securely in AWS Secrets Manager" echo "4. Created tables for sample data" echo "5. Loaded sample data from Amazon S3" echo "6. Run sample queries on the data" echo "" echo "Redshift Serverless Resources:" echo "- Namespace: $NAMESPACE_NAME" echo "- Workgroup: $WORKGROUP_NAME" echo "- Database: $DB_NAME" echo "- Endpoint: $WORKGROUP_ENDPOINT" echo "- Credentials Secret: $SECRET_NAME" echo "" echo "To connect to your Redshift Serverless database using SQL tools:" echo "- Host: $WORKGROUP_ENDPOINT" echo "- Database: $DB_NAME" echo "- Username: $ADMIN_USERNAME" echo "- Password: Retrieve from AWS Secrets Manager secret '$SECRET_NAME'" echo "" echo "To retrieve the password from Secrets Manager (without jq):" echo "aws secretsmanager get-secret-value --secret-id $SECRET_NAME --query 'SecretString' --output text | sed -n 's/.*\"password\":\"\([^\"]*\)\".*/\1/p'" echo "" # Clean up temporary files rm -f redshift-trust-policy.json redshift-s3-policy.json # Clean up resources cleanup_resources echo "Tutorial completed at $(date)"

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Buat Peran IAM yang Diperlukan

  • Aktifkan Pemeriksaan Audit Defender Perangkat IoT

  • Jalankan Audit Sesuai Permintaan

  • Buat Aksi Mitigasi

  • Terapkan Tindakan Mitigasi pada Temuan

  • Mengatur Pemberitahuan SNS (Opsional)

  • Aktifkan Logging IoT

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

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

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Buat VPC untuk kluster EKS Anda

  • Buat peran IAM untuk kluster EKS Anda

  • Buat kluster EKS Anda

  • Konfigurasikan kubectl untuk berkomunikasi dengan klaster Anda

  • Membuat grup simpul terkelola

  • Pembersihan sumber daya

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash # Amazon EKS Cluster Creation Script (v2) # This script creates an Amazon EKS cluster with a managed node group using the AWS CLI # Set up logging LOG_FILE="eks-cluster-creation-v2.log" exec > >(tee -a "$LOG_FILE") 2>&1 echo "Starting Amazon EKS cluster creation script at $(date)" echo "All commands and outputs will be logged to $LOG_FILE" # Error handling function handle_error() { echo "ERROR: $1" echo "Attempting to clean up resources..." cleanup_resources exit 1 } # Function to check command success check_command() { if [ $? -ne 0 ] || echo "$1" | grep -i "error" > /dev/null; then handle_error "$1" fi } # Function to check if kubectl is installed check_kubectl() { if ! command -v kubectl &> /dev/null; then echo "WARNING: kubectl is not installed or not in your PATH." echo "" echo "To install kubectl, follow these instructions based on your operating system:" echo "" echo "For Linux:" echo " 1. Download the latest release:" echo " curl -LO \"https://dl.k8s.io/release/\$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl\"" echo "" echo " 2. Make the kubectl binary executable:" echo " chmod +x ./kubectl" echo "" echo " 3. Move the binary to your PATH:" echo " sudo mv ./kubectl /usr/local/bin/kubectl" echo "" echo "For macOS:" echo " 1. Using Homebrew:" echo " brew install kubectl" echo " or" echo " 2. Using curl:" echo " curl -LO \"https://dl.k8s.io/release/\$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl\"" echo " chmod +x ./kubectl" echo " sudo mv ./kubectl /usr/local/bin/kubectl" echo "" echo "For Windows:" echo " 1. Using curl:" echo " curl -LO \"https://dl.k8s.io/release/v1.28.0/bin/windows/amd64/kubectl.exe\"" echo " Add the binary to your PATH" echo " or" echo " 2. Using Chocolatey:" echo " choco install kubernetes-cli" echo "" echo "After installation, verify with: kubectl version --client" echo "" return 1 fi return 0 } # Generate a random identifier for resource names RANDOM_ID=$(LC_ALL=C tr -dc 'a-z0-9' < /dev/urandom | fold -w 6 | head -n 1) STACK_NAME="eks-vpc-stack-${RANDOM_ID}" CLUSTER_NAME="eks-cluster-${RANDOM_ID}" NODEGROUP_NAME="eks-nodegroup-${RANDOM_ID}" CLUSTER_ROLE_NAME="EKSClusterRole-${RANDOM_ID}" NODE_ROLE_NAME="EKSNodeRole-${RANDOM_ID}" echo "Using the following resource names:" echo "- VPC Stack: $STACK_NAME" echo "- EKS Cluster: $CLUSTER_NAME" echo "- Node Group: $NODEGROUP_NAME" echo "- Cluster IAM Role: $CLUSTER_ROLE_NAME" echo "- Node IAM Role: $NODE_ROLE_NAME" # Array to track created resources for cleanup declare -a CREATED_RESOURCES # Function to clean up resources cleanup_resources() { echo "Cleaning up resources in reverse order..." # Check if node group exists and delete it if aws eks list-nodegroups --cluster-name "$CLUSTER_NAME" --query "nodegroups[?contains(@,'$NODEGROUP_NAME')]" --output text 2>/dev/null | grep -q "$NODEGROUP_NAME"; then echo "Deleting node group: $NODEGROUP_NAME" aws eks delete-nodegroup --cluster-name "$CLUSTER_NAME" --nodegroup-name "$NODEGROUP_NAME" echo "Waiting for node group deletion to complete..." aws eks wait nodegroup-deleted --cluster-name "$CLUSTER_NAME" --nodegroup-name "$NODEGROUP_NAME" echo "Node group deleted successfully." fi # Check if cluster exists and delete it if aws eks describe-cluster --name "$CLUSTER_NAME" 2>/dev/null; then echo "Deleting cluster: $CLUSTER_NAME" aws eks delete-cluster --name "$CLUSTER_NAME" echo "Waiting for cluster deletion to complete (this may take several minutes)..." aws eks wait cluster-deleted --name "$CLUSTER_NAME" echo "Cluster deleted successfully." fi # Check if CloudFormation stack exists and delete it if aws cloudformation describe-stacks --stack-name "$STACK_NAME" 2>/dev/null; then echo "Deleting CloudFormation stack: $STACK_NAME" aws cloudformation delete-stack --stack-name "$STACK_NAME" echo "Waiting for CloudFormation stack deletion to complete..." aws cloudformation wait stack-delete-complete --stack-name "$STACK_NAME" echo "CloudFormation stack deleted successfully." fi # Clean up IAM roles if aws iam get-role --role-name "$NODE_ROLE_NAME" 2>/dev/null; then echo "Detaching policies from node role: $NODE_ROLE_NAME" aws iam detach-role-policy --policy-arn arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy --role-name "$NODE_ROLE_NAME" aws iam detach-role-policy --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly --role-name "$NODE_ROLE_NAME" aws iam detach-role-policy --policy-arn arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy --role-name "$NODE_ROLE_NAME" echo "Deleting node role: $NODE_ROLE_NAME" aws iam delete-role --role-name "$NODE_ROLE_NAME" echo "Node role deleted successfully." fi if aws iam get-role --role-name "$CLUSTER_ROLE_NAME" 2>/dev/null; then echo "Detaching policies from cluster role: $CLUSTER_ROLE_NAME" aws iam detach-role-policy --policy-arn arn:aws:iam::aws:policy/AmazonEKSClusterPolicy --role-name "$CLUSTER_ROLE_NAME" echo "Deleting cluster role: $CLUSTER_ROLE_NAME" aws iam delete-role --role-name "$CLUSTER_ROLE_NAME" echo "Cluster role deleted successfully." fi echo "Cleanup complete." } # Trap to ensure cleanup on script exit trap 'echo "Script interrupted. Cleaning up resources..."; cleanup_resources; exit 1' SIGINT SIGTERM # Verify AWS CLI configuration echo "Verifying AWS CLI configuration..." AWS_ACCOUNT_INFO=$(aws sts get-caller-identity) check_command "$AWS_ACCOUNT_INFO" echo "AWS CLI is properly configured." # Step 1: Create VPC using CloudFormation echo "Step 1: Creating VPC with CloudFormation..." echo "Creating CloudFormation stack: $STACK_NAME" # Create the CloudFormation stack CF_CREATE_OUTPUT=$(aws cloudformation create-stack \ --stack-name "$STACK_NAME" \ --template-url https://s3.us-west-2.amazonaws.com/amazon-eks/cloudformation/2020-10-29/amazon-eks-vpc-private-subnets.yaml \ --tags Key=project,Value=doc-smith Key=tutorial,Value=eks-gs) check_command "$CF_CREATE_OUTPUT" CREATED_RESOURCES+=("CloudFormation Stack: $STACK_NAME") echo "Waiting for CloudFormation stack to complete (this may take a few minutes)..." aws cloudformation wait stack-create-complete --stack-name "$STACK_NAME" if [ $? -ne 0 ]; then handle_error "CloudFormation stack creation failed" fi echo "CloudFormation stack created successfully." # Step 2: Create IAM roles for EKS echo "Step 2: Creating IAM roles for EKS..." # Create cluster role trust policy echo "Creating cluster role trust policy..." cat > eks-cluster-role-trust-policy.json << EOF { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "eks.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF # Create cluster role echo "Creating cluster IAM role: $CLUSTER_ROLE_NAME" CLUSTER_ROLE_OUTPUT=$(aws iam create-role \ --role-name "$CLUSTER_ROLE_NAME" \ --assume-role-policy-document file://"eks-cluster-role-trust-policy.json") check_command "$CLUSTER_ROLE_OUTPUT" aws iam tag-role --role-name "$CLUSTER_ROLE_NAME" \ --tags Key=project,Value=doc-smith Key=tutorial,Value=eks-gs CREATED_RESOURCES+=("IAM Role: $CLUSTER_ROLE_NAME") # Attach policy to cluster role echo "Attaching EKS cluster policy to role..." ATTACH_CLUSTER_POLICY_OUTPUT=$(aws iam attach-role-policy \ --policy-arn arn:aws:iam::aws:policy/AmazonEKSClusterPolicy \ --role-name "$CLUSTER_ROLE_NAME") check_command "$ATTACH_CLUSTER_POLICY_OUTPUT" # Create node role trust policy echo "Creating node role trust policy..." cat > node-role-trust-policy.json << EOF { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF # Create node role echo "Creating node IAM role: $NODE_ROLE_NAME" NODE_ROLE_OUTPUT=$(aws iam create-role \ --role-name "$NODE_ROLE_NAME" \ --assume-role-policy-document file://"node-role-trust-policy.json") check_command "$NODE_ROLE_OUTPUT" aws iam tag-role --role-name "$NODE_ROLE_NAME" \ --tags Key=project,Value=doc-smith Key=tutorial,Value=eks-gs CREATED_RESOURCES+=("IAM Role: $NODE_ROLE_NAME") # Attach policies to node role echo "Attaching EKS node policies to role..." ATTACH_NODE_POLICY1_OUTPUT=$(aws iam attach-role-policy \ --policy-arn arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy \ --role-name "$NODE_ROLE_NAME") check_command "$ATTACH_NODE_POLICY1_OUTPUT" ATTACH_NODE_POLICY2_OUTPUT=$(aws iam attach-role-policy \ --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly \ --role-name "$NODE_ROLE_NAME") check_command "$ATTACH_NODE_POLICY2_OUTPUT" ATTACH_NODE_POLICY3_OUTPUT=$(aws iam attach-role-policy \ --policy-arn arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy \ --role-name "$NODE_ROLE_NAME") check_command "$ATTACH_NODE_POLICY3_OUTPUT" # Step 3: Get VPC and subnet information echo "Step 3: Getting VPC and subnet information..." VPC_ID=$(aws cloudformation describe-stacks \ --stack-name "$STACK_NAME" \ --query "Stacks[0].Outputs[?OutputKey=='VpcId'].OutputValue" \ --output text) if [ -z "$VPC_ID" ]; then handle_error "Failed to get VPC ID from CloudFormation stack" fi echo "VPC ID: $VPC_ID" SUBNET_IDS=$(aws cloudformation describe-stacks \ --stack-name "$STACK_NAME" \ --query "Stacks[0].Outputs[?OutputKey=='SubnetIds'].OutputValue" \ --output text) if [ -z "$SUBNET_IDS" ]; then handle_error "Failed to get Subnet IDs from CloudFormation stack" fi echo "Subnet IDs: $SUBNET_IDS" SECURITY_GROUP_ID=$(aws cloudformation describe-stacks \ --stack-name "$STACK_NAME" \ --query "Stacks[0].Outputs[?OutputKey=='SecurityGroups'].OutputValue" \ --output text) if [ -z "$SECURITY_GROUP_ID" ]; then handle_error "Failed to get Security Group ID from CloudFormation stack" fi echo "Security Group ID: $SECURITY_GROUP_ID" # Step 4: Create EKS cluster echo "Step 4: Creating EKS cluster: $CLUSTER_NAME" CLUSTER_ROLE_ARN=$(aws iam get-role --role-name "$CLUSTER_ROLE_NAME" --query "Role.Arn" --output text) if [ -z "$CLUSTER_ROLE_ARN" ]; then handle_error "Failed to get Cluster Role ARN" fi echo "Creating EKS cluster (this will take 10-15 minutes)..." CREATE_CLUSTER_OUTPUT=$(aws eks create-cluster \ --name "$CLUSTER_NAME" \ --role-arn "$CLUSTER_ROLE_ARN" \ --resources-vpc-config subnetIds="$SUBNET_IDS",securityGroupIds="$SECURITY_GROUP_ID" \ --tags Key=project,Value=doc-smith,Key=tutorial,Value=eks-gs) check_command "$CREATE_CLUSTER_OUTPUT" CREATED_RESOURCES+=("EKS Cluster: $CLUSTER_NAME") echo "Waiting for EKS cluster to become active (this may take 10-15 minutes)..." aws eks wait cluster-active --name "$CLUSTER_NAME" if [ $? -ne 0 ]; then handle_error "Cluster creation failed or timed out" fi echo "EKS cluster is now active." # Step 5: Configure kubectl echo "Step 5: Configuring kubectl to communicate with the cluster..." # Check if kubectl is installed if ! check_kubectl; then echo "Will skip kubectl configuration steps but continue with the script." echo "You can manually configure kubectl later with: aws eks update-kubeconfig --name \"$CLUSTER_NAME\"" else UPDATE_KUBECONFIG_OUTPUT=$(aws eks update-kubeconfig --name "$CLUSTER_NAME") check_command "$UPDATE_KUBECONFIG_OUTPUT" echo "kubectl configured successfully." # Test kubectl configuration echo "Testing kubectl configuration..." KUBECTL_TEST_OUTPUT=$(kubectl get svc 2>&1) if [ $? -ne 0 ]; then echo "Warning: kubectl configuration test failed. This might be due to permissions or network issues." echo "Error details: $KUBECTL_TEST_OUTPUT" echo "Continuing with script execution..." else echo "$KUBECTL_TEST_OUTPUT" echo "kubectl configuration test successful." fi fi # Step 6: Create managed node group echo "Step 6: Creating managed node group: $NODEGROUP_NAME" NODE_ROLE_ARN=$(aws iam get-role --role-name "$NODE_ROLE_NAME" --query "Role.Arn" --output text) if [ -z "$NODE_ROLE_ARN" ]; then handle_error "Failed to get Node Role ARN" fi # Convert comma-separated subnet IDs to space-separated for the create-nodegroup command SUBNET_IDS_ARRAY=(${SUBNET_IDS//,/ }) echo "Creating managed node group (this will take 5-10 minutes)..." CREATE_NODEGROUP_OUTPUT=$(aws eks create-nodegroup \ --cluster-name "$CLUSTER_NAME" \ --nodegroup-name "$NODEGROUP_NAME" \ --node-role "$NODE_ROLE_ARN" \ --subnets "${SUBNET_IDS_ARRAY[@]}" \ --tags Key=project,Value=doc-smith,Key=tutorial,Value=eks-gs) check_command "$CREATE_NODEGROUP_OUTPUT" CREATED_RESOURCES+=("EKS Node Group: $NODEGROUP_NAME") echo "Waiting for node group to become active (this may take 5-10 minutes)..." aws eks wait nodegroup-active --cluster-name "$CLUSTER_NAME" --nodegroup-name "$NODEGROUP_NAME" if [ $? -ne 0 ]; then handle_error "Node group creation failed or timed out" fi echo "Node group is now active." # Step 7: Verify nodes echo "Step 7: Verifying nodes..." echo "Waiting for nodes to register with the cluster (this may take a few minutes)..." sleep 60 # Give nodes more time to register # Check if kubectl is installed before attempting to use it if ! check_kubectl; then echo "Cannot verify nodes without kubectl. Skipping this step." echo "You can manually verify nodes after installing kubectl with: kubectl get nodes" else NODES_OUTPUT=$(kubectl get nodes 2>&1) if [ $? -ne 0 ]; then echo "Warning: Unable to get nodes. This might be due to permissions or the nodes are still registering." echo "Error details: $NODES_OUTPUT" echo "Continuing with script execution..." else echo "$NODES_OUTPUT" echo "Nodes verified successfully." fi fi # Step 8: View resources echo "Step 8: Viewing cluster resources..." echo "Cluster information:" CLUSTER_INFO=$(aws eks describe-cluster --name "$CLUSTER_NAME") echo "$CLUSTER_INFO" echo "Node group information:" NODEGROUP_INFO=$(aws eks describe-nodegroup --cluster-name "$CLUSTER_NAME" --nodegroup-name "$NODEGROUP_NAME") echo "$NODEGROUP_INFO" echo "Kubernetes resources:" if ! check_kubectl; then echo "Cannot list Kubernetes resources without kubectl. Skipping this step." echo "You can manually list resources after installing kubectl with: kubectl get all --all-namespaces" else KUBE_RESOURCES=$(kubectl get all --all-namespaces 2>&1) if [ $? -ne 0 ]; then echo "Warning: Unable to get Kubernetes resources. This might be due to permissions." echo "Error details: $KUBE_RESOURCES" echo "Continuing with script execution..." else echo "$KUBE_RESOURCES" fi fi # Display summary of created resources echo "" echo "===========================================" echo "RESOURCES CREATED" echo "===========================================" for resource in "${CREATED_RESOURCES[@]}"; do echo "- $resource" done echo "===========================================" # Prompt for cleanup 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,,}" == "y" ]]; then cleanup_resources else echo "Resources will not be cleaned up. You can manually clean them up later." echo "To clean up resources, run the following commands:" echo "1. Delete node group: aws eks delete-nodegroup --cluster-name $CLUSTER_NAME --nodegroup-name $NODEGROUP_NAME" echo "2. Wait for node group deletion: aws eks wait nodegroup-deleted --cluster-name $CLUSTER_NAME --nodegroup-name $NODEGROUP_NAME" echo "3. Delete cluster: aws eks delete-cluster --name $CLUSTER_NAME" echo "4. Wait for cluster deletion: aws eks wait cluster-deleted --name $CLUSTER_NAME" echo "5. Delete CloudFormation stack: aws cloudformation delete-stack --stack-name $STACK_NAME" echo "6. Detach and delete IAM roles for the node group and cluster" fi echo "Script completed at $(date)"

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Buat kluster MSK

  • Buat izin IAM untuk akses MSK

  • Buat mesin klien

  • Dapatkan broker bootstrap

  • Siapkan mesin klien

  • Buat topik dan produce/consume data

  • Pembersihan sumber daya

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash # Amazon MSK Getting Started Tutorial Script - Version 8 # This script automates the steps in the Amazon MSK Getting Started tutorial # It creates an MSK cluster, sets up IAM permissions, creates a client machine, # and configures the client to interact with the cluster # Set up logging LOG_FILE="msk_tutorial_$(date +%Y%m%d_%H%M%S).log" exec > >(tee -a "$LOG_FILE") 2>&1 echo "Starting Amazon MSK Getting Started Tutorial Script - Version 8" echo "Logging to $LOG_FILE" echo "==============================================" # Function to handle errors handle_error() { echo "ERROR: $1" echo "Resources created so far:" if [ -n "$CLUSTER_ARN" ]; then echo "- MSK Cluster: $CLUSTER_ARN"; fi if [ -n "$POLICY_ARN" ]; then echo "- IAM Policy: $POLICY_ARN"; fi if [ -n "$ROLE_NAME" ]; then echo "- IAM Role: $ROLE_NAME"; fi if [ -n "$INSTANCE_PROFILE_NAME" ]; then echo "- IAM Instance Profile: $INSTANCE_PROFILE_NAME"; fi if [ -n "$CLIENT_SG_ID" ]; then echo "- Client Security Group: $CLIENT_SG_ID"; fi if [ -n "$INSTANCE_ID" ]; then echo "- EC2 Instance: $INSTANCE_ID"; fi if [ -n "$KEY_NAME" ]; then echo "- Key Pair: $KEY_NAME"; fi echo "Attempting to clean up resources..." cleanup_resources exit 1 } # Function to check if a resource exists resource_exists() { local resource_type="$1" local resource_id="$2" case "$resource_type" in "cluster") aws kafka describe-cluster --cluster-arn "$resource_id" &>/dev/null ;; "policy") aws iam get-policy --policy-arn "$resource_id" &>/dev/null ;; "role") aws iam get-role --role-name "$resource_id" &>/dev/null ;; "instance-profile") aws iam get-instance-profile --instance-profile-name "$resource_id" &>/dev/null ;; "security-group") aws ec2 describe-security-groups --group-ids "$resource_id" &>/dev/null ;; "instance") aws ec2 describe-instances --instance-ids "$resource_id" --query 'Reservations[0].Instances[0].State.Name' --output text | grep -v "terminated" &>/dev/null ;; "key-pair") aws ec2 describe-key-pairs --key-names "$resource_id" &>/dev/null ;; esac } # Function to remove security group references remove_security_group_references() { local sg_id="$1" if [ -z "$sg_id" ]; then echo "No security group ID provided for reference removal" return fi echo "Removing security group references for $sg_id" # Get all security groups in the VPC that might reference our client security group local vpc_security_groups=$(aws ec2 describe-security-groups \ --filters "Name=vpc-id,Values=$DEFAULT_VPC_ID" \ --query 'SecurityGroups[].GroupId' \ --output text 2>/dev/null) if [ -n "$vpc_security_groups" ]; then for other_sg in $vpc_security_groups; do if [ "$other_sg" != "$sg_id" ]; then echo "Checking security group $other_sg for references to $sg_id" # Get the security group details in JSON format local sg_details=$(aws ec2 describe-security-groups \ --group-ids "$other_sg" \ --output json 2>/dev/null) if [ -n "$sg_details" ]; then # Check if our security group is referenced in inbound rules local has_inbound_ref=$(echo "$sg_details" | grep -o "\"GroupId\": \"$sg_id\"" | head -1) if [ -n "$has_inbound_ref" ]; then echo "Found inbound rules in $other_sg referencing $sg_id, removing them..." # Try to remove common rule types echo "Attempting to remove all-traffic rule" aws ec2 revoke-security-group-ingress \ --group-id "$other_sg" \ --protocol all \ --source-group "$sg_id" 2>/dev/null || echo "No all-traffic rule to remove" # Try to remove TCP rules on common ports for port in 22 80 443 9092 9094 9096; do aws ec2 revoke-security-group-ingress \ --group-id "$other_sg" \ --protocol tcp \ --port "$port" \ --source-group "$sg_id" 2>/dev/null || true done # Try to remove UDP rules aws ec2 revoke-security-group-ingress \ --group-id "$other_sg" \ --protocol udp \ --source-group "$sg_id" 2>/dev/null || true fi # Check for outbound rules (less common but possible) local has_outbound_ref=$(echo "$sg_details" | grep -A 20 "IpPermissionsEgress" | grep -o "\"GroupId\": \"$sg_id\"" | head -1) if [ -n "$has_outbound_ref" ]; then echo "Found outbound rules in $other_sg referencing $sg_id, removing them..." aws ec2 revoke-security-group-egress \ --group-id "$other_sg" \ --protocol all \ --source-group "$sg_id" 2>/dev/null || echo "No outbound all-traffic rule to remove" fi fi fi done fi echo "Completed security group reference removal for $sg_id" } # Function to clean up resources cleanup_resources() { echo "Cleaning up resources..." # Delete EC2 instance if it exists if [ -n "$INSTANCE_ID" ] && resource_exists "instance" "$INSTANCE_ID"; then echo "Terminating EC2 instance: $INSTANCE_ID" aws ec2 terminate-instances --instance-ids "$INSTANCE_ID" || echo "Failed to terminate instance" echo "Waiting for instance to terminate..." aws ec2 wait instance-terminated --instance-ids "$INSTANCE_ID" || echo "Failed to wait for instance termination" fi # Delete MSK cluster first (to remove dependencies on security group) if [ -n "$CLUSTER_ARN" ] && resource_exists "cluster" "$CLUSTER_ARN"; then echo "Deleting MSK cluster: $CLUSTER_ARN" aws kafka delete-cluster --cluster-arn "$CLUSTER_ARN" || echo "Failed to delete cluster" # Wait a bit for the cluster deletion to start echo "Waiting 30 seconds for cluster deletion to begin..." sleep 30 fi # Remove security group references before attempting deletion if [ -n "$CLIENT_SG_ID" ] && resource_exists "security-group" "$CLIENT_SG_ID"; then remove_security_group_references "$CLIENT_SG_ID" echo "Deleting security group: $CLIENT_SG_ID" # Try multiple times with longer delays to ensure dependencies are removed for i in {1..10}; do if aws ec2 delete-security-group --group-id "$CLIENT_SG_ID"; then echo "Security group deleted successfully" break fi echo "Failed to delete security group (attempt $i/10), retrying in 30 seconds..." sleep 30 done fi # Delete key pair if it exists if [ -n "$KEY_NAME" ] && resource_exists "key-pair" "$KEY_NAME"; then echo "Deleting key pair: $KEY_NAME" aws ec2 delete-key-pair --key-name "$KEY_NAME" || echo "Failed to delete key pair" fi # Remove role from instance profile if [ -n "$ROLE_NAME" ] && [ -n "$INSTANCE_PROFILE_NAME" ] && resource_exists "instance-profile" "$INSTANCE_PROFILE_NAME"; then echo "Removing role from instance profile" aws iam remove-role-from-instance-profile \ --instance-profile-name "$INSTANCE_PROFILE_NAME" \ --role-name "$ROLE_NAME" || echo "Failed to remove role from instance profile" fi # Delete instance profile if [ -n "$INSTANCE_PROFILE_NAME" ] && resource_exists "instance-profile" "$INSTANCE_PROFILE_NAME"; then echo "Deleting instance profile: $INSTANCE_PROFILE_NAME" aws iam delete-instance-profile \ --instance-profile-name "$INSTANCE_PROFILE_NAME" || echo "Failed to delete instance profile" fi # Detach policy from role if [ -n "$ROLE_NAME" ] && [ -n "$POLICY_ARN" ] && resource_exists "role" "$ROLE_NAME"; then echo "Detaching policy from role" aws iam detach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "$POLICY_ARN" || echo "Failed to detach policy" fi # Delete role if [ -n "$ROLE_NAME" ] && resource_exists "role" "$ROLE_NAME"; then echo "Deleting role: $ROLE_NAME" aws iam delete-role --role-name "$ROLE_NAME" || echo "Failed to delete role" fi # Delete policy if [ -n "$POLICY_ARN" ] && resource_exists "policy" "$POLICY_ARN"; then echo "Deleting policy: $POLICY_ARN" aws iam delete-policy --policy-arn "$POLICY_ARN" || echo "Failed to delete policy" fi echo "Cleanup completed" } # Function to find a suitable subnet and instance type combination find_suitable_subnet_and_instance_type() { local vpc_id="$1" local -a subnet_array=("${!2}") # List of instance types to try, in order of preference local instance_types=("t3.micro" "t2.micro" "t3.small" "t2.small") echo "Finding suitable subnet and instance type combination..." for instance_type in "${instance_types[@]}"; do echo "Trying instance type: $instance_type" for subnet_id in "${subnet_array[@]}"; do # Get the availability zone for this subnet local az=$(aws ec2 describe-subnets \ --subnet-ids "$subnet_id" \ --query 'Subnets[0].AvailabilityZone' \ --output text) echo " Checking subnet $subnet_id in AZ $az" # Check if this instance type is available in this AZ local available=$(aws ec2 describe-instance-type-offerings \ --location-type availability-zone \ --filters "Name=location,Values=$az" "Name=instance-type,Values=$instance_type" \ --query 'InstanceTypeOfferings[0].InstanceType' \ --output text 2>/dev/null) if [ "$available" = "$instance_type" ]; then echo " ✓ Found suitable combination: $instance_type in $az (subnet: $subnet_id)" SELECTED_SUBNET_ID="$subnet_id" SELECTED_INSTANCE_TYPE="$instance_type" return 0 else echo " ✗ $instance_type not available in $az" fi done done echo "ERROR: Could not find any suitable subnet and instance type combination" return 1 } # Generate unique identifiers RANDOM_SUFFIX=$(LC_ALL=C tr -dc 'a-z0-9' < /dev/urandom | fold -w 8 | head -n 1) CLUSTER_NAME="MSKTutorialCluster-${RANDOM_SUFFIX}" POLICY_NAME="msk-tutorial-policy-${RANDOM_SUFFIX}" ROLE_NAME="msk-tutorial-role-${RANDOM_SUFFIX}" INSTANCE_PROFILE_NAME="msk-tutorial-profile-${RANDOM_SUFFIX}" SG_NAME="MSKClientSecurityGroup-${RANDOM_SUFFIX}" echo "Using the following resource names:" echo "- Cluster Name: $CLUSTER_NAME" echo "- Policy Name: $POLICY_NAME" echo "- Role Name: $ROLE_NAME" echo "- Instance Profile Name: $INSTANCE_PROFILE_NAME" echo "- Security Group Name: $SG_NAME" echo "==============================================" # Step 1: Create an MSK Provisioned cluster echo "Step 1: Creating MSK Provisioned cluster" # Get the default VPC ID first echo "Getting default VPC..." DEFAULT_VPC_ID=$(aws ec2 describe-vpcs \ --filters "Name=is-default,Values=true" \ --query "Vpcs[0].VpcId" \ --output text) if [ -z "$DEFAULT_VPC_ID" ] || [ "$DEFAULT_VPC_ID" = "None" ]; then handle_error "Could not find default VPC. Please ensure you have a default VPC in your region." fi echo "Default VPC ID: $DEFAULT_VPC_ID" # Get available subnets in the default VPC echo "Getting available subnets in the default VPC..." SUBNETS=$(aws ec2 describe-subnets \ --filters "Name=vpc-id,Values=$DEFAULT_VPC_ID" "Name=default-for-az,Values=true" \ --query "Subnets[0:3].SubnetId" \ --output text) # Convert space-separated subnet IDs to an array read -r -a SUBNET_ARRAY <<< "$SUBNETS" if [ ${#SUBNET_ARRAY[@]} -lt 3 ]; then handle_error "Not enough subnets available in the default VPC. Need at least 3 subnets, found ${#SUBNET_ARRAY[@]}." fi # Get default security group for the default VPC echo "Getting default security group for the default VPC..." DEFAULT_SG=$(aws ec2 describe-security-groups \ --filters "Name=group-name,Values=default" "Name=vpc-id,Values=$DEFAULT_VPC_ID" \ --query "SecurityGroups[0].GroupId" \ --output text) if [ -z "$DEFAULT_SG" ] || [ "$DEFAULT_SG" = "None" ]; then handle_error "Could not find default security group for VPC $DEFAULT_VPC_ID" fi echo "Creating MSK cluster: $CLUSTER_NAME" echo "Using VPC: $DEFAULT_VPC_ID" echo "Using subnets: ${SUBNET_ARRAY[0]} ${SUBNET_ARRAY[1]} ${SUBNET_ARRAY[2]}" echo "Using security group: $DEFAULT_SG" # Create the MSK cluster with proper error handling CLUSTER_RESPONSE=$(aws kafka create-cluster \ --cluster-name "$CLUSTER_NAME" \ --broker-node-group-info "{\"InstanceType\": \"kafka.t3.small\", \"ClientSubnets\": [\"${SUBNET_ARRAY[0]}\", \"${SUBNET_ARRAY[1]}\", \"${SUBNET_ARRAY[2]}\"], \"SecurityGroups\": [\"$DEFAULT_SG\"]}" \ --kafka-version "3.6.0" \ --number-of-broker-nodes 3 \ --encryption-info "{\"EncryptionInTransit\": {\"InCluster\": true, \"ClientBroker\": \"TLS\"}}" \ --tags project=doc-smith,tutorial=amazon-managed-streaming-for-apache-kafka-gs 2>&1) # Check if the command was successful if [ $? -ne 0 ]; then handle_error "Failed to create MSK cluster: $CLUSTER_RESPONSE" fi # Extract the cluster ARN using grep CLUSTER_ARN=$(echo "$CLUSTER_RESPONSE" | grep -o '"ClusterArn": "[^"]*' | cut -d'"' -f4) if [ -z "$CLUSTER_ARN" ]; then handle_error "Failed to extract cluster ARN from response: $CLUSTER_RESPONSE" fi echo "MSK cluster creation initiated. ARN: $CLUSTER_ARN" echo "Waiting for cluster to become active (this may take 15-20 minutes)..." # Wait for the cluster to become active while true; do CLUSTER_STATUS=$(aws kafka describe-cluster --cluster-arn "$CLUSTER_ARN" --query "ClusterInfo.State" --output text 2>/dev/null) if [ $? -ne 0 ]; then echo "Failed to get cluster status. Retrying in 30 seconds..." sleep 30 continue fi echo "Current cluster status: $CLUSTER_STATUS" if [ "$CLUSTER_STATUS" = "ACTIVE" ]; then echo "Cluster is now active!" break elif [ "$CLUSTER_STATUS" = "FAILED" ]; then handle_error "Cluster creation failed" fi echo "Still waiting for cluster to become active... (checking again in 60 seconds)" sleep 60 done echo "==============================================" # Step 2: Create an IAM role granting access to create topics on the Amazon MSK cluster echo "Step 2: Creating IAM policy and role" # Get account ID and region ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) REGION=$(aws configure get region) if [ -z "$REGION" ]; then REGION=$(aws ec2 describe-availability-zones --query 'AvailabilityZones[0].RegionName' --output text) fi if [ -z "$ACCOUNT_ID" ] || [ -z "$REGION" ]; then handle_error "Could not determine AWS account ID or region" fi echo "Account ID: $ACCOUNT_ID" echo "Region: $REGION" # Create IAM policy echo "Creating IAM policy: $POLICY_NAME" POLICY_DOCUMENT="{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Effect\": \"Allow\", \"Action\": [ \"kafka-cluster:Connect\", \"kafka-cluster:AlterCluster\", \"kafka-cluster:DescribeCluster\" ], \"Resource\": [ \"$CLUSTER_ARN\" ] }, { \"Effect\": \"Allow\", \"Action\": [ \"kafka-cluster:*Topic*\", \"kafka-cluster:WriteData\", \"kafka-cluster:ReadData\" ], \"Resource\": [ \"arn:aws:kafka:$REGION:$ACCOUNT_ID:topic/$CLUSTER_NAME/*\" ] }, { \"Effect\": \"Allow\", \"Action\": [ \"kafka-cluster:AlterGroup\", \"kafka-cluster:DescribeGroup\" ], \"Resource\": [ \"arn:aws:kafka:$REGION:$ACCOUNT_ID:group/$CLUSTER_NAME/*\" ] } ] }" POLICY_RESPONSE=$(aws iam create-policy \ --policy-name "$POLICY_NAME" \ --policy-document "$POLICY_DOCUMENT" 2>&1) # Check if the command was successful if [ $? -ne 0 ]; then handle_error "Failed to create IAM policy: $POLICY_RESPONSE" fi # Extract the policy ARN using grep POLICY_ARN=$(echo "$POLICY_RESPONSE" | grep -o '"Arn": "[^"]*' | cut -d'"' -f4) if [ -z "$POLICY_ARN" ]; then handle_error "Failed to extract policy ARN from response: $POLICY_RESPONSE" fi aws iam tag-policy --policy-arn "$POLICY_ARN" \ --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-managed-streaming-for-apache-kafka-gs echo "IAM policy created. ARN: $POLICY_ARN" # Create IAM role for EC2 echo "Creating IAM role: $ROLE_NAME" TRUST_POLICY="{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}" ROLE_RESPONSE=$(aws iam create-role \ --role-name "$ROLE_NAME" \ --assume-role-policy-document "$TRUST_POLICY" 2>&1) # Check if the command was successful if [ $? -ne 0 ]; then handle_error "Failed to create IAM role: $ROLE_RESPONSE" fi echo "IAM role created: $ROLE_NAME" aws iam tag-role --role-name "$ROLE_NAME" \ --tags Key=project,Value=doc-smith Key=tutorial,Value=amazon-managed-streaming-for-apache-kafka-gs # Attach policy to role echo "Attaching policy to role" ATTACH_RESPONSE=$(aws iam attach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "$POLICY_ARN" 2>&1) # Check if the command was successful if [ $? -ne 0 ]; then handle_error "Failed to attach policy to role: $ATTACH_RESPONSE" fi echo "Policy attached to role" # Create instance profile and add role to it echo "Creating instance profile: $INSTANCE_PROFILE_NAME" PROFILE_RESPONSE=$(aws iam create-instance-profile \ --instance-profile-name "$INSTANCE_PROFILE_NAME" 2>&1) # Check if the command was successful if [ $? -ne 0 ]; then handle_error "Failed to create instance profile: $PROFILE_RESPONSE" fi echo "Instance profile created" echo "Adding role to instance profile" ADD_ROLE_RESPONSE=$(aws iam add-role-to-instance-profile \ --instance-profile-name "$INSTANCE_PROFILE_NAME" \ --role-name "$ROLE_NAME" 2>&1) # Check if the command was successful if [ $? -ne 0 ]; then handle_error "Failed to add role to instance profile: $ADD_ROLE_RESPONSE" fi echo "Role added to instance profile" # Wait a moment for IAM propagation echo "Waiting 10 seconds for IAM propagation..." sleep 10 echo "==============================================" # Step 3: Create a client machine echo "Step 3: Creating client machine" # Find a suitable subnet and instance type combination if ! find_suitable_subnet_and_instance_type "$DEFAULT_VPC_ID" SUBNET_ARRAY[@]; then handle_error "Could not find a suitable subnet and instance type combination" fi echo "Selected subnet: $SELECTED_SUBNET_ID" echo "Selected instance type: $SELECTED_INSTANCE_TYPE" # Verify the subnet is in the same VPC we're using SUBNET_VPC_ID=$(aws ec2 describe-subnets \ --subnet-ids "$SELECTED_SUBNET_ID" \ --query 'Subnets[0].VpcId' \ --output text) if [ "$SUBNET_VPC_ID" != "$DEFAULT_VPC_ID" ]; then handle_error "Subnet VPC ($SUBNET_VPC_ID) does not match default VPC ($DEFAULT_VPC_ID)" fi echo "VPC ID: $SUBNET_VPC_ID" # Get security group ID from the MSK cluster echo "Getting security group ID from the MSK cluster" MSK_SG_ID=$(aws kafka describe-cluster \ --cluster-arn "$CLUSTER_ARN" \ --query 'ClusterInfo.BrokerNodeGroupInfo.SecurityGroups[0]' \ --output text) if [ -z "$MSK_SG_ID" ] || [ "$MSK_SG_ID" = "None" ]; then handle_error "Failed to get security group ID from cluster" fi echo "MSK security group ID: $MSK_SG_ID" # Create security group for client echo "Creating security group for client: $SG_NAME" CLIENT_SG_RESPONSE=$(aws ec2 create-security-group \ --group-name "$SG_NAME" \ --description "Security group for MSK client" \ --vpc-id "$DEFAULT_VPC_ID" \ --tag-specifications 'ResourceType=security-group,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-managed-streaming-for-apache-kafka-gs}]' 2>&1) # Check if the command was successful if [ $? -ne 0 ]; then handle_error "Failed to create security group: $CLIENT_SG_RESPONSE" fi # Extract the security group ID using grep CLIENT_SG_ID=$(echo "$CLIENT_SG_RESPONSE" | grep -o '"GroupId": "[^"]*' | cut -d'"' -f4) if [ -z "$CLIENT_SG_ID" ]; then handle_error "Failed to extract security group ID from response: $CLIENT_SG_RESPONSE" fi echo "Client security group created. ID: $CLIENT_SG_ID" # Allow SSH access to client from your IP only echo "Getting your public IP address" MY_IP=$(curl -s https://checkip.amazonaws.com 2>/dev/null) if [ -z "$MY_IP" ]; then echo "Warning: Could not determine your IP address. Using 0.0.0.0/0 (not recommended for production)" MY_IP="0.0.0.0/0" else MY_IP="$MY_IP/32" echo "Your IP address: $MY_IP" fi echo "Adding SSH ingress rule to client security group" SSH_RULE_RESPONSE=$(aws ec2 authorize-security-group-ingress \ --group-id "$CLIENT_SG_ID" \ --protocol tcp \ --port 22 \ --cidr "$MY_IP" 2>&1) # Check if the command was successful if [ $? -ne 0 ]; then echo "Warning: Failed to add SSH ingress rule: $SSH_RULE_RESPONSE" echo "You may need to manually add SSH access to security group $CLIENT_SG_ID" fi echo "SSH ingress rule added" # Update MSK security group to allow traffic from client security group echo "Adding ingress rule to MSK security group to allow traffic from client" MSK_RULE_RESPONSE=$(aws ec2 authorize-security-group-ingress \ --group-id "$MSK_SG_ID" \ --protocol all \ --source-group "$CLIENT_SG_ID" 2>&1) # Check if the command was successful if [ $? -ne 0 ]; then echo "Warning: Failed to add ingress rule to MSK security group: $MSK_RULE_RESPONSE" echo "You may need to manually add ingress rule to security group $MSK_SG_ID" fi echo "Ingress rule added to MSK security group" # Create key pair KEY_NAME="MSKKeyPair-${RANDOM_SUFFIX}" echo "Creating key pair: $KEY_NAME" KEY_RESPONSE=$(aws ec2 create-key-pair --key-name "$KEY_NAME" --tag-specifications 'ResourceType=key-pair,Tags=[{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-managed-streaming-for-apache-kafka-gs}]' --query 'KeyMaterial' --output text 2>&1) # Check if the command was successful if [ $? -ne 0 ]; then handle_error "Failed to create key pair: $KEY_RESPONSE" fi # Save the private key to a file KEY_FILE="${KEY_NAME}.pem" echo "$KEY_RESPONSE" > "$KEY_FILE" chmod 400 "$KEY_FILE" echo "Key pair created and saved to $KEY_FILE" # Get the latest Amazon Linux 2 AMI echo "Getting latest Amazon Linux 2 AMI ID" AMI_ID=$(aws ec2 describe-images \ --owners amazon \ --filters "Name=name,Values=amzn2-ami-hvm-*-x86_64-gp2" "Name=state,Values=available" \ --query "sort_by(Images, &CreationDate)[-1].ImageId" \ --output text 2>/dev/null) if [ -z "$AMI_ID" ] || [ "$AMI_ID" = "None" ]; then handle_error "Failed to get Amazon Linux 2 AMI ID" fi echo "Using AMI ID: $AMI_ID" # Launch EC2 instance with the selected subnet and instance type echo "Launching EC2 instance" echo "Instance type: $SELECTED_INSTANCE_TYPE" echo "Subnet: $SELECTED_SUBNET_ID" INSTANCE_RESPONSE=$(aws ec2 run-instances \ --image-id "$AMI_ID" \ --instance-type "$SELECTED_INSTANCE_TYPE" \ --key-name "$KEY_NAME" \ --security-group-ids "$CLIENT_SG_ID" \ --subnet-id "$SELECTED_SUBNET_ID" \ --iam-instance-profile "Name=$INSTANCE_PROFILE_NAME" \ --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=MSKTutorialClient-${RANDOM_SUFFIX}},{Key=project,Value=doc-smith},{Key=tutorial,Value=amazon-managed-streaming-for-apache-kafka-gs}]" 2>&1) # Check if the command was successful if [ $? -ne 0 ]; then handle_error "Failed to launch EC2 instance: $INSTANCE_RESPONSE" fi # Extract the instance ID using grep INSTANCE_ID=$(echo "$INSTANCE_RESPONSE" | grep -o '"InstanceId": "[^"]*' | head -1 | cut -d'"' -f4) if [ -z "$INSTANCE_ID" ]; then handle_error "Failed to extract instance ID from response: $INSTANCE_RESPONSE" fi echo "EC2 instance launched successfully. ID: $INSTANCE_ID" echo "Waiting for instance to be running..." # Wait for the instance to be running aws ec2 wait instance-running --instance-ids "$INSTANCE_ID" if [ $? -ne 0 ]; then handle_error "Instance failed to reach running state" fi # Wait a bit more for the instance to initialize echo "Instance is running. Waiting 30 seconds for initialization..." sleep 30 # Get public DNS name of instance CLIENT_DNS=$(aws ec2 describe-instances \ --instance-ids "$INSTANCE_ID" \ --query 'Reservations[0].Instances[0].PublicDnsName' \ --output text) if [ -z "$CLIENT_DNS" ] || [ "$CLIENT_DNS" = "None" ]; then echo "Warning: Could not get public DNS name for instance. Trying public IP..." CLIENT_DNS=$(aws ec2 describe-instances \ --instance-ids "$INSTANCE_ID" \ --query 'Reservations[0].Instances[0].PublicIpAddress' \ --output text) if [ -z "$CLIENT_DNS" ] || [ "$CLIENT_DNS" = "None" ]; then handle_error "Failed to get public DNS name or IP address for instance" fi fi echo "Client instance DNS/IP: $CLIENT_DNS" echo "==============================================" # Get bootstrap brokers with improved logic echo "Getting bootstrap brokers" MAX_RETRIES=10 RETRY_COUNT=0 BOOTSTRAP_BROKERS="" AUTH_METHOD="" while [ -z "$BOOTSTRAP_BROKERS" ] || [ "$BOOTSTRAP_BROKERS" = "None" ]; do # Get the full bootstrap brokers response BOOTSTRAP_RESPONSE=$(aws kafka get-bootstrap-brokers \ --cluster-arn "$CLUSTER_ARN" 2>/dev/null) if [ $? -eq 0 ] && [ -n "$BOOTSTRAP_RESPONSE" ]; then # Try to get IAM authentication brokers first using grep BOOTSTRAP_BROKERS=$(echo "$BOOTSTRAP_RESPONSE" | grep -o '"BootstrapBrokerStringSaslIam": "[^"]*' | cut -d'"' -f4) if [ -n "$BOOTSTRAP_BROKERS" ]; then AUTH_METHOD="IAM" else # Fall back to TLS authentication BOOTSTRAP_BROKERS=$(echo "$BOOTSTRAP_RESPONSE" | grep -o '"BootstrapBrokerStringTls": "[^"]*' | cut -d'"' -f4) if [ -n "$BOOTSTRAP_BROKERS" ]; then AUTH_METHOD="TLS" fi fi fi RETRY_COUNT=$((RETRY_COUNT + 1)) if [ "$RETRY_COUNT" -ge "$MAX_RETRIES" ]; then echo "Warning: Could not get bootstrap brokers after $MAX_RETRIES attempts." echo "You may need to manually retrieve them later using:" echo "aws kafka get-bootstrap-brokers --cluster-arn $CLUSTER_ARN" BOOTSTRAP_BROKERS="BOOTSTRAP_BROKERS_NOT_AVAILABLE" AUTH_METHOD="UNKNOWN" break fi if [ -z "$BOOTSTRAP_BROKERS" ] || [ "$BOOTSTRAP_BROKERS" = "None" ]; then echo "Bootstrap brokers not available yet. Retrying in 30 seconds... (Attempt $RETRY_COUNT/$MAX_RETRIES)" sleep 30 fi done echo "Bootstrap brokers: $BOOTSTRAP_BROKERS" echo "Authentication method: $AUTH_METHOD" echo "==============================================" # Create setup script for the client machine echo "Creating setup script for the client machine" cat > setup_client.sh << 'EOF' #!/bin/bash # Set up logging LOG_FILE="client_setup_$(date +%Y%m%d_%H%M%S).log" exec > >(tee -a "$LOG_FILE") 2>&1 echo "Starting client setup" echo "==============================================" # Install Java echo "Installing Java" sudo yum -y install java-11 # Set environment variables echo "Setting up environment variables" export KAFKA_VERSION="3.6.0" echo "KAFKA_VERSION=$KAFKA_VERSION" # Download and extract Apache Kafka echo "Downloading Apache Kafka" wget https://archive.apache.org/dist/kafka/$KAFKA_VERSION/kafka_2.13-$KAFKA_VERSION.tgz if [ $? -ne 0 ]; then echo "Failed to download Kafka. Trying alternative mirror..." wget https://www.apache.org/dyn/closer.cgi?path=/kafka/$KAFKA_VERSION/kafka_2.13-$KAFKA_VERSION.tgz fi echo "Extracting Kafka" tar -xzf kafka_2.13-$KAFKA_VERSION.tgz export KAFKA_ROOT=$(pwd)/kafka_2.13-$KAFKA_VERSION echo "KAFKA_ROOT=$KAFKA_ROOT" # Download the MSK IAM authentication package (needed for both IAM and TLS) echo "Downloading MSK IAM authentication package" cd $KAFKA_ROOT/libs wget https://github.com/aws/aws-msk-iam-auth/releases/latest/download/aws-msk-iam-auth-1.1.6-all.jar if [ $? -ne 0 ]; then echo "Failed to download specific version. Trying to get latest version..." LATEST_VERSION=$(curl -s https://api.github.com/repos/aws/aws-msk-iam-auth/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4) wget https://github.com/aws/aws-msk-iam-auth/releases/download/$LATEST_VERSION/aws-msk-iam-auth-$LATEST_VERSION-all.jar if [ $? -ne 0 ]; then echo "Failed to download IAM auth package. Please check the URL and try again." exit 1 fi export CLASSPATH=$KAFKA_ROOT/libs/aws-msk-iam-auth-$LATEST_VERSION-all.jar else export CLASSPATH=$KAFKA_ROOT/libs/aws-msk-iam-auth-1.1.6-all.jar fi echo "CLASSPATH=$CLASSPATH" # Create client properties file based on authentication method echo "Creating client properties file" cd $KAFKA_ROOT/config # The AUTH_METHOD_PLACEHOLDER will be replaced by the script AUTH_METHOD="AUTH_METHOD_PLACEHOLDER" if [ "$AUTH_METHOD" = "IAM" ]; then echo "Configuring for IAM authentication" cat > client.properties << 'EOT' security.protocol=SASL_SSL sasl.mechanism=AWS_MSK_IAM sasl.jaas.config=software.amazon.msk.auth.iam.IAMLoginModule required; sasl.client.callback.handler.class=software.amazon.msk.auth.iam.IAMClientCallbackHandler EOT elif [ "$AUTH_METHOD" = "TLS" ]; then echo "Configuring for TLS authentication" cat > client.properties << 'EOT' security.protocol=SSL EOT else echo "Unknown authentication method. Creating basic TLS configuration." cat > client.properties << 'EOT' security.protocol=SSL EOT fi echo "Client setup completed" echo "==============================================" # Create a script to set up environment variables cat > ~/setup_env.sh << 'EOT' #!/bin/bash export KAFKA_VERSION="3.6.0" export KAFKA_ROOT=~/kafka_2.13-$KAFKA_VERSION export CLASSPATH=$KAFKA_ROOT/libs/aws-msk-iam-auth-1.1.6-all.jar export BOOTSTRAP_SERVER="BOOTSTRAP_SERVER_PLACEHOLDER" export AUTH_METHOD="AUTH_METHOD_PLACEHOLDER" echo "Environment variables set:" echo "KAFKA_VERSION=$KAFKA_VERSION" echo "KAFKA_ROOT=$KAFKA_ROOT" echo "CLASSPATH=$CLASSPATH" echo "BOOTSTRAP_SERVER=$BOOTSTRAP_SERVER" echo "AUTH_METHOD=$AUTH_METHOD" EOT chmod +x ~/setup_env.sh echo "Created environment setup script: ~/setup_env.sh" echo "Run 'source ~/setup_env.sh' to set up your environment" EOF # Replace placeholders in the setup script if [ -n "$BOOTSTRAP_BROKERS" ] && [ "$BOOTSTRAP_BROKERS" != "None" ] && [ "$BOOTSTRAP_BROKERS" != "BOOTSTRAP_BROKERS_NOT_AVAILABLE" ]; then sed -i "s|BOOTSTRAP_SERVER_PLACEHOLDER|$BOOTSTRAP_BROKERS|g" setup_client.sh else # If bootstrap brokers are not available, provide instructions to get them sed -i "s|BOOTSTRAP_SERVER_PLACEHOLDER|\$(aws kafka get-bootstrap-brokers --cluster-arn $CLUSTER_ARN --query 'BootstrapBrokerStringTls' --output text)|g" setup_client.sh fi # Replace auth method placeholder sed -i "s|AUTH_METHOD_PLACEHOLDER|$AUTH_METHOD|g" setup_client.sh echo "Setup script created" echo "==============================================" # Display summary of created resources echo "" echo "==============================================" echo "RESOURCE SUMMARY" echo "==============================================" echo "MSK Cluster ARN: $CLUSTER_ARN" echo "MSK Cluster Name: $CLUSTER_NAME" echo "Authentication Method: $AUTH_METHOD" echo "IAM Policy ARN: $POLICY_ARN" echo "IAM Role Name: $ROLE_NAME" echo "IAM Instance Profile: $INSTANCE_PROFILE_NAME" echo "Client Security Group: $CLIENT_SG_ID" echo "EC2 Instance ID: $INSTANCE_ID" echo "EC2 Instance Type: $SELECTED_INSTANCE_TYPE" echo "EC2 Instance DNS: $CLIENT_DNS" echo "Key Pair: $KEY_NAME (saved to $KEY_FILE)" echo "Bootstrap Brokers: $BOOTSTRAP_BROKERS" echo "==============================================" echo "" # Instructions for connecting to the instance and setting up the client echo "NEXT STEPS:" echo "1. Connect to your EC2 instance:" echo " ssh -i $KEY_FILE ec2-user@$CLIENT_DNS" echo "" echo "2. Upload the setup script to your instance:" echo " scp -i $KEY_FILE setup_client.sh ec2-user@$CLIENT_DNS:~/" echo "" echo "3. Run the setup script on your instance:" echo " ssh -i $KEY_FILE ec2-user@$CLIENT_DNS 'chmod +x ~/setup_client.sh && ~/setup_client.sh'" echo "" echo "4. Source the environment setup script:" echo " source ~/setup_env.sh" echo "" # Provide different instructions based on authentication method if [ "$AUTH_METHOD" = "IAM" ]; then echo "5. Create a Kafka topic (using IAM authentication):" echo " \$KAFKA_ROOT/bin/kafka-topics.sh --create \\" echo " --bootstrap-server \$BOOTSTRAP_SERVER \\" echo " --command-config \$KAFKA_ROOT/config/client.properties \\" echo " --replication-factor 3 \\" echo " --partitions 1 \\" echo " --topic MSKTutorialTopic" echo "" echo "6. Start a producer:" echo " \$KAFKA_ROOT/bin/kafka-console-producer.sh \\" echo " --broker-list \$BOOTSTRAP_SERVER \\" echo " --producer.config \$KAFKA_ROOT/config/client.properties \\" echo " --topic MSKTutorialTopic" echo "" echo "7. Start a consumer:" echo " \$KAFKA_ROOT/bin/kafka-console-consumer.sh \\" echo " --bootstrap-server \$BOOTSTRAP_SERVER \\" echo " --consumer.config \$KAFKA_ROOT/config/client.properties \\" echo " --topic MSKTutorialTopic \\" echo " --from-beginning" elif [ "$AUTH_METHOD" = "TLS" ]; then echo "5. Create a Kafka topic (using TLS authentication):" echo " \$KAFKA_ROOT/bin/kafka-topics.sh --create \\" echo " --bootstrap-server \$BOOTSTRAP_SERVER \\" echo " --command-config \$KAFKA_ROOT/config/client.properties \\" echo " --replication-factor 3 \\" echo " --partitions 1 \\" echo " --topic MSKTutorialTopic" echo "" echo "6. Start a producer:" echo " \$KAFKA_ROOT/bin/kafka-console-producer.sh \\" echo " --broker-list \$BOOTSTRAP_SERVER \\" echo " --producer.config \$KAFKA_ROOT/config/client.properties \\" echo " --topic MSKTutorialTopic" echo "" echo "7. Start a consumer:" echo " \$KAFKA_ROOT/bin/kafka-console-consumer.sh \\" echo " --bootstrap-server \$BOOTSTRAP_SERVER \\" echo " --consumer.config \$KAFKA_ROOT/config/client.properties \\" echo " --topic MSKTutorialTopic \\" echo " --from-beginning" else echo "5. Manually retrieve bootstrap brokers and configure authentication:" echo " aws kafka get-bootstrap-brokers --cluster-arn $CLUSTER_ARN" fi echo "" echo "8. Verify the topic was created:" echo " \$KAFKA_ROOT/bin/kafka-topics.sh --list \\" echo " --bootstrap-server \$BOOTSTRAP_SERVER \\" echo " --command-config \$KAFKA_ROOT/config/client.properties" echo "==============================================" echo "" # 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 cleanup_resources echo "All resources have been cleaned up." else echo "Resources will not be cleaned up. You can manually clean them up later." echo "To clean up resources, run this script again and choose 'y' when prompted." fi echo "Script completed successfully!"

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Buat cluster Redshift

  • Buat peran IAM untuk akses S3

  • Buat tabel dan muat data

  • Jalankan contoh kueri

  • Pembersihan sumber daya

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash # Amazon Redshift Provisioned Cluster Tutorial Script # This script creates a Redshift cluster, loads sample data, runs queries, and cleans up resources # Version 4: Security improvements and best practices set -euo pipefail # Set up logging LOG_FILE="redshift_tutorial.log" exec > >(tee -a "$LOG_FILE") 2>&1 echo "Starting Amazon Redshift tutorial script at $(date)" echo "All commands and outputs will be logged to $LOG_FILE" # Function to handle errors handle_error() { echo "ERROR: $1" >&2 echo "Resources created so far:" if [ -n "${CLUSTER_ID:-}" ]; then echo "- Redshift Cluster: $CLUSTER_ID"; fi if [ -n "${ROLE_NAME:-}" ]; then echo "- IAM Role: $ROLE_NAME"; fi echo "Attempting to clean up resources..." cleanup_resources exit 1 } # Function to clean up resources cleanup_resources() { echo "Cleaning up resources..." # Delete the cluster if it exists if [ -n "${CLUSTER_ID:-}" ]; then echo "Deleting Redshift cluster: $CLUSTER_ID" aws redshift delete-cluster --cluster-identifier "$CLUSTER_ID" --skip-final-cluster-snapshot 2>/dev/null || true echo "Waiting for cluster deletion to complete..." aws redshift wait cluster-deleted --cluster-identifier "$CLUSTER_ID" 2>/dev/null || true echo "Cluster deleted successfully." fi # Delete the IAM role if it exists if [ -n "${ROLE_NAME:-}" ]; then echo "Removing IAM role policy..." aws iam delete-role-policy --role-name "$ROLE_NAME" --policy-name RedshiftS3Access 2>/dev/null || true echo "Deleting IAM role: $ROLE_NAME" aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null || true fi # Clean up temporary files rm -f redshift-trust-policy.json redshift-s3-policy.json echo "Cleanup completed." } # Trap errors and cleanup trap 'handle_error "Script interrupted"' INT TERM # Function to wait for SQL statement to complete wait_for_statement() { local statement_id=$1 local max_attempts=30 local attempt=1 local status="" echo "Waiting for statement $statement_id to complete..." while [ $attempt -le $max_attempts ]; do status=$(aws redshift-data describe-statement --id "$statement_id" --query 'Status' --output text 2>/dev/null || echo "") if [ "$status" == "FINISHED" ]; then echo "Statement completed successfully." return 0 elif [ "$status" == "FAILED" ]; then local error=$(aws redshift-data describe-statement --id "$statement_id" --query 'Error' --output text 2>/dev/null || echo "Unknown error") echo "Statement failed with error: $error" >&2 return 1 elif [ "$status" == "ABORTED" ]; then echo "Statement was aborted." >&2 return 1 fi echo "Statement status: $status. Waiting... (Attempt $attempt/$max_attempts)" sleep 10 ((attempt++)) done echo "Timed out waiting for statement to complete." >&2 return 1 } # Function to check if IAM role is attached to cluster check_role_attached() { local role_arn=$1 local max_attempts=10 local attempt=1 echo "Checking if IAM role is attached to the cluster..." while [ $attempt -le $max_attempts ]; do local status=$(aws redshift describe-clusters \ --cluster-identifier "$CLUSTER_ID" \ --query "Clusters[0].IamRoles[?IamRoleArn=='$role_arn'].ApplyStatus" \ --output text 2>/dev/null || echo "") if [ "$status" == "in-sync" ]; then echo "IAM role is successfully attached to the cluster." return 0 fi echo "IAM role status: $status. Waiting... (Attempt $attempt/$max_attempts)" sleep 30 ((attempt++)) done echo "Timed out waiting for IAM role to be attached." >&2 return 1 } # Validate required commands for cmd in aws jq; do if ! command -v "$cmd" &> /dev/null; then handle_error "Required command '$cmd' not found. Please install it and try again." fi done # Validate AWS credentials if ! aws sts get-caller-identity &>/dev/null; then handle_error "AWS credentials not configured or invalid" fi # Variables to track created resources CLUSTER_ID="examplecluster" ROLE_NAME="RedshiftS3Role-$(date +%s)" DB_NAME="dev" DB_USER="awsuser" # Generate secure password using AWS Secrets Manager or random string if command -v openssl &> /dev/null; then DB_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-20) else DB_PASSWORD="TempPass$(date +%s | md5sum | cut -c1-20)" fi # Validate password meets requirements if [ ${#DB_PASSWORD} -lt 8 ]; then handle_error "Generated password does not meet minimum length requirement" fi # Store password securely (optional: use AWS Secrets Manager in production) echo "Generated database password (store securely): $DB_PASSWORD" echo "=== Step 1: Creating Amazon Redshift Cluster ===" # Create the Redshift cluster with encryption and audit logging enabled echo "Creating Redshift cluster: $CLUSTER_ID" CLUSTER_RESULT=$(aws redshift create-cluster \ --cluster-identifier "$CLUSTER_ID" \ --node-type ra3.xlplus \ --number-of-nodes 2 \ --master-username "$DB_USER" \ --master-user-password "$DB_PASSWORD" \ --db-name "$DB_NAME" \ --port 5439 \ --encrypted \ --tags Key=project,Value=doc-smith Key=tutorial,Value=redshift-provisioned \ 2>&1) || handle_error "Failed to create Redshift cluster" echo "$CLUSTER_RESULT" echo "Waiting for cluster to become available..." # Wait for the cluster to be available aws redshift wait cluster-available --cluster-identifier "$CLUSTER_ID" || handle_error "Timeout waiting for cluster to become available" # Get cluster status to confirm CLUSTER_STATUS=$(aws redshift describe-clusters \ --cluster-identifier "$CLUSTER_ID" \ --query 'Clusters[0].ClusterStatus' \ --output text) echo "Cluster status: $CLUSTER_STATUS" echo "=== Step 2: Creating IAM Role for S3 Access ===" # Create trust policy file with restricted permissions echo "Creating trust policy for Redshift" cat > redshift-trust-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "redshift.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF chmod 600 redshift-trust-policy.json # Create IAM role echo "Creating IAM role: $ROLE_NAME" ROLE_RESULT=$(aws iam create-role \ --role-name "$ROLE_NAME" \ --assume-role-policy-document file://redshift-trust-policy.json 2>&1) || handle_error "Failed to create IAM role" echo "$ROLE_RESULT" # Get the role ARN ROLE_ARN=$(aws iam get-role --role-name "$ROLE_NAME" --query 'Role.Arn' --output text) echo "Role ARN: $ROLE_ARN" # Tag the IAM role echo "Tagging IAM role: $ROLE_NAME" aws iam tag-role --role-name "$ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=redshift-provisioned # Create policy document for S3 access with principle of least privilege echo "Creating S3 access policy" cat > redshift-s3-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::redshift-downloads", "arn:aws:s3:::redshift-downloads/*" ] } ] } EOF chmod 600 redshift-s3-policy.json # Attach policy to role echo "Attaching S3 access policy to role" POLICY_RESULT=$(aws iam put-role-policy \ --role-name "$ROLE_NAME" \ --policy-name RedshiftS3Access \ --policy-document file://redshift-s3-policy.json 2>&1) || handle_error "Failed to attach policy to role" echo "$POLICY_RESULT" # Attach role to cluster echo "Attaching IAM role to Redshift cluster" ATTACH_ROLE_RESULT=$(aws redshift modify-cluster-iam-roles \ --cluster-identifier "$CLUSTER_ID" \ --add-iam-roles "$ROLE_ARN" 2>&1) || handle_error "Failed to attach role to cluster" echo "$ATTACH_ROLE_RESULT" # Wait for the role to be attached echo "Waiting for IAM role to be attached to the cluster..." if ! check_role_attached "$ROLE_ARN"; then handle_error "Failed to attach IAM role to cluster" fi echo "=== Step 3: Getting Cluster Connection Information ===" # Get cluster endpoint CLUSTER_INFO=$(aws redshift describe-clusters \ --cluster-identifier "$CLUSTER_ID" \ --query 'Clusters[0].Endpoint.{Address:Address,Port:Port}' \ --output json) echo "Cluster endpoint information:" echo "$CLUSTER_INFO" echo "=== Step 4: Creating Tables and Loading Data ===" echo "Creating sales table" SALES_TABLE_ID=$(aws redshift-data execute-statement \ --cluster-identifier "$CLUSTER_ID" \ --database "$DB_NAME" \ --db-user "$DB_USER" \ --sql "DROP TABLE IF EXISTS sales; CREATE TABLE sales(salesid integer not null, listid integer not null distkey, sellerid integer not null, buyerid integer not null, eventid integer not null, dateid smallint not null sortkey, qtysold smallint not null, pricepaid decimal(8,2), commission decimal(8,2), saletime timestamp);" \ --query 'Id' --output text) echo "Sales table creation statement ID: $SALES_TABLE_ID" # Wait for statement to complete if ! wait_for_statement "$SALES_TABLE_ID"; then handle_error "Failed to create sales table" fi echo "Creating date table" DATE_TABLE_ID=$(aws redshift-data execute-statement \ --cluster-identifier "$CLUSTER_ID" \ --database "$DB_NAME" \ --db-user "$DB_USER" \ --sql "DROP TABLE IF EXISTS date; CREATE TABLE date(dateid smallint not null distkey sortkey, caldate date not null, day character(3) not null, week smallint not null, month character(5) not null, qtr character(5) not null, year smallint not null, holiday boolean default('N'));" \ --query 'Id' --output text) echo "Date table creation statement ID: $DATE_TABLE_ID" # Wait for statement to complete if ! wait_for_statement "$DATE_TABLE_ID"; then handle_error "Failed to create date table" fi echo "Loading data into sales table" SALES_LOAD_ID=$(aws redshift-data execute-statement \ --cluster-identifier "$CLUSTER_ID" \ --database "$DB_NAME" \ --db-user "$DB_USER" \ --sql "COPY sales FROM 's3://redshift-downloads/tickit/sales_tab.txt' DELIMITER '\t' TIMEFORMAT 'MM/DD/YYYY HH:MI:SS' REGION 'us-east-1' IAM_ROLE '$ROLE_ARN';" \ --query 'Id' --output text) echo "Sales data load statement ID: $SALES_LOAD_ID" # Wait for statement to complete if ! wait_for_statement "$SALES_LOAD_ID"; then handle_error "Failed to load data into sales table" fi echo "Loading data into date table" DATE_LOAD_ID=$(aws redshift-data execute-statement \ --cluster-identifier "$CLUSTER_ID" \ --database "$DB_NAME" \ --db-user "$DB_USER" \ --sql "COPY date FROM 's3://redshift-downloads/tickit/date2008_pipe.txt' DELIMITER '|' REGION 'us-east-1' IAM_ROLE '$ROLE_ARN';" \ --query 'Id' --output text) echo "Date data load statement ID: $DATE_LOAD_ID" # Wait for statement to complete if ! wait_for_statement "$DATE_LOAD_ID"; then handle_error "Failed to load data into date table" fi echo "=== Step 5: Running Example Queries ===" echo "Running query: Get definition for the sales table" QUERY1_ID=$(aws redshift-data execute-statement \ --cluster-identifier "$CLUSTER_ID" \ --database "$DB_NAME" \ --db-user "$DB_USER" \ --sql "SELECT * FROM pg_table_def WHERE tablename = 'sales';" \ --query 'Id' --output text) echo "Query 1 statement ID: $QUERY1_ID" # Wait for statement to complete if ! wait_for_statement "$QUERY1_ID"; then handle_error "Query 1 failed" fi # Get and display results echo "Query 1 results (first 10 rows):" aws redshift-data get-statement-result --id "$QUERY1_ID" --max-items 10 echo "Running query: Find total sales on a given calendar date" QUERY2_ID=$(aws redshift-data execute-statement \ --cluster-identifier "$CLUSTER_ID" \ --database "$DB_NAME" \ --db-user "$DB_USER" \ --sql "SELECT sum(qtysold) FROM sales, date WHERE sales.dateid = date.dateid AND caldate = '2008-01-05';" \ --query 'Id' --output text) echo "Query 2 statement ID: $QUERY2_ID" # Wait for statement to complete if ! wait_for_statement "$QUERY2_ID"; then handle_error "Query 2 failed" fi # Get and display results echo "Query 2 results:" aws redshift-data get-statement-result --id "$QUERY2_ID" echo "=== Tutorial Complete ===" echo "The following resources were created:" echo "- Redshift Cluster: $CLUSTER_ID" echo "- IAM Role: $ROLE_NAME" echo "" echo "===========================================" echo "CLEANUP CONFIRMATION" echo "===========================================" echo "Cleaning up all created resources..." cleanup_resources echo "All resources have been cleaned up." # Securely clear password from memory DB_PASSWORD="" echo "Script completed at $(date)"

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Siapkan izin IAM

  • Buat peran SageMaker eksekusi

  • Buat grup fitur

  • Pembersihan sumber daya

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash # Amazon SageMaker Feature Store Tutorial Script - Version 3 # This script demonstrates how to use Amazon SageMaker Feature Store with AWS CLI # Setup logging LOG_FILE="sagemaker-featurestore-tutorial.log" exec > >(tee -a "$LOG_FILE") 2>&1 echo "Starting SageMaker Feature Store tutorial script at $(date)" echo "All commands and outputs will be logged to $LOG_FILE" echo "" # Track created resources for cleanup CREATED_RESOURCES=() # Function to handle errors handle_error() { echo "ERROR: $1" echo "Attempting to clean up resources..." cleanup_resources exit 1 } # Function to check command status check_status() { if echo "$1" | grep -i "error" > /dev/null; then handle_error "$1" fi } # Function to wait for feature group to be created wait_for_feature_group() { local feature_group_name=$1 local status="Creating" echo "Waiting for feature group ${feature_group_name} to be created..." while [ "$status" = "Creating" ]; do sleep 5 status=$(aws sagemaker describe-feature-group \ --feature-group-name "${feature_group_name}" \ --query 'FeatureGroupStatus' \ --output text) echo "Current status: ${status}" if [ "$status" = "Failed" ]; then handle_error "Feature group ${feature_group_name} creation failed" fi done echo "Feature group ${feature_group_name} is now ${status}" } # Function to clean up resources cleanup_resources() { echo "Cleaning up resources..." # Clean up in reverse order for ((i=${#CREATED_RESOURCES[@]}-1; i>=0; i--)); do resource="${CREATED_RESOURCES[$i]}" resource_type=$(echo "$resource" | cut -d: -f1) resource_name=$(echo "$resource" | cut -d: -f2) echo "Deleting $resource_type: $resource_name" case "$resource_type" in "FeatureGroup") aws sagemaker delete-feature-group --feature-group-name "$resource_name" ;; "S3Bucket") echo "Emptying S3 bucket: $resource_name" aws s3 rm "s3://$resource_name" --recursive 2>/dev/null echo "Deleting S3 bucket: $resource_name" aws s3api delete-bucket --bucket "$resource_name" 2>/dev/null ;; "IAMRole") echo "Detaching policies from role: $resource_name" aws iam detach-role-policy --role-name "$resource_name" --policy-arn "arn:aws:iam::aws:policy/AmazonSageMakerFullAccess" 2>/dev/null aws iam detach-role-policy --role-name "$resource_name" --policy-arn "arn:aws:iam::aws:policy/AmazonS3FullAccess" 2>/dev/null echo "Deleting IAM role: $resource_name" aws iam delete-role --role-name "$resource_name" 2>/dev/null ;; *) echo "Unknown resource type: $resource_type" ;; esac done } # Function to create SageMaker execution role create_sagemaker_role() { local role_name="SageMakerFeatureStoreRole-$(openssl rand -hex 4)" echo "Creating SageMaker execution role: $role_name" >&2 # Create trust policy document local trust_policy='{ "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "sagemaker.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }' # Create the role local role_result=$(aws iam create-role \ --role-name "$role_name" \ --assume-role-policy-document "$trust_policy" \ --description "SageMaker execution role for Feature Store tutorial" 2>&1) if echo "$role_result" | grep -i "error" > /dev/null; then handle_error "Failed to create IAM role: $role_result" fi echo "Role created successfully" >&2 CREATED_RESOURCES+=("IAMRole:$role_name") # Tag the role echo "Tagging IAM role..." >&2 aws iam tag-role --role-name "$role_name" --tags Key=project,Value=doc-smith Key=tutorial,Value=sagemaker-featurestore 2>&1 # Attach necessary policies echo "Attaching policies to role..." >&2 # SageMaker execution policy local policy1_result=$(aws iam attach-role-policy \ --role-name "$role_name" \ --policy-arn "arn:aws:iam::aws:policy/AmazonSageMakerFullAccess" 2>&1) if echo "$policy1_result" | grep -i "error" > /dev/null; then handle_error "Failed to attach SageMaker policy: $policy1_result" fi # S3 access policy local policy2_result=$(aws iam attach-role-policy \ --role-name "$role_name" \ --policy-arn "arn:aws:iam::aws:policy/AmazonS3FullAccess" 2>&1) if echo "$policy2_result" | grep -i "error" > /dev/null; then handle_error "Failed to attach S3 policy: $policy2_result" fi # Get account ID for role ARN local account_id=$(aws sts get-caller-identity --query Account --output text) local role_arn="arn:aws:iam::${account_id}:role/${role_name}" echo "Role ARN: $role_arn" >&2 echo "Waiting 10 seconds for role to propagate..." >&2 sleep 10 # Return only the role ARN to stdout echo "$role_arn" } # Handle SageMaker execution role ROLE_ARN="" if [ -z "$1" ]; then echo "Creating SageMaker execution role automatically..." ROLE_ARN=$(create_sagemaker_role) if [ -z "$ROLE_ARN" ]; then handle_error "Failed to create SageMaker execution role" fi else ROLE_ARN="$1" # Validate the role ARN ROLE_NAME=$(echo "$ROLE_ARN" | sed 's/.*role\///') ROLE_CHECK=$(aws iam get-role --role-name "$ROLE_NAME" 2>&1) if echo "$ROLE_CHECK" | grep -i "error" > /dev/null; then echo "Creating a new role automatically..." ROLE_ARN=$(create_sagemaker_role) if [ -z "$ROLE_ARN" ]; then handle_error "Failed to create SageMaker execution role" fi fi fi # Handle cleanup option AUTO_CLEANUP="" if [ -n "$2" ]; then AUTO_CLEANUP="$2" fi # Generate a random identifier for resource names RANDOM_ID=$(openssl rand -hex 4) echo "Using random identifier: $RANDOM_ID" # Set variables ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) check_status "$ACCOUNT_ID" echo "Account ID: $ACCOUNT_ID" # Get current region REGION=$(aws configure get region) if [ -z "$REGION" ]; then REGION="us-east-1" echo "No default region configured, using: $REGION" else echo "Using region: $REGION" fi # 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="sagemaker-featurestore-${RANDOM_ID}-${ACCOUNT_ID}" fi PREFIX="featurestore-tutorial" CURRENT_TIME=$(date +%s) echo "Creating S3 bucket: $S3_BUCKET_NAME" # Create bucket in current region (skip if using shared bucket) if [ "$BUCKET_IS_SHARED" = "false" ]; then if [ "$REGION" = "us-east-1" ]; then BUCKET_RESULT=$(aws s3api create-bucket --bucket "$S3_BUCKET_NAME" \ --region "$REGION" 2>&1) else BUCKET_RESULT=$(aws s3api create-bucket --bucket "$S3_BUCKET_NAME" \ --region "$REGION" \ --create-bucket-configuration LocationConstraint="$REGION" 2>&1) fi if echo "$BUCKET_RESULT" | grep -i "error" > /dev/null; then echo "Failed to create S3 bucket: $BUCKET_RESULT" exit 1 fi echo "$BUCKET_RESULT" CREATED_RESOURCES+=("S3Bucket:$S3_BUCKET_NAME") # Tag the S3 bucket echo "Tagging S3 bucket: $S3_BUCKET_NAME" aws s3api put-bucket-tagging --bucket "$S3_BUCKET_NAME" --tagging 'TagSet=[{Key=project,Value=doc-smith},{Key=tutorial,Value=sagemaker-featurestore}]' 2>&1 # Block public access to the bucket BLOCK_RESULT=$(aws s3api put-public-access-block \ --bucket "$S3_BUCKET_NAME" \ --public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true" 2>&1) if echo "$BLOCK_RESULT" | grep -i "error" > /dev/null; then echo "Failed to block public access to S3 bucket: $BLOCK_RESULT" cleanup_resources exit 1 fi else echo "Using shared bucket (skipping creation)" fi # Create feature groups echo "Creating feature groups..." # Create customers feature group CUSTOMERS_FEATURE_GROUP_NAME="customers-feature-group-${RANDOM_ID}" echo "Creating customers feature group: $CUSTOMERS_FEATURE_GROUP_NAME" CUSTOMERS_RESPONSE=$(aws sagemaker create-feature-group \ --feature-group-name "$CUSTOMERS_FEATURE_GROUP_NAME" \ --record-identifier-feature-name "customer_id" \ --event-time-feature-name "EventTime" \ --feature-definitions '[ {"FeatureName": "customer_id", "FeatureType": "Integral"}, {"FeatureName": "name", "FeatureType": "String"}, {"FeatureName": "age", "FeatureType": "Integral"}, {"FeatureName": "address", "FeatureType": "String"}, {"FeatureName": "membership_type", "FeatureType": "String"}, {"FeatureName": "EventTime", "FeatureType": "Fractional"} ]' \ --online-store-config '{"EnableOnlineStore": true}' \ --offline-store-config '{ "S3StorageConfig": { "S3Uri": "s3://'${S3_BUCKET_NAME}'/'${PREFIX}'" }, "DisableGlueTableCreation": false }' \ --role-arn "$ROLE_ARN" \ --tags Key=project,Value=doc-smith Key=tutorial,Value=sagemaker-featurestore 2>&1) if echo "$CUSTOMERS_RESPONSE" | grep -i "error" > /dev/null; then echo "Failed to create customers feature group: $CUSTOMERS_RESPONSE" cleanup_resources exit 1 fi echo "$CUSTOMERS_RESPONSE" CREATED_RESOURCES+=("FeatureGroup:$CUSTOMERS_FEATURE_GROUP_NAME") # Create orders feature group ORDERS_FEATURE_GROUP_NAME="orders-feature-group-${RANDOM_ID}" echo "Creating orders feature group: $ORDERS_FEATURE_GROUP_NAME" ORDERS_RESPONSE=$(aws sagemaker create-feature-group \ --feature-group-name "$ORDERS_FEATURE_GROUP_NAME" \ --record-identifier-feature-name "customer_id" \ --event-time-feature-name "EventTime" \ --feature-definitions '[ {"FeatureName": "customer_id", "FeatureType": "Integral"}, {"FeatureName": "order_id", "FeatureType": "String"}, {"FeatureName": "order_date", "FeatureType": "String"}, {"FeatureName": "product", "FeatureType": "String"}, {"FeatureName": "quantity", "FeatureType": "Integral"}, {"FeatureName": "amount", "FeatureType": "Fractional"}, {"FeatureName": "EventTime", "FeatureType": "Fractional"} ]' \ --online-store-config '{"EnableOnlineStore": true}' \ --offline-store-config '{ "S3StorageConfig": { "S3Uri": "s3://'${S3_BUCKET_NAME}'/'${PREFIX}'" }, "DisableGlueTableCreation": false }' \ --role-arn "$ROLE_ARN" \ --tags Key=project,Value=doc-smith Key=tutorial,Value=sagemaker-featurestore 2>&1) if echo "$ORDERS_RESPONSE" | grep -i "error" > /dev/null; then echo "Failed to create orders feature group: $ORDERS_RESPONSE" cleanup_resources exit 1 fi echo "$ORDERS_RESPONSE" CREATED_RESOURCES+=("FeatureGroup:$ORDERS_FEATURE_GROUP_NAME") # Wait for feature groups to be created wait_for_feature_group "$CUSTOMERS_FEATURE_GROUP_NAME" wait_for_feature_group "$ORDERS_FEATURE_GROUP_NAME" # Ingest data into feature groups echo "Ingesting data into feature groups..." # Ingest customer data echo "Ingesting customer data..." CUSTOMER1_RESPONSE=$(aws sagemaker-featurestore-runtime put-record \ --feature-group-name "$CUSTOMERS_FEATURE_GROUP_NAME" \ --record '[ {"FeatureName": "customer_id", "ValueAsString": "573291"}, {"FeatureName": "name", "ValueAsString": "John Doe"}, {"FeatureName": "age", "ValueAsString": "35"}, {"FeatureName": "address", "ValueAsString": "123 Main St"}, {"FeatureName": "membership_type", "ValueAsString": "premium"}, {"FeatureName": "EventTime", "ValueAsString": "'${CURRENT_TIME}'"} ]' 2>&1) if echo "$CUSTOMER1_RESPONSE" | grep -i "error" > /dev/null; then echo "Failed to ingest customer 1 data: $CUSTOMER1_RESPONSE" cleanup_resources exit 1 fi echo "$CUSTOMER1_RESPONSE" CUSTOMER2_RESPONSE=$(aws sagemaker-featurestore-runtime put-record \ --feature-group-name "$CUSTOMERS_FEATURE_GROUP_NAME" \ --record '[ {"FeatureName": "customer_id", "ValueAsString": "109382"}, {"FeatureName": "name", "ValueAsString": "Jane Smith"}, {"FeatureName": "age", "ValueAsString": "28"}, {"FeatureName": "address", "ValueAsString": "456 Oak Ave"}, {"FeatureName": "membership_type", "ValueAsString": "standard"}, {"FeatureName": "EventTime", "ValueAsString": "'${CURRENT_TIME}'"} ]' 2>&1) if echo "$CUSTOMER2_RESPONSE" | grep -i "error" > /dev/null; then echo "Failed to ingest customer 2 data: $CUSTOMER2_RESPONSE" cleanup_resources exit 1 fi echo "$CUSTOMER2_RESPONSE" # Ingest order data echo "Ingesting order data..." ORDER1_RESPONSE=$(aws sagemaker-featurestore-runtime put-record \ --feature-group-name "$ORDERS_FEATURE_GROUP_NAME" \ --record '[ {"FeatureName": "customer_id", "ValueAsString": "573291"}, {"FeatureName": "order_id", "ValueAsString": "ORD-001"}, {"FeatureName": "order_date", "ValueAsString": "2023-01-15"}, {"FeatureName": "product", "ValueAsString": "Laptop"}, {"FeatureName": "quantity", "ValueAsString": "1"}, {"FeatureName": "amount", "ValueAsString": "1299.99"}, {"FeatureName": "EventTime", "ValueAsString": "'${CURRENT_TIME}'"} ]' 2>&1) if echo "$ORDER1_RESPONSE" | grep -i "error" > /dev/null; then echo "Failed to ingest order 1 data: $ORDER1_RESPONSE" cleanup_resources exit 1 fi echo "$ORDER1_RESPONSE" ORDER2_RESPONSE=$(aws sagemaker-featurestore-runtime put-record \ --feature-group-name "$ORDERS_FEATURE_GROUP_NAME" \ --record '[ {"FeatureName": "customer_id", "ValueAsString": "109382"}, {"FeatureName": "order_id", "ValueAsString": "ORD-002"}, {"FeatureName": "order_date", "ValueAsString": "2023-01-20"}, {"FeatureName": "product", "ValueAsString": "Smartphone"}, {"FeatureName": "quantity", "ValueAsString": "1"}, {"FeatureName": "amount", "ValueAsString": "899.99"}, {"FeatureName": "EventTime", "ValueAsString": "'${CURRENT_TIME}'"} ]' 2>&1) if echo "$ORDER2_RESPONSE" | grep -i "error" > /dev/null; then echo "Failed to ingest order 2 data: $ORDER2_RESPONSE" cleanup_resources exit 1 fi echo "$ORDER2_RESPONSE" # Retrieve records from feature groups echo "Retrieving records from feature groups..." # Get a single customer record echo "Getting customer record with ID 573291:" CUSTOMER_RECORD=$(aws sagemaker-featurestore-runtime get-record \ --feature-group-name "$CUSTOMERS_FEATURE_GROUP_NAME" \ --record-identifier-value-as-string "573291" 2>&1) if echo "$CUSTOMER_RECORD" | grep -i "error" > /dev/null; then echo "Failed to get customer record: $CUSTOMER_RECORD" cleanup_resources exit 1 fi echo "$CUSTOMER_RECORD" # Get multiple records using batch-get-record echo "Getting multiple records using batch-get-record:" BATCH_RECORDS=$(aws sagemaker-featurestore-runtime batch-get-record \ --identifiers '[ { "FeatureGroupName": "'${CUSTOMERS_FEATURE_GROUP_NAME}'", "RecordIdentifiersValueAsString": ["573291", "109382"] }, { "FeatureGroupName": "'${ORDERS_FEATURE_GROUP_NAME}'", "RecordIdentifiersValueAsString": ["573291", "109382"] } ]' 2>&1) if echo "$BATCH_RECORDS" | grep -i "error" > /dev/null && ! echo "$BATCH_RECORDS" | grep -i "Records" > /dev/null; then echo "Failed to get batch records: $BATCH_RECORDS" cleanup_resources exit 1 fi echo "$BATCH_RECORDS" # List feature groups echo "Listing feature groups:" FEATURE_GROUPS=$(aws sagemaker list-feature-groups 2>&1) if echo "$FEATURE_GROUPS" | grep -i "error" > /dev/null; then echo "Failed to list feature groups: $FEATURE_GROUPS" cleanup_resources exit 1 fi echo "$FEATURE_GROUPS" # Display summary of created resources echo "" echo "===========================================" echo "TUTORIAL COMPLETED SUCCESSFULLY!" echo "===========================================" echo "Resources created:" echo "- S3 Bucket: $S3_BUCKET_NAME" echo "- Customers Feature Group: $CUSTOMERS_FEATURE_GROUP_NAME" echo "- Orders Feature Group: $ORDERS_FEATURE_GROUP_NAME" if [[ " ${CREATED_RESOURCES[@]} " =~ " IAMRole:" ]]; then echo "- IAM Role: $(echo "${CREATED_RESOURCES[@]}" | grep -o 'IAMRole:[^[:space:]]*' | cut -d: -f2)" fi echo "" echo "You can now:" echo "1. View your feature groups in the SageMaker console" echo "2. Query the offline store using Amazon Athena" echo "3. Use the feature groups in your ML workflows" echo "===========================================" echo "" # Handle cleanup if [ "$AUTO_CLEANUP" = "y" ]; then echo "Auto-cleanup enabled. Starting cleanup..." cleanup_resources echo "Cleanup completed." elif [ "$AUTO_CLEANUP" = "n" ]; then echo "Auto-cleanup disabled. Resources will remain in your account." echo "To clean up later, run this script again with cleanup option 'y'" else 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..." cleanup_resources echo "Cleanup completed." else echo "Skipping cleanup. Resources will remain in your account." echo "To clean up later, delete the following resources:" echo "- Feature Groups: $CUSTOMERS_FEATURE_GROUP_NAME, $ORDERS_FEATURE_GROUP_NAME" echo "- S3 Bucket: $S3_BUCKET_NAME" if [[ " ${CREATED_RESOURCES[@]} " =~ " IAMRole:" ]]; then echo "- IAM Role: $(echo "${CREATED_RESOURCES[@]}" | grep -o 'IAMRole:[^[:space:]]*' | cut -d: -f2)" fi echo "" echo "Estimated ongoing cost: ~$0.01 per month for online store" fi fi echo "Script completed at $(date)"

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Buat bucket Amazon S3.

  • Membuat topik Amazon SNS

  • Buat peran IAM untuk Config

  • Siapkan perekam konfigurasi Config

  • Siapkan saluran pengiriman Config

  • Mulai perekam konfigurasi

  • Verifikasi pengaturan Config

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

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

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Membuat peran IAM untuk Step Functions

  • Buat mesin negara bagian pertama Anda

  • Mulai eksekusi mesin negara Anda

  • Pembersihan sumber daya

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash # AWS Step Functions Getting Started Tutorial Script # This script creates and runs a Step Functions state machine based on the AWS Step Functions Getting Started tutorial set -euo pipefail # Security: Restrict umask to prevent unintended file permissions umask 077 # Parse command line arguments AUTO_CLEANUP=true while [[ $# -gt 0 ]]; do case $1 in --auto-cleanup) AUTO_CLEANUP=true shift ;; -h|--help) echo "Usage: $0 [--auto-cleanup] [--help]" echo " --auto-cleanup: Automatically clean up resources without prompting" echo " --help: Show this help message" exit 0 ;; *) echo "Unknown option: $1" echo "Use --help for usage information" exit 1 ;; esac done # Set up logging with secure permissions LOG_FILE="step-functions-tutorial.log" touch "$LOG_FILE" chmod 600 "$LOG_FILE" # Security: Use process substitution with explicit FD cleanup exec 3>&1 4>&2 exec > >(tee -a "$LOG_FILE") 2>&1 trap 'exec 1>&3 2>&4 3>&- 4>&-' EXIT echo "Starting AWS Step Functions Getting Started Tutorial..." echo "Logging to $LOG_FILE" # Verify AWS CLI is installed and configured if ! command -v aws &> /dev/null; then echo "ERROR: AWS CLI is not installed" exit 1 fi # Verify AWS credentials are configured if ! aws sts get-caller-identity &> /dev/null; then echo "ERROR: AWS credentials are not configured or invalid" exit 1 fi # Check if jq is available for better JSON parsing if ! command -v jq &> /dev/null; then echo "WARNING: jq is not installed. Using basic JSON parsing which may be less reliable." echo "Consider installing jq for better error handling: brew install jq (macOS) or apt-get install jq (Ubuntu)" USE_JQ=false else USE_JQ=true fi # Use fixed region that supports Amazon Comprehend CURRENT_REGION="us-west-2" echo "Using fixed AWS region: $CURRENT_REGION (supports Amazon Comprehend)" # Set AWS CLI to use the fixed region for all commands export AWS_DEFAULT_REGION="$CURRENT_REGION" export AWS_REGION="$CURRENT_REGION" # Amazon Comprehend is available in us-west-2, so we can always enable it echo "Amazon Comprehend is available in region $CURRENT_REGION" SKIP_COMPREHEND=false # Security: Initialize all resource variables STATE_MACHINE_ARN="" ROLE_NAME="" ROLE_ARN="" POLICY_ARN="" STEPFUNCTIONS_POLICY_ARN="" EXECUTION_ARN="" EXECUTION2_ARN="" EXECUTION3_ARN="" # Performance: Cache for AWS API calls to reduce redundant requests declare -A API_CACHE # Function to make cached AWS CLI calls aws_call_cached() { local cache_key="$1" shift if [[ -v API_CACHE["$cache_key"] ]]; then echo "${API_CACHE[$cache_key]}" return 0 fi local result result=$(aws "$@" 2>&1) || return $? API_CACHE["$cache_key"]="$result" echo "$result" } # Function to check for API errors in JSON response with optimized jq usage check_api_error() { local response="$1" local operation="$2" if [[ "$USE_JQ" == "true" ]]; then # Use jq for more reliable JSON parsing with efficient error detection if echo "$response" | jq -e '.Error // .error // empty' > /dev/null 2>&1; then local error_message=$(echo "$response" | jq -r '.Error.Message // .Error.Code // .error // "Unknown error"' 2>/dev/null) handle_error "$operation failed: $error_message" fi else # Fallback to grep-based detection with optimized pattern if echo "$response" | grep -qE '"[Ee]rror":|"error":'; then handle_error "$operation failed: $response" fi fi } # Function to extract JSON field efficiently extract_json_field() { local json="$1" local field="$2" if [[ "$USE_JQ" == "true" ]]; then echo "$json" | jq -r "$field" 2>/dev/null else echo "$json" | grep -oP "\"${field}\":\s*\"\K[^\"]+|\"${field}\":\s*\K[^,}]+" | head -1 fi } # Function to securely wait for resource propagation with exponential backoff wait_for_propagation() { local resource_type="$1" local wait_time="${2:-10}" # Validate wait_time is a positive integer if ! [[ "$wait_time" =~ ^[0-9]+$ ]] || [ "$wait_time" -lt 1 ] || [ "$wait_time" -gt 300 ]; then echo "WARNING: Invalid wait time $wait_time, using default 10 seconds" wait_time=10 fi echo "Waiting for $resource_type to propagate ($wait_time seconds)..." sleep "$wait_time" } # Function to validate JSON file efficiently validate_json_file() { local file="$1" if [[ "$USE_JQ" == "true" ]]; then if ! jq empty "$file" 2>/dev/null; then handle_error "Invalid JSON in $file" fi fi } # Function to handle errors handle_error() { echo "ERROR: $1" echo "Resources created:" if [ -n "${STATE_MACHINE_ARN:-}" ]; then echo "- State Machine: $STATE_MACHINE_ARN" fi if [ -n "${ROLE_NAME:-}" ]; then echo "- IAM Role: $ROLE_NAME" fi if [ -n "${POLICY_ARN:-}" ]; then echo "- IAM Policy: $POLICY_ARN" fi if [ -n "${STEPFUNCTIONS_POLICY_ARN:-}" ]; then echo "- Step Functions Policy: $STEPFUNCTIONS_POLICY_ARN" fi echo "Attempting to clean up resources..." cleanup exit 1 } # Function to securely clean up resources with parallel deletion cleanup() { echo "Cleaning up resources..." # Delete state machine if it exists if [ -n "${STATE_MACHINE_ARN:-}" ]; then echo "Deleting state machine: $STATE_MACHINE_ARN" aws stepfunctions delete-state-machine --state-machine-arn "$STATE_MACHINE_ARN" 2>/dev/null & fi # Detach and delete policies if they exist if [ -n "${POLICY_ARN:-}" ] && [ -n "${ROLE_NAME:-}" ]; then echo "Detaching Comprehend policy $POLICY_ARN from role $ROLE_NAME" aws iam detach-role-policy --role-name "$ROLE_NAME" --policy-arn "$POLICY_ARN" 2>/dev/null & fi if [ -n "${STEPFUNCTIONS_POLICY_ARN:-}" ] && [ -n "${ROLE_NAME:-}" ]; then echo "Detaching Step Functions policy $STEPFUNCTIONS_POLICY_ARN from role $ROLE_NAME" aws iam detach-role-policy --role-name "$ROLE_NAME" --policy-arn "$STEPFUNCTIONS_POLICY_ARN" 2>/dev/null & fi # Wait for detach operations to complete wait 2>/dev/null || true # Delete custom policies if they exist if [ -n "${POLICY_ARN:-}" ]; then echo "Deleting Comprehend policy: $POLICY_ARN" aws iam delete-policy --policy-arn "$POLICY_ARN" 2>/dev/null & fi if [ -n "${STEPFUNCTIONS_POLICY_ARN:-}" ]; then echo "Deleting Step Functions policy: $STEPFUNCTIONS_POLICY_ARN" aws iam delete-policy --policy-arn "$STEPFUNCTIONS_POLICY_ARN" 2>/dev/null & fi # Wait for policy deletion to complete wait 2>/dev/null || true # Delete role if it exists if [ -n "${ROLE_NAME:-}" ]; then echo "Deleting role: $ROLE_NAME" aws iam delete-role --role-name "$ROLE_NAME" 2>/dev/null || echo "Failed to delete role" fi # Remove temporary files securely echo "Removing temporary files" local temp_files=( "hello-world.json" "updated-hello-world.json" "sentiment-hello-world.json" "step-functions-trust-policy.json" "comprehend-policy.json" "stepfunctions-policy.json" "input.json" "sentiment-input.json" ) for file in "${temp_files[@]}"; do if [ -f "$file" ]; then if command -v shred &> /dev/null; then shred -vfz -n 3 "$file" 2>/dev/null || rm -f "$file" else rm -f "$file" fi fi done } # Security: Set trap to cleanup on script exit trap cleanup EXIT # Generate a secure random identifier for resource names RANDOM_ID=$(openssl rand -hex 4) ROLE_NAME="StepFunctionsHelloWorldRole-${RANDOM_ID}" POLICY_NAME="DetectSentimentPolicy-${RANDOM_ID}" STATE_MACHINE_NAME="MyFirstStateMachine-${RANDOM_ID}" echo "Using random identifier: $RANDOM_ID" echo "Role name: $ROLE_NAME" echo "Policy name: $POLICY_NAME" echo "State machine name: $STATE_MACHINE_NAME" # Step 1: Create the state machine definition echo "Creating state machine definition..." cat > hello-world.json << 'EOF' { "Comment": "A Hello World example of the Amazon States Language using a Pass state", "StartAt": "SetVariables", "States": { "SetVariables": { "Type": "Pass", "Result": { "IsHelloWorldExample": true, "ExecutionWaitTimeInSeconds": 10 }, "Next": "IsHelloWorldExample" }, "IsHelloWorldExample": { "Type": "Choice", "Choices": [ { "Variable": "$.IsHelloWorldExample", "BooleanEquals": true, "Next": "WaitState" } ], "Default": "FailState" }, "WaitState": { "Type": "Wait", "SecondsPath": "$.ExecutionWaitTimeInSeconds", "Next": "ParallelProcessing" }, "ParallelProcessing": { "Type": "Parallel", "Branches": [ { "StartAt": "Process1", "States": { "Process1": { "Type": "Pass", "Result": { "message": "Processing task 1" }, "End": true } } }, { "StartAt": "Process2", "States": { "Process2": { "Type": "Pass", "Result": { "message": "Processing task 2" }, "End": true } } } ], "Next": "CheckpointState" }, "CheckpointState": { "Type": "Pass", "Result": { "CheckpointMessage": "Workflow completed successfully!" }, "Next": "SuccessState" }, "SuccessState": { "Type": "Succeed" }, "FailState": { "Type": "Fail", "Error": "NotHelloWorldExample", "Cause": "The IsHelloWorldExample value was false" } } } EOF # Create IAM role trust policy echo "Creating IAM role trust policy..." cat > step-functions-trust-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "states.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF # Create IAM role echo "Creating IAM role: $ROLE_NAME" ROLE_RESULT=$(aws iam create-role \ --role-name "$ROLE_NAME" \ --assume-role-policy-document file://step-functions-trust-policy.json 2>&1) check_api_error "$ROLE_RESULT" "Create IAM role" echo "Role created successfully" aws iam tag-role --role-name "$ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-step-functions-gs # Get the role ARN ROLE_ARN=$(extract_json_field "$ROLE_RESULT" ".Role.Arn") if [ -z "$ROLE_ARN" ]; then handle_error "Failed to extract role ARN" fi echo "Role ARN: $ROLE_ARN" # Create a custom policy for Step Functions with least privilege echo "Creating custom policy for Step Functions..." cat > stepfunctions-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "states:StartExecution", "states:DescribeExecution", "states:StopExecution" ], "Resource": "arn:aws:states:*:*:stateMachine:*" } ] } EOF # Create the policy echo "Creating Step Functions policy..." STEPFUNCTIONS_POLICY_RESULT=$(aws iam create-policy \ --policy-name "StepFunctionsPolicy-${RANDOM_ID}" \ --policy-document file://stepfunctions-policy.json 2>&1) check_api_error "$STEPFUNCTIONS_POLICY_RESULT" "Create Step Functions policy" echo "Step Functions policy created successfully" # Get the policy ARN STEPFUNCTIONS_POLICY_ARN=$(extract_json_field "$STEPFUNCTIONS_POLICY_RESULT" ".Policy.Arn") if [ -z "$STEPFUNCTIONS_POLICY_ARN" ]; then handle_error "Failed to extract Step Functions policy ARN" fi echo "Step Functions policy ARN: $STEPFUNCTIONS_POLICY_ARN" # Attach policy to the role echo "Attaching Step Functions policy to role..." ATTACH_RESULT=$(aws iam attach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "$STEPFUNCTIONS_POLICY_ARN" 2>&1) if [ $? -ne 0 ]; then handle_error "Failed to attach Step Functions policy to role: $ATTACH_RESULT" fi # Wait for role to propagate (IAM changes can take time to propagate) wait_for_propagation "IAM role" 8 # Create state machine echo "Creating state machine: $STATE_MACHINE_NAME" SM_RESULT=$(aws stepfunctions create-state-machine \ --name "$STATE_MACHINE_NAME" \ --definition file://hello-world.json \ --role-arn "$ROLE_ARN" \ --type STANDARD \ --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-step-functions-gs 2>&1) check_api_error "$SM_RESULT" "Create state machine" echo "State machine created successfully" # Get the state machine ARN STATE_MACHINE_ARN=$(extract_json_field "$SM_RESULT" ".stateMachineArn") if [ -z "$STATE_MACHINE_ARN" ]; then handle_error "Failed to extract state machine ARN" fi echo "State machine ARN: $STATE_MACHINE_ARN" # Step 2: Start the state machine execution echo "Starting state machine execution..." EXEC_RESULT=$(aws stepfunctions start-execution \ --state-machine-arn "$STATE_MACHINE_ARN" \ --name "hello001-${RANDOM_ID}" 2>&1) check_api_error "$EXEC_RESULT" "Start execution" echo "Execution started successfully" # Get the execution ARN EXECUTION_ARN=$(extract_json_field "$EXEC_RESULT" ".executionArn") if [ -z "$EXECUTION_ARN" ]; then handle_error "Failed to extract execution ARN" fi echo "Execution ARN: $EXECUTION_ARN" # Wait for execution to complete (the workflow has a 10-second wait state) echo "Waiting for execution to complete (12 seconds)..." sleep 12 # Check execution status echo "Checking execution status..." EXEC_STATUS=$(aws stepfunctions describe-execution \ --execution-arn "$EXECUTION_ARN" 2>&1) echo "Execution status: $EXEC_STATUS" # Step 3: Update state machine to process external input echo "Updating state machine to process external input..." cat > updated-hello-world.json << 'EOF' { "Comment": "A Hello World example of the Amazon States Language using a Pass state", "StartAt": "SetVariables", "States": { "SetVariables": { "Type": "Pass", "Parameters": { "IsHelloWorldExample.$": "$.hello_world", "ExecutionWaitTimeInSeconds.$": "$.wait" }, "Next": "IsHelloWorldExample" }, "IsHelloWorldExample": { "Type": "Choice", "Choices": [ { "Variable": "$.IsHelloWorldExample", "BooleanEquals": true, "Next": "WaitState" } ], "Default": "FailState" }, "WaitState": { "Type": "Wait", "SecondsPath": "$.ExecutionWaitTimeInSeconds", "Next": "ParallelProcessing" }, "ParallelProcessing": { "Type": "Parallel", "Branches": [ { "StartAt": "Process1", "States": { "Process1": { "Type": "Pass", "Result": { "message": "Processing task 1" }, "End": true } } }, { "StartAt": "Process2", "States": { "Process2": { "Type": "Pass", "Result": { "message": "Processing task 2" }, "End": true } } } ], "Next": "CheckpointState" }, "CheckpointState": { "Type": "Pass", "Result": { "CheckpointMessage": "Workflow completed successfully!" }, "Next": "SuccessState" }, "SuccessState": { "Type": "Succeed" }, "FailState": { "Type": "Fail", "Error": "NotHelloWorldExample", "Cause": "The IsHelloWorldExample value was false" } } } EOF # Update state machine echo "Updating state machine..." UPDATE_RESULT=$(aws stepfunctions update-state-machine \ --state-machine-arn "$STATE_MACHINE_ARN" \ --definition file://updated-hello-world.json \ --role-arn "$ROLE_ARN" 2>&1) check_api_error "$UPDATE_RESULT" "Update state machine" echo "State machine updated successfully" # Create input file with strict validation echo "Creating input file..." cat > input.json << 'EOF' { "wait": 5, "hello_world": true } EOF # Validate input JSON validate_json_file "input.json" # Start execution with input echo "Starting execution with input..." EXEC2_RESULT=$(aws stepfunctions start-execution \ --state-machine-arn "$STATE_MACHINE_ARN" \ --name "hello002-${RANDOM_ID}" \ --input file://input.json 2>&1) check_api_error "$EXEC2_RESULT" "Start execution with input" echo "Execution with input started successfully" # Get the execution ARN EXECUTION2_ARN=$(extract_json_field "$EXEC2_RESULT" ".executionArn") if [ -z "$EXECUTION2_ARN" ]; then handle_error "Failed to extract execution ARN" fi echo "Execution ARN: $EXECUTION2_ARN" # Wait for execution to complete (the workflow has a 5-second wait state) echo "Waiting for execution to complete (8 seconds)..." sleep 8 # Check execution status echo "Checking execution status..." EXEC2_STATUS=$(aws stepfunctions describe-execution \ --execution-arn "$EXECUTION2_ARN" 2>&1) echo "Execution status: $EXEC2_STATUS" # Step 4: Integrate Amazon Comprehend for sentiment analysis (if available) if [[ "$SKIP_COMPREHEND" == "false" ]]; then echo "Creating policy for Amazon Comprehend access with least privilege..." cat > comprehend-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "comprehend:DetectSentiment" ], "Resource": "*" } ] } EOF # Create policy echo "Creating IAM policy: $POLICY_NAME" POLICY_RESULT=$(aws iam create-policy \ --policy-name "$POLICY_NAME" \ --policy-document file://comprehend-policy.json 2>&1) check_api_error "$POLICY_RESULT" "Create Comprehend policy" echo "Comprehend policy created successfully" # Get policy ARN POLICY_ARN=$(extract_json_field "$POLICY_RESULT" ".Policy.Arn") if [ -z "$POLICY_ARN" ]; then handle_error "Failed to extract policy ARN" fi echo "Policy ARN: $POLICY_ARN" # Attach policy to role echo "Attaching policy to role..." ATTACH2_RESULT=$(aws iam attach-role-policy \ --role-name "$ROLE_NAME" \ --policy-arn "$POLICY_ARN" 2>&1) if [ $? -ne 0 ]; then handle_error "Failed to attach policy to role: $ATTACH2_RESULT" fi # Create updated state machine definition with sentiment analysis echo "Creating updated state machine definition with sentiment analysis..." cat > sentiment-hello-world.json << 'EOF' { "Comment": "A Hello World example with sentiment analysis", "StartAt": "SetVariables", "States": { "SetVariables": { "Type": "Pass", "Parameters": { "IsHelloWorldExample.$": "$.hello_world", "ExecutionWaitTimeInSeconds.$": "$.wait", "FeedbackComment.$": "$.feedback_comment" }, "Next": "IsHelloWorldExample" }, "IsHelloWorldExample": { "Type": "Choice", "Choices": [ { "Variable": "$.IsHelloWorldExample", "BooleanEquals": true, "Next": "WaitState" } ], "Default": "DetectSentiment" }, "WaitState": { "Type": "Wait", "SecondsPath": "$.ExecutionWaitTimeInSeconds", "Next": "ParallelProcessing" }, "ParallelProcessing": { "Type": "Parallel", "Branches": [ { "StartAt": "Process1", "States": { "Process1": { "Type": "Pass", "Result": { "message": "Processing task 1" }, "End": true } } }, { "StartAt": "Process2", "States": { "Process2": { "Type": "Pass", "Result": { "message": "Processing task 2" }, "End": true } } } ], "Next": "CheckpointState" }, "CheckpointState": { "Type": "Pass", "Result": { "CheckpointMessage": "Workflow completed successfully!" }, "Next": "SuccessState" }, "DetectSentiment": { "Type": "Task", "Resource": "arn:aws:states:::aws-sdk:comprehend:detectSentiment", "Parameters": { "LanguageCode": "en", "Text.$": "$.FeedbackComment" }, "Next": "SuccessState" }, "SuccessState": { "Type": "Succeed" } } } EOF # Validate sentiment state machine JSON validate_json_file "sentiment-hello-world.json" # Wait for IAM changes to propagate wait_for_propagation "IAM changes" 8 # Update state machine echo "Updating state machine with sentiment analysis..." UPDATE2_RESULT=$(aws stepfunctions update-state-machine \ --state-machine-arn "$STATE_MACHINE_ARN" \ --definition file://sentiment-hello-world.json \ --role-arn "$ROLE_ARN" 2>&1) check_api_error "$UPDATE2_RESULT" "Update state machine with sentiment analysis" echo "State machine updated with sentiment analysis successfully" # Create input file with feedback comment echo "Creating input file with feedback comment..." cat > sentiment-input.json << 'EOF' { "hello_world": false, "wait": 5, "feedback_comment": "This getting started with Step Functions workshop is a challenge!" } EOF # Validate sentiment input JSON validate_json_file "sentiment-input.json" # Start execution with sentiment analysis input echo "Starting execution with sentiment analysis input..." EXEC3_RESULT=$(aws stepfunctions start-execution \ --state-machine-arn "$STATE_MACHINE_ARN" \ --name "hello003-${RANDOM_ID}" \ --input file://sentiment-input.json 2>&1) check_api_error "$EXEC3_RESULT" "Start execution with sentiment analysis" echo "Execution with sentiment analysis started successfully" # Get the execution ARN EXECUTION3_ARN=$(extract_json_field "$EXEC3_RESULT" ".executionArn") if [ -z "$EXECUTION3_ARN" ]; then handle_error "Failed to extract execution ARN" fi echo "Execution ARN: $EXECUTION3_ARN" # Wait for execution to complete echo "Waiting for execution to complete (3 seconds)..." sleep 3 # Check execution status echo "Checking execution status..." EXEC3_STATUS=$(aws stepfunctions describe-execution \ --execution-arn "$EXECUTION3_ARN" 2>&1) echo "Execution status: $EXEC3_STATUS" else echo "Skipping Amazon Comprehend integration (not available in $CURRENT_REGION)" EXECUTION3_ARN="" fi # Display summary of resources created echo "" echo "===========================================" echo "RESOURCES CREATED" echo "===========================================" echo "State Machine: $STATE_MACHINE_ARN" echo "IAM Role: $ROLE_NAME" echo "Step Functions Policy: StepFunctionsPolicy-${RANDOM_ID} ($STEPFUNCTIONS_POLICY_ARN)" if [[ "$SKIP_COMPREHEND" == "false" ]]; then echo "Comprehend Policy: $POLICY_NAME ($POLICY_ARN)" fi echo "Executions:" echo " - hello001-${RANDOM_ID}: $EXECUTION_ARN" echo " - hello002-${RANDOM_ID}: $EXECUTION2_ARN" if [[ "$SKIP_COMPREHEND" == "false" ]]; then echo " - hello003-${RANDOM_ID}: $EXECUTION3_ARN" fi echo "===========================================" # Cleanup echo "" echo "===========================================" echo "CLEANUP" echo "===========================================" echo "Auto-cleanup enabled. Cleaning up resources..." echo "All resources have been cleaned up." echo "Script completed successfully!"

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Buat peran IAM

  • Buat rahasia di Secrets Manager

  • Perbarui kode aplikasi Anda

  • Perbarui rahasianya

  • Pembersihan sumber daya

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash # Script to move hardcoded secrets to AWS Secrets Manager # This script demonstrates how to create IAM roles, store a secret in AWS Secrets Manager, # and set up appropriate permissions set -euo pipefail # Set up logging LOG_FILE="secrets_manager_tutorial.log" exec > >(tee -a "$LOG_FILE") 2>&1 echo "Starting AWS Secrets Manager tutorial script at $(date)" echo "======================================================" # Function to check for errors in command output check_error() { local output=$1 local cmd=$2 if echo "$output" | grep -qi "error"; then echo "ERROR: Command failed: $cmd" echo "$output" cleanup_resources exit 1 fi } # Function to generate a random identifier using secure method generate_random_id() { python3 -c "import secrets; print('sm' + secrets.token_hex(4))" } # Function to safely clean up resources cleanup_resources() { echo "" echo "===========================================" echo "RESOURCES CREATED" echo "===========================================" if [ -n "${SECRET_NAME:-}" ]; then echo "Secret: $SECRET_NAME" fi if [ -n "${RUNTIME_ROLE_NAME:-}" ]; then echo "IAM Role: $RUNTIME_ROLE_NAME" fi if [ -n "${ADMIN_ROLE_NAME:-}" ]; then echo "IAM Role: $ADMIN_ROLE_NAME" fi echo "" echo "===========================================" echo "CLEANUP CONFIRMATION" echo "===========================================" echo "Cleaning up all created resources..." # Delete secret if it exists if [ -n "${SECRET_NAME:-}" ]; then echo "Deleting secret: $SECRET_NAME" aws secretsmanager delete-secret --secret-id "$SECRET_NAME" --force-delete-without-recovery 2>/dev/null || true fi # Detach policies and delete runtime role if it exists if [ -n "${RUNTIME_ROLE_NAME:-}" ]; then echo "Deleting inline policies from runtime role: $RUNTIME_ROLE_NAME" for policy in $(aws iam list-role-policies --role-name "$RUNTIME_ROLE_NAME" --query 'PolicyNames[]' --output text 2>/dev/null || true); do aws iam delete-role-policy --role-name "$RUNTIME_ROLE_NAME" --policy-name "$policy" 2>/dev/null || true done echo "Deleting IAM role: $RUNTIME_ROLE_NAME" aws iam delete-role --role-name "$RUNTIME_ROLE_NAME" 2>/dev/null || true fi # Detach policies and delete admin role if it exists if [ -n "${ADMIN_ROLE_NAME:-}" ]; then echo "Detaching policy from role: $ADMIN_ROLE_NAME" aws iam detach-role-policy --role-name "$ADMIN_ROLE_NAME" --policy-arn "arn:aws:iam::aws:policy/SecretsManagerReadWrite" 2>/dev/null || true for policy in $(aws iam list-role-policies --role-name "$ADMIN_ROLE_NAME" --query 'PolicyNames[]' --output text 2>/dev/null || true); do aws iam delete-role-policy --role-name "$ADMIN_ROLE_NAME" --policy-name "$policy" 2>/dev/null || true done echo "Deleting IAM role: $ADMIN_ROLE_NAME" aws iam delete-role --role-name "$ADMIN_ROLE_NAME" 2>/dev/null || true fi echo "Cleanup completed." } # Trap to ensure cleanup on script exit trap 'echo "Script interrupted. Running cleanup..."; cleanup_resources' INT TERM EXIT # Generate random identifiers for resources ADMIN_ROLE_NAME="SecretsManagerAdmin-$(generate_random_id)" RUNTIME_ROLE_NAME="RoleToRetrieveSecretAtRuntime-$(generate_random_id)" SECRET_NAME="MyAPIKey-$(generate_random_id)" echo "Using the following resource names:" echo "Admin Role: $ADMIN_ROLE_NAME" echo "Runtime Role: $RUNTIME_ROLE_NAME" echo "Secret Name: $SECRET_NAME" echo "" # Step 1: Create IAM roles echo "Creating IAM roles..." # Create assume role policy document ASSUME_ROLE_POLICY=$(cat <<'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF ) # Create the SecretsManagerAdmin role echo "Creating admin role: $ADMIN_ROLE_NAME" ADMIN_ROLE_OUTPUT=$(aws iam create-role \ --role-name "$ADMIN_ROLE_NAME" \ --assume-role-policy-document "$ASSUME_ROLE_POLICY" 2>&1) check_error "$ADMIN_ROLE_OUTPUT" "create-role for admin" echo "$ADMIN_ROLE_OUTPUT" aws iam tag-role --role-name "$ADMIN_ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-secrets-manager-gs # Attach the SecretsManagerReadWrite policy to the admin role echo "Attaching SecretsManagerReadWrite policy to admin role" ATTACH_POLICY_OUTPUT=$(aws iam attach-role-policy \ --role-name "$ADMIN_ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/SecretsManagerReadWrite" 2>&1) check_error "$ATTACH_POLICY_OUTPUT" "attach-role-policy for admin" echo "Policy attached successfully" # Create the RoleToRetrieveSecretAtRuntime role echo "Creating runtime role: $RUNTIME_ROLE_NAME" RUNTIME_ROLE_OUTPUT=$(aws iam create-role \ --role-name "$RUNTIME_ROLE_NAME" \ --assume-role-policy-document "$ASSUME_ROLE_POLICY" 2>&1) check_error "$RUNTIME_ROLE_OUTPUT" "create-role for runtime" echo "$RUNTIME_ROLE_OUTPUT" aws iam tag-role --role-name "$RUNTIME_ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-secrets-manager-gs # Wait for roles to be fully created echo "Waiting for IAM roles to be fully created..." sleep 10 # Step 2: Create a secret in AWS Secrets Manager echo "Creating secret in AWS Secrets Manager..." # Generate secure secret value using environment variable or secure method # WARNING: In production, use secure methods to inject secrets (AWS CodeBuild, parameter store, etc.) if [ -z "${TUTORIAL_SECRET_VALUE:-}" ]; then SECRET_VALUE=$(python3 -c "import json; print(json.dumps({'ClientID':'my_client_id','ClientSecret':__import__('secrets').token_urlsafe(32)}))") else SECRET_VALUE="$TUTORIAL_SECRET_VALUE" fi CREATE_SECRET_OUTPUT=$(aws secretsmanager create-secret \ --name "$SECRET_NAME" \ --description "API key for my application" \ --secret-string "$SECRET_VALUE" \ --add-replica-regions 'Region=us-east-1' \ --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-secrets-manager-gs 2>&1) check_error "$CREATE_SECRET_OUTPUT" "create-secret" echo "$CREATE_SECRET_OUTPUT" # Get AWS account ID echo "Getting AWS account ID..." ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text 2>&1) check_error "$ACCOUNT_ID" "get-caller-identity" echo "Account ID: $ACCOUNT_ID" # Get secret ARN for precise resource policy echo "Getting secret ARN..." SECRET_ARN=$(aws secretsmanager describe-secret \ --secret-id "$SECRET_NAME" \ --query 'ARN' \ --output text 2>&1) check_error "$SECRET_ARN" "describe-secret" # Add resource policy to the secret with least privilege echo "Adding resource policy to secret..." RESOURCE_POLICY=$(cat <<EOF { "Version":"2012-10-17", "Statement": [ { "Sid": "AllowRuntimeRoleReadSecret", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::$ACCOUNT_ID:role/$RUNTIME_ROLE_NAME" }, "Action": "secretsmanager:GetSecretValue", "Resource": "$SECRET_ARN", "Condition": { "StringEquals": { "secretsmanager:VersionStage": "AWSCURRENT" } } } ] } EOF ) PUT_POLICY_OUTPUT=$(aws secretsmanager put-resource-policy \ --secret-id "$SECRET_NAME" \ --resource-policy "$RESOURCE_POLICY" \ --block-public-policy 2>&1) check_error "$PUT_POLICY_OUTPUT" "put-resource-policy" echo "Resource policy added successfully" # Enable rotation policy recommendation echo "Enabling secret metadata tags for rotation tracking..." aws secretsmanager tag-resource \ --secret-id "$SECRET_NAME" \ --tags Key=Purpose,Value=Tutorial Key=AutoRotation,Value=Recommended 2>/dev/null || true # Step 3: Demonstrate retrieving the secret echo "Retrieving the secret value (for demonstration purposes)..." GET_SECRET_OUTPUT=$(aws secretsmanager get-secret-value \ --secret-id "$SECRET_NAME" 2>&1) check_error "$GET_SECRET_OUTPUT" "get-secret-value" echo "Secret retrieved successfully. Secret metadata:" echo "$GET_SECRET_OUTPUT" | jq '{ARN: .ARN, Name: .Name, LastUpdatedDate: .LastUpdatedDate, VersionIdsToStages: .VersionIdsToStages}' 2>/dev/null || echo "Secret metadata retrieved (jq not available)" # Step 4: Update the secret with new values echo "Updating the secret with new values..." UPDATE_SECRET_VALUE=$(python3 -c "import json; print(json.dumps({'ClientID':'my_new_client_id','ClientSecret':__import__('secrets').token_urlsafe(32)}))") UPDATE_SECRET_OUTPUT=$(aws secretsmanager update-secret \ --secret-id "$SECRET_NAME" \ --secret-string "$UPDATE_SECRET_VALUE" 2>&1) check_error "$UPDATE_SECRET_OUTPUT" "update-secret" echo "Secret updated successfully" # Step 5: Verify the updated secret echo "Verifying the updated secret..." VERIFY_SECRET_OUTPUT=$(aws secretsmanager get-secret-value \ --secret-id "$SECRET_NAME" 2>&1) check_error "$VERIFY_SECRET_OUTPUT" "get-secret-value for verification" echo "Updated secret retrieved successfully. Secret metadata:" echo "$VERIFY_SECRET_OUTPUT" | jq '{ARN: .ARN, Name: .Name, LastUpdatedDate: .LastUpdatedDate, VersionIdsToStages: .VersionIdsToStages}' 2>/dev/null || echo "Secret metadata retrieved (jq not available)" # Step 6: Display rotation recommendations echo "" echo "Rotation Configuration Recommendations:" echo "========================================" DESCRIBE_OUTPUT=$(aws secretsmanager describe-secret --secret-id "$SECRET_NAME" 2>&1) if echo "$DESCRIBE_OUTPUT" | grep -q "RotationRules"; then echo "Current rotation configuration:" echo "$DESCRIBE_OUTPUT" | jq '.RotationRules' 2>/dev/null || echo "Rotation rules available" else echo "No automatic rotation configured. Consider enabling rotation with:" echo "aws secretsmanager rotate-secret --secret-id $SECRET_NAME --rotation-lambda-arn arn:aws:lambda:REGION:ACCOUNT:function:FUNCTION_NAME --rotation-rules AutomaticallyAfterDays=30" fi echo "" echo "======================================================" echo "Tutorial completed successfully!" echo "" echo "Summary of what we did:" echo "1. Created IAM roles for managing and retrieving secrets" echo "2. Created a secret in AWS Secrets Manager with secure generation" echo "3. Added a least-privilege resource policy to control access to the secret" echo "4. Retrieved the secret value (simulating application access)" echo "5. Updated the secret with cryptographically secure values" echo "6. Verified the updated secret" echo "" echo "Security best practices applied:" echo "- Used cryptographically secure random ID generation" echo "- Applied least-privilege resource policies with version stages" echo "- Tagged resources for rotation tracking" echo "- Blocked public access to secrets" echo "- Used ARN-specific permissions instead of wildcards" echo "" echo "Next steps you might want to consider:" echo "- Enable automatic secret rotation with AWS Lambda" echo "- Implement secret caching in your application" echo "- Set up CloudTrail logging for secret access auditing" echo "- Use AWS CodeGuru Reviewer to find hardcoded secrets in your code" echo "- For multi-region applications, replicate your secrets across regions" echo "- Configure VPC endpoints for private access to Secrets Manager" echo "" echo "Script completed at $(date)" exit 0

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Buat peran IAM

  • Buat CloudWatch alarm

  • Buat template eksperimen

  • Jalankan percobaan

  • Verifikasi hasilnya

  • Pembersihan sumber daya

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash # AWS FIS CPU Stress Test Tutorial Script # This script automates the steps in the AWS FIS CPU stress test tutorial # approach using epoch time calculations that work across all Linux distributions # Set up logging LOG_FILE="fis-tutorial-$(date +%Y%m%d-%H%M%S).log" exec > >(tee -a "$LOG_FILE") 2>&1 echo "Starting AWS FIS CPU Stress Test Tutorial Script" echo "Logging to $LOG_FILE" echo "==============================================" # Function to check for errors in command output check_error() { local output=$1 local cmd=$2 if echo "$output" | grep -i "error" > /dev/null; then # Ignore specific expected errors if [[ "$cmd" == *"aws fis get-experiment"* ]] && [[ "$output" == *"ConfigurationFailure"* ]]; then echo "Note: Experiment failed due to configuration issue. This is expected in some cases." return 0 fi echo "ERROR: Command failed: $cmd" echo "Output: $output" cleanup_on_error exit 1 fi } # Function to clean up resources on error cleanup_on_error() { echo "Error encountered. Cleaning up resources..." if [ -n "$EXPERIMENT_ID" ]; then echo "Stopping experiment $EXPERIMENT_ID if running..." aws fis stop-experiment --id "$EXPERIMENT_ID" 2>/dev/null || true fi if [ -n "$TEMPLATE_ID" ]; then echo "Deleting experiment template $TEMPLATE_ID..." aws fis delete-experiment-template --id "$TEMPLATE_ID" || true fi if [ -n "$INSTANCE_ID" ]; then echo "Terminating EC2 instance $INSTANCE_ID..." aws ec2 terminate-instances --instance-ids "$INSTANCE_ID" || true fi if [ -n "$ALARM_NAME" ]; then echo "Deleting CloudWatch alarm $ALARM_NAME..." aws cloudwatch delete-alarms --alarm-names "$ALARM_NAME" || true fi if [ -n "$INSTANCE_PROFILE_NAME" ]; then echo "Removing role from instance profile..." aws iam remove-role-from-instance-profile --instance-profile-name "$INSTANCE_PROFILE_NAME" --role-name "$EC2_ROLE_NAME" || true echo "Deleting instance profile..." aws iam delete-instance-profile --instance-profile-name "$INSTANCE_PROFILE_NAME" || true fi if [ -n "$FIS_ROLE_NAME" ]; then echo "Deleting FIS role policy..." aws iam delete-role-policy --role-name "$FIS_ROLE_NAME" --policy-name "$FIS_POLICY_NAME" || true echo "Deleting FIS role..." aws iam delete-role --role-name "$FIS_ROLE_NAME" || true fi if [ -n "$EC2_ROLE_NAME" ]; then echo "Detaching policy from EC2 role..." aws iam detach-role-policy --role-name "$EC2_ROLE_NAME" --policy-arn "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" || true echo "Deleting EC2 role..." aws iam delete-role --role-name "$EC2_ROLE_NAME" || true fi echo "Cleanup completed." } # Generate unique identifiers for resources TIMESTAMP=$(date +%Y%m%d%H%M%S) FIS_ROLE_NAME="FISRole-${TIMESTAMP}" FIS_POLICY_NAME="FISPolicy-${TIMESTAMP}" EC2_ROLE_NAME="EC2SSMRole-${TIMESTAMP}" INSTANCE_PROFILE_NAME="EC2SSMProfile-${TIMESTAMP}" ALARM_NAME="FIS-CPU-Alarm-${TIMESTAMP}" # Track created resources CREATED_RESOURCES=() echo "Step 1: Creating IAM role for AWS FIS" # Create trust policy file for AWS FIS cat > fis-trust-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "fis.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF # Create IAM role for FIS echo "Creating IAM role $FIS_ROLE_NAME for AWS FIS..." FIS_ROLE_OUTPUT=$(aws iam create-role \ --role-name "$FIS_ROLE_NAME" \ --assume-role-policy-document file://fis-trust-policy.json) check_error "$FIS_ROLE_OUTPUT" "aws iam create-role" aws iam tag-role --role-name "$FIS_ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-fault-injection-service-gs CREATED_RESOURCES+=("IAM Role: $FIS_ROLE_NAME") # Create policy document for SSM actions cat > fis-ssm-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ssm:SendCommand", "ssm:ListCommands", "ssm:ListCommandInvocations" ], "Resource": "*" } ] } EOF # Attach policy to the role echo "Attaching policy $FIS_POLICY_NAME to role $FIS_ROLE_NAME..." FIS_POLICY_OUTPUT=$(aws iam put-role-policy \ --role-name "$FIS_ROLE_NAME" \ --policy-name "$FIS_POLICY_NAME" \ --policy-document file://fis-ssm-policy.json) check_error "$FIS_POLICY_OUTPUT" "aws iam put-role-policy" CREATED_RESOURCES+=("IAM Policy: $FIS_POLICY_NAME attached to $FIS_ROLE_NAME") echo "Step 2: Creating IAM role for EC2 instance with SSM permissions" # Create trust policy file for EC2 cat > ec2-trust-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF # Create IAM role for EC2 echo "Creating IAM role $EC2_ROLE_NAME for EC2 instance..." EC2_ROLE_OUTPUT=$(aws iam create-role \ --role-name "$EC2_ROLE_NAME" \ --assume-role-policy-document file://ec2-trust-policy.json) check_error "$EC2_ROLE_OUTPUT" "aws iam create-role" aws iam tag-role --role-name "$EC2_ROLE_NAME" --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-fault-injection-service-gs CREATED_RESOURCES+=("IAM Role: $EC2_ROLE_NAME") # Attach SSM policy to the EC2 role echo "Attaching AmazonSSMManagedInstanceCore policy to role $EC2_ROLE_NAME..." EC2_POLICY_OUTPUT=$(aws iam attach-role-policy \ --role-name "$EC2_ROLE_NAME" \ --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore) check_error "$EC2_POLICY_OUTPUT" "aws iam attach-role-policy" CREATED_RESOURCES+=("IAM Policy: AmazonSSMManagedInstanceCore attached to $EC2_ROLE_NAME") # Create instance profile echo "Creating instance profile $INSTANCE_PROFILE_NAME..." PROFILE_OUTPUT=$(aws iam create-instance-profile \ --instance-profile-name "$INSTANCE_PROFILE_NAME") check_error "$PROFILE_OUTPUT" "aws iam create-instance-profile" CREATED_RESOURCES+=("IAM Instance Profile: $INSTANCE_PROFILE_NAME") # Add role to instance profile echo "Adding role $EC2_ROLE_NAME to instance profile $INSTANCE_PROFILE_NAME..." ADD_ROLE_OUTPUT=$(aws iam add-role-to-instance-profile \ --instance-profile-name "$INSTANCE_PROFILE_NAME" \ --role-name "$EC2_ROLE_NAME") check_error "$ADD_ROLE_OUTPUT" "aws iam add-role-to-instance-profile" # Wait for role to propagate echo "Waiting for IAM role to propagate..." sleep 10 echo "Step 3: Launching EC2 instance" # Get the latest Amazon Linux 2 AMI ID echo "Finding latest Amazon Linux 2 AMI..." AMI_ID=$(aws ec2 describe-images \ --owners amazon \ --filters "Name=name,Values=amzn2-ami-hvm-*-x86_64-gp2" "Name=state,Values=available" \ --query "sort_by(Images, &CreationDate)[-1].ImageId" \ --output text) check_error "$AMI_ID" "aws ec2 describe-images" echo "Using AMI: $AMI_ID" # Launch EC2 instance echo "Launching EC2 instance with AMI $AMI_ID..." INSTANCE_OUTPUT=$(aws ec2 run-instances \ --image-id "$AMI_ID" \ --instance-type t2.micro \ --iam-instance-profile Name="$INSTANCE_PROFILE_NAME" \ --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=FIS-Test-Instance},{Key=project,Value=doc-smith},{Key=tutorial,Value=aws-fault-injection-service-gs}]') check_error "$INSTANCE_OUTPUT" "aws ec2 run-instances" # Get instance ID INSTANCE_ID=$(echo "$INSTANCE_OUTPUT" | grep -i "InstanceId" | head -1 | awk -F'"' '{print $4}') if [ -z "$INSTANCE_ID" ]; then echo "Failed to get instance ID" cleanup_on_error exit 1 fi echo "Launched instance: $INSTANCE_ID" CREATED_RESOURCES+=("EC2 Instance: $INSTANCE_ID") # Enable detailed monitoring echo "Enabling detailed monitoring for instance $INSTANCE_ID..." MONITOR_OUTPUT=$(aws ec2 monitor-instances --instance-ids "$INSTANCE_ID") check_error "$MONITOR_OUTPUT" "aws ec2 monitor-instances" # Wait for instance to be running and status checks to pass echo "Waiting for instance to be ready..." aws ec2 wait instance-running --instance-ids "$INSTANCE_ID" aws ec2 wait instance-status-ok --instance-ids "$INSTANCE_ID" echo "Instance is ready" echo "Step 4: Creating CloudWatch alarm for CPU utilization" # Create CloudWatch alarm echo "Creating CloudWatch alarm $ALARM_NAME..." ALARM_OUTPUT=$(aws cloudwatch put-metric-alarm \ --alarm-name "$ALARM_NAME" \ --alarm-description "Alarm when CPU exceeds 50%" \ --metric-name CPUUtilization \ --namespace AWS/EC2 \ --statistic Maximum \ --period 60 \ --threshold 50 \ --comparison-operator GreaterThanOrEqualToThreshold \ --dimensions "Name=InstanceId,Value=$INSTANCE_ID" \ --evaluation-periods 1) check_error "$ALARM_OUTPUT" "aws cloudwatch put-metric-alarm" CREATED_RESOURCES+=("CloudWatch Alarm: $ALARM_NAME") # Get the alarm ARN echo "Getting CloudWatch alarm ARN..." ALARM_ARN_OUTPUT=$(aws cloudwatch describe-alarms \ --alarm-names "$ALARM_NAME") check_error "$ALARM_ARN_OUTPUT" "aws cloudwatch describe-alarms" ALARM_ARN=$(echo "$ALARM_ARN_OUTPUT" | grep -i "AlarmArn" | head -1 | awk -F'"' '{print $4}') if [ -z "$ALARM_ARN" ]; then echo "Failed to get alarm ARN" cleanup_on_error exit 1 fi echo "Alarm ARN: $ALARM_ARN" # Wait for the alarm to initialize and reach OK state echo "Waiting for CloudWatch alarm to initialize (60 seconds)..." sleep 60 # Check alarm state echo "Checking alarm state..." ALARM_STATE_OUTPUT=$(aws cloudwatch describe-alarms \ --alarm-names "$ALARM_NAME") ALARM_STATE=$(echo "$ALARM_STATE_OUTPUT" | grep -i "StateValue" | head -1 | awk -F'"' '{print $4}') echo "Current alarm state: $ALARM_STATE" # If alarm is not in OK state, wait longer or generate some baseline metrics if [ "$ALARM_STATE" != "OK" ]; then echo "Alarm not in OK state. Waiting for alarm to stabilize (additional 60 seconds)..." sleep 60 # Check alarm state again ALARM_STATE_OUTPUT=$(aws cloudwatch describe-alarms \ --alarm-names "$ALARM_NAME") ALARM_STATE=$(echo "$ALARM_STATE_OUTPUT" | grep -i "StateValue" | head -1 | awk -F'"' '{print $4}') echo "Updated alarm state: $ALARM_STATE" if [ "$ALARM_STATE" != "OK" ]; then echo "Warning: Alarm still not in OK state. Experiment may fail to start." fi fi echo "Step 5: Creating AWS FIS experiment template" # Get the IAM role ARN echo "Getting IAM role ARN for $FIS_ROLE_NAME..." ROLE_ARN_OUTPUT=$(aws iam get-role \ --role-name "$FIS_ROLE_NAME") check_error "$ROLE_ARN_OUTPUT" "aws iam get-role" ROLE_ARN=$(echo "$ROLE_ARN_OUTPUT" | grep -i "Arn" | head -1 | awk -F'"' '{print $4}') if [ -z "$ROLE_ARN" ]; then echo "Failed to get role ARN" cleanup_on_error exit 1 fi echo "Role ARN: $ROLE_ARN" # Get account ID and region ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) REGION=$(aws configure get region) if [ -z "$REGION" ]; then REGION="us-east-1" # Default to us-east-1 if region not set fi INSTANCE_ARN="arn:aws:ec2:${REGION}:${ACCOUNT_ID}:instance/${INSTANCE_ID}" echo "Instance ARN: $INSTANCE_ARN" # Create experiment template - Fixed JSON escaping issue cat > experiment-template.json << EOF { "description": "Test CPU stress predefined SSM document", "targets": { "testInstance": { "resourceType": "aws:ec2:instance", "resourceArns": ["$INSTANCE_ARN"], "selectionMode": "ALL" } }, "actions": { "runCpuStress": { "actionId": "aws:ssm:send-command", "parameters": { "documentArn": "arn:aws:ssm:$REGION::document/AWSFIS-Run-CPU-Stress", "documentParameters": "{\"DurationSeconds\":\"120\"}", "duration": "PT5M" }, "targets": { "Instances": "testInstance" } } }, "stopConditions": [ { "source": "aws:cloudwatch:alarm", "value": "$ALARM_ARN" } ], "roleArn": "$ROLE_ARN", "tags": { "Name": "FIS-CPU-Stress-Experiment", "project": "doc-smith", "tutorial": "aws-fault-injection-service-gs" } } EOF # Create experiment template echo "Creating AWS FIS experiment template..." TEMPLATE_OUTPUT=$(aws fis create-experiment-template --cli-input-json file://experiment-template.json) check_error "$TEMPLATE_OUTPUT" "aws fis create-experiment-template" TEMPLATE_ID=$(echo "$TEMPLATE_OUTPUT" | grep -i "id" | head -1 | awk -F'"' '{print $4}') if [ -z "$TEMPLATE_ID" ]; then echo "Failed to get template ID" cleanup_on_error exit 1 fi echo "Experiment template created with ID: $TEMPLATE_ID" CREATED_RESOURCES+=("FIS Experiment Template: $TEMPLATE_ID") echo "Step 6: Starting the experiment" # Start the experiment echo "Starting AWS FIS experiment using template $TEMPLATE_ID..." EXPERIMENT_OUTPUT=$(aws fis start-experiment \ --experiment-template-id "$TEMPLATE_ID" \ --tags '{"Name": "FIS-CPU-Stress-Run"}') check_error "$EXPERIMENT_OUTPUT" "aws fis start-experiment" EXPERIMENT_ID=$(echo "$EXPERIMENT_OUTPUT" | grep -i "id" | head -1 | awk -F'"' '{print $4}') if [ -z "$EXPERIMENT_ID" ]; then echo "Failed to get experiment ID" cleanup_on_error exit 1 fi echo "Experiment started with ID: $EXPERIMENT_ID" CREATED_RESOURCES+=("FIS Experiment: $EXPERIMENT_ID") echo "Step 7: Tracking experiment progress" # Track experiment progress echo "Tracking experiment progress..." MAX_CHECKS=30 CHECK_COUNT=0 EXPERIMENT_STATE="" while [ $CHECK_COUNT -lt $MAX_CHECKS ]; do EXPERIMENT_INFO=$(aws fis get-experiment --id "$EXPERIMENT_ID") # Don't check for errors here, as we expect some experiments to fail EXPERIMENT_STATE=$(echo "$EXPERIMENT_INFO" | grep -i "status" | head -1 | awk -F'"' '{print $4}') echo "Experiment state: $EXPERIMENT_STATE" if [ "$EXPERIMENT_STATE" == "completed" ] || [ "$EXPERIMENT_STATE" == "stopped" ] || [ "$EXPERIMENT_STATE" == "failed" ]; then # Show the reason for the state REASON=$(echo "$EXPERIMENT_INFO" | grep -i "reason" | head -1 | awk -F'"' '{print $4}') if [ -n "$REASON" ]; then echo "Reason: $REASON" fi break fi echo "Waiting 10 seconds before checking again..." sleep 10 CHECK_COUNT=$((CHECK_COUNT + 1)) done if [ $CHECK_COUNT -eq $MAX_CHECKS ]; then echo "Experiment is taking longer than expected. You can check its status later using:" echo "aws fis get-experiment --id $EXPERIMENT_ID" fi echo "Step 8: Verifying experiment results" # Check CloudWatch alarm state echo "Checking CloudWatch alarm state..." ALARM_STATE_OUTPUT=$(aws cloudwatch describe-alarms --alarm-names "$ALARM_NAME") check_error "$ALARM_STATE_OUTPUT" "aws cloudwatch describe-alarms" echo "$ALARM_STATE_OUTPUT" # Get CPU utilization metrics echo "Getting CPU utilization metrics..." END_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") # FIXED: Cross-platform compatible way to calculate time 10 minutes ago # This approach uses epoch seconds and basic arithmetic which works on all Linux distributions CURRENT_EPOCH=$(date +%s) TEN_MINUTES_AGO_EPOCH=$((CURRENT_EPOCH - 600)) START_TIME=$(date -u -d "@$TEN_MINUTES_AGO_EPOCH" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date -u -r "$TEN_MINUTES_AGO_EPOCH" +"%Y-%m-%dT%H:%M:%SZ") # Create metric query file cat > metric-query.json << EOF [ { "Id": "cpu", "MetricStat": { "Metric": { "Namespace": "AWS/EC2", "MetricName": "CPUUtilization", "Dimensions": [ { "Name": "InstanceId", "Value": "$INSTANCE_ID" } ] }, "Period": 60, "Stat": "Maximum" } } ] EOF METRICS_OUTPUT=$(aws cloudwatch get-metric-data \ --start-time "$START_TIME" \ --end-time "$END_TIME" \ --metric-data-queries file://metric-query.json) check_error "$METRICS_OUTPUT" "aws cloudwatch get-metric-data" echo "CPU Utilization Metrics:" echo "$METRICS_OUTPUT" # Display summary of created resources echo "" echo "===========================================" echo "RESOURCES CREATED" echo "===========================================" for resource in "${CREATED_RESOURCES[@]}"; do echo "- $resource" done echo "===========================================" # Prompt for cleanup 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 "Starting cleanup process..." # Stop experiment if still running if [ "$EXPERIMENT_STATE" != "completed" ] && [ "$EXPERIMENT_STATE" != "stopped" ] && [ "$EXPERIMENT_STATE" != "failed" ]; then echo "Stopping experiment $EXPERIMENT_ID..." STOP_OUTPUT=$(aws fis stop-experiment --id "$EXPERIMENT_ID") check_error "$STOP_OUTPUT" "aws fis stop-experiment" echo "Waiting for experiment to stop..." sleep 10 fi # Delete experiment template echo "Deleting experiment template $TEMPLATE_ID..." DELETE_TEMPLATE_OUTPUT=$(aws fis delete-experiment-template --id "$TEMPLATE_ID") check_error "$DELETE_TEMPLATE_OUTPUT" "aws fis delete-experiment-template" # Delete CloudWatch alarm echo "Deleting CloudWatch alarm $ALARM_NAME..." DELETE_ALARM_OUTPUT=$(aws cloudwatch delete-alarms --alarm-names "$ALARM_NAME") check_error "$DELETE_ALARM_OUTPUT" "aws cloudwatch delete-alarms" # Terminate EC2 instance echo "Terminating EC2 instance $INSTANCE_ID..." TERMINATE_OUTPUT=$(aws ec2 terminate-instances --instance-ids "$INSTANCE_ID") check_error "$TERMINATE_OUTPUT" "aws ec2 terminate-instances" echo "Waiting for instance to terminate..." aws ec2 wait instance-terminated --instance-ids "$INSTANCE_ID" # Clean up IAM resources echo "Removing role from instance profile..." REMOVE_ROLE_OUTPUT=$(aws iam remove-role-from-instance-profile \ --instance-profile-name "$INSTANCE_PROFILE_NAME" \ --role-name "$EC2_ROLE_NAME") check_error "$REMOVE_ROLE_OUTPUT" "aws iam remove-role-from-instance-profile" echo "Deleting instance profile..." DELETE_PROFILE_OUTPUT=$(aws iam delete-instance-profile \ --instance-profile-name "$INSTANCE_PROFILE_NAME") check_error "$DELETE_PROFILE_OUTPUT" "aws iam delete-instance-profile" echo "Deleting FIS role policy..." DELETE_POLICY_OUTPUT=$(aws iam delete-role-policy \ --role-name "$FIS_ROLE_NAME" \ --policy-name "$FIS_POLICY_NAME") check_error "$DELETE_POLICY_OUTPUT" "aws iam delete-role-policy" echo "Detaching policy from EC2 role..." DETACH_POLICY_OUTPUT=$(aws iam detach-role-policy \ --role-name "$EC2_ROLE_NAME" \ --policy-arn "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore") check_error "$DETACH_POLICY_OUTPUT" "aws iam detach-role-policy" echo "Deleting FIS role..." DELETE_FIS_ROLE_OUTPUT=$(aws iam delete-role \ --role-name "$FIS_ROLE_NAME") check_error "$DELETE_FIS_ROLE_OUTPUT" "aws iam delete-role" echo "Deleting EC2 role..." DELETE_EC2_ROLE_OUTPUT=$(aws iam delete-role \ --role-name "$EC2_ROLE_NAME") check_error "$DELETE_EC2_ROLE_OUTPUT" "aws iam delete-role" # Clean up temporary files echo "Cleaning up temporary files..." rm -f fis-trust-policy.json ec2-trust-policy.json fis-ssm-policy.json experiment-template.json metric-query.json echo "Cleanup completed successfully." else echo "Cleanup skipped. Resources will remain in your AWS account." echo "You can manually clean up the resources listed above." fi echo "" echo "Script execution completed." echo "Log file: $LOG_FILE"

Contoh kode berikut menunjukkan bagaimana menerapkan Attribute-Based Access Control (ABAC) untuk DynamoDB.

  • Buat kebijakan IAM untuk ABAC.

  • Buat tabel dengan tag untuk departemen yang berbeda.

  • Daftar dan filter tabel berdasarkan tag.

AWS CLI dengan skrip Bash

Buat kebijakan IAM untuk ABAC.

# Step 1: Create a policy document for ABAC cat > abac-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:GetItem", "dynamodb:BatchGetItem", "dynamodb:Query", "dynamodb:Scan" ], "Resource": "arn:aws:dynamodb:*:*:table/*", "Condition": { "StringEquals": { "aws:ResourceTag/Department": "${aws:PrincipalTag/Department}" } } }, { "Effect": "Allow", "Action": [ "dynamodb:PutItem", "dynamodb:UpdateItem", "dynamodb:DeleteItem", "dynamodb:BatchWriteItem" ], "Resource": "arn:aws:dynamodb:*:*:table/*", "Condition": { "StringEquals": { "aws:ResourceTag/Department": "${aws:PrincipalTag/Department}", "aws:ResourceTag/Environment": "Development" } } } ] } EOF # Step 2: Create the IAM policy aws iam create-policy \ --policy-name DynamoDBDepartmentBasedAccess \ --policy-document file://abac-policy.json

Buat tabel dengan tag untuk departemen yang berbeda.

# Create a DynamoDB table with tags for ABAC aws dynamodb create-table \ --table-name FinanceData \ --attribute-definitions \ AttributeName=RecordID,AttributeType=S \ --key-schema \ AttributeName=RecordID,KeyType=HASH \ --billing-mode PAY_PER_REQUEST \ --tags \ Key=Department,Value=Finance \ Key=Environment,Value=Development # Create another table with different tags aws dynamodb create-table \ --table-name MarketingData \ --attribute-definitions \ AttributeName=RecordID,AttributeType=S \ --key-schema \ AttributeName=RecordID,KeyType=HASH \ --billing-mode PAY_PER_REQUEST \ --tags \ Key=Department,Value=Marketing \ Key=Environment,Value=Production

Daftar dan filter tabel berdasarkan tag.

# List all DynamoDB tables echo "Listing all tables:" aws dynamodb list-tables # Get ARNs for all tables echo -e "\nGetting ARNs for all tables:" TABLE_ARNS=$(aws dynamodb list-tables --query "TableNames[*]" --output text | xargs -I {} aws dynamodb describe-table --table-name {} --query "Table.TableArn" --output text) # For each table ARN, list its tags echo -e "\nListing tags for each table:" for ARN in $TABLE_ARNS; do TABLE_NAME=$(echo $ARN | awk -F/ '{print $2}') echo -e "\nTags for table: $TABLE_NAME" aws dynamodb list-tags-of-resource --resource-arn $ARN done # Example: Find tables with a specific tag echo -e "\nFinding tables with Environment=Production tag:" for ARN in $TABLE_ARNS; do TABLE_NAME=$(echo $ARN | awk -F/ '{print $2}') TAGS=$(aws dynamodb list-tags-of-resource --resource-arn $ARN --query "Tags[?Key=='Environment' && Value=='Production']" --output text) if [ ! -z "$TAGS" ]; then echo "Table with Production tag: $TABLE_NAME" fi done

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Buat izin IAM untuk Systems Manager

  • Membuat peran IAM untuk Systems Manager

  • Konfigurasikan Systems Manager

  • Verifikasi pengaturan

  • Pembersihan sumber daya

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash # AWS Systems Manager Setup Script # This script sets up AWS Systems Manager for a single account and region # # Version 17 fixes: # 1. Added cloudformation.amazonaws.com to the IAM role trust policy # 2. Systems Manager Quick Setup uses CloudFormation for deployments, so the role must trust CloudFormation service # Initialize log file LOG_FILE="ssm_setup_$(date +%Y%m%d_%H%M%S).log" UNIQUE_ID=$(openssl rand -hex 4) echo "Starting AWS Systems Manager setup at $(date)" > "$LOG_FILE" # Function to log commands and their outputs with immediate terminal display log_cmd() { echo "$(date): Running command: $1" | tee -a "$LOG_FILE" local output output=$(eval "$1" 2>&1) local status=$? echo "$output" | tee -a "$LOG_FILE" return $status } # Function to check for errors in command output check_error() { local cmd_output="$1" local cmd_status="$2" local error_msg="$3" if [[ $cmd_status -ne 0 || "$cmd_output" =~ [Ee][Rr][Rr][Oo][Rr] ]]; then echo "ERROR: $error_msg" | tee -a "$LOG_FILE" echo "Command output: $cmd_output" | tee -a "$LOG_FILE" cleanup_on_error exit 1 fi } # Array to track created resources for cleanup declare -a CREATED_RESOURCES # Function to add a resource to the tracking array track_resource() { local resource_type="$1" local resource_id="$2" CREATED_RESOURCES+=("$resource_type:$resource_id") echo "Tracked resource: $resource_type:$resource_id" | tee -a "$LOG_FILE" } # Function to clean up resources on error cleanup_on_error() { echo "" | tee -a "$LOG_FILE" echo "==========================================" | tee -a "$LOG_FILE" echo "ERROR OCCURRED - CLEANING UP RESOURCES" | tee -a "$LOG_FILE" echo "==========================================" | tee -a "$LOG_FILE" echo "The following resources were created:" | tee -a "$LOG_FILE" # Display resources in reverse order for ((i=${#CREATED_RESOURCES[@]}-1; i>=0; i--)); do echo "${CREATED_RESOURCES[$i]}" | tee -a "$LOG_FILE" done echo "" | tee -a "$LOG_FILE" echo "Attempting to clean up resources..." | tee -a "$LOG_FILE" # Clean up resources in reverse order cleanup_resources } # Function to clean up all created resources cleanup_resources() { # Process resources in reverse order (last created, first deleted) for ((i=${#CREATED_RESOURCES[@]}-1; i>=0; i--)); do IFS=':' read -r resource_type resource_id <<< "${CREATED_RESOURCES[$i]}" echo "Deleting $resource_type: $resource_id" | tee -a "$LOG_FILE" case "$resource_type" in "IAM_POLICY") # Delete the policy (detachment should have been handled when the role was deleted) log_cmd "aws iam delete-policy --policy-arn $resource_id" || true ;; "IAM_ROLE") # Detach all policies from the role first if [[ -n "$POLICY_ARN" ]]; then log_cmd "aws iam detach-role-policy --role-name $resource_id --policy-arn $POLICY_ARN" || true fi # Delete the role log_cmd "aws iam delete-role --role-name $resource_id" || true ;; "SSM_CONFIG_MANAGER") log_cmd "aws ssm-quicksetup delete-configuration-manager --manager-arn $resource_id" || true ;; *) echo "Unknown resource type: $resource_type, cannot delete automatically" | tee -a "$LOG_FILE" ;; esac done echo "Cleanup completed" | tee -a "$LOG_FILE" # Clean up temporary files rm -f ssm-onboarding-policy.json trust-policy.json ssm-config.json 2>/dev/null || true } # Main script execution echo "AWS Systems Manager Setup Script" echo "================================" echo "This script will set up AWS Systems Manager for a single account and region." echo "It will create IAM policies and roles, then enable Systems Manager features." echo "" # Get the current AWS region CURRENT_REGION=$(aws configure get region) if [[ -z "$CURRENT_REGION" ]]; then echo "No AWS region configured. Please specify a region:" CURRENT_REGION="${AWS_DEFAULT_REGION:-us-west-2}" if [[ -z "$CURRENT_REGION" ]]; then echo "ERROR: A region must be specified" | tee -a "$LOG_FILE" exit 1 fi fi echo "Using AWS region: $CURRENT_REGION" | tee -a "$LOG_FILE" # Step 1: Create IAM policy for Systems Manager onboarding echo "Step 1: Creating IAM policy for Systems Manager onboarding..." # Create policy document cat > ssm-onboarding-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Sid": "QuickSetupActions", "Effect": "Allow", "Action": [ "ssm-quicksetup:*" ], "Resource": "*" }, { "Sid": "SsmReadOnly", "Effect": "Allow", "Action": [ "ssm:DescribeAutomationExecutions", "ssm:GetAutomationExecution", "ssm:ListAssociations", "ssm:DescribeAssociation", "ssm:ListDocuments", "ssm:ListResourceDataSync", "ssm:DescribePatchBaselines", "ssm:GetPatchBaseline", "ssm:DescribeMaintenanceWindows", "ssm:DescribeMaintenanceWindowTasks" ], "Resource": "*" }, { "Sid": "SsmDocument", "Effect": "Allow", "Action": [ "ssm:GetDocument", "ssm:DescribeDocument" ], "Resource": [ "arn:aws:ssm:*:*:document/AWSQuickSetupType-*", "arn:aws:ssm:*:*:document/AWS-EnableExplorer" ] }, { "Sid": "SsmEnableExplorer", "Effect": "Allow", "Action": "ssm:StartAutomationExecution", "Resource": "arn:aws:ssm:*:*:automation-definition/AWS-EnableExplorer:*" }, { "Sid": "SsmExplorerRds", "Effect": "Allow", "Action": [ "ssm:GetOpsSummary", "ssm:CreateResourceDataSync", "ssm:UpdateResourceDataSync" ], "Resource": "arn:aws:ssm:*:*:resource-data-sync/AWS-QuickSetup-*" }, { "Sid": "OrgsReadOnly", "Effect": "Allow", "Action": [ "organizations:DescribeAccount", "organizations:DescribeOrganization", "organizations:ListDelegatedAdministrators", "organizations:ListRoots", "organizations:ListParents", "organizations:ListOrganizationalUnitsForParent", "organizations:DescribeOrganizationalUnit", "organizations:ListAWSServiceAccessForOrganization" ], "Resource": "*" }, { "Sid": "OrgsAdministration", "Effect": "Allow", "Action": [ "organizations:EnableAWSServiceAccess", "organizations:RegisterDelegatedAdministrator", "organizations:DeregisterDelegatedAdministrator" ], "Resource": "*", "Condition": { "StringEquals": { "organizations:ServicePrincipal": [ "ssm.amazonaws.com", "ssm-quicksetup.amazonaws.com", "member.org.stacksets.cloudformation.amazonaws.com", "resource-explorer-2.amazonaws.com" ] } } }, { "Sid": "CfnReadOnly", "Effect": "Allow", "Action": [ "cloudformation:ListStacks", "cloudformation:DescribeStacks", "cloudformation:ListStackSets", "cloudformation:DescribeOrganizationsAccess" ], "Resource": "*" }, { "Sid": "OrgCfnAccess", "Effect": "Allow", "Action": [ "cloudformation:ActivateOrganizationsAccess" ], "Resource": "*" }, { "Sid": "CfnStackActions", "Effect": "Allow", "Action": [ "cloudformation:CreateStack", "cloudformation:DeleteStack", "cloudformation:DescribeStackResources", "cloudformation:DescribeStackEvents", "cloudformation:GetTemplate", "cloudformation:RollbackStack", "cloudformation:TagResource", "cloudformation:UntagResource", "cloudformation:UpdateStack" ], "Resource": [ "arn:aws:cloudformation:*:*:stack/StackSet-AWS-QuickSetup-*", "arn:aws:cloudformation:*:*:stack/AWS-QuickSetup-*", "arn:aws:cloudformation:*:*:type/resource/*" ] }, { "Sid": "CfnStackSetActions", "Effect": "Allow", "Action": [ "cloudformation:CreateStackInstances", "cloudformation:CreateStackSet", "cloudformation:DeleteStackInstances", "cloudformation:DeleteStackSet", "cloudformation:DescribeStackInstance", "cloudformation:DetectStackSetDrift", "cloudformation:ListStackInstanceResourceDrifts", "cloudformation:DescribeStackSet", "cloudformation:DescribeStackSetOperation", "cloudformation:ListStackInstances", "cloudformation:ListStackSetOperations", "cloudformation:ListStackSetOperationResults", "cloudformation:TagResource", "cloudformation:UntagResource", "cloudformation:UpdateStackSet" ], "Resource": [ "arn:aws:cloudformation:*:*:stackset/AWS-QuickSetup-*", "arn:aws:cloudformation:*:*:type/resource/*", "arn:aws:cloudformation:*:*:stackset-target/AWS-QuickSetup-*:*" ] }, { "Sid": "ValidationReadonlyActions", "Effect": "Allow", "Action": [ "iam:ListRoles", "iam:GetRole" ], "Resource": "*" }, { "Sid": "IamRolesMgmt", "Effect": "Allow", "Action": [ "iam:CreateRole", "iam:DeleteRole", "iam:GetRole", "iam:AttachRolePolicy", "iam:DetachRolePolicy", "iam:GetRolePolicy", "iam:ListRolePolicies" ], "Resource": [ "arn:aws:iam::*:role/AWS-QuickSetup-*", "arn:aws:iam::*:role/service-role/AWS-QuickSetup-*" ] }, { "Sid": "IamPassRole", "Effect": "Allow", "Action": [ "iam:PassRole" ], "Resource": [ "arn:aws:iam::*:role/AWS-QuickSetup-*", "arn:aws:iam::*:role/service-role/AWS-QuickSetup-*" ], "Condition": { "StringEquals": { "iam:PassedToService": [ "ssm.amazonaws.com", "ssm-quicksetup.amazonaws.com", "cloudformation.amazonaws.com" ] } } }, { "Sid": "IamRolesPoliciesMgmt", "Effect": "Allow", "Action": [ "iam:AttachRolePolicy", "iam:DetachRolePolicy" ], "Resource": [ "arn:aws:iam::*:role/AWS-QuickSetup-*", "arn:aws:iam::*:role/service-role/AWS-QuickSetup-*" ], "Condition": { "ArnEquals": { "iam:PolicyARN": [ "arn:aws:iam::aws:policy/AWSSystemsManagerEnableExplorerExecutionPolicy", "arn:aws:iam::aws:policy/AWSQuickSetupSSMDeploymentRolePolicy" ] } } }, { "Sid": "CfnStackSetsSLR", "Effect": "Allow", "Action": [ "iam:CreateServiceLinkedRole" ], "Resource": [ "arn:aws:iam::*:role/aws-service-role/stacksets.cloudformation.amazonaws.com/AWSServiceRoleForCloudFormationStackSetsOrgAdmin", "arn:aws:iam::*:role/aws-service-role/ssm.amazonaws.com/AWSServiceRoleForAmazonSSM", "arn:aws:iam::*:role/aws-service-role/accountdiscovery.ssm.amazonaws.com/AWSServiceRoleForAmazonSSM_AccountDiscovery", "arn:aws:iam::*:role/aws-service-role/ssm-quicksetup.amazonaws.com/AWSServiceRoleForSSMQuickSetup", "arn:aws:iam::*:role/aws-service-role/resource-explorer-2.amazonaws.com/AWSServiceRoleForResourceExplorer" ] } ] } EOF # Create the IAM policy POLICY_OUTPUT=$(log_cmd "aws iam create-policy --policy-name SSMOnboardingPolicy-$UNIQUE_ID --policy-document file://ssm-onboarding-policy.json --output json") POLICY_STATUS=$? check_error "$POLICY_OUTPUT" $POLICY_STATUS "Failed to create IAM policy" # Extract the policy ARN POLICY_ARN=$(echo "$POLICY_OUTPUT" | grep -o 'arn:aws:iam::[0-9]*:policy/SSMOnboardingPolicy-[a-f0-9]*') if [[ -z "$POLICY_ARN" ]]; then echo "ERROR: Failed to extract policy ARN" | tee -a "$LOG_FILE" exit 1 fi # Track the created policy track_resource "IAM_POLICY" "$POLICY_ARN" aws iam tag-policy --policy-arn "$POLICY_ARN" \ --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-systems-manager-gs echo "Created policy: $POLICY_ARN" | tee -a "$LOG_FILE" # Step 2: Create and configure IAM role for Systems Manager echo "" echo "Step 2: Creating IAM role for Systems Manager..." # Get current user name USER_OUTPUT=$(log_cmd "aws sts get-caller-identity --output json") USER_STATUS=$? check_error "$USER_OUTPUT" $USER_STATUS "Failed to get caller identity" # Extract account ID ACCOUNT_ID=$(echo "$USER_OUTPUT" | grep -o '"Account": "[0-9]*"' | cut -d'"' -f4) if [[ -z "$ACCOUNT_ID" ]]; then echo "ERROR: Failed to extract account ID" | tee -a "$LOG_FILE" exit 1 fi # Generate a unique role name ROLE_NAME="SSMTutorialRole-$(openssl rand -hex 4)" # Create trust policy for the role - FIXED: Added cloudformation.amazonaws.com cat > trust-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "ssm.amazonaws.com", "ssm-quicksetup.amazonaws.com", "cloudformation.amazonaws.com" ] }, "Action": "sts:AssumeRole" }, { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::ACCOUNT_ID:root" }, "Action": "sts:AssumeRole" } ] } EOF # Replace ACCOUNT_ID placeholder in trust policy sed -i "s/ACCOUNT_ID/$ACCOUNT_ID/g" trust-policy.json # Create the IAM role ROLE_OUTPUT=$(log_cmd "aws iam create-role --role-name $ROLE_NAME --assume-role-policy-document file://trust-policy.json --description 'Role for Systems Manager tutorial' --output json") ROLE_STATUS=$? check_error "$ROLE_OUTPUT" $ROLE_STATUS "Failed to create IAM role" # Extract the role ARN ROLE_ARN=$(echo "$ROLE_OUTPUT" | grep -o 'arn:aws:iam::[0-9]*:role/[^"]*') if [[ -z "$ROLE_ARN" ]]; then echo "ERROR: Failed to extract role ARN" | tee -a "$LOG_FILE" cleanup_on_error exit 1 fi # Track the created role track_resource "IAM_ROLE" "$ROLE_NAME" aws iam tag-role --role-name "$ROLE_NAME" \ --tags Key=project,Value=doc-smith Key=tutorial,Value=aws-systems-manager-gs echo "Created IAM role: $ROLE_NAME" | tee -a "$LOG_FILE" echo "Role ARN: $ROLE_ARN" | tee -a "$LOG_FILE" # Set identity variables for cleanup IDENTITY_TYPE="role" IDENTITY_NAME="$ROLE_NAME" # Attach the policy to the role ATTACH_OUTPUT=$(log_cmd "aws iam attach-role-policy --role-name $ROLE_NAME --policy-arn $POLICY_ARN") ATTACH_STATUS=$? check_error "$ATTACH_OUTPUT" $ATTACH_STATUS "Failed to attach policy to role $ROLE_NAME" echo "Policy attached to role: $ROLE_NAME" | tee -a "$LOG_FILE" # Step 3: Create Systems Manager configuration using Host Management echo "" echo "Step 3: Creating Systems Manager configuration..." # Generate a random identifier for the configuration name CONFIG_NAME="SSMSetup-$(openssl rand -hex 4)" # Create configuration file for Systems Manager setup using Host Management # Added both required parameters for single account deployment based on CloudFormation documentation cat > ssm-config.json << EOF [ { "Type": "AWSQuickSetupType-SSMHostMgmt", "LocalDeploymentAdministrationRoleArn": "$ROLE_ARN", "LocalDeploymentExecutionRoleName": "$ROLE_NAME", "Parameters": { "TargetAccounts": "$ACCOUNT_ID", "TargetRegions": "$CURRENT_REGION" } } ] EOF echo "Configuration file created:" | tee -a "$LOG_FILE" cat ssm-config.json | tee -a "$LOG_FILE" # Create the configuration manager CONFIG_OUTPUT=$(log_cmd "aws ssm-quicksetup create-configuration-manager --name \"$CONFIG_NAME\" --configuration-definitions file://ssm-config.json --region $CURRENT_REGION") CONFIG_STATUS=$? check_error "$CONFIG_OUTPUT" $CONFIG_STATUS "Failed to create Systems Manager configuration" # Extract the manager ARN MANAGER_ARN=$(echo "$CONFIG_OUTPUT" | grep -o 'arn:aws:ssm-quicksetup:[^"]*') if [[ -z "$MANAGER_ARN" ]]; then echo "ERROR: Failed to extract manager ARN" | tee -a "$LOG_FILE" exit 1 fi # Track the created configuration manager track_resource "SSM_CONFIG_MANAGER" "$MANAGER_ARN" echo "Created Systems Manager configuration: $MANAGER_ARN" | tee -a "$LOG_FILE" # Step 4: Verify the setup echo "" echo "Step 4: Verifying the setup..." # Wait for the configuration to be fully deployed echo "Waiting for the configuration to be deployed (this may take a few minutes)..." sleep 30 # Check the configuration manager status VERIFY_OUTPUT=$(log_cmd "aws ssm-quicksetup get-configuration-manager --manager-arn $MANAGER_ARN --region $CURRENT_REGION") VERIFY_STATUS=$? check_error "$VERIFY_OUTPUT" $VERIFY_STATUS "Failed to verify configuration manager" echo "Systems Manager setup completed successfully!" | tee -a "$LOG_FILE" # List the created resources echo "" echo "===========================================" echo "CREATED RESOURCES" echo "===========================================" for resource in "${CREATED_RESOURCES[@]+"${CREATED_RESOURCES[@]}"}"; do echo "$resource" done # Prompt for cleanup 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..." | tee -a "$LOG_FILE" cleanup_resources echo "Cleanup completed." | tee -a "$LOG_FILE" else echo "Resources will not be cleaned up. You can manually clean them up later." | tee -a "$LOG_FILE" fi echo "" echo "Script execution completed. See $LOG_FILE for details." # Clean up temporary files rm -f ssm-onboarding-policy.json trust-policy.json ssm-config.json 2>/dev/null || true

Contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Buat fungsi Lambda untuk pemantauan

  • Buat CloudWatch dasbor

  • Tambahkan variabel properti ke dasbor

  • Pembersihan sumber daya

AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara mengatur dan menjalankan di repositori tutorial pengembang Sample.

#!/bin/bash # CloudWatch Dashboard with Lambda Function Variable Script # This script creates a CloudWatch dashboard with a property variable for Lambda function names set -euo pipefail # Security: Set restrictive umask umask 0077 # Set up logging with secure permissions LOG_FILE="cloudwatch-dashboard-script-v4.log" touch "$LOG_FILE" chmod 600 "$LOG_FILE" echo "Starting script execution at $(date)" >> "$LOG_FILE" # Function to log commands and their output (with sensitive data sanitization) log_cmd() { local cmd="$1" local sanitized_cmd="${cmd//--password*/--password [REDACTED]}" sanitized_cmd="${sanitized_cmd//--secret*/--secret [REDACTED]}" echo "$(date): Running command: $sanitized_cmd" >> "$LOG_FILE" eval "$cmd" 2>&1 | tee -a "$LOG_FILE" return ${PIPESTATUS[0]} } # Function to check for errors in command output check_error() { local cmd_output="$1" local cmd_status="$2" local error_msg="$3" if [ $cmd_status -ne 0 ] || echo "$cmd_output" | grep -qi "error"; then echo "ERROR: $error_msg" | tee -a "$LOG_FILE" # Sanitize output before logging local sanitized_output="${cmd_output//arn:aws:iam::[0-9]*/arn:aws:iam::ACCOUNT_ID}" echo "Command output: $sanitized_output" | tee -a "$LOG_FILE" cleanup_resources exit 1 fi } # Trap errors and cleanup trap 'cleanup_resources' EXIT ERR INT TERM # Function to clean up resources cleanup_resources() { local exit_code=$? echo "" | tee -a "$LOG_FILE" echo "==========================================" | tee -a "$LOG_FILE" echo "CLEANUP PROCESS" | tee -a "$LOG_FILE" echo "==========================================" | tee -a "$LOG_FILE" if [ -n "${DASHBOARD_NAME:-}" ]; then echo "Deleting CloudWatch dashboard: $DASHBOARD_NAME" | tee -a "$LOG_FILE" aws cloudwatch delete-dashboards --dashboard-names "$DASHBOARD_NAME" 2>&1 >> "$LOG_FILE" || true fi if [ -n "${LAMBDA_FUNCTION1:-}" ]; then echo "Deleting Lambda function: $LAMBDA_FUNCTION1" | tee -a "$LOG_FILE" aws lambda delete-function --function-name "$LAMBDA_FUNCTION1" 2>&1 >> "$LOG_FILE" || true fi if [ -n "${LAMBDA_FUNCTION2:-}" ]; then echo "Deleting Lambda function: $LAMBDA_FUNCTION2" | tee -a "$LOG_FILE" aws lambda delete-function --function-name "$LAMBDA_FUNCTION2" 2>&1 >> "$LOG_FILE" || true fi if [ -n "${ROLE_NAME:-}" ]; then echo "Detaching policy from role: $ROLE_NAME" | tee -a "$LOG_FILE" aws iam detach-role-policy --role-name "$ROLE_NAME" --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole 2>&1 >> "$LOG_FILE" || true echo "Deleting IAM role: $ROLE_NAME" | tee -a "$LOG_FILE" aws iam delete-role --role-name "$ROLE_NAME" 2>&1 >> "$LOG_FILE" || true fi # Clean up temporary files securely shred -vfz -n 3 trust-policy.json lambda_function.py lambda_function.zip 2>/dev/null || rm -f trust-policy.json lambda_function.py lambda_function.zip echo "Cleanup completed." | tee -a "$LOG_FILE" return $exit_code } # Validate AWS CLI is installed and authenticated if ! command -v aws &> /dev/null; then echo "ERROR: AWS CLI is not installed" | tee -a "$LOG_FILE" exit 1 fi if ! aws sts get-caller-identity &> /dev/null; then echo "ERROR: AWS CLI is not properly authenticated" | tee -a "$LOG_FILE" exit 1 fi # Get AWS region with validation AWS_REGION=$(aws configure get region 2>/dev/null || echo "") if [ -z "$AWS_REGION" ]; then AWS_REGION="us-east-1" echo "No region found in AWS config, defaulting to $AWS_REGION" | tee -a "$LOG_FILE" else echo "Using AWS region: $AWS_REGION" | tee -a "$LOG_FILE" fi # Validate region format if ! [[ "$AWS_REGION" =~ ^[a-z]{2}-[a-z]+-[0-9]$ ]]; then echo "ERROR: Invalid AWS region format: $AWS_REGION" | tee -a "$LOG_FILE" exit 1 fi # Generate unique identifiers using secure random with validation RANDOM_ID=$(openssl rand -hex 6) if [ -z "$RANDOM_ID" ] || [ ${#RANDOM_ID} -ne 12 ]; then echo "ERROR: Failed to generate valid random identifier" | tee -a "$LOG_FILE" exit 1 fi DASHBOARD_NAME="LambdaMetricsDashboard-${RANDOM_ID}" LAMBDA_FUNCTION1="TestFunction1-${RANDOM_ID}" LAMBDA_FUNCTION2="TestFunction2-${RANDOM_ID}" ROLE_NAME="LambdaExecutionRole-${RANDOM_ID}" # Validate resource names don't exceed AWS limits if [ ${#DASHBOARD_NAME} -gt 128 ] || [ ${#LAMBDA_FUNCTION1} -gt 64 ] || [ ${#ROLE_NAME} -gt 64 ]; then echo "ERROR: Generated resource names exceed AWS limits" | tee -a "$LOG_FILE" exit 1 fi echo "Using random identifier: $RANDOM_ID" | tee -a "$LOG_FILE" echo "Dashboard name: $DASHBOARD_NAME" | tee -a "$LOG_FILE" echo "Lambda function names: $LAMBDA_FUNCTION1, $LAMBDA_FUNCTION2" | tee -a "$LOG_FILE" echo "IAM role name: $ROLE_NAME" | tee -a "$LOG_FILE" # Create IAM role for Lambda functions echo "Creating IAM role for Lambda..." | tee -a "$LOG_FILE" TRUST_POLICY='{ "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }' echo "$TRUST_POLICY" > trust-policy.json chmod 600 trust-policy.json # Validate JSON before use if ! python3 -m json.tool trust-policy.json > /dev/null 2>&1; then echo "ERROR: Invalid trust policy JSON" | tee -a "$LOG_FILE" exit 1 fi ROLE_OUTPUT=$(log_cmd "aws iam create-role --role-name '$ROLE_NAME' --assume-role-policy-document file://trust-policy.json --tags Key=project,Value=doc-smith Key=tutorial,Value=cloudwatch-streams --output json") check_error "$ROLE_OUTPUT" $? "Failed to create IAM role" ROLE_ARN=$(echo "$ROLE_OUTPUT" | python3 -c "import sys, json; print(json.load(sys.stdin)['Role']['Arn'])" 2>/dev/null) if [ -z "$ROLE_ARN" ]; then echo "ERROR: Failed to extract Role ARN" | tee -a "$LOG_FILE" exit 1 fi echo "Role ARN: $ROLE_ARN" | tee -a "$LOG_FILE" # Attach Lambda basic execution policy to the role echo "Attaching Lambda execution policy to role..." | tee -a "$LOG_FILE" POLICY_OUTPUT=$(log_cmd "aws iam attach-role-policy --role-name '$ROLE_NAME' --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole") check_error "$POLICY_OUTPUT" $? "Failed to attach policy to role" # Wait for role to propagate echo "Waiting for IAM role to propagate..." | tee -a "$LOG_FILE" sleep 10 # Create simple Python Lambda function code with security validation echo "Creating Lambda function code..." | tee -a "$LOG_FILE" cat > lambda_function.py << 'LAMBDA_EOF' def handler(event, context): print("Lambda function executed successfully") return { 'statusCode': 200, 'body': 'Success' } LAMBDA_EOF chmod 600 lambda_function.py # Validate Python syntax if ! python3 -m py_compile lambda_function.py 2>/dev/null; then echo "ERROR: Invalid Python syntax in Lambda function" | tee -a "$LOG_FILE" exit 1 fi # Zip the Lambda function code zip -j -q lambda_function.zip lambda_function.py if [ ! -f lambda_function.zip ]; then echo "ERROR: Failed to create lambda_function.zip" | tee -a "$LOG_FILE" exit 1 fi chmod 600 lambda_function.zip # Validate zip file integrity if ! unzip -t lambda_function.zip > /dev/null 2>&1; then echo "ERROR: Created zip file is corrupted" | tee -a "$LOG_FILE" exit 1 fi # Create first Lambda function echo "Creating first Lambda function: $LAMBDA_FUNCTION1..." | tee -a "$LOG_FILE" LAMBDA1_OUTPUT=$(log_cmd "aws lambda create-function --function-name '$LAMBDA_FUNCTION1' --runtime python3.11 --role '$ROLE_ARN' --handler lambda_function.handler --zip-file fileb://lambda_function.zip --timeout 30 --memory-size 128 --tags project=doc-smith,tutorial=cloudwatch-streams") check_error "$LAMBDA1_OUTPUT" $? "Failed to create first Lambda function" # Create second Lambda function echo "Creating second Lambda function: $LAMBDA_FUNCTION2..." | tee -a "$LOG_FILE" LAMBDA2_OUTPUT=$(log_cmd "aws lambda create-function --function-name '$LAMBDA_FUNCTION2' --runtime python3.11 --role '$ROLE_ARN' --handler lambda_function.handler --zip-file fileb://lambda_function.zip --timeout 30 --memory-size 128 --tags project=doc-smith,tutorial=cloudwatch-streams") check_error "$LAMBDA2_OUTPUT" $? "Failed to create second Lambda function" # Invoke Lambda functions to generate some metrics echo "Invoking Lambda functions to generate metrics..." | tee -a "$LOG_FILE" log_cmd "aws lambda invoke --function-name '$LAMBDA_FUNCTION1' --payload '{}' /dev/null" || true log_cmd "aws lambda invoke --function-name '$LAMBDA_FUNCTION2' --payload '{}' /dev/null" || true # Create CloudWatch dashboard with property variable echo "Creating CloudWatch dashboard with property variable..." | tee -a "$LOG_FILE" # Create dashboard body with proper escaping and validation DASHBOARD_BODY=$(cat <<EOF { "widgets": [ { "type": "metric", "x": 0, "y": 0, "width": 12, "height": 6, "properties": { "metrics": [ [ "AWS/Lambda", "Invocations", "FunctionName", "$LAMBDA_FUNCTION1" ] ], "view": "timeSeries", "stacked": false, "region": "$AWS_REGION", "title": "Lambda Invocations", "period": 300, "stat": "Sum" } } ] } EOF ) # Validate JSON before sending if ! echo "$DASHBOARD_BODY" | python3 -m json.tool > /dev/null 2>&1; then echo "ERROR: Dashboard body is not valid JSON" | tee -a "$LOG_FILE" exit 1 fi # First create a basic dashboard without variables echo "Creating initial dashboard without variables..." | tee -a "$LOG_FILE" DASHBOARD_OUTPUT=$(aws cloudwatch put-dashboard --dashboard-name "$DASHBOARD_NAME" --dashboard-body "$DASHBOARD_BODY" --output json 2>&1) check_error "$DASHBOARD_OUTPUT" $? "Failed to create initial CloudWatch dashboard" # Now let's try to add a property variable using the console instructions echo "To complete the tutorial, please follow these steps in the CloudWatch console:" | tee -a "$LOG_FILE" echo "1. Open the CloudWatch console at https://console.aws.amazon.com/cloudwatch/" | tee -a "$LOG_FILE" echo "2. Navigate to Dashboards and select your dashboard: $DASHBOARD_NAME" | tee -a "$LOG_FILE" echo "3. Choose Actions > Variables > Create a variable" | tee -a "$LOG_FILE" echo "4. Choose Property variable" | tee -a "$LOG_FILE" echo "5. For Property that the variable changes, choose FunctionName" | tee -a "$LOG_FILE" echo "6. For Input type, choose Select menu (dropdown)" | tee -a "$LOG_FILE" echo "7. Choose Use the results of a metric search" | tee -a "$LOG_FILE" echo "8. Choose Pre-built queries > Lambda > Errors" | tee -a "$LOG_FILE" echo "9. Choose By Function Name and then choose Search" | tee -a "$LOG_FILE" echo "10. (Optional) Configure any secondary settings as desired" | tee -a "$LOG_FILE" echo "11. Choose Add variable" | tee -a "$LOG_FILE" echo "" | tee -a "$LOG_FILE" echo "The dashboard has been created and can be accessed at:" | tee -a "$LOG_FILE" echo "https://console.aws.amazon.com/cloudwatch/home#dashboards:name=$DASHBOARD_NAME" | tee -a "$LOG_FILE" # Verify dashboard creation echo "Verifying dashboard creation..." | tee -a "$LOG_FILE" VERIFY_OUTPUT=$(aws cloudwatch get-dashboard --dashboard-name "$DASHBOARD_NAME" --output json 2>&1) check_error "$VERIFY_OUTPUT" $? "Failed to verify dashboard creation" echo "" | tee -a "$LOG_FILE" echo "==========================================" | tee -a "$LOG_FILE" echo "DASHBOARD CREATED SUCCESSFULLY" | tee -a "$LOG_FILE" echo "==========================================" | tee -a "$LOG_FILE" echo "Dashboard Name: $DASHBOARD_NAME" | tee -a "$LOG_FILE" echo "Lambda Functions: $LAMBDA_FUNCTION1, $LAMBDA_FUNCTION2" | tee -a "$LOG_FILE" echo "" | tee -a "$LOG_FILE" echo "You can view your dashboard in the CloudWatch console:" | tee -a "$LOG_FILE" echo "https://console.aws.amazon.com/cloudwatch/home#dashboards:name=$DASHBOARD_NAME" | tee -a "$LOG_FILE" echo "" | tee -a "$LOG_FILE" # Auto-confirm cleanup echo "" | tee -a "$LOG_FILE" echo "==========================================" | tee -a "$LOG_FILE" echo "CLEANUP CONFIRMATION" | tee -a "$LOG_FILE" echo "==========================================" | tee -a "$LOG_FILE" echo "The following resources were created:" | tee -a "$LOG_FILE" echo "- CloudWatch Dashboard: $DASHBOARD_NAME" | tee -a "$LOG_FILE" echo "- Lambda Function: $LAMBDA_FUNCTION1" | tee -a "$LOG_FILE" echo "- Lambda Function: $LAMBDA_FUNCTION2" | tee -a "$LOG_FILE" echo "- IAM Role: $ROLE_NAME" | tee -a "$LOG_FILE" echo "" | tee -a "$LOG_FILE" echo "Auto-confirming cleanup of all created resources..." | tee -a "$LOG_FILE" echo "Script completed successfully." | tee -a "$LOG_FILE" exit 0

Contoh kode berikut menunjukkan cara mengelola DynamoDB Streams dan fitur. Time-to-Live

  • Buat tabel dengan Streams diaktifkan.

  • Jelaskan Aliran.

  • Buat fungsi Lambda untuk memproses Streams.

  • Aktifkan TTL di atas meja.

  • Tambahkan item dengan atribut TTL.

  • Jelaskan pengaturan TTL.

AWS CLI dengan skrip Bash

Buat tabel dengan Streams diaktifkan.

# Create a table with DynamoDB Streams enabled aws dynamodb create-table \ --table-name StreamsDemo \ --attribute-definitions \ AttributeName=ID,AttributeType=S \ --key-schema \ AttributeName=ID,KeyType=HASH \ --billing-mode PAY_PER_REQUEST \ --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES

Jelaskan Aliran.

# Get information about the stream aws dynamodb describe-table \ --table-name StreamsDemo \ --query "Table.StreamSpecification" # Get the stream ARN STREAM_ARN=$(aws dynamodb describe-table \ --table-name StreamsDemo \ --query "Table.LatestStreamArn" \ --output text) echo "Stream ARN: $STREAM_ARN" # Describe the stream aws dynamodbstreams describe-stream \ --stream-arn $STREAM_ARN

Buat fungsi Lambda untuk Streams.

# Step 1: Create an IAM role for the Lambda function cat > trust-policy.json << 'EOF' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF aws iam create-role \ --role-name DynamoDBStreamsLambdaRole \ --assume-role-policy-document file://trust-policy.json # Step 2: Attach permissions to the role aws iam attach-role-policy \ --role-name DynamoDBStreamsLambdaRole \ --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaDynamoDBExecutionRole # Step 3: Create a Lambda function (code would be in a separate file) echo "Lambda function creation would be done separately with appropriate code" # Step 4: Create an event source mapping echo "Example command to create event source mapping:" echo "aws lambda create-event-source-mapping \\" echo " --function-name ProcessDynamoDBRecords \\" echo " --event-source $STREAM_ARN \\" echo " --batch-size 100 \\" echo " --starting-position LATEST"

Aktifkan TTL di atas meja.

# Create a table for TTL demonstration aws dynamodb create-table \ --table-name TTLDemo \ --attribute-definitions \ AttributeName=ID,AttributeType=S \ --key-schema \ AttributeName=ID,KeyType=HASH \ --billing-mode PAY_PER_REQUEST # Wait for table to become active aws dynamodb wait table-exists --table-name TTLDemo # Enable TTL on the table aws dynamodb update-time-to-live \ --table-name TTLDemo \ --time-to-live-specification "Enabled=true, AttributeName=ExpirationTime"

Tambahkan item dengan atribut TTL.

# Calculate expiration time (current time + 1 day in seconds) EXPIRATION_TIME=$(date -d "+1 day" +%s) # Add an item with TTL attribute aws dynamodb put-item \ --table-name TTLDemo \ --item '{ "ID": {"S": "item1"}, "Data": {"S": "This item will expire in 1 day"}, "ExpirationTime": {"N": "'$EXPIRATION_TIME'"} }' # Add an item that expires in 1 hour EXPIRATION_TIME_HOUR=$(date -d "+1 hour" +%s) aws dynamodb put-item \ --table-name TTLDemo \ --item '{ "ID": {"S": "item2"}, "Data": {"S": "This item will expire in 1 hour"}, "ExpirationTime": {"N": "'$EXPIRATION_TIME_HOUR'"} }'

Jelaskan pengaturan TTL.

# Describe TTL settings for a table aws dynamodb describe-time-to-live \ --table-name TTLDemo