
Ad Security Reviewer Skill
- 93 installs
- 404kidwiz/claude-supercode-skills
Review advertising and marketing content for security vulnerabilities and compliance issues.
About
Security reviewer specialized in advertising and marketing materials. Solo builders use this to audit sponsored content, ads, and promotional materials for security risks before publication.
- Ad content scanning
- Vulnerability detection
- Compliance check
Ad Security Reviewer by the numbers
- 93 all-time installs (skills.sh)
- Ranked #1,072 of 2,222 Security skills by installs in the Skillselion catalog
- Data as of Aug 11, 2026 (Skillselion catalog sync)
npx skills add https://github.com/404kidwiz/claude-supercode-skills --skill ad-security-reviewerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 93 |
|---|---|
| Repository | 404kidwiz/claude-supercode-skills ↗ |
What it does
Review advertising and marketing content for security vulnerabilities and compliance issues.
When should I use this skill?
before launching sponsored or paid content
Files
Active Directory Security Reviewer
Purpose
Provides comprehensive Active Directory security posture analysis specializing in identity attack path evaluation, privilege escalation detection, and enterprise domain hardening. Offers actionable recommendations for securing authentication protocols, privileged group configurations, and attack surface reduction across Windows domains.
When to Use
- Analyzing Active Directory security posture
- Reviewing privileged group design and delegation models
- Assessing authentication protocols and legacy configurations
- Identifying attack surface exposure across enterprise domains
- Detecting orphaned permissions, ACL drift, or excessive rights
- Evaluating domain/forest functional levels and security implications
- Enforcing LDAP signing, channel binding, Kerberos hardening
What This Skill Does
Invoke this skill when:
- User needs to analyze Active Directory security posture
- Reviewing privileged group design and delegation models
- Assessing authentication protocols and legacy configurations
- Identifying attack surface exposure across enterprise domains
- Detecting orphaned permissions, ACL drift, or excessive rights
- Evaluating domain/forest functional levels and security implications
- Enforcing LDAP signing, channel binding, Kerberos hardening
- Identifying NTLM fallback, weak encryption, or legacy trust configurations
- Analyzing GPO security filtering and delegation
- Validating restricted groups and local admin enforcement
- Reviewing SYSVOL permissions and replication security
- Evaluating exposure to common vectors (DCShadow, DCSync, Kerberoasting)
- Identifying stale SPNs, weak service accounts, or unconstrained delegation
What This Skill Does
AD Security Posture Assessment
Analyzes privileged group configurations:
- Domain Admins, Enterprise Admins, Schema Admins
- Tiering models and delegation best practices
- Detection of orphaned permissions, ACL drift, excessive rights
- Domain/forest functional levels and security implications
Authentication & Protocol Hardening
Reviews and recommends:
- LDAP signing, channel binding, Kerberos hardening
- NTLM fallback mitigation
- Weak encryption detection
- Legacy trust configuration risks
- Conditional access transitions (Entra ID) recommendations
GPO & SYSVOL Security Review
Examines:
- Security filtering and delegation patterns
- Restricted groups and local admin enforcement
- SYSVOL permissions and replication security validation
Attack Surface Reduction
Identifies and prioritizes:
- Exposure to common vectors (DCShadow, DCSync, Kerberoasting)
- Stale SPNs, weak service accounts, unconstrained delegation
- Provides prioritization paths (quick wins → structural changes)
Core Capabilities
Security Analysis
- Privileged groups audit with justification
- Delegation boundaries review and documentation
- GPO hardening validation
- Legacy protocols assessment and mitigation
- Service account classification and security
- Attack vector identification and scoring
Risk Assessment
- Identity attack path mapping
- Privilege escalation vector detection
- Domain hardening gap analysis
- Enterprise domain security posture scoring
- Functional level impact evaluation
Remediation Planning
- Executive summary of key risks
- Technical remediation plan with prioritization
- PowerShell or GPO-based implementation scripts
- Validation and rollback procedures
Tool Restrictions
This skill requires:
- Read access - To analyze AD configurations, GPOs, and security policies
- Grep access - To search for security patterns and configurations
- Write access - To create remediation scripts and reports
- Bash access - To execute validation commands (when authorized)
- Glob access - To locate configuration files
This skill cannot:
- Modify production AD without explicit authorization
- Execute changes without validation procedures
- Make irreversible changes without rollback plans
Integration with Other Skills
This skill collaborates with:
- powershell-security-hardening - For implementation of remediation steps
- windows-infra-admin - For operational safety reviews
- security-auditor - For compliance cross-mapping
- powershell-5.1-expert - For AD RSAT automation
- it-ops-orchestrator - For multi-domain, multi-agent task delegation
Example Interactions
Scenario 1: AD Security Review
User: "Review our Active Directory security posture and identify attack vectors"
1. Analyze privileged groups (Domain Admins, Enterprise Admins, Schema Admins)
2. Review tiering models and delegation best practices
3. Detect orphaned permissions, ACL drift, excessive rights
4. Evaluate domain/forest functional levels and security implications
5. Identify attack surface exposure (DCShadow, DCSync, Kerberoasting)
6. Provide executive summary of key risks
7. Generate technical remediation plan with prioritization
8. Create PowerShell or GPO-based implementation scripts
9. Document validation and rollback proceduresScenario 2: Privilege Escalation Analysis
User: "Find potential privilege escalation paths in our domain"
1. Query AD for privileged group membership and delegation
2. Map tiering model violations (e.g., Tier 0 access from Tier 2)
3. Identify Kerberoasting opportunities (service accounts with SPNs)
4. Analyze delegation paths (unconstrained, constrained, resource-based)
5. Detect DCShadow or DCSync replication abuse vectors
6. Score risk severity and provide quick wins
7. Recommend structural changes for long-term hardening
8. Document mitigation steps with validation proceduresScenario 3: Legacy Protocol Assessment
User: "Assess our authentication protocol security and recommend hardening"
1. Review current authentication protocols (Kerberos, NTLM, LDAP)
2. Identify NTLM fallback scenarios and weak encryption
3. Evaluate LDAP signing and channel binding enforcement
4. Assess Kerberos hardening (PAC enforcement, AES encryption)
5. Recommend conditional access transitions to Entra ID
6. Provide GPO-based remediation steps
7. Create validation scripts to test hardening
8. Document rollback procedures for business continuityBest Practices
Security Analysis Excellence
- Always create rollback plans before implementing changes
- Validate in test environment before production changes
- Document all security decisions and justifications
- Prioritize quick wins alongside structural changes
- Test remediation scripts before deployment
- Monitor for unintended side effects after changes
- Use least-privilege principle for all operations
- Maintain audit trail of all security modifications
Assessment Methodology
- Follow a systematic approach: enumerate, analyze, prioritize, remediate
- Use multiple data sources to triangulate findings (LDAP, PowerShell, Azure AD)
- Validate findings against multiple systems to avoid false positives
- Document evidence for every finding (screenshots, query results)
- Consider both technical and organizational security factors
- Assess not just current state but also configuration drift
Remediation Planning
- Prioritize by risk, not just ease of implementation
- Group related changes into cohesive remediation batches
- Provide multiple remediation options with trade-offs
- Include validation steps for each remediation action
- Document rollback procedures even if not expected to be needed
- Consider business impact and schedule changes during maintenance windows
- Communicate changes to affected teams before implementation
Tool Selection and Usage
- Use native tools (PowerShell, ADUC) first, third-party tools second
- Validate tool outputs against multiple data sources
- Keep authentication and privilege escalation tools secure
- Consider audit logging requirements for all tools
- Use automation consistently across all domains
- Test tools in non-production first to validate behavior
Reporting and Documentation
- Executive summaries should be actionable and concise
- Technical details should be reproducible by other analysts
- Include both finding and evidence in every report
- Provide clear remediation steps with PowerShell examples
- Track remediation progress over time
- Update documentation as environment changes
Examples
Example 1: Large Enterprise AD Security Assessment
Scenario: A Fortune 500 company with 50K users, 200+ domains, and complex trust relationships needs comprehensive security assessment.
Assessment Approach: 1. Enumeration Phase: Automated discovery of all domains, trusts, and privileged groups 2. Analysis Phase: Cross-domain analysis of permissions and delegation 3. Risk Scoring: Prioritized findings based on exploitability and impact 4. Remediation Planning: Phased approach addressing critical findings first
Key Findings:
- 847 accounts with Domain Admin privileges (should be <50)
- 23 domains with weak password policies (no complexity, no lockout)
- Cross-forest trusts using outdated authentication protocols
- 156 stale service accounts with excessive privileges
Remediation Delivered:
- Tiered admin model implementation reducing DA count to 32
- Password policy standardization across all domains
- Trust migration to selective authentication
- Service account lifecycle management automation
Example 2: Privilege Escalation Path Analysis
Scenario: Security team suspects lateral movement paths exist from standard user accounts to Domain Admin.
Investigation Approach: 1. Account Enumeration: Query all user accounts and their group memberships 2. Trust Mapping: Map all delegation relationships and ACL permissions 3. Path Analysis: Use BloodHound-like analysis to find attack paths 4. Exploit Validation: Test identified paths in controlled environment
Attack Paths Identified:
- User accounts with "Write to user" permissions allowing DCSync
- Stale computer accounts usable for Kerberoasting
- Unconstrained delegation on legacy application servers
- Overly permissive cross-namespace permissions
Remediation:
- ACL cleanup with explicit justification for each permission
- Computer account restriction to required SPNs
- Migration from unconstrained to constrained delegation
- Cross-forest permission review and normalization
Example 3: Cloud Hybrid Identity Security Review
Scenario: Organization with hybrid identity (AD Connect sync to Entra ID) needs security review of both environments.
Assessment Scope: 1. On-Prem AD: Password policies, MFA registration, risky sign-ins 2. Entra ID: Conditional Access policies, PIM configurations, consent grants 3. AD Connect: Sync permissions, filtering rules, device writeback 4. Integration: Pass-through authentication security, seamless SSO risks
Findings and Remediation:
- Pass-through Authentication agents not isolated from other workloads
- Conditional Access policies allowing legacy authentication
- Global Admins with permanent access (no PIM)
- Consent grants to unverified publisher applications
Deliverables:
- Hybrid identity security architecture diagram
- Entra ID Conditional Access policy recommendations
- AD Connect hardening checklist
- Ongoing monitoring and alerting rules
Automation Scripts and References
The AD security reviewer skill includes comprehensive automation scripts and reference documentation located in:
Scripts (scripts/ directory)
- analyze_ad_security.ts: TypeScript security analyzer with comprehensive AD security assessment including privileged groups, stale accounts, password policies, MFA enrollment, suspicious sign-ins, conditional access, and risky users
- audit_privileged_groups.ps1: PowerShell script for auditing privileged group memberships, inactive accounts, excessive members, and delegation issues with HTML report generation
- review_delegation.ps1: PowerShell delegation review script that analyzes AD delegation permissions, identifies excessive delegation, and generates detailed HTML reports
References (references/ directory)
- security_quickstart.md: Quick start guide with installation, authentication, common patterns, interpretation of findings, and integration with monitoring
- remediation_patterns.md: Comprehensive remediation patterns for privileged groups, account security, delegation, conditional access, incident response, compliance, and recovery procedures
Output Format
This skill delivers: 1. Executive Summary - High-level security posture overview 2. Technical Analysis - Detailed findings with evidence 3. Remediation Plan - Prioritized action items 4. Implementation Scripts - PowerShell/GPO scripts for fixes 5. Validation Procedures - Steps to verify remediation 6. Rollback Plans - Recovery procedures if issues occur
Active Directory Security Remediation Patterns
Common patterns and best practices for remediating Active Directory security findings.
Privileged Group Remediation
Automated Privilege Reduction
function Remove-ExcessivePrivilegedMembers {
param(
[string]$GroupName,
[int]$MaxMembers
)
$group = Get-ADGroup -Identity $GroupName
if ($group) {
$members = Get-ADGroupMember -Identity $group
if ($members.Count -gt $MaxMembers) {
Write-Warning "Group $GroupName has $($members.Count) members (limit: $MaxMembers)"
# Remove non-admin members
foreach ($member in $members) {
$user = Get-ADUser -Identity $member -ErrorAction SilentlyContinue
if ($user) {
$isAdmin = $user.MemberOf | Where-Object {
$_ -match 'Admin|Administrator'
}
if (-not $isAdmin) {
Write-Host "Removing $($user.SamAccountName) from $GroupName"
Remove-ADGroupMember -Identity $group -Members $user -Confirm:$false
}
}
}
}
}
}Just-In-Time Admin Access
interface JitAccessRequest {
requesterId: string;
targetRole: string;
duration: number; // hours
justification: string;
approvalRequired: boolean;
}
async function grantJitAccess(request: JitAccessRequest): Promise<boolean> {
// Validate request
const requester = await getUserById(request.requesterId);
const hasApproval = await checkManagerApproval(request);
if (!hasApproval && request.approvalRequired) {
console.log('Access request pending approval');
return false;
}
// Add to privileged group temporarily
const groupId = await getGroupIdByRole(request.targetRole);
await addMemberToGroup(groupId, request.requesterId);
// Schedule removal
setTimeout(async () => {
await removeMemberFromGroup(groupId, request.requesterId);
console.log(`JIT access expired for user ${request.requesterId}`);
}, request.duration * 60 * 60 * 1000);
// Log the access grant
await logAuditEvent({
action: 'JitAccessGranted',
userId: request.requesterId,
role: request.targetRole,
duration: request.duration,
justification: request.justification
});
return true;
}PIM (Privileged Identity Management) Integration
async function configurePIMRole(
roleId: string,
approvers: string[],
settings: {
maxDuration: number;
justificationRequired: boolean;
approvalRequired: boolean;
notificationRequired: boolean;
}
): Promise<boolean> {
try {
const pimSettings = {
roleSettings: {
isMfaOn: true,
maxEligibleActivationDuration: settings.maxDuration,
justificationRequiredOnActivation: settings.justificationRequired,
approvalRequiredOnActivation: settings.approvalRequired,
ticketingInfoRequiredOnActivation: false,
notificationRequired: settings.notificationRequired,
approvers: approvers.map(a => ({
id: a,
description: 'Manager',
isDefault: true
}))
}
};
await graphClient
.api(`/roleManagement/roleDefinitions/${roleId}/roleSettings`)
.patch(pimSettings);
return true;
} catch (error) {
console.error('Failed to configure PIM role:', error);
return false;
}
}Account Security Remediation
Stale Account Remediation
function Disable-StaleAccounts {
param(
[int]$InactiveDays = 90,
[string[]]$ExcludedUsers = @(),
[string]$LogPath = ".\stale_account_remediation.log"
)
$thresholdDate = (Get-Date).AddDays(-$InactiveDays)
$staleUsers = Get-ADUser -Filter {
Enabled -eq $true -and
LastLogonDate -lt $thresholdDate
} -Properties LastLogonDate,DisplayName,MemberOf
foreach ($user in $staleUsers) {
# Skip excluded users
if ($user.SamAccountName -in $ExcludedUsers) {
continue
}
# Check if user has any active roles
$hasPrivileges = $user.MemberOf | Where-Object {
$_ -match 'Admin|Operator|Creator'
}
if ($hasPrivileges) {
Write-Warning "Privileged stale account: $($user.SamAccountName)"
# Log but don't disable privileged accounts
"$((Get-Date)) - Privileged stale account: $($user.SamAccountName), DN: $($user.DistinguishedName)" |
Add-Content $LogPath
} else {
Write-Host "Disabling stale account: $($user.SamAccountName)"
Disable-ADAccount -Identity $user -Confirm:$false
# Move to disabled OU
$disabledOU = "OU=Disabled Users,DC=example,DC=com"
Move-ADObject -Identity $user.DistinguishedName -TargetPath $disabledOU
"$((Get-Date)) - Disabled stale account: $($user.SamAccountName)" |
Add-Content $LogPath
}
}
}Password Reset and MFA Enforcement
async function enforceMFAForPrivilegedUsers(): Promise<{ enforced: number; failed: number }> {
let enforced = 0;
let failed = 0;
// Get all privileged users
const privilegedGroups = await getPrivilegedGroupIds();
const privilegedUserIds = new Set<string>();
for (const groupId of privilegedGroups) {
const members = await getGroupMembers(groupId);
members.forEach(m => privilegedUserIds.add(m.id));
}
// Check each user's MFA status
for (const userId of privilegedUserIds) {
const authMethods = await getUserAuthMethods(userId);
const hasMFA = authMethods.some(m =>
m['@odata.type'] === '#microsoft.graph.phoneAuthenticationMethod' ||
m['@odata.type'] === '#microsoft.graph.microsoftAuthenticatorAuthenticationMethod'
);
if (!hasMFA) {
try {
// Enable MFA requirement
await setStrongMFARequirement(userId);
// Send password reset link
await initiatePasswordReset(userId);
enforced++;
console.log(`Enforced MFA for user ${userId}`);
} catch (error) {
failed++;
console.error(`Failed to enforce MFA for user ${userId}:`, error);
}
}
}
return { enforced, failed };
}Delegation Remediation
Removing Excessive Delegation
function Remove-ExcessiveDelegation {
param(
[string]$ObjectDN,
[string]$Trustee
)
try {
$acl = Get-Acl -Path ("AD:\\" + $ObjectDN)
foreach ($ace in $acl.Access) {
if ($ace.IdentityReference.Value -eq $Trustee -and
$ace.ActiveDirectoryRights -match 'GenericAll|Write') {
Write-Host "Removing delegation from $ObjectDN to $Trustee"
$acl.RemoveAccessRule($ace)
Set-Acl -Path ("AD:\\" + $ObjectDN) -AclObject $acl
# Log the change
$logEntry = "$((Get-Date)) - Removed delegation: $ObjectDN -> $Trustee, Rights: $($ace.ActiveDirectoryRights)"
Add-Content ".\delegation_remediation.log" $logEntry
}
}
}
catch {
Write-Error "Failed to remove delegation: $($_.Exception.Message)"
}
}Implementing Role-Based Access Control
interface RoleDefinition {
roleName: string;
description: string;
permissions: Permission[];
allowedOUs: string[];
}
interface Permission {
objectType: string;
accessRights: string[];
}
async function implementRBAC(roleDefinitions: RoleDefinition[]): Promise<boolean> {
try {
for (const role of roleDefinitions) {
// Create role groups
const groupName = `RBAC_${role.roleName.replace(/\s+/g, '_')}`;
const existingGroup = await findGroupByName(groupName);
if (!existingGroup) {
await createGroup({
displayName: role.roleName,
mailNickname: groupName,
description: role.description
});
}
// Apply permissions
for (const permission of role.permissions) {
for (const ou of role.allowedOUs) {
await applyPermission({
target: ou,
trustee: groupName,
objectType: permission.objectType,
accessRights: permission.accessRights
});
}
}
// Create documentation
await createRoleDocumentation(role);
}
return true;
} catch (error) {
console.error('Failed to implement RBAC:', error);
return false;
}
}Conditional Access Remediation
Enforcing MFA for Admins
async function createMFAForAdminsPolicy(): Promise<boolean> {
try {
const policy = {
displayName: 'MFA Required for Administrators',
state: 'enabled',
conditions: {
users: {
includeRoles: [
'Global Administrator',
'Security Administrator',
'Privileged Role Administrator',
'Exchange Administrator',
'SharePoint Administrator'
]
},
applications: {
includeApplications: ['All']
}
},
grantControls: {
operator: 'OR',
builtInControls: ['mfa']
}
};
await graphClient
.api('/identity/conditionalAccess/policies')
.post(policy);
console.log('MFA policy for administrators created');
return true;
} catch (error) {
console.error('Failed to create MFA policy:', error);
return false;
}
}Geographic Access Restrictions
async function createGeoRestrictionPolicy(
allowedLocations: string[]
): Promise<boolean> {
try {
// Create named locations
for (const location of allowedLocations) {
await graphClient
.api('/identity/conditionalAccess/namedLocations')
.post({
displayName: location,
ipRanges: [
{
cidrAddress: await getLocationCIDR(location)
}
]
});
}
// Create policy
const policy = {
displayName: 'Geographic Access Restriction',
state: 'enabled',
conditions: {
users: {
includeUsers: ['All']
},
locations: {
includeLocations: allowedLocations
}
},
grantControls: {
operator: 'OR',
builtInControls: ['block']
}
};
await graphClient
.api('/identity/conditionalAccess/policies')
.post(policy);
console.log('Geographic restriction policy created');
return true;
} catch (error) {
console.error('Failed to create geo restriction policy:', error);
return false;
}
}Automated Response Patterns
Incident Response Workflow
async function handleSecurityIncident(incident: SecurityIncident): Promise<boolean> {
const incidentId = await createIncidentTicket(incident);
try {
// Step 1: Contain
await containIncident(incident);
// Step 2: Investigate
const investigation = await investigateIncident(incident);
// Step 3: Remediate
await remediateIncident(incident, investigation);
// Step 4: Document
await updateIncidentTicket(incidentId, investigation, remediationActions);
// Step 5: Post-incident review
await schedulePostIncidentReview(incidentId);
return true;
} catch (error) {
await updateIncidentStatus(incidentId, 'Failed', error.message);
return false;
}
}
async function containIncident(incident: SecurityIncident): Promise<void> {
// Disable affected accounts
for (const userId of incident.affectedUsers) {
await disableAccount(userId);
}
// Block IPs
for (const ip of incident.sourceIPs) {
await blockIPAddress(ip);
}
// Revoke active sessions
await revokeAllSessions(incident.affectedUsers);
}Continuous Monitoring Pattern
async function continuousSecurityMonitoring(): Promise<void> {
console.log('Starting continuous security monitoring...');
setInterval(async () => {
try {
// Run security assessment
const assessment = await performSecurityAssessment();
// Check for new critical findings
const criticalFindings = assessment.findings.filter(
f => f.severity === 'Critical'
);
if (criticalFindings.length > 0) {
await sendSecurityAlert(criticalFindings);
}
// Update security dashboard
await updateSecurityDashboard(assessment);
} catch (error) {
console.error('Monitoring scan failed:', error);
}
}, 3600000); // Every hour
}Compliance Reporting
Automated Compliance Checks
interface ComplianceFramework {
name: string;
controls: ComplianceControl[];
}
interface ComplianceControl {
controlId: string;
description: string;
testFunction: () => Promise<ComplianceResult>;
}
async function runComplianceAssessment(
framework: ComplianceFramework
): Promise<ComplianceReport> {
const results: ComplianceResult[] = [];
for (const control of framework.controls) {
console.log(`Testing control: ${control.controlId}`);
const result = await control.testFunction();
results.push(result);
if (!result.compliant) {
await createComplianceFinding(result);
}
}
const compliantCount = results.filter(r => r.compliant).length;
const compliancePercentage = (compliantCount / results.length) * 100;
const report: ComplianceReport = {
framework: framework.name,
timestamp: new Date().toISOString(),
results,
summary: {
total: results.length,
compliant: compliantCount,
nonCompliant: results.length - compliantCount,
compliancePercentage
}
};
return report;
}Recovery and Restoration
Post-Remediation Verification
function Test-RemediationSuccess {
param(
[string]$RemediationType,
[hashtable]$TestCases
)
$results = @{}
foreach ($testCase in $TestCases.GetEnumerator()) {
Write-Host "Testing: $($testCase.Name)"
switch ($RemediationType) {
'GroupMembership' {
$testGroup = Get-ADGroup -Identity $testCase.Value.GroupName
$currentMembers = Get-ADGroupMember -Identity $testGroup | Select-Object -ExpandProperty Name
if ($currentMembers.Count -eq $testCase.Value.ExpectedCount) {
$results[$testCase.Name] = 'PASS'
} else {
$results[$testCase.Name] = 'FAIL - Expected $($testCase.Value.ExpectedCount), got $($currentMembers.Count)'
}
}
'Delegation' {
$acl = Get-Acl -Path ("AD:\\" + $testCase.Value.ObjectDN)
$delegationExists = $acl.Access | Where-Object {
$_.IdentityReference.Value -eq $testCase.Value.Trustee -and
$_.ActiveDirectoryRights -match $testCase.Value.Rights
}
if ($delegationExists) {
$results[$testCase.Name] = 'FAIL - Delegation still exists'
} else {
$results[$testCase.Name] = 'PASS'
}
}
}
}
return $results
}Rollback Procedures
async function rollbackSecurityChanges(changeId: string): Promise<boolean> {
try {
// Get change details
const change = await getSecurityChange(changeId);
// Restore previous state
switch (change.type) {
case 'GroupMembership':
await restoreGroupMembers(change.groupId, change.previousMembers);
break;
case 'Delegation':
await restoreDelegationPermissions(change.objectDN, change.previousACL);
break;
case 'ConditionalAccess':
await restoreConditionalAccessPolicy(change.policyId, change.previousSettings);
break;
}
// Mark change as rolled back
await updateChangeStatus(changeId, 'Rolled Back');
console.log(`Rollback completed for change ${changeId}`);
return true;
} catch (error) {
console.error('Rollback failed:', error);
return false;
}
}AD Security Reviewer - Quick Start Guide
This guide helps you get started with the AD security reviewer skill's scripts and tools.
Prerequisites
- Windows Server 2016 or later with Active Directory
- Remote Server Administration Tools (RSAT) installed
- Domain Administrator privileges for comprehensive audits
- PowerShell 5.1 or later
- For TypeScript scripts: Node.js 16+, Azure AD app registration
Installing Required Modules
# Install RSAT features (PowerShell)
Install-WindowsFeature RSAT-AD-PowerShell -IncludeManagementTools
# Import modules
Import-Module ActiveDirectoryAzure AD App Registration (for TypeScript scripts)
1. Create App Registration:
# Azure Portal → App registrations → New registration
# Name: AD Security Reviewer
# Permissions: Directory.Read.All, AuditLog.Read.All2. Generate Client Secret:
# Certificates & secrets → New client secretQuick Examples
PowerShell: Auditing Privileged Groups
# Audit all privileged groups in the domain
.\audit_privileged_groups.ps1 -Domain "example.com" -Threshold 5
# Audit with custom inactive days threshold
.\audit_privileged_groups.ps1 -Domain "example.com" -InactiveDays 60
# Generate report to specific location
.\audit_privileged_groups.ps1 -ReportPath "C:\Reports\privileged_audit.html"PowerShell: Reviewing Delegation
# Review delegation across entire domain
.\review_delegation.ps1 -SearchBase "DC=example,DC=com"
# Review specific OU only
.\review_delegation.ps1 -SearchBase "OU=Users,DC=example,DC=com"
# Set custom report path
.\review_delegation.ps1 -ReportPath "C:\Reports\delegation_review.html"TypeScript: Running Security Assessment
import { ADSecurityAnalyzer } from './scripts/analyze_ad_security';
const analyzer = new ADSecurityAnalyzer(
'client-id',
'client-secret',
'tenant-id'
);
// Perform comprehensive security assessment
const assessment = await analyzer.performSecurityAssessment();
// Generate report
const report = generateSecurityReport(assessment);
console.log(report);
// Save report to file
const fs = require('fs');
fs.writeFileSync('security_report.md', report);Common Patterns
Automated Security Assessments
# Run all security audits daily
$today = Get-Date -Format "yyyy-MM-dd"
$reportPath = "C:\Reports\$today-security-audit.html"
# Run privileged group audit
.\audit_privileged_groups.ps1 -ReportPath "$reportPath-groupts.html"
# Run delegation review
.\review_delegation.ps1 -ReportPath "$reportPath-delegation.html"
# Run TypeScript assessment
node assess_security.jsMonthly Security Reviews
import { ADSecurityAnalyzer, generateSecurityReport } from './scripts/analyze_ad_security';
import * as fs from 'fs';
async function monthlySecurityReview() {
const analyzer = new ADSecurityAnalyzer(
process.env.AZURE_CLIENT_ID,
process.env.AZURE_CLIENT_SECRET,
process.env.AZURE_TENANT_ID
);
// Perform assessment
const assessment = await analyzer.performSecurityAssessment();
// Generate report
const report = generateSecurityReport(assessment);
// Save with timestamp
const timestamp = new Date().toISOString().split('T')[0];
const reportPath = `security_report_${timestamp}.md`;
fs.writeFileSync(reportPath, report);
console.log(`Report saved to: ${reportPath}`);
// Alert if critical findings
if (assessment.summary.critical > 0) {
console.error(`ALERT: ${assessment.summary.critical} critical findings detected!`);
// Send alert notification...
}
}
monthlySecurityReview().catch(console.error);Targeted Delegation Audits
# Find all delegation from specific user
$targetUser = "jdoe"
$reportPath = "C:\Reports\delegation_${targetUser}.html"
.\review_delegation.ps1 -SearchBase "DC=example,DC=com" | Out-String |
Select-String -Pattern $targetUser -Context 0, 2 |
Out-File $reportPathSecurity Assessment Components
The TypeScript security analyzer performs the following checks:
1. Privileged Group Memberships
- Enterprise Admins
- Domain Admins
- Schema Admins
- Administrators
- Account Operators
- Backup Operators
2. Stale Account Detection
- Accounts inactive for 90+ days
- Users with no recent sign-in activity
3. Password Policy Review
- Lockout threshold validation
- Lockout duration verification
- Password complexity requirements
4. MFA Enrollment Check
- Percentage of users with MFA enabled
- Identification of non-MFA privileged accounts
5. Suspicious Sign-In Analysis
- High-risk sign-in attempts
- Unusual geographic locations
- Atypical device usage
6. Conditional Access Policies
- Policy existence check
- MFA requirement for admins
- Geographic restrictions
7. Risky User Detection
- Medium and high risk users
- Compromised account indicators
Best Practices
1. Run Regular Audits - Weekly or monthly security assessments 2. Review Findings Promptly - Address critical issues immediately 3. Document Remediation - Track fixes and their effectiveness 4. Implement Baselines - Establish security baselines and monitor deviations 5. Use Least Privilege - Regularly review and reduce privileged access 6. Monitor Changes - Set up alerts for privileged group modifications 7. Educate Users - Train staff on security best practices 8. Test Recovery - Ensure remediation procedures work correctly
Troubleshooting
Module Import Errors
Error: Active Directory module not availableSolution:
Install-WindowsFeature RSAT-AD-PowerShell -IncludeManagementTools
Import-Module ActiveDirectoryPermission Denied Errors
Error: Access is deniedSolutions: 1. Run PowerShell as Administrator 2. Use Domain Admin credentials 3. Ensure account has necessary delegated permissions
Graph API Authentication Failed
Error: Access token request failedSolutions: 1. Verify client ID, client secret, and tenant ID 2. Check if app registration is active 3. Ensure Directory.Read.All permission is granted 4. Verify admin consent has been granted
Large Dataset Timeouts
Error: Operation timed outSolutions: 1. Process in smaller batches 2. Increase script timeout values 3. Use specific OUs instead of entire domain 4. Run during off-peak hours
Interpreting Findings
Critical Severity
Findings that pose immediate security risk:
- Unnecessary Domain Admin memberships
- Disabled accounts with privileges
- GenericAll delegation rights
- High-risk sign-ins from unknown locations
Action Required: Immediate remediation
High Severity
Significant security concerns:
- Excessive privileged group members
- Accounts inactive >90 days with privileges
- MFA not enabled for admins
- Weak password policies
Action Required: Remediation within 24-48 hours
Medium Severity
Security issues that should be addressed:
- Non-privileged accounts with delegation
- Passwords not changed >365 days
- Moderate risk users
- Incomplete conditional access coverage
Action Required: Remediation within 1-2 weeks
Low Severity
Minor security concerns:
- Documentation gaps
- Naming convention violations
- Minor policy inconsistencies
Action Required: Address during next maintenance window
Integration with Monitoring
Email Alerts for Critical Findings
import { sendEmail } from './email_utils';
async function alertOnCriticalFindings(assessment: SecurityAssessmentResult) {
const criticalFindings = assessment.findings.filter(f => f.severity === 'Critical');
if (criticalFindings.length > 0) {
const subject = `CRITICAL: ${criticalFindings.length} security findings detected`;
const body = generateSecurityReport(assessment);
await sendEmail('security-team@example.com', subject, body);
}
}SIEM Integration
# Send audit events to SIEM
$events = Get-Content ".\audit_log.log" | ConvertFrom-Csv
foreach ($event in $events) {
if ($event.Level -eq 'ERROR' -or $event.Level -eq 'CRITICAL') {
# Send to SIEM (example for Sentinel)
Invoke-RestMethod -Uri "https://siem.example.com/api/events" `
-Method Post `
-Body ($event | ConvertTo-Json) `
-ContentType "application/json"
}
}Additional Resources
import { ClientSecretCredential } from '@azure/identity';
import { Client } from '@microsoft/microsoft-graph-client';
import 'isomorphic-fetch';
export interface SecurityFinding {
severity: 'Critical' | 'High' | 'Medium' | 'Low' | 'Informational';
category: string;
title: string;
description: string;
recommendation: string;
affectedResources: string[];
}
export interface SecurityAssessmentResult {
timestamp: string;
findings: SecurityFinding[];
summary: {
critical: number;
high: number;
medium: number;
low: number;
informational: number;
};
}
export class ADSecurityAnalyzer {
private graphClient: Client;
constructor(private clientId: string, private clientSecret: string, private tenantId: string) {
const credential = new ClientSecretCredential(
this.tenantId,
this.clientId,
this.clientSecret
);
this.graphClient = Client.initWithMiddleware({
authProvider: {
getAccessToken: async () => {
const token = await credential.getToken('https://graph.microsoft.com/.default');
return token.token;
}
}
});
}
async performSecurityAssessment(): Promise<SecurityAssessmentResult> {
const findings: SecurityFinding[] = [];
console.log('Starting Active Directory security assessment...');
// Check privileged group memberships
findings.push(...await this.checkPrivilegedGroupMemberships());
// Check for stale accounts
findings.push(...await this.checkStaleAccounts());
// Check password policies
findings.push(...await this.checkPasswordPolicies());
// Check MFA enrollment
findings.push(...await this.checkMFAEnrollment());
// Check for suspicious sign-ins
findings.push(...await this.checkSuspiciousSignIns());
// Check conditional access policies
findings.push(...await this.checkConditionalAccessPolicies());
// Check for risky users
findings.push(...await this.checkRiskyUsers());
// Calculate summary
const summary = this.calculateSummary(findings);
const result: SecurityAssessmentResult = {
timestamp: new Date().toISOString(),
findings,
summary
};
console.log(`Assessment complete. Total findings: ${findings.length}`);
console.log(`Critical: ${summary.critical}, High: ${summary.high}, Medium: ${summary.medium}, Low: ${summary.low}, Info: ${summary.informational}`);
return result;
}
async checkPrivilegedGroupMemberships(): Promise<SecurityFinding[]> {
const findings: SecurityFinding[] = [];
try {
const privilegedGroups = [
'Enterprise Admins',
'Domain Admins',
'Schema Admins',
'Administrators',
'Account Operators',
'Backup Operators'
];
for (const groupName of privilegedGroups) {
const group = await this.findGroupByName(groupName);
if (group) {
const members = await this.graphClient
.api(`/groups/${group.id}/members`)
.select('id,displayName,userPrincipalName,mail')
.top(999)
.get();
if (members.value && members.value.length > 0) {
findings.push({
severity: group.name.includes('Enterprise') || group.name.includes('Domain') ? 'Critical' : 'High',
category: 'Privileged Access',
title: `Large number of members in privileged group: ${groupName}`,
description: `The privileged group "${groupName}" has ${members.value.length} members. This increases the attack surface and risk of privilege escalation.`,
recommendation: 'Review group memberships regularly and implement least privilege principles. Consider removing unnecessary members.',
affectedResources: members.value.map((m: any) => m.userPrincipalName || m.displayName)
});
}
}
}
} catch (error: any) {
console.error(`Error checking privileged groups: ${error.message}`);
}
return findings;
}
async checkStaleAccounts(): Promise<SecurityFinding[]> {
const findings: SecurityFinding[] = [];
try {
const staleThreshold = 90; // days
const thresholdDate = new Date();
thresholdDate.setDate(thresholdDate.getDate() - staleThreshold);
const users = await this.graphClient
.api('/users')
.filter(`accountEnabled eq true and signInActivity/lastSignInDateTime le ${thresholdDate.toISOString()}`)
.select('id,displayName,userPrincipalName,signInActivity')
.top(999)
.get();
if (users.value && users.value.length > 0) {
findings.push({
severity: 'Medium',
category: 'Account Management',
title: `Found ${users.value.length} potentially stale user accounts`,
description: 'Users who haven\'t signed in for more than 90 days should be reviewed. Stale accounts can be exploited by attackers.',
recommendation: 'Review and disable or delete accounts that are no longer needed. Implement a lifecycle policy for user accounts.',
affectedResources: users.value.map((u: any) => u.userPrincipalName)
});
}
} catch (error: any) {
console.error(`Error checking stale accounts: ${error.message}`);
}
return findings;
}
async checkPasswordPolicies(): Promise<SecurityFinding[]> {
const findings: SecurityFinding[] = [];
try {
const passwordPolicy = await this.graphClient
.api('/domainSettings/passwordPolicy')
.get();
if (passwordPolicy.length === 0) {
findings.push({
severity: 'High',
category: 'Password Policy',
title: 'Weak or missing password policy',
description: 'The directory does not have a strong password policy configured.',
recommendation: 'Implement strong password policies including minimum length (12+), complexity requirements, and regular expiration.',
affectedResources: ['Password Policy']
});
} else {
const policy = passwordPolicy[0];
if (policy.lockoutThreshold < 5) {
findings.push({
severity: 'Medium',
category: 'Password Policy',
title: 'Account lockout threshold is too permissive',
description: `Current lockout threshold is ${policy.lockoutThreshold} attempts. This allows more brute force attempts.`,
recommendation: 'Set lockout threshold to 5 or fewer attempts.',
affectedResources: ['Password Policy']
});
}
if (policy.lockoutDuration < 900) {
findings.push({
severity: 'Medium',
category: 'Password Policy',
title: 'Account lockout duration is too short',
description: `Current lockout duration is ${policy.lockoutDuration} seconds. This may not be sufficient to prevent attacks.`,
recommendation: 'Set lockout duration to at least 15 minutes (900 seconds).',
affectedResources: ['Password Policy']
});
}
}
} catch (error: any) {
console.error(`Error checking password policies: ${error.message}`);
}
return findings;
}
async checkMFAEnrollment(): Promise<SecurityFinding[]> {
const findings: SecurityFinding[] = [];
try {
const users = await this.graphClient
.api('/users')
.filter('accountEnabled eq true')
.select('id,displayName,userPrincipalName')
.top(999)
.get();
let mfaEnrolled = 0;
const nonMfaUsers: string[] = [];
for (const user of users.value) {
const authMethods = await this.graphClient
.api(`/users/${user.id}/authentication/methods`)
.get();
const hasMfa = authMethods.value.some((m: any) =>
m['@odata.type'] === '#microsoft.graph.passwordAuthenticationMethod' ||
m['@odata.type'] === '#microsoft.graph.fido2AuthenticationMethod' ||
m['@odata.type'] === '#microsoft.graph.microsoftAuthenticatorAuthenticationMethod'
);
if (hasMfa) {
mfaEnrolled++;
} else {
nonMfaUsers.push(user.userPrincipalName);
}
}
const mfaPercentage = (mfaEnrolled / users.value.length) * 100;
if (mfaPercentage < 80) {
findings.push({
severity: 'High',
category: 'Authentication',
title: `Low MFA enrollment rate: ${mfaPercentage.toFixed(1)}%`,
description: `${nonMfaUsers.length} users do not have MFA enabled. This increases risk of credential compromise.`,
recommendation: 'Enable MFA for all users. Consider implementing MFA as mandatory for privileged accounts.',
affectedResources: nonMfaUsers
});
}
} catch (error: any) {
console.error(`Error checking MFA enrollment: ${error.message}`);
}
return findings;
}
async checkSuspiciousSignIns(): Promise<SecurityFinding[]> {
const findings: SecurityFinding[] = [];
try {
const signIns = await this.graphClient
.api('/auditLogs/signIns')
.filter("riskEventTypes/riskLevel eq 'high'")
.select('id,userPrincipalName,createdDateTime,riskEventTypes,location')
.orderby('createdDateTime desc')
.top(50)
.get();
if (signIns.value && signIns.value.length > 0) {
findings.push({
severity: 'Critical',
category: 'Suspicious Activity',
title: `Found ${signIns.value.length} high-risk sign-in attempts`,
description: 'High-risk sign-in activities detected. These may indicate compromised credentials or attacks.',
recommendation: 'Investigate these sign-ins immediately. Consider blocking suspicious IPs and resetting affected user passwords.',
affectedResources: signIns.value.map((s: any) => `${s.userPrincipalName} (${s.createdDateTime})`)
});
}
} catch (error: any) {
console.error(`Error checking suspicious sign-ins: ${error.message}`);
}
return findings;
}
async checkConditionalAccessPolicies(): Promise<SecurityFinding[]> {
const findings: SecurityFinding[] = [];
try {
const policies = await this.graphClient
.api('/identity/conditionalAccess/policies')
.select('id,displayName,state,conditions,grantControls')
.get();
const enabledPolicies = policies.value.filter((p: any) => p.state === 'enabled');
if (enabledPolicies.length === 0) {
findings.push({
severity: 'Critical',
category: 'Access Control',
title: 'No conditional access policies enabled',
description: 'Conditional Access is not configured. This leaves the organization vulnerable to unauthorized access.',
recommendation: 'Implement Conditional Access policies to enforce MFA, location-based access, and device compliance.',
affectedResources: ['Conditional Access']
});
} else {
// Check for MFA requirement in privileged access
const hasMfaForAdmins = enabledPolicies.some((p: any) =>
p.conditions?.users?.includeRoles?.some((r: any) => r.id.includes('Admin')) &&
p.grantControls?.builtInControls?.includes('mfa')
);
if (!hasMfaForAdmins) {
findings.push({
severity: 'High',
category: 'Access Control',
title: 'MFA not required for admin access',
description: 'Conditional Access policies do not require MFA for administrator accounts.',
recommendation: 'Create a Conditional Access policy requiring MFA for all privileged role assignments.',
affectedResources: ['Conditional Access']
});
}
}
} catch (error: any) {
console.error(`Error checking conditional access policies: ${error.message}`);
}
return findings;
}
async checkRiskyUsers(): Promise<SecurityFinding[]> {
const findings: SecurityFinding[] = [];
try {
const riskyUsers = await this.graphClient
.api('/identityProtection/riskyUsers')
.filter('riskLevel eq \'high\' or riskLevel eq \'medium\'')
.select('id,userPrincipalName,riskLevel,riskDetail')
.top(50)
.get();
if (riskyUsers.value && riskyUsers.value.length > 0) {
findings.push({
severity: 'High',
category: 'Identity Protection',
title: `Found ${riskyUsers.value.length} risky user accounts`,
description: 'Users flagged with medium or high risk levels. These may be compromised.',
recommendation: 'Review risky users, force password reset if necessary, and investigate suspicious activity.',
affectedResources: riskyUsers.value.map((u: any) => `${u.userPrincipalName} (${u.riskLevel})`)
});
}
} catch (error: any) {
console.error(`Error checking risky users: ${error.message}`);
}
return findings;
}
private async findGroupByName(groupName: string): Promise<any> {
const groups = await this.graphClient
.api('/groups')
.filter(`displayName eq '${groupName}'`)
.select('id,name')
.get();
return groups.value?.[0];
}
private calculateSummary(findings: SecurityFinding[]): any {
return {
critical: findings.filter(f => f.severity === 'Critical').length,
high: findings.filter(f => f.severity === 'High').length,
medium: findings.filter(f => f.severity === 'Medium').length,
low: findings.filter(f => f.severity === 'Low').length,
informational: findings.filter(f => f.severity === 'Informational').length
};
}
}
export function generateSecurityReport(assessment: SecurityAssessmentResult): string {
let report = `
# Active Directory Security Assessment Report
**Generated:** ${assessment.timestamp}
## Executive Summary
| Severity | Count |
|-----------|-------|
| Critical | ${assessment.summary.critical} |
| High | ${assessment.summary.high} |
| Medium | ${assessment.summary.medium} |
| Low | ${assessment.summary.low} |
| Info | ${assessment.summary.informational} |
## Detailed Findings
`;
for (const finding of assessment.findings) {
report += `
### ${finding.title}
**Severity:** ${finding.severity}
**Category:** ${finding.category}
${finding.description}
**Affected Resources:**
${finding.affectedResources.map(r => `- ${r}`).join('\n')}
**Recommendation:** ${finding.recommendation}
---
`;
}
return report;
}