Skip to content

Commit

Permalink
initial commit;
Browse files Browse the repository at this point in the history
  • Loading branch information
braun-daniel committed Jun 28, 2024
1 parent 0c616f1 commit b34bba6
Show file tree
Hide file tree
Showing 2 changed files with 277 additions and 0 deletions.
81 changes: 81 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@

# PIM Assignment Script

This script automates the process of making Privileged Identity Management (PIM) assignments in Azure. It leverages `fzf` for fuzzy searching subscription names and resource group names for user convenience.

## Features

- Fuzzy search for subscription names and resource group names using `fzf`
- Customizable role assignments with duration and justification messages
- Supports verbose output for detailed operation logs

## Prerequisites

Ensure the following commands are installed and accessible in your environment:

- `curl`
- `az` (Azure CLI)
- `jq`
- `fzf`

## Usage

```bash
./pim.sh [OPTIONS]
```

### Required Parameters

- `--subscription, -s`: Subscription ID or name (fuzzy search enabled)
- `--resource-group, -g`: Resource group name (fuzzy search enabled)

### Optional Parameters

- `--message, -m`: Justification message
- `--role, -r`: Role name (default: Contributor)
- `--time, -t`: Duration (default: 8H). Format: 8H (hours) or 8M (minutes)
- `--verbose, -v`: Enable verbose output
- `--help`: Show help message

### Example

```bash
./pim.sh --subscription "My Subscription" --resource-group "MyResourceGroup" --message "Access required for deployment" --role "Contributor" --time "4H" --verbose
```

### Fuzzy Search

If you do not provide `--subscription` or `--resource-group`, the script will invoke `fzf` to let you select from available options.

### Running Without Parameters

You can run the script without specifying `--subscription` or `--resource-group`, and you will be prompted to select them using `fzf`.

```bash
./pim.sh
```

## Example Workflow

1. Ensure you are logged into Azure CLI:
```bash
az login
```
2. Run the script:
```bash
./pim.sh --verbose
```
3. Select the subscription and resource group using `fzf`.
4. Provide the required justification message when prompted.

## Error Handling

The script performs various checks and provides meaningful error messages for missing dependencies, invalid input formats, and operational errors.

## Contribution

Contributions are welcome! Please fork the repository and create a pull request with your changes.

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
196 changes: 196 additions & 0 deletions pim.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
#!/bin/bash
# This script is used to make PIM assignments

print_help() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS]
Required parameters:
--subscription, -s Subscription ID or name (fuzzy search enabled)
--resource-group, -g Resource group name (fuzzy search enabled)
Optional parameters:
--message, -m Justification message
--role, -r Role name (default: Contributor)
--time, -t Duration (default: 8H). Format: 8H (hours) or 8M (minutes)
--verbose, -v Enable verbose output
--help Show this help message
EOF
exit 1
}

check_dependencies() {
for cmd in curl az jq fzf; do
if ! command -v $cmd &>/dev/null; then
echo "$cmd is required but not installed. Exiting."
exit 1
fi
done
}

validate_time() {
if [[ ! $time =~ ^[0-9]+[HM]$ ]]; then
echo "Invalid time format. Use nH (hours, max 8) or nM (minutes, min 5, max 60)"
exit 1
fi
if [[ $time == *H ]]; then
local hours=${time%H}
if ((hours < 1 || hours > 8)); then
echo "Invalid hour format. Use 1-8H."
exit 1
fi
elif [[ $time == *M ]]; then
local minutes=${time%M}
if ((minutes < 5 || minutes > 60)); then
echo "Invalid minute format. Use 5-60M."
exit 1
fi
fi
}

parse_arguments() {
while [[ "$#" -gt 0 ]]; do
case $1 in
-s|--subscription)
subscription="$2"
shift
;;
-g|--resource-group)
resource_group="$2"
shift
;;
-m|--message)
message="$2"
shift
;;
-r|--role)
role="$2"
shift
;;
-t|--time)
time="$2"
shift
;;
-v|--verbose)
verbose=true
;;
--help)
print_help
;;
*)
echo "Unknown parameter passed: $1"
print_help
;;
esac
shift
done
}

set_defaults() {
role=${role:-"Contributor"}
time=${time:-"8H"}
verbose=${verbose:-false}
}

