audit-labs/audit-tools

A collection of scripts, queries, and other goodies you can use in an audit.

clone: git clone https://gitbay.org/audit-labs/audit-tools.git

main: applications/aws/aws_iam_users.sh · raw

  1#!/bin/bash
  2
  3# This script analyzes all IAM Identity Center assignments for a specific
  4# AWS account and outputs the details to a single JSON file.
  5# Corrected Logic: First finds provisioned permission sets, then gets assignments for each.
  6
  7# --- Configuration ---
  8ACCOUNT_NAME=""
  9
 10# --- Prerequisite check ---
 11if ! command -v aws &> /dev/null || ! command -v jq &> /dev/null; then
 12    echo "Error: Both AWS CLI and jq are required. Please install them and ensure they are in your PATH." >&2
 13    exit 1
 14fi
 15
 16# --- Step 1: Get Instance details and find the target Account ID ---
 17echo "Fetching IAM Identity Center and Account details..."
 18
 19INSTANCE_ARN=$(aws sso-admin list-instances --query "Instances[0].InstanceArn" --output text)
 20IDENTITY_STORE_ID=$(aws sso-admin list-instances --query "Instances[0].IdentityStoreId" --output text)
 21
 22if [[ -z "$INSTANCE_ARN" ]] || [[ -z "$IDENTITY_STORE_ID" ]]; then
 23    echo "Error: Could not find IAM Identity Center instance ARN or Identity Store ID." >&2
 24    exit 1
 25fi
 26
 27ACCOUNT_ID=$(aws organizations list-accounts --query "Accounts[?Name=='$ACCOUNT_NAME' && Status=='ACTIVE'].Id" --output text)
 28
 29if [[ -z "$ACCOUNT_ID" ]]; then
 30    echo "Error: Could not find an active AWS account with the name '$ACCOUNT_NAME'." >&2
 31    exit 1
 32fi
 33
 34echo "Successfully found Account '$ACCOUNT_NAME' with ID: $ACCOUNT_ID"
 35echo "---"
 36
 37# --- Step 2: Get all Permission Sets provisioned to the account ---
 38echo "Step 2: Finding all permission sets provisioned to '$ACCOUNT_NAME'..."
 39PROVISIONED_SETS_ARN=$(aws sso-admin list-permission-sets-provisioned-to-account \
 40    --instance-arn "$INSTANCE_ARN" \
 41    --account-id "$ACCOUNT_ID" \
 42    --query "PermissionSets[]" --output text)
 43
 44if [[ -z "$PROVISIONED_SETS_ARN" ]]; then
 45    echo "No permission sets are provisioned for account '$ACCOUNT_NAME'."
 46    exit 0
 47fi
 48
 49echo "Found provisioned permission sets. Now checking assignments for each..."
 50echo "---"
 51
 52# --- Caches to store fetched data ---
 53declare -A PERMISSION_SET_CACHE
 54declare -A PRINCIPAL_NAME_CACHE
 55FINAL_JSON_ARRAY="[]" # Initialize an empty JSON array
 56
 57# --- Step 3: Loop through each provisioned permission set and get its assignments ---
 58for PS_ARN in $PROVISIONED_SETS_ARN; do
 59    
 60    # Get assignments for this specific permission set in this account
 61    ACCOUNT_ASSIGNMENTS=$(aws sso-admin list-account-assignments \
 62        --instance-arn "$INSTANCE_ARN" \
 63        --account-id "$ACCOUNT_ID" \
 64        --permission-set-arn "$PS_ARN" \
 65        --query "AccountAssignments[]" --output json)
 66
 67    if [[ "$(echo "$ACCOUNT_ASSIGNMENTS" | jq 'length')" -eq 0 ]]; then
 68        echo "  -> Permission Set ARN $PS_ARN is provisioned but has no active assignments."
 69        continue
 70    fi
 71
 72    # Since there are assignments, let's get the permission set's details (policies, name)
 73    # Using a cache to avoid redundant calls if a PS is somehow listed twice
 74    if [[ -z "${PERMISSION_SET_CACHE[$PS_ARN]}" ]]; then
 75        echo "  -> Fetching policies for Permission Set: $PS_ARN"
 76        PS_NAME=$(aws sso-admin describe-permission-set --instance-arn "$INSTANCE_ARN" --permission-set-arn "$PS_ARN" --query "PermissionSet.Name" --output text)
 77        MANAGED_POLICIES=$(aws sso-admin list-managed-policies-in-permission-set --instance-arn "$INSTANCE_ARN" --permission-set-arn "$PS_ARN" --query "AttachedManagedPolicies[].Arn" --output json)
 78        INLINE_POLICY=$(aws sso-admin get-inline-policy-for-permission-set --instance-arn "$INSTANCE_ARN" --permission-set-arn "$PS_ARN" --query "InlinePolicy" --output json)
 79        
 80        PERMISSION_SET_CACHE[$PS_ARN]=$(jq -n \
 81            --arg name "$PS_NAME" \
 82            --argjson managed "$MANAGED_POLICIES" \
 83            --argjson inline "$INLINE_POLICY" \
 84            '{name: $name, policies: {managed_policies: $managed, inline_policy: $inline}}')
 85    fi
 86    CACHED_PS_DETAILS=${PERMISSION_SET_CACHE[$PS_ARN]}
 87
 88    # Now process each assignment found for this permission set
 89    for row in $(echo "${ACCOUNT_ASSIGNMENTS}" | jq -r '.[] | @base64'); do
 90        _jq() { echo ${row} | base64 --decode | jq -r ${1}; return 0; }
 91        PRINCIPAL_TYPE=$(_jq '.PrincipalType')
 92        PRINCIPAL_ID=$(_jq '.PrincipalId')
 93
 94        # Get Principal (User/Group) Name, using a cache
 95        if [[ -z "${PRINCIPAL_NAME_CACHE[$PRINCIPAL_ID]}" ]]; then
 96            PRINCIPAL_NAME=""
 97            if [[ "$PRINCIPAL_TYPE" == "USER" ]]; then
 98                PRINCIPAL_NAME=$(aws identitystore describe-user --identity-store-id "$IDENTITY_STORE_ID" --user-id "$PRINCIPAL_ID" --query "UserName" --output text 2>/dev/null)
 99            elif [[ "$PRINCIPAL_TYPE" == "GROUP" ]]; then
