From 6651c02e6d919ad153992fc6d62b113a3c781173 Mon Sep 17 00:00:00 2001 From: Christopher Weibel Date: Thu, 29 Jan 2026 12:13:19 -0500 Subject: [PATCH 1/4] Add script to copy Cloud Foundry user roles from source to target user --- cloudfoundry/copy-user-org-and-space-roles.sh | 568 ++++++++++++++++++ 1 file changed, 568 insertions(+) create mode 100644 cloudfoundry/copy-user-org-and-space-roles.sh diff --git a/cloudfoundry/copy-user-org-and-space-roles.sh b/cloudfoundry/copy-user-org-and-space-roles.sh new file mode 100644 index 0000000..54c5f7b --- /dev/null +++ b/cloudfoundry/copy-user-org-and-space-roles.sh @@ -0,0 +1,568 @@ +#!/bin/bash +# Script to copy Cloud Foundry user roles from source to target user +set -e + +# Function to display usage +usage() { + echo "Usage: $0 --source-user-id --source-origin --target-user-id --target-origin [OPTIONS]" + echo "" + echo "Required:" + echo " --source-user-id Source CF username" + echo " --source-origin UAA origin for source user" + echo " --target-user-id Target CF username" + echo " --target-origin UAA origin for target user" + echo "" + echo "Optional:" + echo " --dry-run-source Show source user roles only" + echo " --dry-run-target Show commands to setup target user roles" + echo " --dry-run Print all commands without executing (legacy mode)" + echo " --delete Delete source user after role transfer" + echo " --deactivate Deactivate source user in UAA after role transfer" + echo " --verbose Enable verbose logging" + echo "" + echo "Examples:" + echo " # Show source user roles" + echo " $0 --source-user-id john.doe --source-origin uaa --target-user-id jane.doe --target-origin uaa --dry-run-source" + echo "" + echo " # Show target setup commands" + echo " $0 --source-user-id john.doe --source-origin uaa --target-user-id jane.doe --target-origin uaa --dry-run-target" + echo "" + echo " # Copy roles and delete source user" + echo " $0 --source-user-id john.doe --source-origin uaa --target-user-id jane.doe --target-origin uaa --delete" + echo "" + echo "Notes:" + echo "- This will not copy over uaa group memberships like 'scim.read' or 'cloud_controller.admin'." + echo "- Log into the CF CLI and ensure you have the necessary permissions to assign roles to the target user." + echo "- Current limit is a hard coded maximum of 5000 roles per user due to CF API pagination." + exit 1 +} + +# Initialize variables +SOURCE_USER_ID="" +SOURCE_ORIGIN="" +TARGET_USER_ID="" +TARGET_ORIGIN="" +DRY_RUN=false +DRY_RUN_SOURCE=false +DRY_RUN_TARGET=false +DELETE=false +DEACTIVATE=false +VERBOSE=false + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --source-user-id) + SOURCE_USER_ID="$2" + shift 2 + ;; + --source-origin) + SOURCE_ORIGIN="$2" + shift 2 + ;; + --target-user-id) + TARGET_USER_ID="$2" + shift 2 + ;; + --target-origin) + TARGET_ORIGIN="$2" + shift 2 + ;; + --dry-run) + DRY_RUN=true + shift + ;; + --dry-run-source) + DRY_RUN_SOURCE=true + shift + ;; + --dry-run-target) + DRY_RUN_TARGET=true + shift + ;; + --delete) + DELETE=true + shift + ;; + --deactivate) + DEACTIVATE=true + shift + ;; + --verbose) + VERBOSE=true + shift + ;; + *) + echo "Unknown option: $1" + usage + ;; + esac +done + +# Validate required parameters +if [[ -z "$SOURCE_USER_ID" || -z "$SOURCE_ORIGIN" || -z "$TARGET_USER_ID" || -z "$TARGET_ORIGIN" ]]; then + echo "Error: Missing required parameters" + usage +fi + +# Validate mutually exclusive options +if [[ "$DELETE" == true && "$DEACTIVATE" == true ]]; then + echo "Error: Cannot both delete and deactivate source user" + exit 1 +fi + +# Logging function +log() { + if [[ "$VERBOSE" == true ]]; then + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >&2 + fi +} + +# Function to execute or print commands based on dry-run mode +execute_command() { + local cmd="$1" + echo "Command: $cmd" + if [[ "$DRY_RUN" == false ]]; then + eval "$cmd" + fi +} + +# Function to get user GUID from CF API +get_user_guid() { + local user_id="$1" + local origin="$2" + + cf curl "/v3/users?origins=${origin}&usernames=${user_id}" | jq -r '.resources[0].guid // empty' +} + +# Function to check if user exists +check_user_exists() { + local user_id="$1" + local origin="$2" + local user_guid + + user_guid=$(get_user_guid "$user_id" "$origin") + if [[ -z "$user_guid" || "$user_guid" == "null" ]]; then + return 1 + else + return 0 + fi +} + +# Function to get org name from space +get_org_name_from_space() { + local space_guid="$1" + local space_info org_guid + + space_info=$(cf curl "/v3/spaces/$space_guid") + org_guid=$(echo "$space_info" | jq -r '.relationships.organization.data.guid // empty') + + if [[ -n "$org_guid" ]]; then + query_org_name "$org_guid" + fi +} + +# Function to query org name +query_org_name() { + local org_guid="$1" + if [[ -n "$org_guid" ]]; then + cf curl "/v3/organizations/$org_guid" | jq -r '.name // empty' + fi +} + +# Function to query space name +query_space_name() { + local space_guid="$1" + if [[ -n "$space_guid" ]]; then + cf curl "/v3/spaces/$space_guid" | jq -r '.name // empty' + fi +} + +# Function to add organization_user role via CF API +add_organization_user_role() { + local user_guid="$1" + local org_guid="$2" + local org_name="$3" + + local payload=$(cat < Date: Thu, 29 Jan 2026 15:38:17 -0500 Subject: [PATCH 2/4] Update cloudfoundry/copy-user-org-and-space-roles.sh Co-authored-by: Peter Burkholder Signed-off-by: Chris Weibel --- cloudfoundry/copy-user-org-and-space-roles.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cloudfoundry/copy-user-org-and-space-roles.sh b/cloudfoundry/copy-user-org-and-space-roles.sh index 54c5f7b..b603dc1 100644 --- a/cloudfoundry/copy-user-org-and-space-roles.sh +++ b/cloudfoundry/copy-user-org-and-space-roles.sh @@ -184,7 +184,8 @@ add_organization_user_role() { local org_guid="$2" local org_name="$3" - local payload=$(cat < Date: Thu, 29 Jan 2026 15:57:34 -0500 Subject: [PATCH 3/4] Add suggestions for exit and local assignments --- cloudfoundry/copy-user-org-and-space-roles.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cloudfoundry/copy-user-org-and-space-roles.sh b/cloudfoundry/copy-user-org-and-space-roles.sh index b603dc1..787a15a 100644 --- a/cloudfoundry/copy-user-org-and-space-roles.sh +++ b/cloudfoundry/copy-user-org-and-space-roles.sh @@ -1,6 +1,6 @@ #!/bin/bash # Script to copy Cloud Foundry user roles from source to target user -set -e +set -e -u -o pipefail # Function to display usage usage() { @@ -208,8 +208,11 @@ EOF echo "cf curl -X POST '/v3/roles' -d '$payload'" else echo "Adding organization_user role for $TARGET_USER_ID in org $org_name..." - local response=$(cf curl -X POST "/v3/roles" -d "$payload") - local status_code=$(echo "$response" | jq -r '.errors[0].code // empty') + local response="" + local status_code="" + + response=$(cf curl -X POST "/v3/roles" -d "$payload") + status_code=$(echo "$response" | jq -r '.errors[0].code // empty') if [[ -n "$status_code" ]]; then # Check if it's a duplicate role error (10008) @@ -347,8 +350,9 @@ while IFS='|' read -r role_type org_guid space_guid; do cli_role="SpaceSupporter" ;; *) + echo "Error: Unknown role type detected: $role_type" >&2 log "Unknown role type: $role_type" - continue + exit 1 ;; esac From 846c4f734fde26dafce2bc8d841c15364158e975 Mon Sep 17 00:00:00 2001 From: Christopher Weibel Date: Thu, 29 Jan 2026 16:05:45 -0500 Subject: [PATCH 4/4] Turn on deactivate --- cloudfoundry/copy-user-org-and-space-roles.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudfoundry/copy-user-org-and-space-roles.sh b/cloudfoundry/copy-user-org-and-space-roles.sh index 787a15a..720f094 100644 --- a/cloudfoundry/copy-user-org-and-space-roles.sh +++ b/cloudfoundry/copy-user-org-and-space-roles.sh @@ -546,7 +546,7 @@ if [[ "$SUCCESS" == true || "$DRY_RUN" == true ]]; then if [[ "$DEACTIVATE" == true ]]; then echo "Deactivating user $SOURCE_USER_ID..." DEACTIVATE_CMD="uaac user deactivate \"$SOURCE_USER_ID\" --origin \"$SOURCE_ORIGIN\"" - #execute_command "$DEACTIVATE_CMD" + execute_command "$DEACTIVATE_CMD" fi else echo "Skipping post-transfer actions due to role assignment failures"