azp-cli 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +110 -31
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +13 -4
- package/dist/auth.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +126 -46
- package/dist/cli.js.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/presets-cli.d.ts +1 -0
- package/dist/presets-cli.d.ts.map +1 -1
- package/dist/presets-cli.js +178 -1
- package/dist/presets-cli.js.map +1 -1
- package/dist/ui.d.ts +1 -0
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +65 -32
- package/dist/ui.js.map +1 -1
- package/dist/update-check.d.ts.map +1 -1
- package/dist/update-check.js.map +1 -1
- package/package.json +15 -11
- package/src/auth.ts +15 -5
- package/src/cli.ts +133 -51
- package/src/index.ts +7 -10
- package/src/presets-cli.ts +192 -15
- package/src/ui.ts +87 -34
- package/src/update-check.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [1.4.0](https://github.com/tapanmeena/azp-cli/compare/v1.3.1...v1.4.0) (2026-01-15)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* add presets management functionality to CLI ([253c66c](https://github.com/tapanmeena/azp-cli/commit/253c66c3691d31f7bc032c293b069667a6a148b5))
|
|
11
|
+
* add subscription reader support for role activation and deactivation ([182027b](https://github.com/tapanmeena/azp-cli/commit/182027be73accf0d9f3b13385b28da1701b50297))
|
|
12
|
+
* utilize cached update check result if within interval ([b4c999d](https://github.com/tapanmeena/azp-cli/commit/b4c999d37fcbbb23ba9d2f637833ba48dd7a38e9))
|
|
13
|
+
|
|
14
|
+
### [1.3.1](https://github.com/tapanmeena/azp-cli/compare/v1.3.0...v1.3.1) (2026-01-14)
|
|
15
|
+
|
|
16
|
+
## [1.3.0](https://github.com/tapanmeena/azp-cli/compare/v1.2.0...v1.3.0) (2026-01-14)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
* add Azure CLI installation check before authentication ([c291268](https://github.com/tapanmeena/azp-cli/commit/c291268359854e7da74a0cea046741c0449609d7))
|
|
22
|
+
* update package.json with repository type; rename CLI command to 'check-update' and adjust header formatting ([8ef92d4](https://github.com/tapanmeena/azp-cli/commit/8ef92d4da233459f7631f2bf4f85ed5a4113c918))
|
|
23
|
+
* update README with new features and installation instructions; refactor CLI command name to 'azp' ([24510f7](https://github.com/tapanmeena/azp-cli/commit/24510f78f44f9e1570d93c8b750aa6a41b27b62e))
|
|
24
|
+
|
|
5
25
|
## [1.2.0](https://github.com/tapanmeena/azp-cli/compare/v1.1.0...v1.2.0) (2026-01-13)
|
|
6
26
|
|
|
7
27
|
|
package/README.md
CHANGED
|
@@ -14,6 +14,10 @@ A command-line interface tool for managing Azure Privileged Identity Management
|
|
|
14
14
|
- ✨ **Beautiful UI** - Polished terminal experience with spinners and colors
|
|
15
15
|
- 🔄 **Multi-role Support** - Activate or deactivate multiple roles at once
|
|
16
16
|
- 📊 **Status Tracking** - Real-time feedback on activation/deactivation status
|
|
17
|
+
- 💾 **Presets** - Save and reuse activation/deactivation configurations
|
|
18
|
+
- 🚀 **Non-interactive Mode** - CLI flags for scripting and automation
|
|
19
|
+
- 🔔 **Update Notifications** - Automatic update checks with configurable behavior
|
|
20
|
+
- 📤 **JSON Output** - Machine-readable output for integration with other tools
|
|
17
21
|
|
|
18
22
|
## Prerequisites
|
|
19
23
|
|
|
@@ -38,7 +42,22 @@ az account show
|
|
|
38
42
|
|
|
39
43
|
## Installation
|
|
40
44
|
|
|
41
|
-
###
|
|
45
|
+
### Global Installation (Recommended)
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Using npm
|
|
49
|
+
npm install -g azp-cli
|
|
50
|
+
|
|
51
|
+
# Using pnpm
|
|
52
|
+
pnpm add -g azp-cli
|
|
53
|
+
|
|
54
|
+
# Using yarn
|
|
55
|
+
yarn global add azp-cli
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
After installation, the `azp` command will be available globally.
|
|
59
|
+
|
|
60
|
+
### From Source (Development)
|
|
42
61
|
|
|
43
62
|
```bash
|
|
44
63
|
# Clone the repository
|
|
@@ -47,13 +66,12 @@ cd azp-cli
|
|
|
47
66
|
|
|
48
67
|
# Install dependencies
|
|
49
68
|
pnpm install
|
|
50
|
-
# or
|
|
51
|
-
npm install
|
|
52
69
|
|
|
53
70
|
# Build the project
|
|
54
71
|
pnpm build
|
|
55
|
-
|
|
56
|
-
|
|
72
|
+
|
|
73
|
+
# Link globally for development
|
|
74
|
+
npm link
|
|
57
75
|
```
|
|
58
76
|
|
|
59
77
|
## Usage
|
|
@@ -61,24 +79,38 @@ npm run build
|
|
|
61
79
|
### Running the CLI
|
|
62
80
|
|
|
63
81
|
```bash
|
|
64
|
-
#
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
82
|
+
# After global installation
|
|
83
|
+
azp
|
|
84
|
+
|
|
85
|
+
# Or with specific commands
|
|
86
|
+
azp activate
|
|
87
|
+
azp deactivate
|
|
88
|
+
azp preset list
|
|
89
|
+
azp update
|
|
68
90
|
|
|
69
|
-
#
|
|
70
|
-
|
|
91
|
+
# Development mode (from source)
|
|
92
|
+
pnpm dev
|
|
71
93
|
```
|
|
72
94
|
|
|
73
95
|
### Commands
|
|
74
96
|
|
|
75
|
-
| Command | Alias
|
|
76
|
-
| ------------ |
|
|
77
|
-
| `activate` | `a`
|
|
78
|
-
| `deactivate` | `d`
|
|
79
|
-
| `
|
|
80
|
-
| `
|
|
81
|
-
| `help` | -
|
|
97
|
+
| Command | Alias | Description |
|
|
98
|
+
| ------------ | --------- | -------------------------------------- |
|
|
99
|
+
| `activate` | `a` | Activate a role in Azure PIM (default) |
|
|
100
|
+
| `deactivate` | `d` | Deactivate a role in Azure PIM |
|
|
101
|
+
| `preset` | - | Manage reusable presets |
|
|
102
|
+
| `update` | `upgrade` | Check for a newer version |
|
|
103
|
+
| `help` | - | Display help information |
|
|
104
|
+
|
|
105
|
+
#### Preset Subcommands
|
|
106
|
+
|
|
107
|
+
| Command | Description |
|
|
108
|
+
| --------------- | -------------------------------------------- |
|
|
109
|
+
| `preset list` | List all available presets |
|
|
110
|
+
| `preset show` | Show details of a specific preset |
|
|
111
|
+
| `preset add` | Add a new preset (interactive wizard) |
|
|
112
|
+
| `preset edit` | Edit an existing preset (interactive wizard) |
|
|
113
|
+
| `preset remove` | Remove a preset |
|
|
82
114
|
|
|
83
115
|
### Updates
|
|
84
116
|
|
|
@@ -102,9 +134,11 @@ The update-check cache is stored alongside presets in your config directory:
|
|
|
102
134
|
- macOS/Linux: `~/.config/azp-cli/update-check.json` (or `$XDG_CONFIG_HOME/azp-cli/update-check.json`)
|
|
103
135
|
- Windows: `%APPDATA%\azp-cli\update-check.json`
|
|
104
136
|
|
|
105
|
-
###
|
|
137
|
+
### Non-interactive Mode (Automation)
|
|
106
138
|
|
|
107
|
-
Use flags to activate PIM roles directly without going through the
|
|
139
|
+
Use flags to activate or deactivate PIM roles directly without going through the interactive menu, perfect for scripting and CI/CD workflows.
|
|
140
|
+
|
|
141
|
+
#### Activation Examples
|
|
108
142
|
|
|
109
143
|
```bash
|
|
110
144
|
# Activate a single role by name (non-interactive)
|
|
@@ -135,6 +169,43 @@ azp activate --no-interactive --dry-run \
|
|
|
135
169
|
--output json
|
|
136
170
|
```
|
|
137
171
|
|
|
172
|
+
#### Deactivation Examples
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Deactivate specific roles
|
|
176
|
+
azp deactivate --no-interactive --yes \
|
|
177
|
+
--subscription-id <SUBSCRIPTION_GUID> \
|
|
178
|
+
--role-name "Owner" \
|
|
179
|
+
--justification "Task completed"
|
|
180
|
+
|
|
181
|
+
# Deactivate across all subscriptions (omit subscription-id)
|
|
182
|
+
azp deactivate --no-interactive --yes \
|
|
183
|
+
--role-name "Contributor" \
|
|
184
|
+
--allow-multiple
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### Available Flags
|
|
188
|
+
|
|
189
|
+
**Common flags (activate/deactivate):**
|
|
190
|
+
|
|
191
|
+
- `--no-interactive` - Disable interactive prompts
|
|
192
|
+
- `-y, --yes` - Skip confirmation prompts
|
|
193
|
+
- `--subscription-id <id>` - Target subscription (optional for deactivate)
|
|
194
|
+
- `--role-name <name>` - Role name(s) to target (can be repeated)
|
|
195
|
+
- `--allow-multiple` - Allow multiple role matches
|
|
196
|
+
- `--dry-run` - Preview without submitting
|
|
197
|
+
- `--output <text|json>` - Output format (default: text)
|
|
198
|
+
- `--quiet` - Suppress non-essential output
|
|
199
|
+
|
|
200
|
+
**Activation-specific:**
|
|
201
|
+
|
|
202
|
+
- `--duration-hours <n>` - Duration (1-8 hours, default varies by role)
|
|
203
|
+
- `--justification <text>` - Justification for activation
|
|
204
|
+
|
|
205
|
+
**Deactivation-specific:**
|
|
206
|
+
|
|
207
|
+
- `--justification <text>` - Justification for deactivation (optional)
|
|
208
|
+
|
|
138
209
|
## Presets
|
|
139
210
|
|
|
140
211
|
Presets let you save your daily activation/deactivation routines (subscription + role names + duration + justification) and reuse them with `--preset <name>`.
|
|
@@ -163,24 +234,27 @@ A preset can define one or both blocks:
|
|
|
163
234
|
- `${datetime}` → ISO timestamp
|
|
164
235
|
- `${userPrincipalName}` → resolved from Microsoft Graph `/me`
|
|
165
236
|
|
|
166
|
-
### Common
|
|
237
|
+
### Common Workflows
|
|
167
238
|
|
|
168
239
|
```bash
|
|
169
240
|
# Create a preset (interactive wizard)
|
|
170
241
|
azp preset add daily-ops
|
|
171
242
|
|
|
243
|
+
# Create a preset with Azure integration (fetches subscriptions/roles)
|
|
244
|
+
azp preset add daily-ops --from-azure
|
|
245
|
+
|
|
172
246
|
# Edit a preset (interactive wizard)
|
|
173
247
|
azp preset edit daily-ops
|
|
174
248
|
|
|
175
|
-
#
|
|
176
|
-
azp preset add daily-ops
|
|
177
|
-
|
|
178
|
-
# List presets
|
|
249
|
+
# List all presets
|
|
179
250
|
azp preset list
|
|
180
251
|
|
|
181
|
-
# Show one preset
|
|
252
|
+
# Show one preset details
|
|
182
253
|
azp preset show daily-ops
|
|
183
254
|
|
|
255
|
+
# Remove a preset
|
|
256
|
+
azp preset remove daily-ops
|
|
257
|
+
|
|
184
258
|
# Use a preset (flags still override preset values)
|
|
185
259
|
azp activate --preset daily-ops --yes
|
|
186
260
|
|
|
@@ -293,6 +367,8 @@ git push --follow-tags
|
|
|
293
367
|
4. Publish to npm (if desired):
|
|
294
368
|
|
|
295
369
|
```bash
|
|
370
|
+
npm publish
|
|
371
|
+
# or
|
|
296
372
|
pnpm publish
|
|
297
373
|
```
|
|
298
374
|
|
|
@@ -301,11 +377,14 @@ pnpm publish
|
|
|
301
377
|
```
|
|
302
378
|
azp-cli/
|
|
303
379
|
├── src/
|
|
304
|
-
│ ├── index.ts
|
|
305
|
-
│ ├── auth.ts
|
|
306
|
-
│ ├── azure-pim.ts
|
|
307
|
-
│ ├── cli.ts
|
|
308
|
-
│
|
|
380
|
+
│ ├── index.ts # CLI entry point and command definitions
|
|
381
|
+
│ ├── auth.ts # Azure authentication handling
|
|
382
|
+
│ ├── azure-pim.ts # Azure PIM API operations
|
|
383
|
+
│ ├── cli.ts # Interactive menu and user flows
|
|
384
|
+
│ ├── presets.ts # Preset configuration and storage
|
|
385
|
+
│ ├── presets-cli.ts # Preset wizard flows
|
|
386
|
+
│ ├── ui.ts # Terminal UI utilities (spinners, formatting)
|
|
387
|
+
│ └── update-check.ts # Update notification system
|
|
309
388
|
├── package.json
|
|
310
389
|
├── tsconfig.json
|
|
311
390
|
└── README.md
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAU3D,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,kBAAkB,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAqBD,eAAO,MAAM,YAAY,QAAa,OAAO,CAAC,WAAW,CA8BxD,CAAC;AAEF,eAAO,MAAM,WAAW,GAAU,YAAY,kBAAkB,KAAG,OAAO,CAAC,MAAM,CAMhF,CAAC"}
|
package/dist/auth.js
CHANGED
|
@@ -4,6 +4,8 @@ exports.getArmToken = exports.authenticate = void 0;
|
|
|
4
4
|
const identity_1 = require("@azure/identity");
|
|
5
5
|
const microsoft_graph_client_1 = require("@microsoft/microsoft-graph-client");
|
|
6
6
|
const azureTokenCredentials_1 = require("@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials");
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const util_1 = require("util");
|
|
7
9
|
const ui_1 = require("./ui");
|
|
8
10
|
const GRAPH_SCOPES = ["https://graph.microsoft.com/.default"];
|
|
9
11
|
const ARM_SCOPES = ["https://management.azure.com/.default"];
|
|
@@ -14,7 +16,17 @@ const getCredential = () => {
|
|
|
14
16
|
}
|
|
15
17
|
return cachedCredential;
|
|
16
18
|
};
|
|
19
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
20
|
+
const checkAzureCliInstalled = async () => {
|
|
21
|
+
try {
|
|
22
|
+
await execAsync("az --version");
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
throw new Error("Azure CLI is not installed or not in PATH. Please install it to use this tool.");
|
|
26
|
+
}
|
|
27
|
+
};
|
|
17
28
|
const authenticate = async () => {
|
|
29
|
+
await checkAzureCliInstalled();
|
|
18
30
|
(0, ui_1.startSpinner)("Authenticating with Azure CLI...");
|
|
19
31
|
try {
|
|
20
32
|
const credential = getCredential();
|
|
@@ -24,10 +36,7 @@ const authenticate = async () => {
|
|
|
24
36
|
const userId = user.id;
|
|
25
37
|
const userPrincipalName = user.userPrincipalName;
|
|
26
38
|
(0, ui_1.succeedSpinner)("Authentication successful");
|
|
27
|
-
(0, ui_1.
|
|
28
|
-
{ label: "Name", value: user.displayName },
|
|
29
|
-
{ label: "Email", value: userPrincipalName },
|
|
30
|
-
]);
|
|
39
|
+
(0, ui_1.showUserInfo)(user.displayName, userPrincipalName);
|
|
31
40
|
return {
|
|
32
41
|
credential,
|
|
33
42
|
graphClient,
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":";;;AAAA,8CAAqD;AACrD,8EAA2D;AAC3D,iHAA8H;AAC9H,
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":";;;AAAA,8CAAqD;AACrD,8EAA2D;AAC3D,iHAA8H;AAC9H,iDAAqC;AACrC,+BAAiC;AACjC,6BAA+E;AAE/E,MAAM,YAAY,GAAG,CAAC,sCAAsC,CAAC,CAAC;AAE9D,MAAM,UAAU,GAAG,CAAC,uCAAuC,CAAC,CAAC;AAS7D,IAAI,gBAAgB,GAA8B,IAAI,CAAC;AAEvD,MAAM,aAAa,GAAG,GAAuB,EAAE;IAC7C,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,gBAAgB,GAAG,IAAI,6BAAkB,EAAE,CAAC;IAC9C,CAAC;IACD,OAAO,gBAAgB,CAAC;AAC1B,CAAC,CAAC;AAEF,MAAM,SAAS,GAAG,IAAA,gBAAS,EAAC,oBAAI,CAAC,CAAC;AAElC,MAAM,sBAAsB,GAAG,KAAK,IAAmB,EAAE;IACvD,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,cAAc,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,gFAAgF,CAAC,CAAC;IACpG,CAAC;AACH,CAAC,CAAC;AAEK,MAAM,YAAY,GAAG,KAAK,IAA0B,EAAE;IAC3D,MAAM,sBAAsB,EAAE,CAAC;IAC/B,IAAA,iBAAY,EAAC,kCAAkC,CAAC,CAAC;IAEjD,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;QAGnC,MAAM,YAAY,GAAG,IAAI,6DAAqC,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC;QAErG,MAAM,WAAW,GAAG,+BAAM,CAAC,kBAAkB,CAAC,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,CAAC;QAGxF,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,kCAAkC,CAAC,CAAC,GAAG,EAAE,CAAC;QAC9H,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;QACvB,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,CAAC;QAEjD,IAAA,mBAAc,EAAC,2BAA2B,CAAC,CAAC;QAC5C,IAAA,iBAAY,EAAC,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;QAElD,OAAO;YACL,UAAU;YACV,WAAW;YACX,MAAM;YACN,iBAAiB;SAClB,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAA,gBAAW,EAAC,uBAAuB,CAAC,CAAC;QACrC,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AA9BW,QAAA,YAAY,gBA8BvB;AAEK,MAAM,WAAW,GAAG,KAAK,EAAE,UAA8B,EAAmB,EAAE;IACnF,MAAM,aAAa,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC5D,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,aAAa,CAAC,KAAK,CAAC;AAC7B,CAAC,CAAC;AANW,QAAA,WAAW,eAMtB"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAyBrC,MAAM,MAAM,mBAAmB,GAAG;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,eAAe,EAAE,KAAK,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,gBAAgB,EAAE,MAAM,CAAC;QACzB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC,CAAC;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,KAAK,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,gBAAgB,EAAE,MAAM,CAAC;QACzB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;CACJ,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,eAAe,EAAE,KAAK,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,gBAAgB,EAAE,MAAM,CAAC;QACzB,cAAc,EAAE,MAAM,CAAC;QACvB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC,CAAC;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,KAAK,CAAC;QACd,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,gBAAgB,EAAE,MAAM,CAAC;QACzB,gBAAgB,EAAE,MAAM,CAAC;QACzB,OAAO,EAAE,OAAO,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;CACJ,CAAC;AAUF,eAAO,MAAM,YAAY,GAAU,aAAa,WAAW,EAAE,SAAS,mBAAmB,KAAG,OAAO,CAAC,kBAAkB,CAkOrH,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,aAAa,WAAW,EAAE,SAAS,qBAAqB,KAAG,OAAO,CAAC,oBAAoB,CAkP3H,CAAC;AAwBF,eAAO,MAAM,YAAY,GAAU,aAAa,WAAW,KAAG,OAAO,CAAC,IAAI,CAoCzE,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAU,aAAa,WAAW,KAAG,OAAO,CAAC,IAAI,CA2N7E,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAU,aAAa,WAAW,KAAG,OAAO,CAAC,IAAI,CAqK/E,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
8
8
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
9
9
|
const azure_pim_1 = require("./azure-pim");
|
|
10
10
|
const ui_1 = require("./ui");
|
|
11
|
+
const presets_cli_1 = require("./presets-cli");
|
|
11
12
|
const normalizeRoleName = (value) => value.trim().toLowerCase();
|
|
12
13
|
const validateDurationHours = (value) => {
|
|
13
14
|
if (!Number.isFinite(value) || value < 1 || value > 8) {
|
|
@@ -30,11 +31,10 @@ const activateOnce = async (authContext, options) => {
|
|
|
30
31
|
}
|
|
31
32
|
(0, ui_1.logBlank)();
|
|
32
33
|
(0, ui_1.logInfo)("Resolving subscription and eligible roles...");
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
34
|
+
const selectedSubscription = {
|
|
35
|
+
subscriptionId: options.subscriptionId,
|
|
36
|
+
displayName: `${options.subscriptionId} (name unavailable)`,
|
|
37
|
+
};
|
|
38
38
|
const eligibleRoles = await (0, azure_pim_1.fetchEligibleRolesForSubscription)(authContext.credential, selectedSubscription.subscriptionId, selectedSubscription.displayName, authContext.userId);
|
|
39
39
|
const eligibleByName = new Map();
|
|
40
40
|
for (const role of eligibleRoles) {
|
|
@@ -219,17 +219,21 @@ const deactivateOnce = async (authContext, options) => {
|
|
|
219
219
|
}
|
|
220
220
|
(0, ui_1.logBlank)();
|
|
221
221
|
(0, ui_1.logInfo)("Resolving subscriptions and active roles...");
|
|
222
|
-
|
|
223
|
-
if (subscriptions.length === 0) {
|
|
224
|
-
throw new Error("No subscriptions found.");
|
|
225
|
-
}
|
|
226
|
-
let targetSubscriptions = subscriptions;
|
|
222
|
+
let targetSubscriptions;
|
|
227
223
|
if (options.subscriptionId?.trim()) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
224
|
+
targetSubscriptions = [
|
|
225
|
+
{
|
|
226
|
+
subscriptionId: options.subscriptionId,
|
|
227
|
+
displayName: `${options.subscriptionId} (name unavailable)`,
|
|
228
|
+
},
|
|
229
|
+
];
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
const subscriptions = await (0, azure_pim_1.fetchSubscriptions)(authContext.credential);
|
|
233
|
+
if (subscriptions.length === 0) {
|
|
234
|
+
throw new Error("No subscriptions found. use --subscription-id to specify a subscription.");
|
|
231
235
|
}
|
|
232
|
-
targetSubscriptions =
|
|
236
|
+
targetSubscriptions = subscriptions;
|
|
233
237
|
}
|
|
234
238
|
let allActiveRoles = [];
|
|
235
239
|
for (const sub of targetSubscriptions) {
|
|
@@ -443,6 +447,7 @@ const showMainMenu = async (authContext) => {
|
|
|
443
447
|
choices: [
|
|
444
448
|
{ name: chalk_1.default.green("▶ Activate Role(s)"), value: "activate" },
|
|
445
449
|
{ name: chalk_1.default.yellow("◼ Deactivate Role(s)"), value: "deactivate" },
|
|
450
|
+
{ name: chalk_1.default.magenta("⚙ Presets..."), value: "presets" },
|
|
446
451
|
{ name: chalk_1.default.red("✕ Exit"), value: "exit" },
|
|
447
452
|
],
|
|
448
453
|
default: "activate",
|
|
@@ -455,6 +460,9 @@ const showMainMenu = async (authContext) => {
|
|
|
455
460
|
case "deactivate":
|
|
456
461
|
await (0, exports.handleDeactivation)(authContext);
|
|
457
462
|
break;
|
|
463
|
+
case "presets":
|
|
464
|
+
await (0, presets_cli_1.runPresetsManager)();
|
|
465
|
+
break;
|
|
458
466
|
case "exit":
|
|
459
467
|
(0, ui_1.logBlank)();
|
|
460
468
|
(0, ui_1.logDim)("Goodbye! 👋");
|
|
@@ -469,38 +477,76 @@ const handleActivation = async (authContext) => {
|
|
|
469
477
|
(0, ui_1.logBlank)();
|
|
470
478
|
(0, ui_1.logInfo)("Starting role activation flow...");
|
|
471
479
|
(0, ui_1.logBlank)();
|
|
472
|
-
|
|
480
|
+
let subscriptions = await (0, azure_pim_1.fetchSubscriptions)(authContext.credential);
|
|
481
|
+
let selectedSubscription;
|
|
473
482
|
if (subscriptions.length === 0) {
|
|
474
483
|
(0, ui_1.logWarning)("No subscriptions found.");
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
484
|
+
const { action } = await inquirer_1.default.prompt([
|
|
485
|
+
{
|
|
486
|
+
type: "select",
|
|
487
|
+
name: "action",
|
|
488
|
+
message: chalk_1.default.yellow("No subscriptions found. What would you like to do?"),
|
|
489
|
+
choices: [
|
|
490
|
+
{ name: chalk_1.default.cyan("Enter subscription ID manually"), value: "enter" },
|
|
491
|
+
{ name: chalk_1.default.cyan("↩ Back to Main Menu"), value: "back" },
|
|
492
|
+
{ name: chalk_1.default.red("✕ Exit"), value: "exit" },
|
|
493
|
+
],
|
|
494
|
+
default: "enter",
|
|
495
|
+
},
|
|
496
|
+
]);
|
|
497
|
+
if (action === "back") {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
else if (action === "exit") {
|
|
501
|
+
(0, ui_1.logBlank)();
|
|
502
|
+
(0, ui_1.logDim)("Goodbye! 👋");
|
|
503
|
+
process.exit(0);
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
const { manualId } = await inquirer_1.default.prompt([
|
|
507
|
+
{
|
|
508
|
+
type: "input",
|
|
509
|
+
name: "manualId",
|
|
510
|
+
message: chalk_1.default.cyan("Subscription ID:"),
|
|
511
|
+
validate: (value) => {
|
|
512
|
+
if (!value || !value.trim())
|
|
513
|
+
return chalk_1.default.red("Please enter a subscription ID.");
|
|
514
|
+
return true;
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
]);
|
|
518
|
+
selectedSubscription = { subscriptionId: manualId.trim(), displayName: manualId.trim() };
|
|
519
|
+
}
|
|
499
520
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
521
|
+
else {
|
|
522
|
+
const BACK_VALUE = "__BACK__";
|
|
523
|
+
const subscriptionChoices = subscriptions
|
|
524
|
+
.map((sub) => ({
|
|
525
|
+
name: (0, ui_1.formatSubscription)(sub.displayName, sub.subscriptionId),
|
|
526
|
+
value: sub.subscriptionId,
|
|
527
|
+
}))
|
|
528
|
+
.concat([{ name: chalk_1.default.dim("↩ Back to Main Menu"), value: BACK_VALUE }]);
|
|
529
|
+
(0, ui_1.logBlank)();
|
|
530
|
+
const { selectedSubscriptionId } = await inquirer_1.default.prompt([
|
|
531
|
+
{
|
|
532
|
+
type: "select",
|
|
533
|
+
name: "selectedSubscriptionId",
|
|
534
|
+
message: chalk_1.default.cyan("Select a subscription:"),
|
|
535
|
+
choices: subscriptionChoices,
|
|
536
|
+
pageSize: 15,
|
|
537
|
+
default: subscriptionChoices[0]?.value,
|
|
538
|
+
},
|
|
539
|
+
]);
|
|
540
|
+
if (selectedSubscriptionId === BACK_VALUE) {
|
|
541
|
+
(0, ui_1.logDim)("Returning to main menu...");
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
const found = subscriptions.find((sub) => sub.subscriptionId === selectedSubscriptionId);
|
|
545
|
+
if (!found) {
|
|
546
|
+
(0, ui_1.logError)("Selected subscription not found.");
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
selectedSubscription = { subscriptionId: found.subscriptionId, displayName: found.displayName };
|
|
504
550
|
}
|
|
505
551
|
const eligibleRoles = await (0, azure_pim_1.fetchEligibleRolesForSubscription)(authContext.credential, selectedSubscription.subscriptionId, selectedSubscription.displayName, authContext.userId);
|
|
506
552
|
if (eligibleRoles.length === 0) {
|
|
@@ -631,12 +677,46 @@ const handleDeactivation = async (authContext) => {
|
|
|
631
677
|
(0, ui_1.logBlank)();
|
|
632
678
|
(0, ui_1.logInfo)("Starting role deactivation flow...");
|
|
633
679
|
(0, ui_1.logBlank)();
|
|
634
|
-
|
|
680
|
+
let subscriptions = await (0, azure_pim_1.fetchSubscriptions)(authContext.credential);
|
|
635
681
|
let activeAzureRoles = [];
|
|
636
682
|
if (subscriptions.length === 0) {
|
|
637
683
|
(0, ui_1.logWarning)("No subscriptions found.");
|
|
638
|
-
|
|
639
|
-
|
|
684
|
+
const { action } = await inquirer_1.default.prompt([
|
|
685
|
+
{
|
|
686
|
+
type: "select",
|
|
687
|
+
name: "action",
|
|
688
|
+
message: chalk_1.default.yellow("No subscriptions found. What would you like to do?"),
|
|
689
|
+
choices: [
|
|
690
|
+
{ name: chalk_1.default.cyan("Enter subscription ID manually"), value: "enter" },
|
|
691
|
+
{ name: chalk_1.default.cyan("↩ Back to Main Menu"), value: "back" },
|
|
692
|
+
{ name: chalk_1.default.red("✕ Exit"), value: "exit" },
|
|
693
|
+
],
|
|
694
|
+
default: "enter",
|
|
695
|
+
},
|
|
696
|
+
]);
|
|
697
|
+
if (action === "back") {
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
else if (action === "exit") {
|
|
701
|
+
(0, ui_1.logBlank)();
|
|
702
|
+
(0, ui_1.logDim)("Goodbye! 👋");
|
|
703
|
+
process.exit(0);
|
|
704
|
+
}
|
|
705
|
+
else {
|
|
706
|
+
const { manualId } = await inquirer_1.default.prompt([
|
|
707
|
+
{
|
|
708
|
+
type: "input",
|
|
709
|
+
name: "manualId",
|
|
710
|
+
message: chalk_1.default.cyan("Subscription ID to inspect (for active roles):"),
|
|
711
|
+
validate: (value) => {
|
|
712
|
+
if (!value || !value.trim())
|
|
713
|
+
return chalk_1.default.red("Please enter a subscription ID.");
|
|
714
|
+
return true;
|
|
715
|
+
},
|
|
716
|
+
},
|
|
717
|
+
]);
|
|
718
|
+
subscriptions.push({ subscriptionId: manualId.trim(), displayName: manualId.trim(), tenantId: "" });
|
|
719
|
+
}
|
|
640
720
|
}
|
|
641
721
|
for (const sub of subscriptions) {
|
|
642
722
|
const roles = await (0, azure_pim_1.listActiveAzureRoles)(authContext.credential, sub.subscriptionId, sub.displayName, authContext.userId);
|