New-AADConnectRule-DisableExpiredAccts.ps1

<#PSScriptInfo
 
.VERSION 1.1
 
.GUID d5b9d5b5-3360-44e6-bb32-77561e2367e5
 
.AUTHOR Aaron Guilmette
 
.COMPANYNAME Planet Technologies
 
.COPYRIGHT 2023
 
.TAGS AADCONNECT Expired
 
.LICENSEURI
 
.PROJECTURI https://www.undocumented-features.com/2023-01-25-working-around-accounts-that-expire-with-aad-connect
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
.DESCRIPTION Create a new AADConnect rule to disable corresponding cloud accounts for on-premises accounts that meet conditions for accountExpires or ForceChangePassword.
 
.PRIVATEDATA
 
#>


<#
.SYNOPSIS
Create a new AADConnect rule to disable corresponding cloud accounts for on-premises
accounts that meet conditions for accountExpires or ForceChangePassword.
  
.PARAMETER BlockExpiredAccounts
This option sets a cloud account to disabled (BlockCredential:$true) if the corresponding
on-premises account has expired. This parameter is enabled by default. Set this parameter to $false
to skip configuring this rule.
 
.PARAMETER BlockForceChangePassswordAccounts
This option sets a cloud account to disabled (BlockCredential:$true) if the corresponding
on-premises account is flagged for immediate password reset. If your organization uses
Microsoft 365 Self-Service Password Reset (SSPR), enabling this option will prevent users
forced to change password on next logon from using SSPR. Accounts with BlockCredential:true
are ineligible to use SSPR. This parameter is disabled by default. Set this parameter to $true
to configure this rule.
 
.EXAMPLE
.\New-AADConnect-DisableExpiredAccounts.ps1
 
Run with default options (-BlockExpiredAccounts $true -BlockForceChangePasswordAccounts $false)
  
.LINK
https://www.powershellgallery.com/packages/New-AADConnectRule-DisableExpiredAccts
 
.LINK
https://www.undocumented-features.com/2023-01-25-working-around-accounts-that-expire-with-aad-connect
 
.NOTES
2023-02-13 - 1.1 Updated to correctly account for objects that previously had an account expiration
             set (accountExpires -eq 0).
2023-01-25 - 1.0.2 Updated help file.
2023-01-25 - 1.0.1 Updated help file.
2023-01-24 - 1.0 Initial release.
#>

param (
    [bool]$BlockExpiredAccounts = $true,
    [bool]$BlockForceChangePassswordAccounts = $false
    )

## Define vars
# Select AAD Connector
[string]$Connector = (Get-ADSyncConnector | ? { $_.Name -like "* - AAD" }).Identifier.ToString()

# Retrieve ImmutableTag values, which are used to determine if this script has been run before to create the rules.
[array]$Tags = (Get-ADSyncRule).ImmutableTag.TagValue

## Disable accounts past accountExpires time
if ($BlockExpiredAccounts)
{
    If ($Tags -notcontains "DisableExpiredAccount")
    {
                # Generate Guid for rule
        [string]$DisableExpiredAccountIdentifier = [Guid]::NewGuid().ToString()
        
        # Determine lowest precedence for rules
        [array]$AllRulesPrecedence = (Get-ADSyncRule).Precedence
        $DisableExpiredAccountPrecedence = (($AllRulesPrecedence | Measure-Object -Minimum).Minimum - 1)
        
        New-ADSyncRule  `
                       -Name 'Out to AAD - Disable Expired Account' `
                       -Identifier $DisableExpiredAccountIdentifier `
                       -Description 'Disable corresponding cloud account for on-premises account past accountExpires time' `
                       -Direction 'Outbound' `
                       -Precedence $DisableExpiredAccountPrecedence `
                       -PrecedenceAfter '00000000-0000-0000-0000-000000000000' `
                       -PrecedenceBefore '00000000-0000-0000-0000-000000000000' `
                       -SourceObjectType 'person' `
                       -TargetObjectType 'user' `
                       -Connector $Connector `
                       -LinkType 'Join' `
                       -SoftDeleteExpiryInterval 0 `
                       -ImmutableTag 'DisableExpiredAccount.2' `
                       -OutVariable syncRule
        
        Add-ADSyncAttributeFlowMapping  `
                                       -SynchronizationRule $syncRule[0] `
                                       -Destination 'accountEnabled' `
                                       -FlowType 'Expression' `
                                       -ValueMergeType 'Update' `
                                       -Expression 'IIF(CStr([extension_accountExpires])="0",True,(IIF(CStr([extension_accountExpires])="9223372036854775807",True,IIF(DateFromNum([extension_accountExpires])>(CDate(NumFromDate(Now()))),True,False))))' `
                                       -OutVariable syncRule
        
        New-Object  `
                   -TypeName 'Microsoft.IdentityManagement.PowerShell.ObjectModel.ScopeCondition' `
                   -ArgumentList 'accountEnabled', 'TRUE', 'EQUAL' `
                   -OutVariable condition0
        
        
        New-Object  `
                   -TypeName 'Microsoft.IdentityManagement.PowerShell.ObjectModel.ScopeCondition' `
                   -ArgumentList 'extension_accountExpires', '9223372036854775807', 'NOTEQUAL' `
                   -OutVariable condition1
        
        Add-ADSyncScopeConditionGroup  `
                                      -SynchronizationRule $syncRule[0] `
                                      -ScopeConditions @($condition0[0], $condition1[0]) `
                                      -OutVariable syncRule
        
        New-Object  `
                   -TypeName 'Microsoft.IdentityManagement.PowerShell.ObjectModel.ScopeCondition' `
                   -ArgumentList 'accountEnabled', 'TRUE', 'EQUAL' `
                   -OutVariable condition0
        
        New-Object  `
                   -TypeName 'Microsoft.IdentityManagement.PowerShell.ObjectModel.ScopeCondition' `
                   -ArgumentList 'extension_accountExpires', '0', 'NOTEQUAL' `
                   -OutVariable condition1
        
        Add-ADSyncScopeConditionGroup  `
                                      -SynchronizationRule $syncRule[0] `
                                      -ScopeConditions @($condition0[0], $condition1[0]) `
                                      -OutVariable syncRule
        
        Add-ADSyncRule  `
                       -SynchronizationRule $syncRule[0]
        
        
        ## END RULE
        
        $RuleData1 = Get-ADSyncRule -Identifier $DisableExpiredAccountIdentifier
        IF ($RuleData1)
        {
            Write-Host -ForegroundColor Green 'Rule "Out to AAD - Disable Expired Accounts" has been successfully created by this script.'
        }
    }
    Else
    {
        Write-Host -ForegroundColor Cyan 'Rule "Out to AAD - Disable Expired Accounts" has already been created by this script.'
        Write-Host -ForegroundColor Cyan 'Please use the AAD Connect Synchronization Rules Editor to update it.'
    }
}

## Disable corresponding cloud accounts for on-premises account with 'Force Change Password' set
if ($BlockForceChangePassswordAccounts)
{
    If ($Tags -notcontains "DisableForcedChangePassword")
    {
        # Generate Guid for rule
        [string]$DisableForceChangePasswordAccountIdentifier = [Guid]::NewGuid().ToString()
        
        # Determine precedence for new rule
        [array]$AllRulesPrecedence = (Get-ADSyncRule).Precedence
        $DisableForceChangePasswordAccountPrecedence = (($AllRulesPrecedence | Measure-Object -Minimum).Minimum - 1)
        
        New-ADSyncRule  `
                       -Name 'Out to AAD - Disable accounts with passwords set to ForceChange' `
                       -Identifier $DisableForceChangePasswordAccountIdentifier `
                       -Description '' `
                       -Direction 'Outbound' `
                       -Precedence $DisableForceChangePasswordAccountPrecedence `
                       -PrecedenceAfter '00000000-0000-0000-0000-000000000000' `
                       -PrecedenceBefore '00000000-0000-0000-0000-000000000000' `
                       -SourceObjectType 'person' `
                       -TargetObjectType 'user' `
                       -Connector $Connector `
                       -LinkType 'Join' `
                       -SoftDeleteExpiryInterval 0 `
                       -ImmutableTag 'DisableForcedChangePassword' `
                       -OutVariable syncRule
        
        Add-ADSyncAttributeFlowMapping  `
                                       -SynchronizationRule $syncRule[0] `
                                       -Destination 'accountEnabled' `
                                       -FlowType 'Expression' `
                                       -ValueMergeType 'Update' `
                                       -Expression 'IIF(CStr([pwdLastSet])="16010101000000.0Z",False,True)' `
                                       -OutVariable syncRule
        
        New-Object  `
                   -TypeName 'Microsoft.IdentityManagement.PowerShell.ObjectModel.ScopeCondition' `
                   -ArgumentList 'accountEnabled', 'TRUE', 'EQUAL' `
                   -OutVariable condition0
        
        Add-ADSyncScopeConditionGroup  `
                                      -SynchronizationRule $syncRule[0] `
                                      -ScopeConditions @($condition0[0]) `
                                      -OutVariable syncRule
        
        Add-ADSyncRule  `
                       -SynchronizationRule $syncRule[0]
        $RuleData2 = Get-ADSyncRule $DisableForceChangePasswordAccountIdentifier
        If ($RuleData2)
        {
            Write-Host -ForegroundColor Green 'Rule Out to AAD - Disable accounts with passwords set to ForceChange" has been successfully created by this script.'
        }
    }
    else
    {
        Write-Host -ForegroundColor Cyan 'Rule "Out to AAD - Disable accounts with passwords set to ForceChange" has already been created by this script.'
        Write-Host -ForegroundColor Cyan 'Please use the AAD Connect Synchronization Rules Editor to update it.'
    }
}