diff --git a/ebs/IAM_permission.txt b/ebs/IAM_permission.txt new file mode 100644 index 00000000000..ad64e25d772 --- /dev/null +++ b/ebs/IAM_permission.txt @@ -0,0 +1,21 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "VisualEditor0", + "Effect": "Allow", + "Action": [ + "ec2:ModifyVolume", + "ec2:DescribeVolumes", + "ec2:DescribeVolumesModifications", + "ec2:DescribeVolumeStatus", + "ec2:DescribeTags", + "ec2:CreateTags", + "ec2:DescribeRegions", + "ec2:DescribeSnapshots", + "ec2:CreateSnapshot", + ], + "Resource": "*" + } + ] +} diff --git a/ebs/README.md b/ebs/README.md new file mode 100644 index 00000000000..2a18a0679db --- /dev/null +++ b/ebs/README.md @@ -0,0 +1,185 @@ +# Discover EBS volumes type GP2 and modify them to type GP3 with/without snapshot + +This example demonstrates how to modify EBS volume type GP2 to GP3 across a given region or all regions where customer has workload. It can auto scan all GP2 volumes or read the volume_ids input from any .text file in case modification needed only on user provided volumes. +Elastic volumes supports online modification of EBS volume type GP2 to GP3, it does not bring any outage to instance. + +Files + + gp2_gp3_migration.sh - main script to perform modification + gp2_gp3_migration_progress.sh - side script to track progress of modification triggered by main script + + +Purpose + +The main script file contains the several function that perform the following tasks based on parameters used with the script : + + - Discover and create list of all GP2, GP3, io1, io2 volumes in any single region or all regions. + + - Modify listed GP2 volumes from a file with or without snapshot. + + - Discover all GP2 volumes in a region and modify all of them with or without snapshot. + + - Discover all GP2 volumes in all regions and modify all of them with or without snapshot. + + - Perform any of the above action on cross accounts where AWS configuration profile, IAM role and permissions are already setup. + +Prerequisites + +- An Amazon Web Services (AWS) account. +- AWS CLI setup - [Install AWS CLI](http://docs.aws.amazon.com/cli/latest/userguide/installing.html) +- IAM policy/role that allows to perform volume discovery, modification and snapshot creation - Refer: IAM_permission.txt). +- Configuration profile for cross account access - refer second part of this README 'Scale the execution across accounts' + + +Script execution + +- To create list of all GP2, GP3, io1, io2 volumes in any single region or all regions :: gp2_gp3_migration.sh --region /all discover no-snapshot + +- To migrate listed GP2 volumes from a file w/o snapshot :: gp2_gp3_migration.sh --region no-snapshot + +- To migrate listed GP2 volumes from a file with snapshot :: gp2_gp3_migration.sh --region snapshot + +- To migrate all GP2 volumes in a region w/o snapshot :: gp2_gp3_migration.sh --region migrate no-snapshot + +- To migrate all GP2 volumes in a region with snapshot :: gp2_gp3_migration.sh --region migrate snapshot + +- To migrate all GP2 volumes across all regions w/o snapshot :: gp2_gp3_migration.sh --region all migrate no-snapshot + +- To migrate all GP2 volumes across all regions with snapshot :: gp2_gp3_migration.sh --region all migrate snapshot + +- To perform any of the above action on cross accounts where IAM role, permission and profile is already setup, add last 2 parameters --profile + +- To track the progress of volume modification :: gp2_gp3_migration_progress.sh + +Note - snapshot created by this script will have description: "Pre GP3 migration" and tag: key=state, value=pre-gp3 + + +# Scale the execution across accounts + +We can scale the execution of this script across accounts by setting the configuration and credentials for all member's account and trigger assume role by selecting the account's profile from one executer account . + + +Install the AWS CLI if not already done preferably on an EC2 instance in any account that will trigger automation action on all member accounts. To set up your default CLI credentials, you should gather the AWS access key and secret key for your script runner user, create an IAM user and then run the aws configure command. You will be prompted for 4 inputs (replace the placeholder keys with your user’s keys). + + + AWS access key ID [None]: + AWS secret access key [None]: + Default region name [None]: us-west-1 + Default output format [None]: json + +The AWS CLI organizes configuration and credentials into two separate files found in the home directory of your operating system. They are separated to isolate your credentials from the less sensitive configuration options of region and output. + + ~/.aws/config + [default] + region = us-west-1 + output = json + Additional Information + + ~/.aws/credentials + [default] + aws_access_key_id = + aws_secret_access_key = + +As you can see, the CLI has created these two files and identified them with [default]. Now we will be using the CLI’s ability to assume a role as per named profiles of all member accounts. + +Reference doc : [Configuration and Credential File Settings](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) + +Setup - + +-- Assuming a user created in above step in executer account is ec2-ebs-modify-user, make note of its arn that will be used in next steps. + +-- Now create an IAM role in all member accounts with a trust policy to allow sts:AssumeRole action to be assumed by our user ec2-ebs-modify-user from executer account. + +-- This IAM role must have the following IAM policy to allow certain permissions in order to achieve the volume modification. + +IAM Role : cross-account-modify-vol + + { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "VisualEditor0", + "Effect": "Allow", + "Action": [ + "ec2:ModifyVolume", + "ec2:DescribeVolumes", + "ec2:DescribeVolumesModifications", + "ec2:DescribeVolumeStatus", + "ec2:DescribeTags", + "ec2:CreateTags", + "ec2:DescribeRegions", + "ec2:CreateVolume", + "ec2:DescribeSnapshots", + "ec2:CreateSnapshot" + ], + "Resource": "*" + } + ] + } + + +IAM policy : ec2-ebs-modify-policy + + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::999999999999:user/ec2-ebs-modify-user" + }, + "Action": "sts:AssumeRole" + } + ] + } +Trust relationship : Pls update the highlighted placeholder as per you user arn from mgmt account. + +-- Make note of IAM roles created in member's accounts. + +-- Now add/update the IAM policy of IAM user "ec2-ebs-modify-user" in executer account where we will allow our user to assume the roles created in memeber's accounts. + + { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "VisualEditor0", + "Effect": "Allow", + "Action": "sts:AssumeRole", + "Resource": [ + "arn:aws:iam::333333333333:role/cross-account-modify-vol", + "arn:aws:iam::444444444444:role/cross-account-modify-vol", + "arn:aws:iam::555555555555:role/cross-account-modify-vol", + ] + } + ] + } + +-- Setup profile for all your member accounts - + + aws configure set profile.333333333333.role_arn arn:aws:iam::333333333333role/cross-account-modify-vol + + aws configure set profile.333333333333.source_profile default + + aws configure set profile.444444444444.role_arn arn:aws:iam::444444444444:role/cross-account-modify-vol + + aws configure set profile.444444444444.source_profile default + + aws configure set profile.555555555555.role_arn arn:aws:iam::5555555555555:role/cross-account-modify-vol + + aws configure set profile.555555555555.source_profile default + +-- Create a list of account numbers to be used in our script, assuming all account numbers are listed in account_list + +-- Now call the script as per you use case with for loop to execute on all accounts listed in our account_list based on the profile setup. + +Sample commands : + + for a in `cat account_list`; do /home/gp2_gp3_migration.sh --region all discover no-snapshot --profile $a; done + for a in `cat account_list`; do /home/gp2_gp3_migration.sh --region all migrate snapshot --profile $a; done + bash gp2_gp3_migration.sh --region all migrate no-snapshot --profile + + + +As an AWS best practice, grant this code least privilege, or only the permissions required to perform a task. For more information, see Grant Least Privilege in the AWS Identity and Access Management (IAM) User Guide. + This code has not been tested in all AWS Regions. Some AWS services are available only in specific Regions. For more information, see Service Endpoints and Quotas in the AWS General Reference Guide. + Running this code can result in charges to your AWS account. It's your responsibility to ensure that any resources created by this script are removed when you are done with them. diff --git a/ebs/gp2_gp3_migration.sh b/ebs/gp2_gp3_migration.sh new file mode 100644 index 00000000000..399c6095677 --- /dev/null +++ b/ebs/gp2_gp3_migration.sh @@ -0,0 +1,248 @@ +#! /bin/bash +# Author : @razguru +# Version : 1.1 - - Added cross account action through --profile assume role +# Desc : Discover or/and modify GP2 volumes with/without snapshot + +reg="$1" +region="$2" +file_name="$3" +snap="$4" +account="$6" +dt=`date +%F-%T` + +# Ensure region is provided +if [ -z $region ] || [[ $reg != --region ]] || [ -z $file_name ]; then + echo "Please provide region/all, target volume-list/all, snapshot option and optional --profile for cross account action .." + echo "" + echo "To create list of all GP2, GP3, io1, io2 volumes in any single region or all regions :: $0 --region /all discover no-snapshot" + echo "To migrate listed GP2 volumes from a file w/o snapshot :: $0 --region no-snapshot" + echo "To migrate listed GP2 volumes from a file with snapshot :: $0 --region snapshot" + echo "To migrate all GP2 volumes in a region w/o snapshot :: $0 --region migrate no-snapshot" + echo "To migrate all GP2 volumes in a region with snapshot :: $0 --region migrate snapshot" + echo "To migrate all GP2 volumes across all regions w/o snapshot :: $0 --region all migrate no-snapshot" + echo "To migrate all GP2 volumes across all regions with snapshot :: $0 --region all migrate snapshot" + echo "To perform any of the above action on cross accounts where IAM role, permission and profile is already setup, add last 2 parameters --profile " + + echo "" +exit 1 +fi + +if [[ $region != all ]]; then gp2_vol_id="$account"_"$region"_"$dt"_gp2_vol_id.txt +fi + +if [[ $region != all ]] && [[ $file_name == discover ]]; then disc="discover" +fi + +if [[ $file_name != migrate ]] && [[ $file_name != discover ]] && [ $region == all ]; then + echo "To target listed GP2 volumes from $file_name pls use - $0 region " +exit 1 +fi + +if [[ $file_name != migrate ]] && [[ $file_name != discover ]] && [ ! -s "$file_name" ] ; then + echo "Provided file "$file_name" is either empty or does not exist !" + exit 1 +fi + +if [ ! -z "$file_name" ] && [[ $file_name != migrate ]] && [[ $file_name != discover ]] && [ -s "$file_name" ] ; then + manual="conversion" +fi +if [ ! -z "$file_name" ] && [[ $file_name != migrate ]] && [[ $file_name != discover ]] && [ -s "$file_name" ] && [[ $snap = snapshot ]]; then + manual="snap_conversion" +fi + +if [[ $region != all ]] && [[ $file_name = migrate ]] && [[ $file_name != discover ]] && [[ $snap != snapshot ]]; then + action="auto_conversion" +fi + +# Find all GP2 volumes within the given list +manual-disc() +{ + if [ ! -z "$file_name" ] && [ -s "$file_name" ] ; then + /bin/cp "$file_name" "$gp2_vol_id" &> /dev/null + echo "Discovered ....( `egrep vol "$gp2_vol_id"|wc -l` ).... GP2 volumes from "$file_name"" + else + echo "Provided file "$file_name" is either empty or does not exist !" + exit 1 + fi +} + +# Find all GP2 volumes within the given region +auto-disc() +{ +gp2_vol_id="$account"_"$region"_"$dt"_gp2_vol_id.txt +gp3_vol_id="$account"_"$region"_"$dt"_gp3_vol_id.txt +io1_vol_id="$account"_"$region"_"$dt"_io1_vol_id.txt +io2_vol_id="$account"_"$region"_"$dt"_io2_vol_id.txt + +gp2_vol_error="$account"_"$region"_"$dt"_gp2_vol_error.txt +/usr/bin/aws ec2 describe-volumes --region "${region}" --profile "${account}" --filters Name=volume-type,Values=gp2 > /dev/null 2> "$gp2_vol_error" +if [ -s "$gp2_vol_error" ]; then + echo -n "$region -- error -- " && echo `cat $gp2_vol_error` + echo "" +else + rm -f $gp2_vol_error &> /dev/null + /usr/bin/aws ec2 describe-volumes --region "${region}" --profile "${account}" --filters Name=volume-type,Values=gp2 | jq -r '.Volumes[].VolumeId' > "$gp2_vol_id" + /usr/bin/aws ec2 describe-volumes --region "${region}" --profile "${account}" --filters Name=volume-type,Values=gp3 | jq -r '.Volumes[].VolumeId' > "$gp3_vol_id" + /usr/bin/aws ec2 describe-volumes --region "${region}" --profile "${account}" --filters Name=volume-type,Values=io1 | jq -r '.Volumes[].VolumeId' > "$io1_vol_id" + /usr/bin/aws ec2 describe-volumes --region "${region}" --profile "${account}" --filters Name=volume-type,Values=io2 | jq -r '.Volumes[].VolumeId' > "$io2_vol_id" + if [ -s "$gp2_vol_id" ]; then + echo -n "$account -- Discovered ....( `egrep vol "$gp2_vol_id"|wc -l` ).... GP2 volumes in $region" && echo " || GP2 volume-ids list : $gp2_vol_id" + echo "" +else + echo "$account -- No GP2 volume found in $region" && rm -f $gp2_vol_id &> /dev/null + echo "" +fi + if [ -s "$gp3_vol_id" ]; then + echo -n "$account -- Discovered ....( `egrep vol "$gp3_vol_id"|wc -l` ).... GP3 volumes in $region" && echo " || GP3 volume-ids list : $gp3_vol_id" + echo "" +else + echo "$account -- No GP3 volume found in $region" && rm -f $gp3_vol_id &> /dev/null + echo "" +fi + if [ -s "$io1_vol_id" ]; then + echo -n "$account -- Discovered ....( `egrep vol "$io1_vol_id"|wc -l` ).... io1 volumes in $region" && echo " || io1 volume-ids list : $io1_vol_id" + echo "" +else + echo "$account -- No io1 volume found in $region" && rm -f $io1_vol_id &> /dev/null + echo "" +fi + if [ -s "$io2_vol_id" ]; then + echo -n "$account -- Discovered ....( `egrep vol "$io2_vol_id"|wc -l` ).... io2 volumes in $region" && echo " || io2 volume-ids list : $io2_vol_id" + echo "" +else + echo "$account -- No io2 volume found in $region" && rm -f $io2_vol_id &> /dev/null + echo "" +fi +fi +} + +# Converting discovered GP2 volumes to GP3 +convert() +{ +if [ -s "$gp2_vol_id" ]; then + for vol_id in `cat "$gp2_vol_id"`;do echo "Modifying volume ${vol_id} to GP3" + result=$(/usr/bin/aws ec2 modify-volume --region "${region}" --volume-type=gp3 --volume-id "${vol_id}" --profile "${account}" | jq '.VolumeModification.ModificationState' | sed 's/"//g') + if [ $? -eq 0 ] && [ "${result}" == "modifying" ];then + echo "`date +%F-%T` SUCCESS: volume ${vol_id} changed to GP3 .. state 'modifying'" |tee -a "$account"_"$region"_"$dt"_modify_vol.log + echo "" + else + echo -n "`date +%F-%T` ERROR: couldn't change volume ${vol_id} type to GP3!" |tee -a "$account"_"$region"_"$dt"_modify_vol.log + echo "" + exit 1 + fi +done + +echo "" +echo "To track the progress of migration, please run - bash gp2_gp3_migration_progress.sh $gp2_vol_id $region" +echo "" +echo "GP2 volume-ids list : $gp2_vol_id" +echo "" +echo "GP2-GP3 volume modification logs : "$account"_"$region"_"$dt"_modify_vol.log" +echo "~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~" +echo "" +fi +} + +snap-convert() +{ +if [ -s "$gp2_vol_id" ]; then + for vol_id in `cat "$gp2_vol_id"`;do + /usr/bin/aws ec2 create-snapshot --region "${region}" --volume-id "${vol_id}" --profile "${account}" --description 'Pre GP3 migration' --tag-specifications 'ResourceType=snapshot,Tags=[{Key=state,Value=pre-gp3}]' &>> "$account"_"$region"_"$dt"_snapshot_vol.log + if [ $? -ne 0 ]; then + echo ERROR: "snapshot creation failed for volume_id $vol_id, skipping this volume, check "$account"_"$region"_"$dt"_snapshot_vol.log" + else + echo "Creating snapshot for "${vol_id}"" + + sleep 10 ; snap_state=$(/usr/bin/aws ec2 describe-snapshots --region "${region}" --profile "${account}" --filters Name=volume-id,Values="${vol_id}" Name=tag:state,Values=pre-gp3| jq -r '.Snapshots[].State') + + while [ "${snap_state}" != "completed" ];do + echo "`date +%F-%T` : waiting for snapshot completion .. .. " + sleep 10 + snap_state=$(/usr/bin/aws ec2 describe-snapshots --region "${region}" --profile "${account}" --filters Name=volume-id,Values="${vol_id}" Name=tag:state,Values=pre-gp3| jq -r '.Snapshots[].State') + done + snapshot_id=$(/usr/bin/aws ec2 describe-snapshots --region "${region}" --profile "${account}" --filters Name=volume-id,Values="${vol_id}" Name=tag:state,Values=pre-gp3 --query "reverse(sort_by(Snapshots, &StartTime))[0].SnapshotId") + echo "`date +%F-%T` SUCCESS: snapshot $snapshot_id completed for "${vol_id}"" |tee -a "$account"_"$region"_"$dt"_snapshot_vol.log + + echo "Modifying volume ${vol_id} to GP3" + result=$(/usr/bin/aws ec2 modify-volume --region "${region}" --volume-type=gp3 --volume-id "${vol_id}" --profile "${account}"| jq '.VolumeModification.ModificationState' | sed 's/"//g') + if [ $? -eq 0 ] && [ "${result}" == "modifying" ];then + echo "`date +%F-%T` SUCCESS: volume ${vol_id} changed to GP3 .. state 'modifying'" |tee -a "$account"_"$region"_"$dt"_modify_vol.log + echo "" + echo "~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~" + else + echo "`date +%F-%T` ERROR: could not change volume ${vol_id} type to GP3!" |tee -a "$account"_"$region"_"$dt"_modify_vol.log + echo "" + fi + fi +done +echo "To track the progress of migration, please run - bash gp2_gp3_migration_progress.sh $gp2_vol_id $region" +echo "" +echo "GP2 volume-ids list : $gp2_vol_id" +echo "" +echo "Snapshot logs before GP3 migration : "$account"_"$region"_"$dt"_snapshot_vol.log" +echo "" +echo "GP2-GP3 volume modification logs : "$account"_"$region"_"$dt"_modify_vol.log" +echo "~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~*~~~~~~" +echo "" +fi +} + +# Calling functions based on conditions .. +if [ $region = all ] && [[ $file_name == discover ]]; then + /usr/bin/aws ec2 describe-regions --all-regions --profile "${account}" --filters Name=opt-in-status,Values=opt-in-not-required,opted-in --query "Regions[].{Name:RegionName}" --output text > "$account"_region_list.txt + for region in `cat "$account"_region_list.txt`; do auto-disc ; done + exit +fi + +if [ $region = all ] && [[ $file_name == migrate ]] && [[ $snap != snapshot ]]; then + /usr/bin/aws ec2 describe-regions --all-regions --profile "${account}" --filters Name=opt-in-status,Values=opt-in-not-required,opted-in --query "Regions[].{Name:RegionName}" --output text > "$account"_region_list.txt + for region in `cat "$account"_region_list.txt`; do auto-disc ; convert ; done +exit +fi + +if [ $region = all ] && [[ $file_name == migrate ]] && [[ $snap == snapshot ]]; then + /usr/bin/aws ec2 describe-regions --all-regions --profile "${account}" --filters Name=opt-in-status,Values=opt-in-not-required,opted-in --query "Regions[].{Name:RegionName}" --output text > "$account"_region_list.txt + for region in `cat "$account"_region_list.txt`; do auto-disc ; snap-convert ; done +exit +fi + +case $disc in + discover ) + auto-disc + exit 0 + ;; +esac + +case $manual in + conversion ) + manual-disc + convert + exit 0 + ;; +esac + +case $manual in + snap_conversion ) + manual-disc + snap-convert + exit 0 + ;; +esac + +case $snap in + snapshot ) echo ""${account}" -- Discovering all GP2 volumes in $region ... " && echo ".. ... .... ..... ...... " && echo ".. ... .... ..... ...... ....... ........ ........." + auto-disc + snap-convert + exit 0 + ;; +esac + +case $action in + auto_conversion ) echo ""${account}" -- Discovering all GP2 volumes in $region ... " && echo ".. ... .... ..... ...... " && echo ".. ... .... ..... ...... ....... ........ ........." + auto-disc + convert + ;; + * ) echo "invalid response"; + exit 1;; +esac +# -- END -- diff --git a/ebs/gp2_gp3_migration_progress.sh b/ebs/gp2_gp3_migration_progress.sh new file mode 100644 index 00000000000..1555e67e1c0 --- /dev/null +++ b/ebs/gp2_gp3_migration_progress.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# Author : @razguru +# Version : 1.1 - Added cross account action through --profile assume role +# Desc : Track modification status of volumes .. +# +vol_id_list="$1" +region="$2" +account="$4" +dt=`date +%F-%T` +log=""$region"_"$dt"_vol_modification_progress.log" + +#Ensure vol_id_list is provided +if [ -z $vol_id_list ] || [ ! -s $vol_id_list ] ; then + echo "Please provide a file name listing target volume_ids followed by region_name .. example - $0 vol_id_list us-east-1" +exit 1 +fi + +#Ensure region is provided +if [ -z $region ]; then + echo "Please provide a region .. example - $0 vol_id_list us-east-1" +exit 1 +fi + + +for vol_id in `cat "$vol_id_list"`;do + echo `date +%F-%T` >> $log && /usr/bin/aws ec2 describe-volumes-modifications --region "${region}" --profile "${account}" --volume-ids "${vol_id}" --query "VolumesModifications[*].{ID:VolumeId,STATE:ModificationState,Progress:Progress}"; done| tee -a $log +if [ $? -eq 0 ]; then + echo " Logged the output to $log" +else + echo "Failed to run ec2 describe-volumes-modifications, pls investigate" +fi