Created
October 10, 2017 11:28
-
-
Save rowleyaj/c46e72bfda41ccb4d38db27e0bbd4363 to your computer and use it in GitHub Desktop.
AWS IAM Import -> Terraform
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
pgname=$0 | |
LOG_INFO=false | |
# | |
# Colours | |
# | |
RED="\\033[31m" | |
GREEN="\\033[32m" | |
YELLOW="\\033[33m" | |
RESET="\\033[0m" | |
# | |
# Logging functions | |
# | |
function log { | |
echo "$*" | |
} | |
function log_info { | |
if [ "$LOG_INFO" = false ]; then | |
echo "$*" | |
fi | |
} | |
function log_warn { | |
echo -e "${YELLOW}$*${RESET}" | |
} | |
function log_err { | |
echo -e "${RED}$*${RESET}" | |
} | |
# | |
# Helpers | |
# | |
function prompt { | |
local message=$1 | |
local yes_cmd=$2 | |
local no_cmd=$3 | |
local rc=0 | |
log "${message} (y/n)" | |
while read -r yn; do | |
case $yn in | |
[Yy] ) eval "${yes_cmd}"; rc=0; break;; | |
[Nn] ) eval "${no_cmd}"; rc=1; break;; | |
* ) echo "${message}"; | |
esac | |
done | |
return $rc | |
} | |
function file_exists { | |
local filename=$1 | |
local rc=0 | |
if [ -e "${filename}" ]; then | |
prompt \ | |
"${filename} exists, overwrite file?" \ | |
"rm ${filename}" \ | |
"log_warn \"Skipping ${filename}\"; exit 1" | |
rc=$? | |
fi | |
} | |
function timestamp_label { | |
echo "# Generated by import script on $(date -u)." | |
} | |
function cache_lookup { | |
local cmd=$1 | |
local cache_dir='.cache' | |
local cache_file="${cache_dir}/${cmd//[[:space:]\:\/]/}" | |
# Create .cache directory if doesn't exist. | |
if [ ! -d .cache ]; then | |
mkdir "${cache_dir}" | |
fi | |
if [ "${USE_CACHE}" == "false" ] || [ ! -e "${cache_file}" ]; then | |
eval "${cmd}" > "${cache_file}" | |
fi | |
cat "${cache_file}" | |
} | |
function safe_tf_run { | |
local resource=$1 | |
terraform plan -target="${resource}" -out=plan.out -detailed-exitcode > /dev/null | |
case $? in | |
0) # Succeeded with empty diff (no changes) | |
return | |
;; | |
1) # Error | |
log_err "Error planning resource"; | |
exit 1 | |
;; | |
2) # Succeeded with non-empty diff (changes present) | |
log_warn "Plan shows changes:" | |
;; | |
esac | |
terraform show plan.out | |
prompt 'Apply plan?' 'terraform apply plan.out' 'log_warn skipping apply' | |
} | |
function managed_policy_attachments { | |
# subject is the group, role or users that we want to get the attached policies for. | |
local subject=$1 | |
# type should be one of group, role, user | |
local type=$2 | |
local run=$3 | |
safe_name="${subject//\./}" | |
filename="${type}_${safe_name}.tf" | |
aws_cmd="aws iam list-attached-${type}-policies --${type}-name ${subject}" | |
for managed_policy in $(cache_lookup "${aws_cmd}" | jq -r '.AttachedPolicies[].PolicyName'); do | |
log "Creating aws_iam_${type}_policy_attachment resource: ${subject}_${managed_policy}" | |
echo " | |
resource \"aws_iam_${type}_policy_attachment\" \"${subject}_${managed_policy}\" { | |
${type} = \"${subject}\" | |
# This contents of this policy can be viewed in policy_${managed_policy}.tf | |
policy_arn = \"\${aws_iam_policy.${managed_policy}.arn}\" | |
}" >> "${filename}" | |
if [ "${run}" == 'true' ]; then | |
safe_tf_run "aws_iam_${type}_policy_attachment.${subject}_${managed_policy}" | |
fi | |
done | |
} | |
function inline_policies { | |
# subject is the group, role or users that we want to get the inline policies for. | |
local subject=$1 | |
# type should be one of group, role, user | |
local type=$2 | |
local run=$3 | |
safe_name="${subject//\./}" | |
filename="${type}_${safe_name}.tf" | |
aws_cmd="aws iam list-${type}-policies --${type}-name ${subject}" | |
for inline_policy in $(cache_lookup "${aws_cmd}" | jq -r '.PolicyNames[]'); do | |
policy=$(cache_lookup "aws iam get-${type}-policy --${type}-name ${subject} --policy-name ${inline_policy}" | jq -r '.PolicyDocument | walk(if type == "string" then gsub("\\$"; "$$") else . end)') | |
log "Creating aws_iam_${type}_policy resource: ${subject}_${inline_policy}" | |
echo " | |
resource \"aws_iam_${type}_policy\" \"${subject}_${inline_policy}\" { | |
name = \"${inline_policy}\" | |
${type} = \"${subject}\" | |
policy = <<POLICY_DOCUMENT | |
${policy} | |
POLICY_DOCUMENT | |
}" >> "${filename}" | |
if [ "${run}" == 'true' ]; then | |
safe_tf_run "aws_iam_${type}_policy.${subject}_${inline_policy}" | |
fi | |
done | |
} | |
# | |
# main | |
# | |
if [ "$IMPORT" == 'true' ] || [ "$IMPORT" == 'false' ] ; then | |
IMPORT_MANAGED_POLICIES="$IMPORT" | |
IMPORT_USERS="$IMPORT" | |
IMPORT_GROUPS="$IMPORT" | |
IMPORT_ROLES="$IMPORT" | |
IMPORT_INSTANCE_PROFILES="$IMPORT" | |
else | |
if [ -z "${IMPORT_MANAGED_POLICIES}" ]; then | |
prompt 'Managed Policies - Import?' 'IMPORT_MANAGED_POLICIES=true' 'IMPORT_MANAGED_POLICIES=false' | |
fi | |
if [ -z "${IMPORT_USERS}" ]; then | |
prompt 'Users - Import?' 'IMPORT_USERS=true' 'IMPORT_USERS=false' | |
fi | |
if [ -z "${IMPORT_GROUPS}" ]; then | |
prompt 'Groups - Import?' 'IMPORT_GROUPS=true' 'IMPORT_GROUPS=false' | |
fi | |
if [ -z "${IMPORT_ROLES}" ]; then | |
prompt 'Roles - Import?' 'IMPORT_ROLES=true' 'IMPORT_ROLES=false' | |
fi | |
if [ -z "${IMPORT_INSTANCE_PROFILES}" ]; then | |
prompt 'Instance Profiles - Import?' 'IMPORT_INSTANCE_PROFILES=true' 'IMPORT_INSTANCE_PROFILES=false' | |
fi | |
fi | |
if [ "$RUN" == 'true' ] || [ "$RUN" == 'false' ] ; then | |
RUN_MANAGED_POLICIES="$RUN" | |
RUN_USERS="$RUN" | |
RUN_GROUPS="$RUN" | |
RUN_ROLES="$RUN" | |
RUN_INSTANCE_PROFILES="$RUN" | |
else | |
if [ -z "${RUN_MANAGED_POLICIES}" ]; then | |
prompt 'Managed Policies - Run terraform?' 'RUN_MANAGED_POLICIES=true' 'RUN_MANAGED_POLICIES=false' | |
fi | |
if [ -z "${RUN_USERS}" ]; then | |
prompt 'Users - Run terraform?' 'RUN_USERS=true' 'RUN_USERS=false' | |
fi | |
if [ -z "${RUN_GROUPS}" ]; then | |
prompt 'Groups - Run terraform?' 'RUN_GROUPS=true' 'RUN_GROUPS=false' | |
fi | |
if [ -z "${RUN_ROLES}" ]; then | |
prompt 'Roles - Run terraform?' 'RUN_ROLES=true' 'RUN_ROLES=false' | |
fi | |
if [ -z "${RUN_INSTANCE_PROFILES}" ]; then | |
prompt 'Instance Profiles - Run terraform?' 'RUN_INSTANCE_PROFILES=true' 'RUN_INSTANCE_PROFILES=false' | |
fi | |
fi | |
terraform init | |
# Pull managed policies | |
timestamp_label > policies.txt | |
for managed_policy in $(cache_lookup 'aws iam list-policies' | jq -r '.Policies[].PolicyName'); do | |
arn=$(cache_lookup 'aws iam list-policies' | jq -r ".Policies[] | select(.PolicyName==\"${managed_policy}\").Arn") | |
aws_managed=$(cache_lookup "aws iam get-policy --policy-arn ${arn}" | jq -r '.Policy.Arn | test("::aws")') | |
path=$(cache_lookup "aws iam get-policy --policy-arn ${arn}" | jq -r '.Policy.Path') | |
# -r is not used here as we want to keep the JSON escaped quotes | |
description=$(cache_lookup "aws iam get-policy --policy-arn ${arn}" | jq '.Policy.Description') | |
# however we don't want double quotes around the outside, so strip the first and last " | |
description="${description#\"}" | |
description="${description%\"}" | |
version=$(cache_lookup "aws iam get-policy --policy-arn ${arn}" | jq -r '.Policy.DefaultVersionId') | |
policy=$(cache_lookup "aws iam get-policy-version --policy-arn ${arn} --version-id ${version}" | jq -r '.PolicyVersion.Document | walk(if type == "string" then gsub("\\$"; "$$") else . end)') | |
safe_name="${managed_policy//\./}" | |
filename="policy_${safe_name}.tf" | |
log "Creating aws_iam_policy resource: ${safe_name}" | |
timestamp_label > "${filename}" | |
if [ "${aws_managed}" == 'true' ]; then | |
echo "### AWS MANAGED POLICY - READ ONLY - DO NOT EDIT" >> "${filename}" | |
fi | |
echo " | |
resource \"aws_iam_policy\" \"${safe_name}\" { | |
name = \"${managed_policy}\" | |
description = \"${description#null}\" | |
path = \"${path}\"" >> "${filename}" | |
if [ "${aws_managed}" == 'true' ]; then | |
echo "### AWS MANAGED POLICY - READ ONLY - DO NOT EDIT" >> "${filename}" | |
fi | |
echo " | |
policy = <<POLICY_DOCUMENT | |
${policy} | |
POLICY_DOCUMENT" >> "${filename}" | |
if [ "${aws_managed}" == 'true' ]; then | |
echo " | |
# As this is an AWS managed policy, ignore any changes to the policy | |
lifecycle { | |
ignore_changes = [ \"policy\" ] | |
}" >> "${filename}" | |
fi | |
echo "}" >> "${filename}" | |
echo "${safe_name}" >> policies.txt | |
if [ "$IMPORT_MANAGED_POLICIES" == true ]; then | |
log "Importingaws_iam_policy resource: ${safe_name}" | |
terraform import "aws_iam_policy.${safe_name}" "${arn}" | |
fi | |
if [ "$RUN_MANAGED_POLICIES" == true ]; then | |
safe_tf_run "aws_iam_policy.${safe_name}" "{RUN_MANAGED_POLICIES}" | |
fi | |
done | |
# Create a per user file with any policies attached to user | |
timestamp_label > users.txt | |
for user in $(cache_lookup 'aws iam list-users' | jq -r '.Users[].UserName'); do | |
path=$(cache_lookup 'aws iam list-users' | jq -r ".Users[] | select(.UserName==\"${user}\").Path") | |
safe_name=${user//\./} | |
filename="user_${safe_name}.tf" | |
log "Creating aws_iam_user resource: ${safe_name}" | |
timestamp_label > "${filename}" | |
echo " | |
resource \"aws_iam_user\" \"${safe_name}\" { | |
name = \"${user}\" | |
path = \"${path}\" | |
force_destroy = false | |
}" >> "${filename}" | |
echo "${safe_name}" >> users.txt | |
if [ "$IMPORT_USERS" == true ]; then | |
log "Importing aws_iam_user resource: ${safe_name}" | |
terraform import "aws_iam_user.${safe_name}" "${user}" | |
fi | |
if [ "$RUN_USERS" == true ]; then | |
safe_tf_run "aws_iam_user.${safe_name}" "{RUN_USERS}" | |
fi | |
managed_policy_attachments "${user}" 'user' | |
inline_policies "${user}" 'user' | |
done | |
# Create file per group with memberships | |
timestamp_label > groups.txt | |
for group in $(cache_lookup 'aws iam list-groups' | jq -r '.Groups[].GroupName'); do | |
path=$(cache_lookup 'aws iam list-groups' | jq -r ".Groups[] | select(.GroupName==\"${group}\").Path") | |
safe_name=${group//\./} | |
filename="group_${safe_name}.tf" | |
log "Creating aws_iam_group resource: ${safe_name}" | |
timestamp_label > "${filename}" | |
echo " | |
resource \"aws_iam_group\" \"${safe_name}\" { | |
name = \"${group}\" | |
path = \"${path}\" | |
}" >> "${filename}" | |
echo "${safe_name}" >> groups.txt | |
if [ "$IMPORT_GROUPS" == true ]; then | |
log "Importing aws_iam_group resource: ${safe_name}" | |
terraform import "aws_iam_group.${safe_name}" "${group}" | |
fi | |
if [ "$RUN_GROUPS" == true ]; then | |
safe_tf_run "aws_iam_group.${safe_name}" "{RUN_GROUPS}" | |
fi | |
# Whilst looping group also write membership | |
log "Creating aws_iam_group_membership resource: ${safe_name}" | |
echo " | |
resource \"aws_iam_group_membership\" \"${safe_name}\" { | |
name = \"${safe_name}-membership\" | |
group = \"\${aws_iam_group.${safe_name}.name}\" | |
users = [" >> "${filename}" | |
for groupmember in $(cache_lookup "aws iam get-group --group-name ${group}" | jq -r '.Users[].UserName' | sort); do | |
member_safe_name=${groupmember//\./} | |
echo " | |
\"\${aws_iam_user.${member_safe_name}.name}\"," >> "${filename}" | |
done | |
echo " | |
]}" >> "${filename}" | |
managed_policy_attachments "${group}" 'group' | |
inline_policies "${group}" 'group' | |
done | |
# Create file per role | |
timestamp_label > roles.txt | |
for role in $(cache_lookup 'aws iam list-roles' | jq -r '.Roles[].RoleName'); do | |
path=$(cache_lookup 'aws iam list-roles' | jq -r ".Roles[] | select(.RoleName==\"${role}\").Path") | |
# -r is not used here as we want to keep the JSON escaped quotes | |
description=$(cache_lookup 'aws iam list-roles' | jq ".Roles[] | select(.RoleName==\"${role}\").Description") | |
# however we don't want double quotes around the outside, so strip the first and last " | |
description=${description#\"} | |
description=${description%\"} | |
assume_role_policy=$(cache_lookup 'aws iam list-roles' | jq -r ".Roles[] | select(.RoleName==\"${role}\").AssumeRolePolicyDocument | walk(if type == \"string\" then gsub(\"\\\\$\"; \"\$\$\") else . end)") | |
safe_name=${role//\./} | |
filename="role_${safe_name}.tf" | |
log "Creating aws_iam_role resource: ${safe_name}" | |
timestamp_label > "${filename}" | |
echo " | |
resource \"aws_iam_role\" \"${safe_name}\" { | |
name = \"${role}\" | |
description = \"${description#null}\" | |
path = \"${path}\" | |
assume_role_policy = <<POLICY_DOCUMENT | |
${assume_role_policy} | |
POLICY_DOCUMENT | |
}" >> "${filename}" | |
echo "${safe_name}" >> roles.txt | |
if [ "$IMPORT_ROLES" == true ]; then | |
log "Importing aws_iam_role resource: ${safe_name}" | |
terraform import "aws_iam_role.${safe_name}" "${role}" | |
fi | |
if [ "$RUN_ROLES" == true ]; then | |
safe_tf_run "aws_iam_role.${safe_name}" "{RUN_ROLES}" | |
fi | |
managed_policy_attachments "${role}" 'role' | |
inline_policies "${role}" 'role' | |
done | |
# Pull instance profiles, add to role file based on linked role | |
timestamp_label > instance_profiles.txt | |
for instance_profile in $(cache_lookup 'aws iam list-instance-profiles' | jq -r '.InstanceProfiles[].InstanceProfileName'); do | |
path=$(cache_lookup 'aws iam list-instance-profiles' | jq -r ".InstanceProfiles[] | select(.InstanceProfileName==\"${instance_profile}\").Path") | |
role=$(cache_lookup 'aws iam list-instance-profiles' | jq -r ".InstanceProfiles[] | select(.InstanceProfileName==\"${instance_profile}\").Roles[0].RoleName") | |
safe_name=${instance_profile//\./} | |
role_safe_name=${role//\./} | |
filename="role_${role_safe_name}.tf" | |
log "Creating aws_iam_instance_profile resource: ${safe_name}" | |
echo " | |
resource \"aws_iam_instance_profile\" \"${safe_name}\" { | |
name = \"${instance_profile}\" | |
path = \"${path}\" | |
role = \"\${aws_iam_role.${role_safe_name}.name}\" | |
}" >> "${filename}" | |
echo "${safe_name}" >> instance_profiles.txt | |
if [ "$IMPORT_INSTANCE_PROFILES" == true ]; then | |
log "Importing aws_iam_instance_profile resource: ${safe_name}" | |
terraform import "aws_iam_instance_profile.${safe_name}" "${instance_profile}" | |
fi | |
if [ "$RUN_INSTANCE_PROFILES" == true ]; then | |
safe_tf_run "aws_iam_instance_profile.${safe_name}" "{RUN_INSTANCE_PROFILES}" | |
fi | |
done | |
terraform fmt > /dev/null |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment