Skip to content

Instantly share code, notes, and snippets.

@tsallou
Last active April 29, 2025 14:27
Show Gist options
  • Save tsallou/bb4a3156a55e03c675d1be5b27be846f to your computer and use it in GitHub Desktop.
Save tsallou/bb4a3156a55e03c675d1be5b27be846f to your computer and use it in GitHub Desktop.
Gcloud PAM TUI
#!/bin/bash
# Define project list and folder mapping (ID -> Display Name)
projects=("project_id_a" "project_id_b" "project_id_c")
declare -A folders
folders=(
["folder_id_1"]="Folder 1"
["folder_id_2"]="Folder 2"
)
# Function to display a menu and return the selected item
select_from_list() {
local prompt="$1"
shift
local options=("$@")
local menu_items=()
for i in "${!options[@]}"; do
menu_items+=("$i" "${options[i]}")
done
dialog --clear --menu "$prompt" 15 50 10 "${menu_items[@]}" 3>&1 1>&2 2>&3
}
# Function to map folder display name back to folder ID
get_folder_id() {
local display_name="$1"
for folder_id in "${!folders[@]}"; do
if [[ "${folders[$folder_id]}" == "$display_name" ]]; then
echo "$folder_id"
return
fi
done
}
# Ensure dialog is installed
if ! command -v dialog &> /dev/null; then
echo "Error: 'dialog' is not installed. Please install it and try again."
echo "For example, on Debian/Ubuntu: sudo apt-get install dialog"
exit 1
fi
# Prepare the combined selection list
scopes_list=("${projects[@]}")
for folder_id in "${!folders[@]}"; do
scopes_list+=("${folders[$folder_id]}")
done
# Let the user select a project or folder
scope_index=$(select_from_list "Select a Project or Folder:" "${scopes_list[@]}")
selected_scope="${scopes_list[$scope_index]}"
# Determine scope type and internal scope
if [[ " ${projects[*]} " =~ " ${selected_scope} " ]]; then
scope_type="project"
internal_scope="$selected_scope"
else
scope_type="folder"
internal_scope=$(get_folder_id "$selected_scope")
fi
scopes="$scope_type/$internal_scope"
# Fetch roles dynamically using gcloud command and suppress stderr
roles_output=$(gcloud pam entitlements search --location=global --caller-access-type=grant-requester --$scope_type="$internal_scope" --format="value(privilegedAccess.gcpIamAccess.roleBindings[].role, name.segment(5))" 2>/dev/null)
# Handle empty output
if [[ -z "$roles_output" ]]; then
dialog --msgbox "No roles found for the selected $scope_type: $selected_scope" 10 50
clear
exit 1
fi
# Parse roles and sort by role names alphabetically
IFS=$'\n'
read -r -d '' -a roles <<< "$roles_output"
sorted_roles=$(printf "%s\n" "${roles[@]}" | sort)
role_names=()
entitlement_ids=()
while IFS= read -r role; do
role_names+=("$(echo "$role" | awk '{print $1}')") # First field: role name
entitlement_ids+=("$(echo "$role" | awk '{print $2}')") # Second field: entitlement ID
done <<< "$sorted_roles"
# Select a role
role_index=$(select_from_list "Select a Role for $scope_type: $selected_scope" "${role_names[@]}")
selected_role="${role_names[$role_index]}"
selected_entitlement="${entitlement_ids[$role_index]}"
# Input duration and justification
duration_minutes=$(dialog --clear --inputbox "Enter Duration (in minutes):" 8 50 30 3>&1 1>&2 2>&3)
duration_seconds=$((duration_minutes * 60))
justification=$(dialog --clear --inputbox "Enter Justification:" 8 50 "" 3>&1 1>&2 2>&3)
# Confirm the selections
dialog --msgbox "Selected Scope: $scopes\nSelected Role: $selected_role\nDuration: $duration_seconds seconds\nJustification: $justification" 10 50
# Construct and execute the gcloud command
gcloud_command="gcloud pam grants create --entitlement=$selected_entitlement --$scope_type=$internal_scope --location=global --requested-duration=${duration_seconds}s --justification=\"$justification\""
command_output=$(eval "$gcloud_command" 2>&1)
command_status=$?
# Display the result
if [[ $command_status -eq 0 ]]; then
dialog --msgbox "Command executed successfully:\n$gcloud_command" 15 50
else
dialog --msgbox "Command execution failed with status $command_status:\n\nLogs:\n$command_output" 15 60
fi
# Cleanup and exit
clear
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment