
A command-line interface tool for managing Azure Privileged Identity Management (PIM) role activations directly from your terminal.
A command-line interface tool for managing Azure Privileged Identity Management (PIM) role activations directly from your terminal.
Before using azpim, ensure you have:
# Install Azure CLI (if not installed)
# See: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli
# Login to Azure
az login
# Verify you're logged in
az account show
# Using npm
npm install -g azpim
# Using pnpm
pnpm add -g azpim
# Using yarn
yarn global add azpim
After installation, the azpim command will be available globally.
If you previously used azp-cli, your presets are stored in ~/.config/azp-cli/ (or %APPDATA%\azp-cli\ on Windows). To migrate:
presets.json to the new location: ~/.config/azpim/ (or %APPDATA%\azpim\)npm uninstall -g azp-cli# Clone the repository
git clone https://github.com/tapanmeena/azpim.git
cd azpim
# Install dependencies
pnpm install
# Build the project
pnpm build
# Link globally for development
npm link
# After global installation
azpim
# Or with specific commands
azpim activate
azpim deactivate
azpim preset list
azpim update
# Development mode (from source)
pnpm dev
Global Flags:
--debug - Enable debug logging--version - Show version numberYou can check if a newer version is available:
azpim update
# alias
azpim upgrade
Notes:
azpim update exits with code 0 when up-to-date, 2 when an update is available, and 1 on error.--check-only - Only check and print status without showing upgrade instructions.--output json returns a structured response suitable for scripts.azpim activate and azpim deactivate will also show a short "update available" hint (text mode only) at most once per day.AZPIM_NO_UPDATE_NOTIFIER=1 (or AZPIM_DISABLE_UPDATE_CHECK=1).The update-check cache is stored alongside presets in your config directory:
~/.config/azpim/update-check.json (or $XDG_CONFIG_HOME/azpim/update-check.json)%APPDATA%\azpim\update-check.jsonUse flags to activate or deactivate PIM roles directly without going through the interactive menu, perfect for scripting and CI/CD workflows.
# Activate a single role by name (non-interactive)
azpim activate --non-interactive --yes \
--subscription-id <SUBSCRIPTION_GUID> \
--role-name "Owner" \
--duration-hours 2 \
--justification "Break-glass for incident" \
--output json
# Activate multiple roles (repeat --role-name)
azpim activate --non-interactive --yes \
--subscription-id <SUBSCRIPTION_GUID> \
--role-name "Contributor" \
--role-name "User Access Administrator"
# If a role name matches multiple eligible roles (different scopes),
# --non-interactive will error unless you explicitly allow activating all matches
azpim activate --non-interactive --yes \
--subscription-id <SUBSCRIPTION_GUID> \
--role-name "Contributor" \
--allow-multiple
# Preview what would happen without submitting requests
azpim activate --non-interactive --dry-run \
--subscription-id <SUBSCRIPTION_GUID> \
--role-name "Contributor" \
--output json
# Deactivate specific roles
azpim deactivate --non-interactive --yes \
--subscription-id <SUBSCRIPTION_GUID> \
--role-name "Owner" \
--justification "Task completed"
# Deactivate across all subscriptions (omit subscription-id)
azpim deactivate --non-interactive --yes \
--role-name "Contributor" \
--allow-multiple
Common flags (activate/deactivate):
--non-interactive - Disable interactive prompts-y, --yes - Skip confirmation prompts--subscription-id <id> - Target subscription (optional for deactivate)--role-name <name> - Role name(s) to target (can be repeated)--allow-multiple - Allow multiple role matches--dry-run - Preview without submitting--output <text|json> - Output format (default: text)--quiet - Suppress non-essential outputActivation-specific:
--duration-hours <n> - Duration (1-8 hours, default varies by role)--justification <text> - Justification for activationDeactivation-specific:
--justification <text> - Justification for deactivation (optional)Presets let you save your daily activation/deactivation routines (subscription + role names + duration + justification) and reuse them with --preset <name>.
By default, presets are stored in a per-user config file:
~/.config/azpim/presets.json (or $XDG_CONFIG_HOME/azpim/presets.json)%APPDATA%\azpim\presets.jsonOverride the location with:
AZPIM_PRESETS_PATH=/path/to/presets.jsonA preset can define one or both blocks:
activate: subscriptionId, roleNames[], durationHours, justification, allowMultipledeactivate: subscriptionId (optional), roleNames[], justification, allowMultiplejustification supports simple templates:
${date} → YYYY-MM-DD${datetime} → ISO timestamp${userPrincipalName} → resolved from Microsoft Graph /me# Create a preset (interactive wizard)
azpim preset add daily-ops
# Create a preset with Azure integration (fetches subscriptions/roles)
azpim preset add daily-ops --from-azure
# Edit a preset (interactive wizard)
azpim preset edit daily-ops
# List all presets
azpim preset list
# Show one preset details
azpim preset show daily-ops
# Remove a preset
azpim preset remove daily-ops
# Use a preset (flags still override preset values)
azpim activate --preset daily-ops --yes
# Non-interactive run using the preset
azpim activate --preset daily-ops --non-interactive --yes --output json
# Deactivate using a preset
azpim deactivate --preset daily-ops --non-interactive --yes
When you create a preset via azpim preset add, you can optionally set it as the default for activate and/or deactivate.
azpim activate --non-interactive --yes can work without specifying --subscription-id/--role-name.╔════════════════════════════════════════════════════╗
║ Azure PIM CLI - Role Activation Manager ║
╚════════════════════════════════════════════════════╝
✔ Authentication successful
┌─ User Information ──────────────────────────────────
│ Name: John Doe
│ Email: [email protected]
└──────────────────────────────────────────────────────
✔ Found 3 subscription(s)
? What would you like to do?
❯ ▶ Activate Role(s)
◼ Deactivate Role(s)
✕ Exit
Favorites allow you to mark specific subscriptions for quick access. Favorite subscriptions are displayed at the top of subscription lists in the interactive menu.
By default, favorites are stored per-user:
~/.config/azpim/users/<userId>/favorites.json%APPDATA%\azpim\users\<userId>\favorites.jsonOverride the location with:
AZPIM_FAVORITES_PATH=/path/to/favorites.json# List all favorites
azpim favorites list
azpim fav list
# Add a subscription to favorites
azpim favorites add <subscription-id>
# Add even if subscription is not in cache
azpim favorites add <subscription-id> --force
# Remove a subscription from favorites
azpim favorites remove <subscription-id>
# Clear all favorites
azpim favorites clear
# Export favorites to a file
azpim favorites export ./my-favorites.json
# Import favorites from a file (replaces existing)
azpim favorites import ./my-favorites.json
# Import favorites and merge with existing
azpim favorites import ./my-favorites.json --merge
# Refresh the subscription cache
azpim favorites refresh
azpim automatically caches subscription information to improve startup time. The cache has a 6-hour TTL (time-to-live) and is refreshed automatically when expired.
Cache files are stored per-user:
~/.config/azpim/users/<userId>/subscriptions-cache.json%APPDATA%\azpim\users\<userId>\subscriptions-cache.json# Force refresh the subscription cache
azpim favorites refresh
# Run in development mode with hot reload
pnpm dev
# Build the TypeScript project
pnpm build
# Run the built application
pnpm start
# Lint the codebase
pnpm lint
This repo uses Keep a Changelog format in CHANGELOG.md.
For best results, use Conventional Commits:
feat: ... (new feature) → minor bumpfix: ... (bug fix) → patch bumpchore: ..., docs: ..., refactor: ... (no bump unless breaking)Make sure CHANGELOG.md has up-to-date entries under Unreleased.
Run one of the following:
# Automatically determines next version from commits, updates CHANGELOG.md,
# bumps package.json, and creates a git tag.
pnpm release
# Preview what would change
pnpm release:dry
# Force a specific bump if needed
pnpm release -- --release-as patch
pnpm release -- --release-as minor
pnpm release -- --release-as major
git push --follow-tags
npm publish
# or
pnpm publish
azpim/
├── src/
│ ├── index.ts # CLI entry point and command definitions
│ ├── core/ # Foundational utilities (no domain logic)
│ │ ├── constants.ts # Shared magic values and defaults
│ │ ├── errors.ts # Unified error handling utilities
│ │ ├── json-store.ts # Generic JSON file persistence
│ │ ├── paths.ts # Config/data file path resolution
│ │ └── ui.ts # Terminal UI (spinners, formatting, colors)
│ ├── azure/ # Azure SDK wrappers
│ │ ├── auth.ts # Azure CLI credential + Graph /me lookup
│ │ └── azure-pim.ts # PIM role activation/deactivation API
│ ├── data/ # Local data persistence
│ │ ├── favorites.ts # Favorites management
│ │ ├── presets.ts # Preset configuration and validation
│ │ ├── subscription-cache.ts # Subscription caching (6-hour TTL)
│ │ └── update-check.ts # Update notification system
│ └── cli/ # Interactive flows and command scaffolding
│ ├── cli.ts # Main menu loop and shared helpers
│ ├── command-handler.ts # Reusable command wrapper (auth, UI, errors)
│ ├── activate-flow.ts # Role activation (one-shot + interactive)
│ ├── deactivate-flow.ts # Role deactivation (one-shot + interactive)
│ ├── subscription-selector.ts # Subscription search/select with favorites
│ ├── favorites-manager.ts # Interactive favorites management menu
│ └── presets-cli.ts # Preset add/edit/manage wizards
├── package.json
├── tsconfig.json
├── CHANGELOG.md
└── README.md
The codebase follows a layered module structure:
core/ — Zero-dependency utilities shared across the entire project. json-store.ts provides a generic load/save pattern used by all data persistence files. errors.ts centralizes error extraction and command-level error handling. constants.ts eliminates magic values.azure/ — Thin wrappers around Azure SDKs (@azure/identity, @azure/arm-authorization) and a lightweight Microsoft Graph /me call via native fetch. No UI logic.data/ — Local file persistence (favorites, presets, subscription cache, update state). All use json-store for consistent file I/O.cli/ — User-facing interactive flows and command scaffolding. command-handler.ts provides a wrapper that eliminates boilerplate (auth, UI setup, error handling) across all Commander commands. Activation and deactivation flows share a function from .Ensure Azure CLI is installed and accessible in your PATH:
az --version
Make sure you're logged in to Azure CLI:
az login
Verify your account has PIM-eligible roles:
az account show
Check if you have the necessary permissions in Azure AD
az login --refreshThis project is licensed under the ISC License.
Tapan Meena - [email protected]
Contributions are welcome! Please feel free to submit a Pull Request.
git checkout -b feature/amazing-feature)git commit -m 'Add some amazing feature')git push origin feature/amazing-feature)| Command | Alias | Description |
|---|
activate | a | Activate a role in Azure PIM (default) |
deactivate | d | Deactivate a role in Azure PIM |
preset | - | Manage reusable presets |
favorites | fav | Manage favorite subscriptions |
check-update | update, upgrade | Check for a newer version |
help | - | Display help information |
| Command | Description |
|---|
preset list | List all available presets |
preset show | Show details of a specific preset |
preset add | Add a new preset (interactive wizard) |
preset edit | Edit an existing preset (interactive wizard) |
preset remove | Remove a preset |
| Command | Description |
|---|
favorites list | List all favorite subscriptions |
favorites add | Add a subscription to favorites |
favorites remove | Remove a subscription from favorites |
favorites clear | Clear all favorites |
favorites export | Export favorites to a file |
favorites import | Import favorites from a file |
favorites refresh | Refresh the subscription cache |
withCommandHandlerselectSubscriptionInteractivesubscription-selector.ts| Variable | Description |
|---|
AZPIM_PRESETS_PATH | Override the presets file path |
AZPIM_FAVORITES_PATH | Override the favorites file path |
AZPIM_NO_UPDATE_NOTIFIER | Set to 1 to disable automatic update checks |
AZPIM_DISABLE_UPDATE_CHECK | Alias for AZPIM_NO_UPDATE_NOTIFIER |