Table of Contents

Identity and Access Management (IAM) is the framework of policies and technologies for ensuring that the right individuals access the right resources at the right times for the right reasons. This guide covers comprehensive IAM implementation strategies.

IAM Architecture Overview

Core Components

┌─────────────────────────────────────────────────────────────────┐
│                    IAM Architecture                             │
├─────────────────────────────────────────────────────────────────┤
│  Layer                │ Components                              │
│  ├─ Identity Sources  │ AD, LDAP, HR Systems, Cloud Providers  │
│  ├─ Authentication    │ MFA, SSO, Biometrics, Certificates     │
│  ├─ Authorization     │ RBAC, ABAC, Policy Engines            │
│  ├─ Provisioning     │ Automated Account Lifecycle            │
│  ├─ Governance        │ Access Reviews, Compliance, Audit     │
│  └─ Integration       │ APIs, SAML, OAuth, OIDC, SCIM         │
└─────────────────────────────────────────────────────────────────┘

IAM Ecosystem Components

  • Identity Providers (IdP): Central authentication authorities
  • Service Providers (SP): Applications consuming identity services
  • Directory Services: User and group repositories
  • Policy Decision Points: Authorization engines
  • Governance Systems: Compliance and audit platforms

Enterprise Authentication Framework

Multi-Factor Authentication Implementation

<#
.SYNOPSIS
    Enterprise Multi-Factor Authentication management framework.
.DESCRIPTION
    Provides comprehensive MFA implementation, policy management,
    and compliance reporting for enterprise environments.
#>

class MFAPolicy {
    [string]$PolicyName
    [string[]]$UserGroups
    [string[]]$ApplicationGroups
    [string[]]$RequiredMethods
    [hashtable]$MethodSettings
    [bool]$IsEnabled
    [datetime]$LastModified
    [string]$CreatedBy
    
    MFAPolicy([string]$Name) {
        $this.PolicyName = $Name
        $this.UserGroups = @()
        $this.ApplicationGroups = @()
        $this.RequiredMethods = @()
        $this.MethodSettings = @{}
        $this.IsEnabled = $true
        $this.LastModified = Get-Date
        $this.CreatedBy = $env:USERNAME
    }
    
    [void] AddUserGroup([string]$GroupName) {
        if ($GroupName -notin $this.UserGroups) {
            $this.UserGroups += $GroupName
            $this.LastModified = Get-Date
        }
    }
    
    [void] AddApplicationGroup([string]$AppGroup) {
        if ($AppGroup -notin $this.ApplicationGroups) {
            $this.ApplicationGroups += $AppGroup
            $this.LastModified = Get-Date
        }
    }
    
    [void] SetRequiredMethods([string[]]$Methods) {
        $this.RequiredMethods = $Methods
        $this.LastModified = Get-Date
    }
    
    [void] ConfigureMethod([string]$Method, [hashtable]$Settings) {
        $this.MethodSettings[$Method] = $Settings
        $this.LastModified = Get-Date
    }
}

class MFAManager {
    [hashtable]$Policies
    [string]$ConfigPath
    [string]$LogPath
    [hashtable]$MethodProviders
    
    MFAManager([string]$ConfigurationPath) {
        $this.Policies = @{}
        $this.ConfigPath = $ConfigurationPath
        $this.LogPath = Join-Path (Split-Path $ConfigurationPath) "Logs"
        $this.MethodProviders = @{}
        
        # Ensure directories exist
        $ConfigDir = Split-Path $this.ConfigPath
        if (!(Test-Path $ConfigDir)) {
            New-Item -ItemType Directory -Path $ConfigDir -Force | Out-Null
        }
        if (!(Test-Path $this.LogPath)) {
            New-Item -ItemType Directory -Path $this.LogPath -Force | Out-Null
        }
        
        # Initialize default method providers
        $this.InitializeMethodProviders()
        
        # Load existing policies
        $this.LoadPolicies()
    }
    
    [void] InitializeMethodProviders() {
        $this.MethodProviders = @{
            "SMS" = @{
                Name = "SMS Text Message"
                Configuration = @{
                    Provider = "Twilio"
                    APIEndpoint = "https://api.twilio.com/2010-04-01"
                    MessageTemplate = "Your verification code is: {0}"
                    CodeLength = 6
                    CodeExpiry = 300  # 5 minutes
                }
                Enabled = $true
            }
            "Email" = @{
                Name = "Email Verification"
                Configuration = @{
                    SMTPServer = "smtp.company.com"
                    FromAddress = "noreply@company.com"
                    Subject = "Multi-Factor Authentication Code"
                    Template = "email-mfa-template.html"
                    CodeLength = 8
                    CodeExpiry = 600  # 10 minutes
                }
                Enabled = $true
            }
            "TOTP" = @{
                Name = "Time-based One-Time Password"
                Configuration = @{
                    Issuer = "Company IAM"
                    Algorithm = "SHA1"
                    Digits = 6
                    Period = 30
                    Window = 1  # Allow 1 period drift
                }
                Enabled = $true
            }
            "Push" = @{
                Name = "Push Notification"
                Configuration = @{
                    Provider = "Microsoft Authenticator"
                    Timeout = 60
                    AllowNumberMatching = $true
                    RequireBiometric = $false
                }
                Enabled = $true
            }
            "Hardware" = @{
                Name = "Hardware Token"
                Configuration = @{
                    SupportedTokens = @("YubiKey", "RSA SecurID")
                    TokenValidationEndpoint = "https://yubikey-validation.company.com"
                    RequireTouchPresence = $true
                }
                Enabled = $false
            }
            "Biometric" = @{
                Name = "Biometric Authentication"
                Configuration = @{
                    SupportedMethods = @("Fingerprint", "FaceID", "WindowsHello")
                    RequireLiveness = $true
                    FallbackToPin = $true
                }
                Enabled = $true
            }
        }
    }
    
