Initial commit - 611 cybersecurity skills across all subdomains

This commit is contained in:
mukul975
2026-02-25 10:47:44 +01:00
commit 22a7ab1462
1765 changed files with 280648 additions and 0 deletions
@@ -0,0 +1,221 @@
---
name: implementing-azure-ad-privileged-identity-management
description: Configure Microsoft Entra Privileged Identity Management to enforce just-in-time role activation, approval workflows, and access reviews for Azure AD privileged roles.
domain: cybersecurity
subdomain: identity-access-management
tags: [azure-ad, pim, entra-id, just-in-time, privileged-roles, identity-governance, zero-trust]
version: "1.0"
author: mahipal
license: MIT
---
# Implementing Azure AD Privileged Identity Management
## Overview
Microsoft Entra Privileged Identity Management (PIM) provides time-based and approval-based role activation to mitigate risks from excessive, unnecessary, or misused access to critical resources. PIM replaces permanent (standing) privilege assignments with eligible assignments that require users to explicitly activate their role before use, with configurable duration, MFA enforcement, approval workflows, and justification requirements. This is a core component of Zero Trust identity governance in Microsoft environments.
## Prerequisites
- Microsoft Entra ID P2 or Microsoft Entra ID Governance license
- Global Administrator or Privileged Role Administrator role
- Azure subscription for Azure resource role management
- MFA configured for all privileged users
- Microsoft Authenticator or FIDO2 key for admin accounts
## Core Concepts
### Assignment Types
| Type | Behavior | Use Case |
|------|----------|----------|
| Eligible | User must activate the role before use; expires after configured duration | Day-to-day admin work |
| Active | Role is always active; no activation needed | Service accounts, break-glass accounts |
| Time-Bound | Either type with explicit start/end dates | Temporary project access, contractor access |
### PIM Activation Flow
```
User with Eligible Assignment
├── Opens PIM portal → My Roles
├── Clicks "Activate" on the desired role
├── Provides justification and optional ticket number
├── Completes MFA challenge (if required)
├── [If approval required] → Notification sent to approvers
│ │
│ ├── Approver reviews and approves/denies
│ └── User notified of decision
├── Role activated for configured duration (e.g., 8 hours)
└── Role automatically deactivated when duration expires
```
### Supported Resource Types
1. **Microsoft Entra Roles**: Global Admin, Exchange Admin, Security Admin, etc.
2. **Azure Resource Roles**: Owner, Contributor, User Access Administrator on subscriptions/resource groups
3. **PIM for Groups**: Manage membership in privileged security groups
## Implementation Steps
### Step 1: Plan Role Assignments
Audit current permanent role assignments and determine which should be converted to eligible:
| Current Role | Permanent Holders | Action |
|-------------|-------------------|--------|
| Global Administrator | 2-3 admins | Convert to eligible, keep 1 break-glass active |
| Exchange Administrator | IT team | Convert all to eligible |
| Security Administrator | SOC team | Convert to eligible |
| User Administrator | Help desk | Convert to eligible |
| Application Administrator | DevOps | Convert to eligible |
Best practice: Maintain no more than 2 permanent Global Administrators (break-glass accounts).
### Step 2: Configure Role Settings
For each Entra directory role, configure PIM settings:
**Via Microsoft Entra Admin Center:**
1. Navigate to Identity Governance > Privileged Identity Management > Microsoft Entra roles
2. Select "Settings" and choose the role to configure
3. Configure the following:
**Activation Settings:**
- Maximum activation duration: 8 hours (recommended; max 72 hours)
- Require MFA on activation: Enabled
- Require justification: Enabled
- Require ticket information: Enabled (for change management integration)
- Require approval: Enabled for Global Admin, Security Admin
**Assignment Settings:**
- Allow permanent eligible assignment: No (set expiry)
- Expire eligible assignments after: 6 months (requires re-certification)
- Allow permanent active assignment: Only for break-glass accounts
- Require MFA on active assignment: Enabled
- Require justification on active assignment: Enabled
**Notification Settings:**
- Send email when members are assigned eligible: Role assigners, admins
- Send email when members activate: Admins, security team
- Send email when eligible members activate roles: Role assignees
### Step 3: Configure via Microsoft Graph API
```python
import requests
# Acquire token for Microsoft Graph
def get_graph_token(tenant_id, client_id, client_secret):
url = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
data = {
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret,
"scope": "https://graph.microsoft.com/.default"
}
response = requests.post(url, data=data)
return response.json()["access_token"]
# Create eligible role assignment
def create_eligible_assignment(token, role_definition_id, principal_id,
directory_scope="/", duration_hours=8):
url = "https://graph.microsoft.com/v1.0/roleManagement/directory/roleEligibilityScheduleRequests"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
body = {
"action": "adminAssign",
"justification": "PIM eligible assignment",
"roleDefinitionId": role_definition_id,
"directoryScopeId": directory_scope,
"principalId": principal_id,
"scheduleInfo": {
"startDateTime": "2025-01-01T00:00:00Z",
"expiration": {
"type": "afterDuration",
"duration": "P180D" # 180-day eligible window
}
}
}
response = requests.post(url, headers=headers, json=body)
return response.json()
# Activate a role (user self-service)
def activate_role(token, role_definition_id, principal_id, justification,
duration_hours=8):
url = "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignmentScheduleRequests"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
body = {
"action": "selfActivate",
"principalId": principal_id,
"roleDefinitionId": role_definition_id,
"directoryScopeId": "/",
"justification": justification,
"scheduleInfo": {
"startDateTime": None, # Now
"expiration": {
"type": "afterDuration",
"duration": f"PT{duration_hours}H"
}
}
}
response = requests.post(url, headers=headers, json=body)
return response.json()
```
### Step 4: Configure Access Reviews
Set up recurring access reviews to verify eligible assignments remain appropriate:
1. Navigate to Identity Governance > Access Reviews > New Access Review
2. Configure:
- Review scope: Privileged Identity Management role assignments
- Roles: Select all critical roles (Global Admin, Security Admin, etc.)
- Reviewers: Managers or self-review with justification
- Frequency: Quarterly for critical roles, semi-annually for others
- Auto-apply results: Remove access for non-responsive reviews
- Duration: 14 days for reviewers to respond
### Step 5: Configure Alerts
Enable PIM security alerts:
| Alert | Trigger | Action |
|-------|---------|--------|
| Too many global admins | > 5 Global Admins | Review and reduce |
| Roles being assigned outside PIM | Direct role assignment | Investigate and convert to PIM |
| Roles not requiring MFA | Activation without MFA | Enable MFA requirement |
| Stale eligible assignments | Not activated in 90 days | Review and potentially remove |
| Potential stale service accounts | Active assignments not used | Investigate and decommission |
## Validation Checklist
- [ ] All permanent privileged role assignments converted to eligible (except break-glass)
- [ ] Break-glass accounts configured as active with monitoring alerts
- [ ] MFA required for all role activations
- [ ] Approval workflow configured for Global Administrator and Security Administrator
- [ ] Maximum activation duration set to 8 hours or less for critical roles
- [ ] Eligible assignments expire after 6 months (requires re-certification)
- [ ] Justification and ticket information required for activations
- [ ] Email notifications configured for role assignments and activations
- [ ] Access reviews scheduled quarterly for all privileged roles
- [ ] PIM alerts enabled and reviewed weekly
- [ ] Audit logs forwarded to SIEM for monitoring
## References
- [Microsoft Entra PIM Documentation](https://learn.microsoft.com/en-us/entra/id-governance/privileged-identity-management/pim-configure)
- [Plan a PIM Deployment](https://learn.microsoft.com/en-us/entra/id-governance/privileged-identity-management/pim-deployment-plan)
- [Start Using PIM](https://learn.microsoft.com/en-us/entra/id-governance/privileged-identity-management/pim-getting-started)
- [Microsoft Graph PIM API](https://learn.microsoft.com/en-us/graph/api/resources/privilegedidentitymanagementv3-overview)
@@ -0,0 +1,51 @@
# Azure AD PIM Implementation Template
## Tenant Details
| Field | Value |
|-------|-------|
| Tenant ID | |
| Tenant Name | |
| License | Entra ID P2 / Entra ID Governance |
| Implementation Date | |
| Project Lead | |
## Role Configuration Matrix
| Role | Assignment Type | Max Activation | MFA Required | Approval Required | Approver |
|------|----------------|---------------|--------------|-------------------|----------|
| Global Administrator | Eligible | 1 hour | Yes | Yes | |
| Security Administrator | Eligible | 4 hours | Yes | Yes | |
| Exchange Administrator | Eligible | 8 hours | Yes | No | |
| User Administrator | Eligible | 8 hours | Yes | No | |
| Application Administrator | Eligible | 8 hours | Yes | No | |
## Break-Glass Accounts
| Account | UPN | Assignment | MFA | Storage Location |
|---------|-----|-----------|-----|-----------------|
| Break-Glass 1 | | Active Global Admin | FIDO2 key | |
| Break-Glass 2 | | Active Global Admin | FIDO2 key | |
## Migration Checklist
- [ ] All permanent role assignments inventoried
- [ ] Break-glass accounts identified and documented
- [ ] PIM role settings configured for each role
- [ ] Approval workflows configured with designated approvers
- [ ] MFA enforced for all role activations
- [ ] Permanent assignments converted to eligible (except break-glass)
- [ ] Notification settings configured (admin, security team)
- [ ] Access reviews scheduled (quarterly)
- [ ] PIM alerts enabled and monitored
- [ ] Audit logs forwarded to SIEM
- [ ] User communication sent with activation instructions
- [ ] Help desk trained on PIM troubleshooting
## Access Review Schedule
| Role | Frequency | Reviewer | Auto-Apply | Duration |
|------|-----------|----------|------------|----------|
| Global Administrator | Monthly | Security Lead | Yes - Remove | 7 days |
| Security Administrator | Quarterly | CISO | Yes - Remove | 14 days |
| All Other Admin Roles | Quarterly | Manager | Yes - Remove | 14 days |
@@ -0,0 +1,46 @@
# Azure AD PIM - Standards Reference
## Microsoft Entra ID Licensing
| Feature | Required License |
|---------|-----------------|
| PIM for Entra Roles | Entra ID P2 or Entra ID Governance |
| PIM for Azure Resources | Entra ID P2 or Entra ID Governance |
| PIM for Groups | Entra ID P2 or Entra ID Governance |
| Access Reviews | Entra ID P2 or Entra ID Governance |
| Conditional Access | Entra ID P1 (minimum) |
## Critical Entra Directory Roles
| Role | Risk Level | Recommended PIM Setting |
|------|-----------|------------------------|
| Global Administrator | Critical | Eligible, approval required, max 1hr activation |
| Privileged Role Administrator | Critical | Eligible, approval required |
| Security Administrator | High | Eligible, MFA required |
| Exchange Administrator | High | Eligible, MFA required |
| SharePoint Administrator | High | Eligible, MFA required |
| User Administrator | Medium | Eligible, MFA required |
| Application Administrator | High | Eligible, MFA required |
| Cloud Application Administrator | High | Eligible, MFA required |
| Intune Administrator | Medium | Eligible, justification required |
| Compliance Administrator | Medium | Eligible, justification required |
## Compliance Framework Mapping
### NIST SP 800-53 Rev 5
- AC-2(4): Automated Audit Actions (PIM audit logs)
- AC-2(5): Inactivity Logout (time-bound activations)
- AC-6(1): Authorize Access to Security Functions
- AC-6(2): Non-Privileged Access for Non-Security Functions
- AC-6(5): Privileged Accounts (eligible vs. active)
### CIS Microsoft 365 Foundations Benchmark
- 1.1.1: Ensure MFA is enabled for all users in admin roles
- 1.1.3: Ensure that between two and four Global Admins are designated
- 1.1.6: Ensure Administrative accounts are separate and cloud-only
- 1.3.1: Ensure PIM is used to manage roles
### SOC 2 Trust Service Criteria
- CC6.1: Logical and physical access controls
- CC6.2: Prior to issuing credentials, registration and authorization
- CC6.3: Authorize, modify, or remove access timely
@@ -0,0 +1,107 @@
# Azure AD PIM - Workflows
## PIM Deployment Workflow
```
Phase 1: DISCOVERY
├── Export all permanent role assignments via Microsoft Graph
├── Identify users with multiple admin roles
├── Flag accounts without MFA enabled
└── Document break-glass account strategy
Phase 2: PLANNING
├── Define activation settings per role (duration, MFA, approval)
├── Identify approvers for each critical role
├── Create communication plan for affected admins
└── Schedule pilot group for initial rollout
Phase 3: CONFIGURATION
├── Configure PIM role settings (activation, assignment, notification)
├── Convert permanent assignments to eligible (except break-glass)
├── Configure conditional access policies for admin activation
└── Enable audit logging and SIEM integration
Phase 4: TESTING
├── Test role activation with pilot users
├── Test approval workflow end-to-end
├── Test MFA enforcement during activation
├── Test auto-deactivation after duration expires
└── Validate audit logs capture all PIM events
Phase 5: ROLLOUT
├── Convert remaining permanent assignments to eligible
├── Notify all affected users with activation instructions
├── Monitor for activation failures and help desk tickets
└── Configure access reviews on quarterly schedule
```
## Role Activation Workflow
```
Admin needs to perform privileged task
├── Navigate to PIM portal (Entra Admin Center > PIM > My Roles)
├── Click "Activate" on the needed role
├── Select activation duration (up to configured max)
├── Enter justification and optional ticket number
├── Complete MFA challenge
├── [If approval required]
│ ├── Request submitted to approvers
│ ├── Approvers receive email notification
│ ├── Approver reviews justification and approves/denies
│ └── Admin receives approval notification
├── Role becomes active
├── Admin performs required task
└── Role automatically deactivates when duration expires
(or admin manually deactivates early)
```
## Access Review Workflow
```
Quarterly Access Review Triggered
├── PIM sends review notifications to designated reviewers
├── For each eligible assignment:
│ ├── Reviewer checks: Is this role still needed?
│ ├── Reviewer checks: When was role last activated?
│ ├── Decision: Approve (maintain), Deny (remove), or Don't know
│ └── Provide justification for decision
├── Review period expires (14 days default)
├── Auto-apply results:
│ ├── Approved assignments maintained
│ ├── Denied assignments removed
│ └── No-response: configurable (remove or maintain)
└── Review summary report generated for compliance
```
## Break-Glass Account Workflow
```
Normal Operations:
└── Break-glass accounts exist as ACTIVE Global Admin
├── Stored in secure physical safe (password printout)
├── Excluded from conditional access policies
├── Monitored by Azure Monitor alert rule
└── Monthly verification: confirm no unauthorized sign-ins
Emergency Use:
├── Primary admin methods unavailable (MFA outage, PIM issue)
├── Retrieve break-glass credentials from safe
├── Sign in and resolve the emergency
├── Document all actions taken
├── Reset break-glass credentials after use
└── Review and document in incident log
```
@@ -0,0 +1,219 @@
#!/usr/bin/env python3
"""
Azure AD PIM Audit and Configuration Tool
Audits Entra ID role assignments, identifies permanent privileged assignments
that should be converted to PIM eligible, and monitors PIM activation events.
Requirements:
pip install msal requests pandas
"""
import json
import sys
from datetime import datetime, timezone
try:
import requests
import msal
except ImportError:
print("[ERROR] Required: pip install msal requests")
sys.exit(1)
class EntraPIMAuditor:
"""Audit and manage Microsoft Entra PIM configuration."""
def __init__(self, tenant_id, client_id, client_secret):
self.tenant_id = tenant_id
self.client_id = client_id
self.client_secret = client_secret
self.token = None
self._authenticate()
def _authenticate(self):
"""Acquire Microsoft Graph access token using client credentials."""
app = msal.ConfidentialClientApplication(
self.client_id,
authority=f"https://login.microsoftonline.com/{self.tenant_id}",
client_credential=self.client_secret,
)
result = app.acquire_token_for_client(
scopes=["https://graph.microsoft.com/.default"]
)
if "access_token" in result:
self.token = result["access_token"]
print("[OK] Authenticated to Microsoft Graph")
else:
raise Exception(f"Auth failed: {result.get('error_description')}")
def _graph_get(self, endpoint):
"""Make authenticated GET request to Microsoft Graph."""
headers = {"Authorization": f"Bearer {self.token}"}
url = f"https://graph.microsoft.com/v1.0{endpoint}"
response = requests.get(url, headers=headers)
response.raise_for_status()
return response.json()
def get_directory_roles(self):
"""List all Entra directory role definitions."""
result = self._graph_get("/roleManagement/directory/roleDefinitions")
roles = {}
for role in result.get("value", []):
roles[role["id"]] = {
"displayName": role["displayName"],
"description": role.get("description", ""),
"isBuiltIn": role.get("isBuiltIn", False),
}
return roles
def get_active_assignments(self):
"""List all permanently active role assignments (non-PIM)."""
result = self._graph_get(
"/roleManagement/directory/roleAssignments?$expand=principal"
)
assignments = []
for item in result.get("value", []):
assignments.append({
"roleDefinitionId": item["roleDefinitionId"],
"principalId": item["principalId"],
"directoryScopeId": item.get("directoryScopeId", "/"),
"principalDisplayName": item.get("principal", {}).get("displayName", ""),
"principalType": item.get("principal", {}).get("@odata.type", ""),
})
return assignments
def get_eligible_assignments(self):
"""List all PIM eligible role assignments."""
result = self._graph_get(
"/roleManagement/directory/roleEligibilityScheduleInstances"
)
eligible = []
for item in result.get("value", []):
eligible.append({
"roleDefinitionId": item["roleDefinitionId"],
"principalId": item["principalId"],
"directoryScopeId": item.get("directoryScopeId", "/"),
"startDateTime": item.get("startDateTime"),
"endDateTime": item.get("endDateTime"),
})
return eligible
def get_pim_activation_history(self, days=30):
"""Retrieve PIM role activation audit events."""
result = self._graph_get(
f"/auditLogs/directoryAudits?"
f"$filter=category eq 'RoleManagement' and "
f"activityDisplayName eq 'Add member to role completed (PIM activation)'"
f"&$top=100"
)
activations = []
for event in result.get("value", []):
activations.append({
"activityDateTime": event.get("activityDateTime"),
"activityDisplayName": event.get("activityDisplayName"),
"initiatedBy": event.get("initiatedBy", {}).get("user", {}).get("displayName", ""),
"targetResources": [
t.get("displayName", "") for t in event.get("targetResources", [])
],
"result": event.get("result"),
})
return activations
def identify_permanent_admins(self):
"""Find permanently active admin assignments that should be PIM eligible."""
roles = self.get_directory_roles()
active = self.get_active_assignments()
eligible = self.get_eligible_assignments()
critical_roles = {
rid: info for rid, info in roles.items()
if info["displayName"] in [
"Global Administrator",
"Privileged Role Administrator",
"Security Administrator",
"Exchange Administrator",
"SharePoint Administrator",
"User Administrator",
"Application Administrator",
"Cloud Application Administrator",
"Intune Administrator",
"Compliance Administrator",
]
}
findings = []
eligible_principals = {
(e["roleDefinitionId"], e["principalId"]) for e in eligible
}
for assignment in active:
role_id = assignment["roleDefinitionId"]
if role_id in critical_roles:
is_also_eligible = (role_id, assignment["principalId"]) in eligible_principals
findings.append({
"role": critical_roles[role_id]["displayName"],
"principal": assignment["principalDisplayName"],
"principalType": assignment["principalType"],
"hasEligibleAssignment": is_also_eligible,
"recommendation": "Convert to PIM eligible" if not is_also_eligible else "Review - has both active and eligible",
})
return findings
def generate_audit_report(self):
"""Generate comprehensive PIM audit report."""
roles = self.get_directory_roles()
active = self.get_active_assignments()
eligible = self.get_eligible_assignments()
permanent_findings = self.identify_permanent_admins()
report = {
"report_title": "Microsoft Entra PIM Audit Report",
"tenant_id": self.tenant_id,
"generated_at": datetime.now(timezone.utc).isoformat(),
"summary": {
"total_directory_roles": len(roles),
"total_active_assignments": len(active),
"total_eligible_assignments": len(eligible),
"permanent_admin_findings": len(permanent_findings),
},
"findings": permanent_findings,
"recommendations": [],
}
if len(permanent_findings) > 0:
report["recommendations"].append({
"priority": "Critical",
"finding": f"{len(permanent_findings)} permanent privileged role assignments found",
"action": "Convert to PIM eligible assignments with MFA and approval requirements"
})
active_global_admins = sum(
1 for f in permanent_findings
if f["role"] == "Global Administrator"
)
if active_global_admins > 2:
report["recommendations"].append({
"priority": "High",
"finding": f"{active_global_admins} permanent Global Administrators (should be max 2 break-glass)",
"action": "Reduce to 2 break-glass accounts, convert rest to PIM eligible"
})
return report
if __name__ == "__main__":
print("=" * 60)
print("Microsoft Entra PIM Audit Tool")
print("=" * 60)
print()
print("Usage:")
print(" auditor = EntraPIMAuditor(tenant_id, client_id, client_secret)")
print(" report = auditor.generate_audit_report()")
print(" print(json.dumps(report, indent=2))")
print()
print("Required Microsoft Graph permissions:")
print(" - RoleManagement.Read.All")
print(" - AuditLog.Read.All")
print(" - Directory.Read.All")