100                PRINCIPAL_NAME=$(aws identitystore describe-group --identity-store-id "$IDENTITY_STORE_ID" --group-id "$PRINCIPAL_ID" --query "DisplayName" --output text 2>/dev/null)
101            fi
102            PRINCIPAL_NAME_CACHE[$PRINCIPAL_ID]=${PRINCIPAL_NAME:-"ID: $PRINCIPAL_ID"}
103        fi
104        CACHED_PRINCIPAL_NAME=${PRINCIPAL_NAME_CACHE[$PRINCIPAL_ID]}
105        
106        echo "  -> Found Assignment: ${CACHED_PRINCIPAL_NAME} ($PRINCIPAL_TYPE) -> $(echo "$CACHED_PS_DETAILS" | jq -r .name)"
107
108        # Build the final JSON object for this assignment
109        ASSIGNMENT_OUTPUT=$(jq -n \
110            --arg principal_type "$PRINCIPAL_TYPE" \
111            --arg principal_name "$CACHED_PRINCIPAL_NAME" \
112            --argjson ps_details "$CACHED_PS_DETAILS" \
113            '{principal: {type: $principal_type, name: $principal_name}, permission_set: $ps_details}')
114
115        FINAL_JSON_ARRAY=$(echo "$FINAL_JSON_ARRAY" | jq --argjson new_entry "$ASSIGNMENT_OUTPUT" '. += [$new_entry]')
116    done
117done
118
119# --- Step 4: Save the final report ---
120OUTPUT_FILENAME="report_${ACCOUNT_NAME}.json"
121echo "$FINAL_JSON_ARRAY" | jq '.' > "$OUTPUT_FILENAME"
122
123echo "---"
124echo "Success! Report saved to '$OUTPUT_FILENAME'"
125echo "The file contains all user/group assignments and their policies for account '$ACCOUNT_NAME'."