azp-cli 1.2.0 → 1.3.1

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 CHANGED
@@ -2,6 +2,17 @@
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.3.1](https://github.com/tapanmeena/azp-cli/compare/v1.3.0...v1.3.1) (2026-01-14)
6
+
7
+ ## [1.3.0](https://github.com/tapanmeena/azp-cli/compare/v1.2.0...v1.3.0) (2026-01-14)
8
+
9
+
10
+ ### Features
11
+
12
+ * add Azure CLI installation check before authentication ([c291268](https://github.com/tapanmeena/azp-cli/commit/c291268359854e7da74a0cea046741c0449609d7))
13
+ * 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))
14
+ * update README with new features and installation instructions; refactor CLI command name to 'azp' ([24510f7](https://github.com/tapanmeena/azp-cli/commit/24510f78f44f9e1570d93c8b750aa6a41b27b62e))
15
+
5
16
  ## [1.2.0](https://github.com/tapanmeena/azp-cli/compare/v1.1.0...v1.2.0) (2026-01-13)
6
17
 
7
18
 
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
- ### From Source
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
- # or
56
- npm run build
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
- # Development mode
65
- pnpm dev
66
- # or
67
- npm run dev
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
- # After building
70
- node dist/index.js
91
+ # Development mode (from source)
92
+ pnpm dev
71
93
  ```
72
94
 
73
95
  ### Commands
74
96
 
75
- | Command | Alias | Description |
76
- | ------------ | ----- | -------------------------------------- |
77
- | `activate` | `a` | Activate a role in Azure PIM (default) |
78
- | `deactivate` | `d` | Deactivate a role in Azure PIM |
79
- | `update` | - | Check for a newer version |
80
- | `preset` | - | Manage reusable presets |
81
- | `help` | - | Display help information |
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
- ### One-command (non-interactive) activation
137
+ ### Non-interactive Mode (Automation)
106
138
 
107
- Use flags to activate PIM roles directly without going through the main menu.
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 workflows
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
- # You can also re-run add to overwrite an existing preset
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 # CLI entry point and command definitions
305
- │ ├── auth.ts # Azure authentication handling
306
- │ ├── azure-pim.ts # Azure PIM API operations
307
- │ ├── cli.ts # Interactive menu and user flows
308
- └── ui.ts # Terminal UI utilities (spinners, formatting)
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/package.json CHANGED
@@ -1,19 +1,11 @@
1
1
  {
2
2
  "name": "azp-cli",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Terminal app to manage Azure PIM role activations",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "azp": "./dist/index.js"
8
8
  },
9
- "scripts": {
10
- "build": "tsc",
11
- "start": "node dist/index.js",
12
- "dev": "ts-node -r tsconfig-paths/register src/index.ts",
13
- "lint": "tsc -p tsconfig.json --noEmit",
14
- "release": "standard-version",
15
- "release:dry": "standard-version --dry-run"
16
- },
17
9
  "keywords": [
18
10
  "azure",
19
11
  "pim",
@@ -25,8 +17,12 @@
25
17
  "name": "Tapan Meena",
26
18
  "email": "tapanmeena1998@gmail.com"
27
19
  },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/tapanmeena/azp-cli.git"
23
+ },
24
+ "homepage": "https://github.com/tapanmeena/azp-cli#readme",
28
25
  "license": "ISC",
29
- "packageManager": "pnpm@10.28.0",
30
26
  "dependencies": {
31
27
  "@azure/arm-authorization": "^9.0.0",
32
28
  "@azure/arm-resources-subscriptions": "^2.1.0",
@@ -47,5 +43,13 @@
47
43
  "ts-node": "^10.9.2",
48
44
  "tsconfig-paths": "^4.2.0",
49
45
  "typescript": "^5.9.3"
46
+ },
47
+ "scripts": {
48
+ "build": "tsc",
49
+ "start": "node dist/index.js",
50
+ "dev": "ts-node -r tsconfig-paths/register src/index.ts",
51
+ "lint": "tsc -p tsconfig.json --noEmit",
52
+ "release": "standard-version",
53
+ "release:dry": "standard-version --dry-run"
50
54
  }
51
- }
55
+ }
package/src/auth.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { exec } from "child_process";
2
+ import { promisify } from "util";
1
3
  import { AzureCliCredential } from "@azure/identity";
2
4
  import { Client } from "@microsoft/microsoft-graph-client";
3
5
  import { TokenCredentialAuthenticationProvider } from "@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials";
@@ -23,7 +25,18 @@ const getCredential = (): AzureCliCredential => {
23
25
  return cachedCredential;
24
26
  };
25
27
 
28
+ const execAsync = promisify(exec);
29
+
30
+ const checkAzureCliInstalled = async (): Promise<void> => {
31
+ try {
32
+ await execAsync("az --version");
33
+ } catch (error) {
34
+ throw new Error("Azure CLI is not installed or not in PATH. Please install it to use this tool.");
35
+ }
36
+ };
37
+
26
38
  export const authenticate = async (): Promise<AuthContext> => {
39
+ await checkAzureCliInstalled();
27
40
  startSpinner("Authenticating with Azure CLI...");
28
41
 
29
42
  try {
package/src/index.ts CHANGED
@@ -5,7 +5,6 @@ import inquirer from "inquirer";
5
5
  import { name as npmPackageName, version } from "../package.json";
6
6
  import { authenticate } from "./auth";
7
7
  import { activateOnce, deactivateOnce, showMainMenu } from "./cli";
8
- import { runPresetAddWizard, runPresetEditWizard } from "./presets-cli";
9
8
  import {
10
9
  expandTemplate,
11
10
  getDefaultPresetsFilePath,
@@ -13,12 +12,13 @@ import {
13
12
  listPresetNames,
14
13
  loadPresets,
15
14
  removePreset,
16
- savePresets,
17
15
  resolveActivatePresetOptions,
18
16
  resolveDeactivatePresetOptions,
17
+ savePresets,
19
18
  setDefaultPresetName,
20
19
  upsertPreset,
21
20
  } from "./presets";
21
+ import { runPresetAddWizard, runPresetEditWizard } from "./presets-cli";
22
22
  import { configureUi, logBlank, logDim, logError, logInfo, logSuccess, logWarning, showHeader } from "./ui";
23
23
  import { checkForUpdate } from "./update-check";
24
24
 
@@ -98,7 +98,7 @@ const resolveValue = <T>(command: Command, optionName: string, cliValue: T, pres
98
98
 
99
99
  const program = new Command();
100
100
 
101
- program.name("azp-cli").description("Azure PIM CLI - A CLI tool for Azure Privilege Identity Management (PIM)").version(version);
101
+ program.name("azp").description("Azure PIM CLI - A CLI tool for Azure Privilege Identity Management (PIM)").version(version);
102
102
 
103
103
  program
104
104
  .command("activate", { isDefault: true })
@@ -153,8 +153,7 @@ program
153
153
  const hasSubscriptionFromCli = getOptionValueSource(command, "subscriptionId") === "cli";
154
154
 
155
155
  const defaultPresetName = presets.data.defaults?.activatePreset;
156
- const shouldUseDefaultPreset =
157
- !explicitPresetName && Boolean(defaultPresetName) && (!hasSubscriptionFromCli || !hasRoleNamesFromCli);
156
+ const shouldUseDefaultPreset = !explicitPresetName && Boolean(defaultPresetName) && (!hasSubscriptionFromCli || !hasRoleNamesFromCli);
158
157
 
159
158
  const presetNameToUse = explicitPresetName ?? (shouldUseDefaultPreset ? defaultPresetName : undefined);
160
159
 
@@ -349,9 +348,9 @@ program
349
348
  });
350
349
 
351
350
  program
352
- .command("update")
351
+ .command("check-update")
353
352
  .description("Check if a newer azp-cli version is available")
354
- .alias("upgrade")
353
+ .alias("update")
355
354
  .option("--check-only", "Only check and print status (no upgrade instructions)")
356
355
  .option("--output <text|json>", "Output format", "text")
357
356
  .option("--quiet", "Suppress non-essential output (recommended with --output json)")
@@ -525,9 +524,7 @@ presetCommand
525
524
  const entry = getPreset(loaded.data, name);
526
525
  if (!entry) {
527
526
  const names = listPresetNames(loaded.data);
528
- throw new Error(
529
- `Preset not found: "${name}". Presets file: ${loaded.filePath}. Available: ${names.length ? names.join(", ") : "(none)"}`
530
- );
527
+ throw new Error(`Preset not found: "${name}". Presets file: ${loaded.filePath}. Available: ${names.length ? names.join(", ") : "(none)"}`);
531
528
  }
532
529
 
533
530
  if (output === "json") {
package/src/ui.ts CHANGED
@@ -223,7 +223,7 @@ export const showHeader = (): void => {
223
223
 
224
224
  console.log(chalk.cyan(top));
225
225
  printLine();
226
- printLine(title, 2);
226
+ printLine(title, 4);
227
227
  printLine();
228
228
  printLine(tagline);
229
229
  printLine();