
Auditing Azure Active Directory Configuration
- 199 installs
- 27.3k repo stars
- Updated August 2, 2026
- mukul975/anthropic-cybersecurity-skills
auditing-azure-active-directory-configuration is an agent skill that structures review of Azure AD / Entra ID tenant settings for security and compliance g
About
auditing-azure-active-directory-configuration is an agent skill from the Anthropic cybersecurity skills lineage focused on reviewing Microsoft Entra ID (Azure Active Directory) tenant configuration. Solo founders shipping B2B SaaS or internal tools on Azure use it when identity is the blast radius: conditional access policies, legacy auth exposure, guest access, privileged roles, and sync misconfigurations that agents can walk through systematically instead of improvising checklists in chat. Prism lists it under Ship security as the primary shelf, with secondary use in Operate monitoring when you re-audit after org changes. Public SKILL body in the ingest was sparse (license text only), so operational steps should be validated against the full repo file before production audits. Treat outputs as guidance that still needs human verification and your change-management process.
- Azure Active Directory / Microsoft Entra configuration audit workflow for agent-assisted review
- Identity and access hygiene checks aligned with cybersecurity skill-pack patterns
- Suited to pre-launch and periodic compliance sweeps on Microsoft cloud tenants
- Pairs with Anthropic cybersecurity skills collection for enterprise-style hardening
- Apache 2.0 licensed skill artifact from anthropic-cybersecurity-skills repo
Auditing Azure Active Directory Configuration by the numbers
- 199 all-time installs (skills.sh)
- +8 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #775 of 2,203 Security skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mukul975/anthropic-cybersecurity-skills --skill auditing-azure-active-directory-configurationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 199 |
|---|---|
| repo stars | ★ 27.3k |
| Security audit | 3 / 3 scanners passed |
| Last updated | August 2, 2026 |
| Repository | mukul975/anthropic-cybersecurity-skills ↗ |
What it does
Guide an agent through structured review of Microsoft Entra ID (Azure AD) settings—conditional access, identities, and risky configs—before or after production ship.
Who is it for?
Best when you're on Microsoft 365 / Entra ID and need a scripted identity audit before enterprise pilots or SOC-style questionnaires.
Skip if: Skip if you're on Auth0-only or pure custom JWT stacks with no Azure AD footprint, or organizations that require certified penetration tests instead of configuration review skills.
When should I use this skill?
User needs to audit, review, or harden Azure Active Directory / Microsoft Entra ID tenant configuration for security or compliance.
What you get
You get an agent-guided audit pass highlighting risky Azure AD configuration areas to remediate before widening user access or passing customer security reviews.
- Structured audit findings on Entra ID configuration areas
- Prioritized remediation notes for identity and access risks
- Summary suitable for internal ship/security checklist
Files
Auditing Azure Active Directory Configuration
When to Use
- When performing a security assessment of an Azure tenant's identity configuration
- When compliance audits require review of authentication policies, MFA enforcement, and role assignments
- When onboarding a new Azure tenant after merger or acquisition
- When investigating suspicious sign-in activity or compromised accounts
- When validating conditional access policies adequately protect against identity-based attacks
Do not use for on-premises Active Directory auditing (use PingCastle or BloodHound AD), for Azure resource-level RBAC auditing without identity context, or for real-time threat detection (use Microsoft Defender for Identity).
Prerequisites
- Global Reader or Security Reader role in the target Microsoft Entra ID tenant
- Microsoft Graph PowerShell SDK installed (
Install-Module Microsoft.Graph) - Az CLI authenticated to the target tenant (
az login --tenant TENANT_ID) - ScoutSuite with Azure provider configured for automated assessment
- Access to Azure AD audit logs and sign-in logs (requires Azure AD Premium P1/P2)
Workflow
Step 1: Enumerate Tenant Configuration and Security Defaults
Assess the tenant's baseline identity security settings including security defaults and legacy authentication status.
# Connect to Microsoft Graph
Connect-MgGraph -Scopes "Directory.Read.All","Policy.Read.All","AuditLog.Read.All"
# Get tenant details
Get-MgOrganization | Select-Object DisplayName, Id, VerifiedDomains
# Check if Security Defaults are enabled
Get-MgPolicyIdentitySecurityDefaultEnforcementPolicy | Select-Object IsEnabled
# List authentication methods policies
Get-MgPolicyAuthenticationMethodPolicy | ConvertTo-Json -Depth 5
# Check legacy authentication status via Conditional Access
Get-MgIdentityConditionalAccessPolicy | Where-Object {
$_.Conditions.ClientAppTypes -contains "exchangeActiveSync" -or
$_.Conditions.ClientAppTypes -contains "other"
} | Select-Object DisplayName, StateStep 2: Audit Privileged Role Assignments
Review directory role assignments to identify over-privileged users, permanent admin accounts, and risky role configurations.
# List all Global Administrator assignments
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/directoryRoles/filterByIds" \
--body '{"ids":["62e90394-69f5-4237-9190-012177145e10"]}' | \
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/directoryRoles?filter=displayName eq 'Global Administrator'" \
--query "value[0].id" -o tsv
# List all privileged role assignments using Graph API
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments?\$expand=principal" \
--query "value[*].{Role:roleDefinitionId, Principal:principal.displayName, PrincipalType:principal.@odata.type}" \
-o table
# Check for users with multiple admin roles
az ad user list --query "[].{UPN:userPrincipalName, DisplayName:displayName}" -o table
# List service principals with admin role assignments
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments?\$filter=principalOrganizationId eq 'TENANT_ID'" \
-o jsonStep 3: Review Conditional Access Policies
Audit conditional access policies for coverage gaps, particularly around MFA enforcement, device compliance, and location-based restrictions.
# List all Conditional Access policies
Get-MgIdentityConditionalAccessPolicy | Select-Object DisplayName, State, @{
N='GrantControls'; E={$_.GrantControls.BuiltInControls -join ', '}
} | Format-Table -AutoSize
# Identify policies in report-only mode (not enforced)
Get-MgIdentityConditionalAccessPolicy | Where-Object {$_.State -eq "enabledForReportingButNotEnforced"} |
Select-Object DisplayName
# Check MFA enforcement coverage
Get-MgIdentityConditionalAccessPolicy | Where-Object {
$_.GrantControls.BuiltInControls -contains "mfa"
} | Select-Object DisplayName, State, @{
N='Users'; E={$_.Conditions.Users.IncludeUsers -join ', '}
}
# Find policies that exclude groups (potential bypass)
Get-MgIdentityConditionalAccessPolicy | Where-Object {
$_.Conditions.Users.ExcludeGroups.Count -gt 0
} | Select-Object DisplayName, @{
N='ExcludedGroups'; E={$_.Conditions.Users.ExcludeGroups -join ', '}
}Step 4: Identify Stale Accounts and Guest Users
Find accounts that have not signed in recently, disabled accounts with active role assignments, and risky guest user configurations.
# Find users who haven't signed in for 90+ days
az ad user list --query "[?signInActivity.lastSignInDateTime < '2025-11-25T00:00:00Z'].{UPN:userPrincipalName, LastSignIn:signInActivity.lastSignInDateTime, Enabled:accountEnabled}" -o table
# List all guest users
az ad user list --filter "userType eq 'Guest'" \
--query "[].{UPN:userPrincipalName, DisplayName:displayName, CreatedDate:createdDateTime}" \
-o table
# Find guest users with privileged roles
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments?\$expand=principal" \
--query "value[?principal.userType=='Guest'].{Role:roleDefinitionId,Guest:principal.userPrincipalName}" \
-o table
# Check for accounts with disabled MFA
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/reports/authenticationMethods/userRegistrationDetails" \
--query "value[?!isMfaRegistered].{UPN:userPrincipalName,MfaRegistered:isMfaRegistered}" \
-o tableStep 5: Analyze Sign-In Logs for Risky Activity
Review sign-in logs to identify anomalous authentication patterns, failed MFA challenges, and risky sign-in detections.
# Get risky sign-ins from last 7 days
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/auditLogs/signIns?\$filter=riskLevelDuringSignIn ne 'none' and createdDateTime ge 2026-02-16T00:00:00Z" \
--query "value[*].{User:userPrincipalName,Risk:riskLevelDuringSignIn,IP:ipAddress,App:appDisplayName,Status:status.errorCode}" \
-o table
# Get sign-ins from unfamiliar locations
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/auditLogs/signIns?\$filter=riskEventTypes_v2/any(r:r eq 'unfamiliarFeatures')" \
--query "value[*].{User:userPrincipalName,Location:location.city,IP:ipAddress}" \
-o table
# Check for legacy authentication sign-ins
az rest --method GET \
--url "https://graph.microsoft.com/v1.0/auditLogs/signIns?\$filter=clientAppUsed ne 'Browser' and clientAppUsed ne 'Mobile Apps and Desktop clients'" \
--query "value[*].{User:userPrincipalName,ClientApp:clientAppUsed,Status:status.errorCode}" \
-o tableStep 6: Run ScoutSuite Automated Assessment
Execute ScoutSuite for comprehensive automated checks across the Azure tenant configuration.
# Run ScoutSuite against Azure
python3 -m ScoutSuite azure --cli \
--report-dir ./scoutsuite-azure-report \
--all-subscriptions
# Review the generated HTML report
open ./scoutsuite-azure-report/azure-report.htmlKey Concepts
| Term | Definition |
|---|---|
| Microsoft Entra ID | Microsoft's cloud identity and access management service, formerly Azure Active Directory, providing authentication and authorization |
| Conditional Access | Policy engine that evaluates signals (user, device, location, risk) to enforce access controls like MFA, device compliance, or block access |
| Security Defaults | Microsoft's baseline identity protection settings that enforce MFA registration, block legacy auth, and protect privileged actions |
| Privileged Identity Management | Azure AD Premium P2 feature enabling just-in-time privileged access with approval workflows and time-bound role activation |
| Legacy Authentication | Older authentication protocols (POP3, IMAP, SMTP, ActiveSync) that do not support MFA and are commonly exploited for credential attacks |
| Risky Sign-In | Microsoft Entra Identity Protection detection of sign-in anomalies including impossible travel, unfamiliar locations, and malware-linked IPs |
Tools & Systems
- Microsoft Graph API: Primary programmatic interface for querying Entra ID configuration, policies, roles, and audit logs
- Microsoft Graph PowerShell SDK: PowerShell module for Entra ID management and security auditing tasks
- ScoutSuite: Multi-cloud auditing tool with Azure provider support for IAM, storage, networking, and identity checks
- AzureADRecon: Community tool for comprehensive Azure AD reconnaissance and security assessment reporting
- Microsoft Defender for Identity: Cloud-based security solution for detecting identity-based threats and compromised credentials
Common Scenarios
Scenario: Post-Acquisition Azure Tenant Security Assessment
Context: After acquiring a company, the security team needs to assess the Azure tenant identity posture before integrating it with the corporate Entra ID.
Approach: 1. Enumerate all Global Administrators and check for personal accounts in admin roles 2. Review conditional access policies to verify MFA is enforced for all users, not just admins 3. Identify guest users with privileged access that may indicate third-party vendor over-permissioning 4. Check for stale accounts (no sign-in for 90+ days) that could be targets for credential attacks 5. Review sign-in logs for legacy authentication usage that bypasses MFA 6. Verify Security Defaults or equivalent CA policies block legacy auth protocols 7. Produce a risk report with prioritized remediation steps before tenant integration
Pitfalls: Azure AD Premium P2 is required for risky sign-in detections and PIM. If the acquired tenant uses a lower license tier, many identity protection features will be unavailable. Guest users from partner tenants may have implicit access through dynamic groups that are not visible in standard role assignment queries.
Output Format
Azure Active Directory Security Audit Report
===============================================
Tenant: acme-acquired.onmicrosoft.com
Tenant ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Audit Date: 2026-02-23
License: Azure AD Premium P2
IDENTITY CONFIGURATION:
Security Defaults: Disabled (Conditional Access in use)
Conditional Access Policies: 12 (8 enforced, 3 report-only, 1 disabled)
Legacy Auth Blocked: Partial (blocked for admins only)
PRIVILEGED ACCESS:
Global Administrators: 8 (recommended: <= 4)
Permanent admin assignments: 6 (no PIM activation required)
Service principals with admin: 3
Guest users with privileged roles: 2
ACCOUNT HYGIENE:
Total users: 1,247
Stale accounts (90+ days): 89
Guest users: 234
Users without MFA registered: 156
SIGN-IN RISK:
Risky sign-ins (last 30 days): 34
Legacy auth sign-ins (last 7 days): 67
Impossible travel detections: 5
Unfamiliar location sign-ins: 12
CRITICAL FINDINGS:
1. 8 Global Administrators with permanent assignments (use PIM)
2. Legacy authentication not blocked for non-admin users
3. 156 users without MFA registration
4. 2 guest users with Privileged Role Administrator role
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to the Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by the Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding any notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. Please do not remove or change
the license header comment from a contributed file except when
necessary.
Copyright 2026 mukul975
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
API Reference: Auditing Azure Active Directory Configuration
azure-identity Authentication
from azure.identity import DefaultAzureCredential, ClientSecretCredential
# Default (managed identity, env vars, CLI)
credential = DefaultAzureCredential()
# Service principal
credential = ClientSecretCredential(tenant_id, client_id, client_secret)
# Get Graph API token
token = credential.get_token("https://graph.microsoft.com/.default")Microsoft Graph API Endpoints
| Endpoint | Description |
|---|---|
GET /organization | Tenant info and verified domains |
GET /directoryRoles | List directory roles |
GET /directoryRoles/{id}/members | Members of a role |
GET /identity/conditionalAccess/policies | Conditional Access policies |
GET /users?$filter=userType eq 'Guest' | Guest users |
GET /users?$select=signInActivity | User sign-in activity |
GET /auditLogs/signIns | Sign-in logs |
GET /reports/authenticationMethods/userRegistrationDetails | MFA registration |
Python Graph API Helper
import requests
def graph_get(token, endpoint, params=None):
headers = {"Authorization": f"Bearer {token}"}
url = f"https://graph.microsoft.com/v1.0{endpoint}"
return requests.get(url, headers=headers, params=params).json()
# List Global Admins
roles = graph_get(token, "/directoryRoles")
for role in roles["value"]:
if role["displayName"] == "Global Administrator":
members = graph_get(token, f"/directoryRoles/{role['id']}/members")Key Conditional Access Policy Fields
{
"displayName": "Require MFA for admins",
"state": "enabled",
"conditions": {
"users": {"includeUsers": ["All"], "excludeGroups": ["break-glass"]},
"clientAppTypes": ["all"]
},
"grantControls": {
"builtInControls": ["mfa"]
}
}azure-mgmt-authorization (RBAC)
from azure.mgmt.authorization import AuthorizationManagementClient
client = AuthorizationManagementClient(credential, subscription_id)
for assignment in client.role_assignments.list():
print(assignment.principal_id, assignment.role_definition_id)References
- azure-identity: https://pypi.org/project/azure-identity/
- MS Graph API: https://learn.microsoft.com/en-us/graph/api/overview
- azure-mgmt-authorization: https://pypi.org/project/azure-mgmt-authorization/
#!/usr/bin/env python3
"""Agent for auditing Azure Active Directory (Entra ID) configuration."""
import os
import json
import argparse
from datetime import datetime, timedelta
from azure.identity import DefaultAzureCredential, ClientSecretCredential
import requests
def get_graph_token(credential):
"""Obtain a Microsoft Graph API access token."""
token = credential.get_token("https://graph.microsoft.com/.default")
return token.token
def graph_get(token, endpoint, params=None):
"""Make an authenticated GET request to Microsoft Graph API."""
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
url = f"https://graph.microsoft.com/v1.0{endpoint}"
resp = requests.get(url, headers=headers, params=params, timeout=30)
resp.raise_for_status()
return resp.json()
def get_tenant_info(token):
"""Get tenant organization details."""
data = graph_get(token, "/organization")
orgs = data.get("value", [])
if orgs:
org = orgs[0]
return {
"display_name": org.get("displayName"),
"tenant_id": org.get("id"),
"verified_domains": [d["name"] for d in org.get("verifiedDomains", [])],
}
return {}
def list_global_admins(token):
"""List all Global Administrator role assignments."""
roles = graph_get(token, "/directoryRoles")
ga_role = None
for role in roles.get("value", []):
if role["displayName"] == "Global Administrator":
ga_role = role["id"]
break
if not ga_role:
return []
members = graph_get(token, f"/directoryRoles/{ga_role}/members")
return [
{"displayName": m.get("displayName"), "upn": m.get("userPrincipalName"),
"type": m.get("@odata.type", "").split(".")[-1]}
for m in members.get("value", [])
]
def list_conditional_access_policies(token):
"""List all Conditional Access policies with their state and grant controls."""
policies = graph_get(token, "/identity/conditionalAccess/policies")
results = []
for p in policies.get("value", []):
results.append({
"name": p.get("displayName"),
"state": p.get("state"),
"grant_controls": p.get("grantControls", {}).get("builtInControls", []),
"excluded_groups": p.get("conditions", {}).get("users", {}).get("excludeGroups", []),
})
return results
def find_stale_users(token, days=90):
"""Find users who have not signed in for specified number of days."""
cutoff = (datetime.utcnow() - timedelta(days=days)).strftime("%Y-%m-%dT00:00:00Z")
users = graph_get(
token, "/users",
params={
"$select": "displayName,userPrincipalName,signInActivity,accountEnabled",
"$top": "999",
}
)
stale = []
for u in users.get("value", []):
sign_in = u.get("signInActivity", {})
last_sign_in = sign_in.get("lastSignInDateTime")
if last_sign_in and last_sign_in < cutoff:
stale.append({
"upn": u.get("userPrincipalName"),
"display_name": u.get("displayName"),
"last_sign_in": last_sign_in,
"enabled": u.get("accountEnabled"),
})
return stale
def list_guest_users(token):
"""List all guest users in the tenant."""
users = graph_get(
token, "/users",
params={"$filter": "userType eq 'Guest'", "$select": "displayName,userPrincipalName,createdDateTime"}
)
return [
{"upn": u.get("userPrincipalName"), "display_name": u.get("displayName"),
"created": u.get("createdDateTime")}
for u in users.get("value", [])
]
def check_mfa_registration(token):
"""Check users without MFA registered."""
try:
data = graph_get(token, "/reports/authenticationMethods/userRegistrationDetails")
no_mfa = [
{"upn": u.get("userPrincipalName"), "mfa_registered": u.get("isMfaRegistered")}
for u in data.get("value", []) if not u.get("isMfaRegistered")
]
return no_mfa
except Exception:
return []
def get_risky_signins(token, days=7):
"""Get risky sign-in events from the last N days."""
since = (datetime.utcnow() - timedelta(days=days)).strftime("%Y-%m-%dT00:00:00Z")
try:
data = graph_get(
token, "/auditLogs/signIns",
params={"$filter": f"riskLevelDuringSignIn ne 'none' and createdDateTime ge {since}"}
)
return [
{"user": s.get("userPrincipalName"), "risk": s.get("riskLevelDuringSignIn"),
"ip": s.get("ipAddress"), "app": s.get("appDisplayName")}
for s in data.get("value", [])
]
except Exception:
return []
def main():
parser = argparse.ArgumentParser(description="Azure AD Configuration Audit Agent")
parser.add_argument("--tenant-id", default=os.getenv("AZURE_TENANT_ID"))
parser.add_argument("--client-id", default=os.getenv("AZURE_CLIENT_ID"))
parser.add_argument("--client-secret", default=os.getenv("AZURE_CLIENT_SECRET"))
parser.add_argument("--stale-days", type=int, default=90)
parser.add_argument("--output", default="azure_ad_audit.json")
args = parser.parse_args()
if args.client_id and args.client_secret and args.tenant_id:
credential = ClientSecretCredential(args.tenant_id, args.client_id, args.client_secret)
else:
credential = DefaultAzureCredential()
token = get_graph_token(credential)
report = {"audit_date": datetime.utcnow().isoformat(), "findings": {}}
report["findings"]["tenant_info"] = get_tenant_info(token)
print(f"[+] Tenant: {report['findings']['tenant_info'].get('display_name')}")
admins = list_global_admins(token)
report["findings"]["global_admins"] = admins
print(f"[+] Global Admins: {len(admins)}")
ca_policies = list_conditional_access_policies(token)
report["findings"]["conditional_access"] = ca_policies
print(f"[+] Conditional Access policies: {len(ca_policies)}")
stale = find_stale_users(token, args.stale_days)
report["findings"]["stale_users"] = stale
print(f"[+] Stale users ({args.stale_days}+ days): {len(stale)}")
guests = list_guest_users(token)
report["findings"]["guest_users"] = guests
print(f"[+] Guest users: {len(guests)}")
no_mfa = check_mfa_registration(token)
report["findings"]["users_without_mfa"] = no_mfa
print(f"[+] Users without MFA: {len(no_mfa)}")
risky = get_risky_signins(token)
report["findings"]["risky_signins"] = risky
print(f"[+] Risky sign-ins (7d): {len(risky)}")
with open(args.output, "w") as f:
json.dump(report, f, indent=2, default=str)
print(f"[+] Report saved to {args.output}")
if __name__ == "__main__":
main()
Related skills
How it compares
Configuration audit skill for Entra ID—not a replacement for Microsoft Defender portal automation or full purple-team exercises.
FAQ
Who is auditing-azure-active-directory-configuration for?
SaaS founders and small ops leads managing Azure / Entra tenants who want agent-assisted identity configuration reviews.
When should I use auditing-azure-active-directory-configuration?
In Ship security before go-live with SSO customers; in Operate monitoring after role or CA policy changes; when prepping answers for vendor security assessments on Microsoft identity.
Is auditing-azure-active-directory-configuration safe to install?
Expect network and cloud API access for tenant inspection; review the Security Audits panel on this Prism page and restrict agent credentials to least-privilege audit roles.