    [MFAPolicy] CreatePolicy([string]$PolicyName) {
        if ($this.Policies.ContainsKey($PolicyName)) {
            throw "Policy '$PolicyName' already exists"
        }
        
        $Policy = [MFAPolicy]::new($PolicyName)
        $this.Policies[$PolicyName] = $Policy
        $this.SavePolicies()
        
        $this.LogActivity("PolicyCreated", "Created MFA policy: $PolicyName")
        
        return $Policy
    }
    
    [void] RemovePolicy([string]$PolicyName) {
        if (!$this.Policies.ContainsKey($PolicyName)) {
            throw "Policy '$PolicyName' not found"
        }
        
        $this.Policies.Remove($PolicyName)
        $this.SavePolicies()
        
        $this.LogActivity("PolicyRemoved", "Removed MFA policy: $PolicyName")
    }
    
    [MFAPolicy] GetPolicy([string]$PolicyName) {
        if (!$this.Policies.ContainsKey($PolicyName)) {
            throw "Policy '$PolicyName' not found"
        }
        
        return $this.Policies[$PolicyName]
    }
    
    [hashtable] EvaluateUserMFA([string]$UserName, [string]$Application) {
        $ApplicablePolicies = @()
        
        # Get user's group memberships
        try {
            $User = Get-ADUser -Identity $UserName -Properties MemberOf
            $UserGroups = $User.MemberOf | ForEach-Object { (Get-ADGroup -Identity $_).Name }
        }
        catch {
            Write-Warning "Could not retrieve user groups for $UserName"
            $UserGroups = @()
        }
        
        # Find applicable policies
        foreach ($Policy in $this.Policies.Values) {
            if (!$Policy.IsEnabled) { continue }
            
            $UserGroupMatch = $false
            foreach ($Group in $Policy.UserGroups) {
                if ($Group -in $UserGroups -or $Group -eq "*") {
                    $UserGroupMatch = $true
                    break
                }
            }
            
            $AppGroupMatch = $false
            foreach ($AppGroup in $Policy.ApplicationGroups) {
                if ($Application -like $AppGroup -or $AppGroup -eq "*") {
                    $AppGroupMatch = $true
                    break
                }
            }
            
            if ($UserGroupMatch -and $AppGroupMatch) {
                $ApplicablePolicies += $Policy
            }
        }
        
        # Combine requirements from all applicable policies
        $RequiredMethods = @()
        $MethodSettings = @{}
        
        foreach ($Policy in $ApplicablePolicies) {
            $RequiredMethods += $Policy.RequiredMethods
            foreach ($Method in $Policy.MethodSettings.Keys) {
                if (!$MethodSettings.ContainsKey($Method)) {
                    $MethodSettings[$Method] = $Policy.MethodSettings[$Method]
                }
            }
        }
        
        # Remove duplicates and prioritize stronger methods
        $RequiredMethods = $RequiredMethods | Select-Object -Unique
        
        return @{
            RequiredMethods = $RequiredMethods
            MethodSettings = $MethodSettings
            ApplicablePolicies = $ApplicablePolicies.PolicyName
            Evaluation = @{
                UserName = $UserName
                Application = $Application
                Timestamp = Get-Date
                UserGroups = $UserGroups
            }
        }
    }
    
    [string] GenerateVerificationCode([string]$Method, [int]$Length = 6) {
        $Characters = "0123456789"
        
        if ($Method -eq "Email") {
            $Characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        }
        
        $Code = ""
        for ($i = 0; $i -lt $Length; $i++) {
            $Code += $Characters[(Get-Random -Minimum 0 -Maximum $Characters.Length)]
        }
        
        return $Code
    }
    
    [bool] SendVerificationCode([string]$UserName, [string]$Method, [string]$Code, [hashtable]$MethodSettings) {
        try {
            switch ($Method) {
                "SMS" {
                    $User = Get-ADUser -Identity $UserName -Properties MobilePhone
                    if ($User.MobilePhone) {
                        $Message = $MethodSettings.MessageTemplate -f $Code
                        Send-SMSMessage -PhoneNumber $User.MobilePhone -Message $Message -Settings $MethodSettings
                        return $true
                    }
                    return $false
                }
                
                "Email" {
                    $User = Get-ADUser -Identity $UserName -Properties EmailAddress
                    if ($User.EmailAddress) {
                        Send-EmailCode -EmailAddress $User.EmailAddress -Code $Code -Settings $MethodSettings
                        return $true
                    }
                    return $false
                }
                
                "Push" {
                    Send-PushNotification -UserName $UserName -Settings $MethodSettings
                    return $true
                }
                
                default {
                    Write-Warning "Unsupported MFA method: $Method"
                    return $false
                }
            }
        }
        catch {
            $this.LogActivity("SendCodeFailed", "Failed to send $Method code to $UserName : $($_.Exception.Message)")
            return $false
        }
    }
    
    [bool] ValidateVerificationCode([string]$UserName, [string]$Method, [string]$ProvidedCode, [string]$StoredCode, [datetime]$CodeTimestamp, [hashtable]$MethodSettings) {
        # Check if code has expired
        $Expiry = $MethodSettings.CodeExpiry
        if ((Get-Date) -gt $CodeTimestamp.AddSeconds($Expiry)) {
            $this.LogActivity("CodeExpired", "Verification code expired for user $UserName using method $Method")
            return $false
        }
        
        # Validate code based on method
        switch ($Method) {
            { $_ -in @("SMS", "Email") } {
                $IsValid = $ProvidedCode -eq $StoredCode
                break
            }
            
            "TOTP" {
                $IsValid = $this.ValidateTOTPCode($UserName, $ProvidedCode, $MethodSettings)
                break
            }
            
            "Hardware" {
                $IsValid = $this.ValidateHardwareToken($UserName, $ProvidedCode, $MethodSettings)
                break
            }
            
            default {
                $IsValid = $false
                break
            }
        }
        
        # Log validation result
        if ($IsValid) {
            $this.LogActivity("CodeValidated", "Successful MFA validation for user $UserName using method $Method")
        } else {
            $this.LogActivity("CodeValidationFailed", "Failed MFA validation for user $UserName using method $Method")
        }
        
        return $IsValid
    }
    
    [bool] ValidateTOTPCode([string]$UserName, [string]$ProvidedCode, [hashtable]$Settings) {
        # This would integrate with your TOTP library
        # Implementation depends on your TOTP provider (Google Authenticator, Microsoft Authenticator, etc.)
        
        try {
            # Get user's TOTP secret from secure storage
            $UserSecret = Get-UserTOTPSecret -UserName $UserName
            
            if (!$UserSecret) {
                return $false
            }
            
            # Calculate current time window
            $TimeWindow = [math]::Floor((Get-Date).ToUniversalTime().Subtract([datetime]'1970-01-01').TotalSeconds / $Settings.Period)
            
            # Allow for clock drift (check current window and adjacent windows)
            for ($drift = -$Settings.Window; $drift -le $Settings.Window; $drift++) {
                $TestWindow = $TimeWindow + $drift
                $ExpectedCode = Generate-TOTPCode -Secret $UserSecret -TimeWindow $TestWindow -Settings $Settings
                
                if ($ProvidedCode -eq $ExpectedCode) {
                    return $true
                }
            }
            
            return $false
        }
        catch {
            Write-Warning "TOTP validation error: $($_.Exception.Message)"
            return $false
        }
    }
    
    [void] SavePolicies() {
        $PolicyData = @{}
        foreach ($PolicyName in $this.Policies.Keys) {
            $Policy = $this.Policies[$PolicyName]
            $PolicyData[$PolicyName] = @{
                PolicyName = $Policy.PolicyName
                UserGroups = $Policy.UserGroups
                ApplicationGroups = $Policy.ApplicationGroups
                RequiredMethods = $Policy.RequiredMethods
                MethodSettings = $Policy.MethodSettings
                IsEnabled = $Policy.IsEnabled
                LastModified = $Policy.LastModified
                CreatedBy = $Policy.CreatedBy
            }
        }
        
        $PolicyData | ConvertTo-Json -Depth 10 | Out-File -FilePath $this.ConfigPath -Encoding UTF8
    }
    
    [void] LoadPolicies() {
        if (Test-Path $this.ConfigPath) {
            try {
                $PolicyData = Get-Content $this.ConfigPath | ConvertFrom-Json
                
                foreach ($PolicyName in $PolicyData.PSObject.Properties.Name) {
                    $PolicyInfo = $PolicyData.$PolicyName
                    $Policy = [MFAPolicy]::new($PolicyInfo.PolicyName)
                    
                    $Policy.UserGroups = $PolicyInfo.UserGroups
                    $Policy.ApplicationGroups = $PolicyInfo.ApplicationGroups
                    $Policy.RequiredMethods = $PolicyInfo.RequiredMethods
                    $Policy.MethodSettings = $PolicyInfo.MethodSettings
                    $Policy.IsEnabled = $PolicyInfo.IsEnabled
                    $Policy.LastModified = $PolicyInfo.LastModified
                    $Policy.CreatedBy = $PolicyInfo.CreatedBy
                    
                    $this.Policies[$PolicyName] = $Policy
                }
            }
            catch {
                Write-Warning "Failed to load MFA policies: $($_.Exception.Message)"
            }
        }
    }
    
    [void] LogActivity([string]$Action, [string]$Message) {
        $LogEntry = [PSCustomObject]@{
            Timestamp = Get-Date
            Action = $Action
            Message = $Message
            User = $env:USERNAME
            Computer = $env:COMPUTERNAME
        }
        
        $LogFile = Join-Path $this.LogPath "MFA-$(Get-Date -Format 'yyyyMM').log"
        $LogEntry | ConvertTo-Json -Compress | Out-File -FilePath $LogFile -Append -Encoding UTF8
        
        # Also write to Windows Event Log
        try {
            Write-EventLog -LogName Application -Source "IAM-MFA" -EventId 8001 -EntryType Information -Message "$Action : $Message"
        }
        catch {
            # Event source might not exist, continue without error
        }
    }
}

# Global functions for MFA management
function Initialize-MFAFramework {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$ConfigPath = "C:\IAM\Config\MFA-Policies.json"
    )
    
    $Global:MFAManager = [MFAManager]::new($ConfigPath)
    
    # Create default policies
    $AdminPolicy = $Global:MFAManager.CreatePolicy("HighPrivilegeUsers")
    $AdminPolicy.AddUserGroup("Domain Admins")
    $AdminPolicy.AddUserGroup("Enterprise Admins")
    $AdminPolicy.AddUserGroup("Schema Admins")
    $AdminPolicy.AddApplicationGroup("*")
    $AdminPolicy.SetRequiredMethods(@("TOTP", "Push"))
    $AdminPolicy.ConfigureMethod("TOTP", @{ RequiredForAllSessions = $true })
    $AdminPolicy.ConfigureMethod("Push", @{ RequireBiometric = $true })
    
    $StandardPolicy = $Global:MFAManager.CreatePolicy("StandardUsers")
    $StandardPolicy.AddUserGroup("Domain Users")
    $StandardPolicy.AddApplicationGroup("Office365")
    $StandardPolicy.AddApplicationGroup("VPN")
    $StandardPolicy.SetRequiredMethods(@("SMS", "Email", "TOTP"))
    
    Write-Host "MFA Framework initialized with default policies" -ForegroundColor Green
}

function New-MFAPolicy {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$PolicyName,
        
        [Parameter()]
        [string[]]$UserGroups = @(),
        
        [Parameter()]
        [string[]]$ApplicationGroups = @(),
        
        [Parameter()]
        [string[]]$RequiredMethods = @()
    )
    
    if (!$Global:MFAManager) {
        Initialize-MFAFramework
    }
    
    try {
        $Policy = $Global:MFAManager.CreatePolicy($PolicyName)
        
        foreach ($Group in $UserGroups) {
            $Policy.AddUserGroup($Group)
        }
        
        foreach ($AppGroup in $ApplicationGroups) {
            $Policy.AddApplicationGroup($AppGroup)
        }
        
        if ($RequiredMethods.Count -gt 0) {
            $Policy.SetRequiredMethods($RequiredMethods)
        }
        
        Write-Host "MFA Policy '$PolicyName' created successfully" -ForegroundColor Green
        return $Policy
    }
    catch {
        Write-Error "Failed to create MFA policy: $($_.Exception.Message)"
    }
}

function Test-UserMFARequirements {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$UserName,
        
        [Parameter(Mandatory)]
        [string]$Application
    )
    
    if (!$Global:MFAManager) {
        Initialize-MFAFramework
    }
    
    try {
        $Requirements = $Global:MFAManager.EvaluateUserMFA($UserName, $Application)
        
        Write-Host "MFA Requirements for $UserName accessing $Application:" -ForegroundColor Yellow
        Write-Host "Required Methods: $($Requirements.RequiredMethods -join ', ')" -ForegroundColor White
        Write-Host "Applicable Policies: $($Requirements.ApplicablePolicies -join ', ')" -ForegroundColor White
        
        return $Requirements
    }
    catch {
        Write-Error "Failed to evaluate MFA requirements: $($_.Exception.Message)"
    }
}

Single Sign-On (SSO) Implementation

SAML and OAuth Integration

function Deploy-SSOConfiguration {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$IdentityProvider = "ADFS",
        
        [Parameter()]
        [string[]]$ServiceProviders = @(),
        
        [Parameter()]
        [string]$ConfigurationPath = "C:\IAM\SSO\Configuration.xml"
    )
    
    $SSOConfig = @{
        IdentityProvider = @{
            Type = $IdentityProvider
            Endpoint = "https://sts.company.com/adfs/ls/"
            MetadataURL = "https://sts.company.com/FederationMetadata/2007-06/FederationMetadata.xml"
            SigningCertificate = "C:\IAM\Certificates\ADFS-Signing.cer"
            EncryptionCertificate = "C:\IAM\Certificates\ADFS-Encryption.cer"
            NameIDFormat = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
            AttributeMapping = @{
                "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" = "mail"
                "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" = "givenName"
                "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" = "sn"
                "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" = "sAMAccountName"
                "http://schemas.microsoft.com/ws/2008/06/identity/claims/groups" = "memberOf"
            }
        }
        ServiceProviders = @{}
        TrustRelationships = @{}
        SecurityPolicies = @{
            SessionTimeout = 480  # 8 hours
            RequireSignedRequests = $true
            RequireEncryptedAssertions = $true
            AllowedClockSkew = 300  # 5 minutes
            MaxAssertionAge = 3600  # 1 hour
        }
    }
    
    # Configure service providers
    foreach ($SP in $ServiceProviders) {
        $SSOConfig.ServiceProviders[$SP] = @{
            Name = $SP
            EntityID = "https://$SP.company.com"
            AssertionConsumerService = "https://$SP.company.com/saml/acs"
            SingleLogoutService = "https://$SP.company.com/saml/sls"
            RequiredAttributes = @("emailaddress", "givenname", "surname", "groups")
            AttributeReleasePolicy = "StandardUser"
            EncryptAssertions = $true
            SignAssertions = $true
        }
    }
    
    # Generate configuration
    $ConfigXML = @"
<?xml version="1.0" encoding="UTF-8"?>
<SSOConfiguration>
    <IdentityProvider>
        <Type>$($SSOConfig.IdentityProvider.Type)</Type>
        <Endpoint>$($SSOConfig.IdentityProvider.Endpoint)</Endpoint>
        <MetadataURL>$($SSOConfig.IdentityProvider.MetadataURL)</MetadataURL>
        <NameIDFormat>$($SSOConfig.IdentityProvider.NameIDFormat)</NameIDFormat>
        <AttributeMapping>
"@
    
    foreach ($Mapping in $SSOConfig.IdentityProvider.AttributeMapping.GetEnumerator()) {
        $ConfigXML += @"
            <Attribute ClaimType="$($Mapping.Key)" AttributeName="$($Mapping.Value)" />
"@
    }
    
    $ConfigXML += @"
        </AttributeMapping>
    </IdentityProvider>
    <ServiceProviders>
"@
    
    foreach ($SP in $SSOConfig.ServiceProviders.GetEnumerator()) {
        $SPConfig = $SP.Value
        $ConfigXML += @"
        <ServiceProvider Name="$($SPConfig.Name)">
            <EntityID>$($SPConfig.EntityID)</EntityID>
            <AssertionConsumerService>$($SPConfig.AssertionConsumerService)</AssertionConsumerService>
            <SingleLogoutService>$($SPConfig.SingleLogoutService)</SingleLogoutService>
            <EncryptAssertions>$($SPConfig.EncryptAssertions)</EncryptAssertions>
            <SignAssertions>$($SPConfig.SignAssertions)</SignAssertions>
            <RequiredAttributes>
"@
        foreach ($Attr in $SPConfig.RequiredAttributes) {
            $ConfigXML += "                <Attribute>$Attr</Attribute>`n"
        }
        $ConfigXML += @"
            </RequiredAttributes>
        </ServiceProvider>
"@
    }
    
    $ConfigXML += @"
    </ServiceProviders>
    <SecurityPolicies>
        <SessionTimeout>$($SSOConfig.SecurityPolicies.SessionTimeout)</SessionTimeout>
        <RequireSignedRequests>$($SSOConfig.SecurityPolicies.RequireSignedRequests)</RequireSignedRequests>
        <RequireEncryptedAssertions>$($SSOConfig.SecurityPolicies.RequireEncryptedAssertions)</RequireEncryptedAssertions>
        <AllowedClockSkew>$($SSOConfig.SecurityPolicies.AllowedClockSkew)</AllowedClockSkew>
        <MaxAssertionAge>$($SSOConfig.SecurityPolicies.MaxAssertionAge)</MaxAssertionAge>
    </SecurityPolicies>
</SSOConfiguration>
"@
    
    # Save configuration
    $ConfigDir = Split-Path $ConfigurationPath
    if (!(Test-Path $ConfigDir)) {
        New-Item -ItemType Directory -Path $ConfigDir -Force | Out-Null
    }
    
    $ConfigXML | Out-File -FilePath $ConfigurationPath -Encoding UTF8
    
    Write-Host "SSO Configuration saved to: $ConfigurationPath" -ForegroundColor Green
    Write-Host "Configured Service Providers: $($ServiceProviders.Count)" -ForegroundColor White
}

Role-Based Access Control (RBAC)

Advanced RBAC Framework

function Deploy-RBACFramework {
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$ConfigPath = "C:\IAM\RBAC\Framework.json"
    )
    
    $RBACFramework = @{
        Roles = @{
            "SystemAdministrator" = @{
                Description = "Full system administration privileges"
                Permissions = @(
                    "System.FullControl",
                    "User.Create", "User.Read", "User.Update", "User.Delete",
                    "Group.Create", "Group.Read", "Group.Update", "Group.Delete",
                    "Policy.Create", "Policy.Read", "Policy.Update", "Policy.Delete",
                    "Security.Audit", "Security.Configure"
                )
                Constraints = @{
                    TimeRestriction = $false
                    IPRestriction = $false
                    ApprovalRequired = $true
                    SessionDuration = 480  # 8 hours
                    RequiredMFA = @("TOTP", "Hardware")
                }
                InheritedRoles = @()
            }
            
            "SecurityAnalyst" = @{
                Description = "Security monitoring and incident response"
                Permissions = @(
                    "Security.Read", "Security.Audit",
                    "Log.Read", "Event.Read",
                    "User.Read", "Group.Read",
                    "Report.Generate", "Alert.Manage"
                )
                Constraints = @{
                    TimeRestriction = $true
                    AllowedHours = @{ Start = 6; End = 22 }
                    IPRestriction = $true
                    AllowedNetworks = @("10.0.0.0/8", "192.168.0.0/16")
                    ApprovalRequired = $false
                    SessionDuration = 240  # 4 hours
                    RequiredMFA = @("TOTP")
                }
                InheritedRoles = @("BaseUser")
            }
            
            "HelpDeskTier1" = @{
                Description = "Basic user support and password resets"
                Permissions = @(
                    "User.Read", "User.UnlockAccount", "User.ResetPassword",
                    "Group.Read",
                    "Ticket.Create", "Ticket.Update"
                )
                Constraints = @{
                    TimeRestriction = $true
                    AllowedHours = @{ Start = 8; End = 17 }
                    IPRestriction = $true
                    AllowedNetworks = @("10.1.0.0/24")
                    ApprovalRequired = $false
                    SessionDuration = 480  # 8 hours
                    RequiredMFA = @("SMS", "Email")
                }
                InheritedRoles = @("BaseUser")
            }
            
            "HelpDeskTier2" = @{
                Description = "Advanced user support and group management"
                Permissions = @(
                    "User.Read", "User.Create", "User.Update", "User.UnlockAccount", "User.ResetPassword",
                    "Group.Read", "Group.Update", "Group.AddMember", "Group.RemoveMember",
                    "Computer.Read", "Computer.Reset",
                    "Ticket.Create", "Ticket.Update", "Ticket.Escalate"
                )
                Constraints = @{
                    TimeRestriction = $true
                    AllowedHours = @{ Start = 7; End = 19 }
                    IPRestriction = $true
                    AllowedNetworks = @("10.1.0.0/24")
                    ApprovalRequired = $false
                    SessionDuration = 480  # 8 hours
                    RequiredMFA = @("TOTP", "Push")
                }
                InheritedRoles = @("HelpDeskTier1")
            }
            
            "BaseUser" = @{
                Description = "Standard user access rights"
                Permissions = @(
                    "Profile.Read", "Profile.Update",
                    "File.Read", "File.Write",
                    "Application.Access"
                )
                Constraints = @{
                    TimeRestriction = $false
                    IPRestriction = $false
                    ApprovalRequired = $false
                    SessionDuration = 480  # 8 hours
                    RequiredMFA = @()
                }
                InheritedRoles = @()
            }
        }
        
        Resources = @{
            "ActiveDirectory" = @{
                Type = "Directory"
                Permissions = @("User.Create", "User.Read", "User.Update", "User.Delete", "Group.Create", "Group.Read", "Group.Update", "Group.Delete")
                AccessPattern = "LDAP"
            }
            
            "SecurityLogs" = @{
                Type = "LogStore"
                Permissions = @("Log.Read", "Event.Read", "Security.Audit")
                AccessPattern = "EventLog"
            }
            
            "FileServer" = @{
                Type = "FileSystem"
                Permissions = @("File.Read", "File.Write", "File.Delete", "File.Execute")
                AccessPattern = "NTFS"
            }
            
            "Applications" = @{
                Type = "ApplicationSuite"
                Permissions = @("Application.Access", "Application.Configure")
                AccessPattern = "SAML"
            }
        }
        
        PolicyEngine = @{
            DefaultDeny = $true
            CombinePermissions = "Union"  # Union, Intersection, Deny-Override
            InheritanceModel = "Hierarchical"
            AuditAllAccess = $true
            CacheTimeout = 300  # 5 minutes
        }
    }
    
    # Save RBAC framework configuration
    $ConfigDir = Split-Path $ConfigPath
    if (!(Test-Path $ConfigDir)) {
        New-Item -ItemType Directory -Path $ConfigDir -Force | Out-Null
    }
    
    $RBACFramework | ConvertTo-Json -Depth 10 | Out-File -FilePath $ConfigPath -Encoding UTF8
    
    Write-Host "RBAC Framework deployed to: $ConfigPath" -ForegroundColor Green
    Write-Host "Roles configured: $($RBACFramework.Roles.Count)" -ForegroundColor White
    Write-Host "Resources defined: $($RBACFramework.Resources.Count)" -ForegroundColor White
}

function Test-UserAccess {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$UserName,
        
        [Parameter(Mandatory)]
        [string]$Resource,
        
        [Parameter(Mandatory)]
        [string]$Permission,
        
        [Parameter()]
        [string]$ConfigPath = "C:\IAM\RBAC\Framework.json"
    )
    
    if (!(Test-Path $ConfigPath)) {
        Write-Error "RBAC configuration not found. Run Deploy-RBACFramework first."
        return
    }
    
    try {
        $RBACConfig = Get-Content $ConfigPath | ConvertFrom-Json
        
        # Get user's roles
        $User = Get-ADUser -Identity $UserName -Properties MemberOf
        $UserGroups = $User.MemberOf | ForEach-Object { (Get-ADGroup -Identity $_).Name }
        
        # Map groups to roles (this would be configurable)
        $UserRoles = @()
        foreach ($Group in $UserGroups) {
            switch ($Group) {
                "Domain Admins" { $UserRoles += "SystemAdministrator" }
                "Security Team" { $UserRoles += "SecurityAnalyst" }
                "Help Desk L1" { $UserRoles += "HelpDeskTier1" }
                "Help Desk L2" { $UserRoles += "HelpDeskTier2" }
                default { $UserRoles += "BaseUser" }
            }
        }
        
        # Remove duplicates
        $UserRoles = $UserRoles | Select-Object -Unique
        
        # Evaluate access
        $AccessGranted = $false
        $GrantingRole = $null
        $AppliedConstraints = @()
        
        foreach ($RoleName in $UserRoles) {
            if ($RBACConfig.Roles.$RoleName) {
                $Role = $RBACConfig.Roles.$RoleName
                
                # Check direct permissions
                if ($Permission -in $Role.Permissions -or "System.FullControl" -in $Role.Permissions) {
                    $AccessGranted = $true
                    $GrantingRole = $RoleName
                    $AppliedConstraints += $Role.Constraints
                    break
                }
                
                # Check inherited permissions
                foreach ($InheritedRole in $Role.InheritedRoles) {
                    $ParentRole = $RBACConfig.Roles.$InheritedRole
                    if ($ParentRole -and ($Permission -in $ParentRole.Permissions)) {
                        $AccessGranted = $true
                        $GrantingRole = "$RoleName (inherited from $InheritedRole)"
                        $AppliedConstraints += $Role.Constraints
                        $AppliedConstraints += $ParentRole.Constraints
                        break
                    }
                }
                
                if ($AccessGranted) { break }
            }
        }
        
        # Evaluate constraints
        $ConstraintViolations = @()
        foreach ($Constraint in $AppliedConstraints) {
            if ($Constraint.TimeRestriction -and $Constraint.AllowedHours) {
                $CurrentHour = (Get-Date).Hour
                if ($CurrentHour -lt $Constraint.AllowedHours.Start -or $CurrentHour -gt $Constraint.AllowedHours.End) {
                    $ConstraintViolations += "Time restriction: Current hour $CurrentHour is outside allowed hours $($Constraint.AllowedHours.Start)-$($Constraint.AllowedHours.End)"
                }
            }
            
            if ($Constraint.IPRestriction -and $Constraint.AllowedNetworks) {
                # This would check current user IP against allowed networks
                # Implementation depends on your network configuration
            }
            
            if ($Constraint.ApprovalRequired) {
                $ConstraintViolations += "Approval required for this access"
            }
        }
        
        # Generate access decision
        $AccessDecision = [PSCustomObject]@{
            UserName = $UserName
            Resource = $Resource
            Permission = $Permission
            AccessGranted = $AccessGranted -and ($ConstraintViolations.Count -eq 0)
            GrantingRole = $GrantingRole
            UserRoles = $UserRoles
            ConstraintViolations = $ConstraintViolations
            Timestamp = Get-Date
            DecisionId = [Guid]::NewGuid().ToString()
        }
        
        # Log access decision
        $LogEntry = @{
            Timestamp = Get-Date
            User = $UserName
            Resource = $Resource
            Permission = $Permission
            Decision = if ($AccessDecision.AccessGranted) { "ALLOW" } else { "DENY" }
            GrantingRole = $GrantingRole
            Violations = $ConstraintViolations
            DecisionId = $AccessDecision.DecisionId
        }
        
        $LogPath = "C:\IAM\Logs\AccessDecisions-$(Get-Date -Format 'yyyyMM').log"
        $LogDir = Split-Path $LogPath
        if (!(Test-Path $LogDir)) {
            New-Item -ItemType Directory -Path $LogDir -Force | Out-Null
        }
        
        $LogEntry | ConvertTo-Json -Compress | Out-File -FilePath $LogPath -Append -Encoding UTF8
        
        return $AccessDecision
    }
    catch {
        Write-Error "Failed to evaluate user access: $($_.Exception.Message)"
    }
}

Identity Governance and Compliance

Access Certification and Review

function Start-AccessCertificationCampaign {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$CampaignName,
        
        [Parameter()]
        [string[]]$TargetGroups = @(),
        
        [Parameter()]
        [int]$DurationDays = 30,
        
        [Parameter()]
        [string[]]$Reviewers = @(),
        
        [Parameter()]
        [string]$ReportPath = "C:\IAM\AccessReviews"
    )
    
    $Campaign = @{
        CampaignId = [Guid]::NewGuid().ToString()
        Name = $CampaignName
        StartDate = Get-Date
        EndDate = (Get-Date).AddDays($DurationDays)
        Status = "Active"
        TargetGroups = $TargetGroups
        Reviewers = $Reviewers
        CreatedBy = $env:USERNAME
        ReviewItems = @()
        CompletionRate = 0
        Statistics = @{
            TotalItems = 0
            ReviewedItems = 0
            CertifiedItems = 0
            RevokedItems = 0
            PendingItems = 0
        }
    }
    
    # Generate review items
    foreach ($GroupName in $TargetGroups) {
        try {
            $Group = Get-ADGroup -Identity $GroupName -Properties Description, ManagedBy
            $Members = Get-ADGroupMember -Identity $Group -Recursive
            
            foreach ($Member in $Members) {
                if ($Member.objectClass -eq 'user') {
                    $User = Get-ADUser -Identity $Member.SamAccountName -Properties Department, Manager, LastLogonDate, PasswordLastSet
                    
                    $ReviewItem = @{
                        ItemId = [Guid]::NewGuid().ToString()
                        UserName = $User.SamAccountName
                        DisplayName = $User.Name
                        Department = $User.Department
                        Manager = if ($User.Manager) { (Get-ADUser -Identity $User.Manager).Name } else { "N/A" }
                        GroupName = $GroupName
                        GroupDescription = $Group.Description
                        LastLogon = $User.LastLogonDate
                        PasswordLastSet = $User.PasswordLastSet
                        AccessRisk = Get-AccessRiskScore -User $User -Group $Group
                        Status = "Pending"
                        ReviewDate = $null
                        ReviewedBy = $null
                        Decision = $null
                        Comments = $null
                        DueDate = (Get-Date).AddDays($DurationDays)
                    }
                    
                    $Campaign.ReviewItems += $ReviewItem
                }
            }
        }
        catch {
            Write-Warning "Failed to process group $GroupName : $($_.Exception.Message)"
        }
    }
    
    $Campaign.Statistics.TotalItems = $Campaign.ReviewItems.Count
    $Campaign.Statistics.PendingItems = $Campaign.ReviewItems.Count
    
    # Save campaign
    $CampaignFile = Join-Path $ReportPath "$($Campaign.CampaignId).json"
    $CampaignDir = Split-Path $CampaignFile
    if (!(Test-Path $CampaignDir)) {
        New-Item -ItemType Directory -Path $CampaignDir -Force | Out-Null
    }
    
    $Campaign | ConvertTo-Json -Depth 10 | Out-File -FilePath $CampaignFile -Encoding UTF8
    
    # Generate reviewer assignments
    $ReviewerAssignments = @{}
    $ItemsPerReviewer = [math]::Ceiling($Campaign.ReviewItems.Count / $Reviewers.Count)
    
    for ($i = 0; $i -lt $Campaign.ReviewItems.Count; $i++) {
        $ReviewerIndex = [math]::Floor($i / $ItemsPerReviewer)
        if ($ReviewerIndex -ge $Reviewers.Count) {
            $ReviewerIndex = $Reviewers.Count - 1
        }
        
        $Reviewer = $Reviewers[$ReviewerIndex]
        if (!$ReviewerAssignments.ContainsKey($Reviewer)) {
            $ReviewerAssignments[$Reviewer] = @()
        }
        
        $ReviewerAssignments[$Reviewer] += $Campaign.ReviewItems[$i]
    }
    
    # Send review notifications
    foreach ($Reviewer in $ReviewerAssignments.Keys) {
        $AssignedItems = $ReviewerAssignments[$Reviewer]
        Send-AccessReviewNotification -Reviewer $Reviewer -Campaign $Campaign -ReviewItems $AssignedItems
    }
    
    Write-Host "Access certification campaign '$CampaignName' started" -ForegroundColor Green
    Write-Host "Campaign ID: $($Campaign.CampaignId)" -ForegroundColor White
    Write-Host "Total review items: $($Campaign.Statistics.TotalItems)" -ForegroundColor White
    Write-Host "Duration: $DurationDays days" -ForegroundColor White
    
    return $Campaign
}

function Get-AccessRiskScore {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [Microsoft.ActiveDirectory.Management.ADUser]$User,
        
        [Parameter(Mandatory)]
        [Microsoft.ActiveDirectory.Management.ADGroup]$Group
    )
    
    $RiskScore = 0
    
    # Inactive user risk
    if ($User.LastLogonDate -lt (Get-Date).AddDays(-90)) {
        $RiskScore += 30
    }
    elseif ($User.LastLogonDate -lt (Get-Date).AddDays(-30)) {
        $RiskScore += 15
    }
    
    # Password age risk
    if ($User.PasswordLastSet -lt (Get-Date).AddDays(-180)) {
        $RiskScore += 20
    }
    elseif ($User.PasswordLastSet -lt (Get-Date).AddDays(-90)) {
        $RiskScore += 10
    }
    
    # Group privilege level
    $HighPrivilegeGroups = @("Domain Admins", "Enterprise Admins", "Schema Admins", "Administrators")
    if ($Group.Name -in $HighPrivilegeGroups) {
        $RiskScore += 25
    }
    
    # Orphaned account (no manager)
    if (!$User.Manager) {
        $RiskScore += 15
    }
    
    # Account disabled but still in group
    if (!$User.Enabled) {
        $RiskScore += 40
    }
    
    # Classify risk level
    $RiskLevel = switch ($RiskScore) {
        {$_ -ge 60} { "Critical" }
        {$_ -ge 40} { "High" }
        {$_ -ge 20} { "Medium" }
        default { "Low" }
    }
    
    return @{
        Score = $RiskScore
        Level = $RiskLevel
    }
}

function Send-AccessReviewNotification {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Reviewer,
        
        [Parameter(Mandatory)]
        [hashtable]$Campaign,
        
        [Parameter(Mandatory)]
        [array]$ReviewItems
    )
    
    try {
        $ReviewerUser = Get-ADUser -Identity $Reviewer -Properties EmailAddress
        
        if ($ReviewerUser.EmailAddress) {
            $Subject = "Access Review Required: $($Campaign.Name)"
            
            $Body = @"
You have been assigned $($ReviewItems.Count) items to review in the access certification campaign: $($Campaign.Name)

Campaign Details:
- Campaign ID: $($Campaign.CampaignId)
- Due Date: $($Campaign.EndDate)
- Total Items: $($Campaign.Statistics.TotalItems)

Your Review Items:
"@
            
            foreach ($Item in ($ReviewItems | Select-Object -First 10)) {
                $Body += "`n- $($Item.DisplayName) ($($Item.UserName)) - Access to $($Item.GroupName)"
            }
            
            if ($ReviewItems.Count -gt 10) {
                $Body += "`n... and $($ReviewItems.Count - 10) more items"
            }
            
            $Body += @"

To complete your review:
1. Log into the IAM portal at https://iam.company.com
2. Navigate to Access Reviews > Pending Reviews
3. Review each item and make certification decisions
4. Complete all reviews before the due date

If you have questions, contact the Identity Team at identity@company.com
"@
            
            Send-MailMessage -From "identity@company.com" -To $ReviewerUser.EmailAddress -Subject $Subject -Body $Body -SmtpServer "smtp.company.com"
        }
    }
    catch {
        Write-Warning "Failed to send notification to $Reviewer : $($_.Exception.Message)"
    }
}