fuzzy_select_subscription() {
subscription=$(az account list --query "[].{name:name, id:id}" -o tsv | fzf --prompt="Select Subscription: " | awk '{print $1}')
if [[ -z "$subscription" ]]; then
echo "No subscription selected. Exiting."
exit 1
fi
}

fuzzy_select_resource_group() {
resource_group=$(az group list --query "[].name" -o tsv | fzf --prompt="Select Resource Group: ")
if [[ -z "$resource_group" ]]; then
echo "No resource group selected. Exiting."
exit 1
fi
}

convert_date() {
local date="$1"
local format="$2"
if [[ $(uname) == "Darwin" ]]; then
date -j -f "%Y-%m-%dT%H:%M:%S" "$date" +"$format"
else
date -d "$date" +"$format"
fi
}

main() {
check_dependencies
parse_arguments "$@"
set_defaults

if [[ -z "$subscription" ]]; then
fuzzy_select_subscription
fi

if [[ -z "$resource_group" ]]; then
fuzzy_select_resource_group
fi

validate_time

if [[ -z "$message" ]]; then
read -p "Please provide a justification message: " message
if [[ -z "$message" ]]; then
echo "Justification message cannot be empty"
exit 1
fi
fi

user_object_id=$(az ad user list --filter "mail eq '$(az account show --query user.name -o tsv)'" --query "[0].id" -o tsv)
start_date=$(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ")
guid=$(uuidgen | tr '[:upper:]' '[:lower:]')
token=$(az account get-access-token --query accessToken -o tsv)
subscription_id=$(az account list --query "[?id=='$subscription' || name=='$subscription'].id" -o tsv)

role_eligibility_schedule_instances=$(curl -s -H "Authorization: Bearer $token" -X GET \
"https://management.azure.com/subscriptions/${subscription_id}/resourceGroups/${resource_group}/providers/Microsoft.Authorization/roleEligibilityScheduleInstances?api-version=2020-10-01&\$filter=asTarget()")

role_definition_id=$(jq -r --arg resource_group "$resource_group" --arg role "$role" \
'.value[] | select(.properties.scope | endswith($resource_group)) | select(.properties.expandedProperties.roleDefinition.displayName == $role) | .properties.expandedProperties.roleDefinition.id' <<<"$role_eligibility_schedule_instances" | awk -F'/' '{print $NF}')

justification="${message// /_}"
data=$(cat <<EOF
{
"Properties": {
"RoleDefinitionId": "/subscriptions/${subscription_id}/providers/Microsoft.Authorization/roleDefinitions/${role_definition_id}",
"PrincipalId": "${user_object_id}",
"RequestType": "SelfActivate",
"Justification": "${justification}",
"ScheduleInfo": {
"StartDateTime": "${start_date}",
"Expiration": {
"Type": "AfterDuration",
"EndDateTime": null,
"Duration": "PT${time}"
}
}
}
}
EOF
)

response=$(curl -s -H "Authorization: Bearer $token" -X PUT -H "Content-Type: application/json" -d "$data" \
"https://management.azure.com/subscriptions/${subscription_id}/resourceGroups/${resource_group}/providers/Microsoft.Authorization/roleAssignmentScheduleRequests/${guid}?api-version=2020-10-01")

if [[ $(echo "$response" | jq -r '.error') != "null" ]]; then
echo "Error: $(echo "$response" | jq -r '.error.message')"
exit 1
fi

if [[ $(echo "$response" | jq -r '.properties.status') == "Provisioned" ]]; then
start_date_time=$(echo "$response" | jq -r '.properties.scheduleInfo.startDateTime')
end_date_time=$(convert_date "$start_date_time" +"%Y-%m-%dT%H:%M:%S.%3NZ + ${time//H/h} ${time//M/m} minutes")
end_date_formatted=$(convert_date "$end_date_time" "%d.%m.%Y - %H:%M")
start_date_formatted=$(convert_date "$start_date_time" "%d.%m.%Y - %H:%M")
echo "PIM assignment active from $start_date_formatted to $end_date_formatted"
else
echo "An unknown error occurred."
fi
}

main "$@"

0 comments on commit b34bba6

Please sign in to comment.