Recently, the need for others to be able to force a manual sync from on-prem Active Directory (AD) to Entra became apparent. I knew this could be done with PowerShell, so I got to work writing a script. Users that may run this script will need to be in the local ‘ADSyncOperators’ group on your Entra Connect server, and, for script execution, they will need to be an administrator as well.
Since this script may be run by those with little PowerShell knowledge, I wanted to ensure the script could not be inadvertently modified. I added logic to perform an integrity check on the body of the script by generating a hash, and comparing it with a hardcoded hash that I set after an initial run. This isn’t a foolproof method to keep the script from being modified by a determined editor, but it does ensure this cannot happen by accident.
Because of the integrity check, you’ll need to do an initial run of the script. The string contained in the $freshHash variable from the initial run should be added to the $ogHash variable for subsequent runs. Any change in the variables within the body of the script will require this process to be repeated.
Here is the GitHub link: https://github.com/p8nflnt/Cloud-Toolbox/blob/main/Sync-AD2Entra.ps1
.NOTES
Name: Sync-AD2Entra.ps1
Author: Payton Flint
Version: 1.0
DateCreated: 2024-Mar
.LINK
https://github.com/p8nflnt/Cloud-Toolbox/blob/main/Sync-AD2Entra.ps1
IAM – PowerShell – Manual Entra Connect Sync via Script
#>
# original hash string for integrity check
$ogHash = "<Initial Run $freshHash>"
# body of script within scriptblock (for integrity check)
$scriptBlock = {
# fqdn of AAD/Entra Connect Server
$connectSrv = "<Entra Connect Server>"
# nested scriptblock for invocation
$nestedBlock = { Start-ADSyncSyncCycle -PolicyType Delta } # execute delta sync to Azure/Entra
# prompt for creds & invoke nested scriptblock on AAD/Entra Connect Server
Invoke-Command -ComputerName $connectSrv -ScriptBlock $nestedBlock -Credential (Get-Credential)
}
# convert scriptblock to string
$scriptString = $scriptBlock.ToString()
# create a SHA256 object from .NET
$sha256 = [System.Security.Cryptography.SHA256]::Create()
# convert string to a byte array & compute hash
$bytes = [System.Text.Encoding]::UTF8.GetBytes($scriptString)
$hashBytes = $sha256.ComputeHash($bytes)
# convert hash bytes to a hexadecimal string
$freshHash = [BitConverter]::ToString($hashBytes) -replace '-'
# clean up the SHA256 object
$sha256.Dispose()
# if script body is unmodified, execute
if ($ogHash -eq $freshHash) {
$scriptBlock.Invoke()
} else {
clear
Write-Host -ForegroundColor Red "INTEGRITY CHECK FAILURE`r`nCONTACT SYSADMIN"
}