Skip to content

Commit

Permalink
feat: support for PIM Entra groups (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
netr0m authored Jun 10, 2024
1 parent 57e4472 commit 6fddc87
Show file tree
Hide file tree
Showing 9 changed files with 453 additions and 32 deletions.
154 changes: 136 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ $ mv ./az-pim-cli /usr/local/bin

## Configuration
In addition to supporting environment variables and command line arguments, the script also supports certain config parameters stored in a file. By default, the script will try to look for a YAML config file at `$HOME/.az-pim-cli.yaml`, but you may also override the config file to use by supplying the `--config` flag.
See [Configuration options](#configuration-options) for more details

### Prerequisites
This tool depends on [`az-cli`](https://learn.microsoft.com/en-us/cli/azure/) for authentication. Please ensure that you've authenticated with your Azure tenant by running the command `az login`. A new browser window will open, asking you to authenticate. This should only be necessary to do once.
Expand All @@ -44,42 +45,76 @@ Available Commands:
list Query Azure PIM for eligible role assignments

Flags:
-c, --config string config file (default is $HOME/.az-pim-cli.yaml)
-h, --help help for az-pim-cli
-c, --config string config file (default is $HOME/.az-pim-cli.yaml)
-h, --help help for az-pim-cli
-t, --token string An access token for the PIM Groups API

Use "az-pim-cli [command] --help" for more information about a command.

```

### List eligible role assignments
### List eligible role assignments (Azure resources)
```bash
$ az-pim-cli list --help
Query Azure PIM for eligible role assignments

Usage:
az-pim-cli list [flags]
az-pim-cli list [command]

Aliases:
list, l, ls

Available Commands:
group Query Azure PIM for eligible group assignments

Flags:
-h, --help help for list

Global Flags:
-c, --config string config file (default is $HOME/.az-pim-cli.yaml)
-c, --config string config file (default is $HOME/.az-pim-cli.yaml)
-t, --token string An access token for the PIM Groups API

Use "az-pim-cli list [command] --help" for more information about a command.

```

### Activate a role
### List eligible group assignments (Entra Groups)
> :warn: Requires an access token with the appropriate scope. See [Token for Entra ID Groups](#token-for-entra-id-groups) for more details.
```bash
$ az-pim-cli list group --help
Query Azure PIM for eligible group assignments

Usage:
az-pim-cli list group [flags]

Aliases:
group, g, grp, groups

Flags:
-h, --help help for group

Global Flags:
-c, --config string config file (default is $HOME/.az-pim-cli.yaml)
-t, --token string An access token for the PIM Groups API

```

### Activate a role (Azure resources)
```bash
$ az-pim-cli activate --help
Sends a request to Azure PIM to activate the given role

Usage:
az-pim-cli activate [flags]
az-pim-cli activate [command]

Aliases:
activate, a, ac, act

Available Commands:
group Sends a request to Azure PIM to activate the given group

Flags:
-d, --duration int Duration in minutes that the role should be activated for (default 480)
-h, --help help for activate
Expand All @@ -89,26 +124,109 @@ Flags:
-p, --subscription-prefix string The name prefix of the subscription to activate (e.g. 'S399'). Alternative to 'subscription-name'.

Global Flags:
-c, --config string config file (default is $HOME/.az-pim-cli.yaml)
-c, --config string config file (default is $HOME/.az-pim-cli.yaml)
-t, --token string An access token for the PIM Groups API

Use "az-pim-cli activate [command] --help" for more information about a command.

```
### Activate a role (Entra Groups)
> :warn: Requires an access token with the appropriate scope. See [Token for Entra ID Groups](#token-for-entra-id-groups) for more details.
```bash
$ az-pim-cli activate group --help
Sends a request to Azure PIM to activate the given group
Usage:
az-pim-cli activate group [group name] [flags]
Aliases:
group, g, grp, groups
Flags:
-h, --help help for group
Global Flags:
-c, --config string config file (default is $HOME/.az-pim-cli.yaml)
-d, --duration int Duration in minutes that the role should be activated for (default 480)
--reason string Reason for the activation (default "config")
-r, --role-name string Specify the role to activate, if multiple roles are found for a subscription (e.g. 'Owner' and 'Contributor')
-s, --subscription-name string The name of the subscription to activate
-p, --subscription-prefix string The name prefix of the subscription to activate (e.g. 'S399'). Alternative to 'subscription-name'.
-t, --token string An access token for the PIM Groups API
```
### Examples
#### Azure resources
```bash
# List eligible role assignments
# List eligible Azure resource role assignments
$ az-pim-cli list
Opening in existing browser session.
== S398-XXX ==
- Owner
== S100-Example-Subscription ==
- Contributor
== S250-XXX ==
- Owner
== S1337-Another-Subscription ==
- Contributor
# Activate the first matching role in a subscription with the prefix 's398'
$ az-pim-cli activate --subscription-prefix s398 --duration 60
Opening in existing browser session.
2023/06/30 14:27:04 Activating role 'Owner' in subscription 'S398-XXX'
2023/06/30 14:27:11 The role 'Owner' in 'S398-XXX' is now Active
# Activate the first matching role in a subscription with the prefix 'S100'
$ az-pim-cli activate --subscription-prefix S100
2024/05/31 15:05:25 Activating role 'Contributor' in subscription 'S100-Example-Subscription' with reason 'config'
2024/05/31 15:05:34 The role 'Contributor' in 'S100-Example-Subscription' is now Provisioned
# Activate a specific role ('Owner') in a subscription with the prefix 's100'
$ az-pim-cli activate -p s100 --role-name owner
2024/05/31 15:06:25 Activating role 'Owner' in subscription 'S100-Example-Subscription' with reason 'config'
2024/05/31 15:06:34 The role 'Owner' in 'S100-Example-Subscription' is now Provisioned
```
#### Entra groups
```bash
# List eligible group assignments
$ az-pim-cli list groups
== my-entra-id-group ==
- Owner
# Activate the first matching role for the group 'my-entra-id-group'
$ az-pim-cli activate group my-entra-id-group -d 5
2024/05/31 15:00:10 Activating role 'Owner' for group 'my-entra-id-group' with reason 'config'
2024/05/31 15:00:23 The role 'Owner' for group 'my-entra-id-group' is now Active
```
### Configuration options
- `token`: The Bearer token to use for authorization when requesting the Azure PIM Groups endpoint, i.e. listing/activating Azure PIM Groups
#### YAML file
You may define global configuration options in a YAML file.
By default, the program will use the file ~/.az-pim-cli.yaml ($HOME/.az-pim-cli.yaml), if present. You may override this path with the command line flag `--config [PATH]`.
```bash
$ cat ~/.az-pim-cli.yaml
token: eyJ0[...]
# Activate a specific role ('Owner') in a subscription with the prefix 's398'
$ az-pim-cli activate -p s398 --role-name owner
```
#### Environment variables
You may also define these configuration options as environment variables by prefixing any global variable with `PIM_`.
```bash
export PIM_TOKEN=eyJ0[...]
```
### Token for Entra ID Groups
Due to limitations with authorization for Azure PIM, this software may only acquire a token authorized for listing and activating ['Azure resources' roles](https://portal.azure.com/#view/Microsoft_Azure_PIMCommon/ActivationMenuBlade/~/azurerbac).
In order to list or activate ['Entra groups'](https://portal.azure.com/#view/Microsoft_Azure_PIMCommon/ActivationMenuBlade/~/aadgroup), you must acquire a token from an authenticated browser session. This token will have a limited lifetime, which means you'll likely have to perform this step each time you wish to activate or list Entra groups.
To acquire the token, do the following:
1. Navigate to ['Microsoft Entra Privileged Identity Management > Activate > Groups'](https://portal.azure.com/#view/Microsoft_Azure_PIMCommon/ActivationMenuBlade/~/aadgroup)
2. Open *DevTools* (`CTRL+Shift+I`), and locate a request to `https://api.azrbac.mspim.azure.com/api/v2/privilegedAccess/aadGroups/roleAssignments`
- If no such request can be seen, press the "Refresh" button above the table to issue a new request
- In *DevTools*, the "File" attribute should start with "roleAssignments"
3. In *DevTools*, under the "Headers" tab for the given request, copy the value of the `Authorization` header, which should start with "Bearer eyJ0[...]"
4. Remove the prefix "Bearer" from the value, resulting in "eyJ0[...]"
5. Set an environment variable or config file value according to the description in [Configuration options](#configuration-options), e.g.
```
PIM_TOKEN=eyJ0[...]
```
6. You may now, and for the duration of the token's lifetime, list and activate 'Entra groups' using this tool
43 changes: 42 additions & 1 deletion cmd/activate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Copyright © 2023 netr0m <netr0m@pm.me>
package cmd

import (
"fmt"
"log"

"github.com/netr0m/az-pim-cli/pkg/pim"
Expand All @@ -25,7 +26,7 @@ var activateCmd = &cobra.Command{
if subscriptionName == "" && subscriptionPrefix == "" {
log.Fatalf("Missing required parameter: You must specify either 'subscription-name' or 'subscription-prefix'.")
}
token := pim.GetPIMAccessTokenAzureCLI()
token := pim.GetPIMAccessTokenAzureCLI(pim.AZ_PIM_SCOPE)
subjectId := pim.GetUserInfo(token).ObjectId

eligibleRoleAssignments := pim.GetEligibleRoleAssignments(token)
Expand All @@ -43,8 +44,48 @@ var activateCmd = &cobra.Command{
},
}

var activateGroupCmd = &cobra.Command{
Use: "group [group name]",
Aliases: []string{"g", "grp", "groups"},
Short: "Sends a request to Azure PIM to activate the given group",
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
var comps []string
if len(args) == 0 {
comps = cobra.AppendActiveHelp(comps, "Please specify the name of the group")
} else if len(args) == 1 {
comps = cobra.AppendActiveHelp(comps, "This command does not take any more arguments (but may accept flags)")
} else {
comps = cobra.AppendActiveHelp(comps, "ERROR: Too many arguments specified")
}
return comps, cobra.ShellCompDirectiveNoFileComp
},
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 {
cobra.CheckErr(fmt.Errorf("activate group needs a name for the group"))
}
if Token == "" {
log.Fatalf("Activating a group requires providing a token manually due to restrictions in token permissions. Consult the docs for more information.")
}
subjectId := pim.GetUserInfo(Token).ObjectId

eligibleGroupAssignments := pim.GetEligibleGroupAssignments(Token, subjectId)
groupAssignment := utils.GetGroupAssignment(args[0], roleName, eligibleGroupAssignments)

log.Printf(
"Activating role '%s' for group '%s' with reason '%s'",
groupAssignment.RoleDefinition.DisplayName,
groupAssignment.RoleDefinition.Resource.DisplayName,
reason,
)

requestResponse := pim.RequestGroupAssignment(subjectId, groupAssignment, duration, reason, Token)
log.Printf("The role '%s' for group '%s' is now %s", groupAssignment.RoleDefinition.DisplayName, groupAssignment.RoleDefinition.Resource.DisplayName, requestResponse.AssignmentState)
},
}

func init() {
rootCmd.AddCommand(activateCmd)
activateCmd.AddCommand(activateGroupCmd)

// Flags
activateCmd.PersistentFlags().StringVarP(&subscriptionName, "subscription-name", "s", "", "The name of the subscription to activate")
Expand Down
19 changes: 18 additions & 1 deletion cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Copyright © 2023 netr0m <netr0m@pm.me>
package cmd

import (
"log"

"github.com/netr0m/az-pim-cli/pkg/pim"
"github.com/netr0m/az-pim-cli/pkg/utils"
"github.com/spf13/cobra"
Expand All @@ -14,13 +16,28 @@ var listCmd = &cobra.Command{
Aliases: []string{"l", "ls"},
Short: "Query Azure PIM for eligible role assignments",
Run: func(cmd *cobra.Command, args []string) {
token := pim.GetPIMAccessTokenAzureCLI()
token := pim.GetPIMAccessTokenAzureCLI(pim.AZ_PIM_SCOPE)

eligibleRoleAssignments := pim.GetEligibleRoleAssignments(token)
utils.PrintEligibleRoles(eligibleRoleAssignments)
},
}

var listGroupCmd = &cobra.Command{
Use: "group",
Aliases: []string{"g", "grp", "groups"},
Short: "Query Azure PIM for eligible group assignments",
Run: func(cmd *cobra.Command, args []string) {
if Token == "" {
log.Fatalf("Listing eligible groups requires providing a token manually due to restrictions in token permissions. Consult the docs for more information.")
}
subjectId := pim.GetUserInfo(Token).ObjectId
eligibleGroupAssignments := pim.GetEligibleGroupAssignments(Token, subjectId)
utils.PrintEligibleGroups(eligibleGroupAssignments)
},
}

func init() {
rootCmd.AddCommand(listCmd)
listCmd.AddCommand(listGroupCmd)
}
2 changes: 2 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
)

var cfgFile string
var Token string

var rootCmd = &cobra.Command{
Use: "az-pim-cli",
Expand All @@ -36,6 +37,7 @@ func init() {

// Global flags
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (default is $HOME/.az-pim-cli.yaml)")
rootCmd.PersistentFlags().StringVarP(&Token, "token", "t", "", "An access token for the PIM Groups API")
}

// initConfig reads in config file and ENV variables if set.
Expand Down
Loading

0 comments on commit 6fddc87

Please sign in to comment.