New-AADConnectInboundRuleDisableExpiredAccounts.ps1

<#PSScriptInfo
 
.VERSION 2.2
 
.GUID 0e878ef7-98d6-4159-9b8b-e6a0870f0c81
 
.DESCRIPTION Create a new AADConnect inbound rule to disable cloud accounts for expired on-premises accounts.
 
.AUTHOR Aaron Guilmette
 
.COMPANYNAME Planet Technologies
 
.COPYRIGHT 2023
 
.TAGS Azure AD Connect AADConnect disable expired account
 
.LICENSEURI
 
.PROJECTURI https://www.undocumented-features.com
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
THIS CODE AND ANY ASSOCIATED INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK OF USE, INABILITY TO USE, OR RESULTS FROM THE USE OF
THIS CODE REMAINS WITH THE USER.
 
Author: Aaron Guilmette
        aguilmette@go-planet.com
#>


<#
.SYNOPSIS
Create new AAD Connect inbound rules to disable expired accounts
 
.PARAMETER Create
Create a scheduled task.
 
.PARAMETER Domain
Specify one or more domains to create the schedule for.
 
.EXAMPLE
.\New-AADConnectInboundRuleDisableExpiredAccounts.ps1
 
Create a new AAD Connect inbound rule to disable expired accounts using the first available
low precedence value
 
.LINK
https://aka.ms/aarongallery
 
#>

param (
    [array]$Domain,
    [switch]$Create
    )

# Collect all on-premises AD Connectors
## If $Domain is specified, select the matching connectors
If ($Domain)
{
    $DomainQuery = "^$($Domain -join "|")`$"
    [array]$ADConnectors = Get-ADSyncConnector | ? { $_.Subtype -notlike "*Windows Azure Active Directory*" -and $DomainQuery -match $_.Name }
}
else
{
    [array]$ADConnectors = Get-ADSyncConnector | ? { $_.Subtype -notlike "*Windows Azure Active Directory*" }
}

# Create the $NewConnectors variable to contain list of newly created connectors
[array]$NewConnectors = @()

# Create an inbound rule with the lowest precedence for each connector
If ($ADConnectors.Count -ge 1)
{
    foreach ($Connector in $ADConnectors)
    {
        [string]$Identifier = [Guid]::NewGuid().ToString()
        [array]$AllRulesPrecedence = (Get-ADSyncRule).Precedence
        $Precedence = (($AllRulesPrecedence | Measure-Object -Minimum).Minimum - 1)
        $RuleName = "In from AD - Disable Expired Accounts - $($Connector.Name)"
        
        New-ADSyncRule  `
            -Name $RuleName `
            -Identifier $Identifier `
            -Description 'Inbound rule to disable expired accounts' `
            -Direction 'Inbound' `
            -Precedence $Precedence `
            -PrecedenceAfter '00000000-0000-0000-0000-000000000000' `
            -PrecedenceBefore '00000000-0000-0000-0000-000000000000' `
            -SourceObjectType 'user' `
            -TargetObjectType 'person' `
            -Connector $Connector.Identifier.ToString() `
            -LinkType 'Join' `
            -SoftDeleteExpiryInterval 0 `
            -ImmutableTag 'New-AADConnectInboundRuleDisableExpiredAccounts.1' `
            -OutVariable syncRule | Out-Null
        
        # Transformation based on accountExpires
        # If AD accountExpires value is 0 or 9223372036854775807, the account is already configured to never expire
        # so flow accountEnabled "True." Otherwise, calculate the value from the accountExpires date and compare it
        # to the Now() date; if accountExpires compared to Now() is in the future, flow accountEnabled "True." If
        # accountExpires compared to Now() is in the past, flow accountEnabled "False."
        Add-ADSyncAttributeFlowMapping  `
            -SynchronizationRule $syncRule[0] `
            -Destination 'accountEnabled' `
            -FlowType 'Expression' `
            -ValueMergeType 'Update' `
            -Expression 'IIF(CStr([accountExpires])="0",True,(IIF(CStr([accountExpires])="9223372036854775807",True,IIF(DateFromNum([accountExpires])>(CDate(NumFromDate(Now()))),True,False))))' `
            -OutVariable syncRule | Out-Null
        
        # Create the scoping rule conditions
        New-Object  `
            -TypeName 'Microsoft.IdentityManagement.PowerShell.ObjectModel.ScopeCondition' `
            -ArgumentList 'userAccountControl', '2', 'ISBITNOTSET' `
            -OutVariable condition0 | Out-Null
        
        # Add the scoping rule conditions to the rule object
        Add-ADSyncScopeConditionGroup  `
            -SynchronizationRule $syncRule[0] `
            -ScopeConditions @($condition0[0]) `
            -OutVariable syncRule | Out-Null
        
        # Create the rule
        Add-ADSyncRule -SynchronizationRule $syncRule[0] | Out-Null
        
        # Get-ADSyncRule -Identifier $Identifier
        $NewConnectors += "$($RuleName), $($Identifier)"
    }
    Write-Output "New rules created:"
    $NewConnectors
    If ($Create)
    {
        $Trigger = New-JobTrigger -Weekly -At "11:00 PM" -DaysOfWeek Saturday -WeeksInterval 2
        Register-ScheduledJob -Name "AAD Connect Full Sync" -ScriptBlock { Get-ADSyncConnector | ? { $_.Subtype -notlike "*Windows Azure Active Directory*" } | % { Invoke-ADSyncRunProfile -ConnectorName $_.Name -RunProfileName 'Full Synchronization' } } -Trigger $Trigger
    }
}
Else
{ Write-Output "No connectors matching $($Domain) found." }