Last active
November 17, 2018 20:44
-
-
Save darrenjrobinson/7ec0c64f9aa618508052c519c20a4557 to your computer and use it in GitHub Desktop.
Paged Imports for the Granfeldt PowerShell Management Agent for Microsoft Identity Manager. Supporting blog post here https://blog.darrenjrobinson.com/how-to-configure-paged-imports-on-the-granfeldt-fimmim-powershell-management-agent/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
param ( | |
$Username, | |
$Password, | |
$Credentials, | |
$OperationType, | |
[bool] $usepagedimport, | |
$pagesize | |
) | |
$DebugFilePath = "C:\PROGRA~1\MICROS~2\2010\SYNCHR~1\EXTENS~2\AzureA~2\Debug\AADUsersImport.txt" | |
if(!(Test-Path $DebugFilePath)) | |
{ | |
$DebugFile = New-Item -Path $DebugFilePath -ItemType File | |
} | |
else | |
{ | |
$DebugFile = Get-Item -Path $DebugFilePath | |
} | |
"Starting Import as : " + $OperationType + (Get-Date) | Out-File $DebugFile -Append | |
"Paged Import : " + $usepagedimport | Out-File $DebugFile -Append | |
"PageSize : " + $pagesize | Out-File $DebugFile -Append | |
# Adding the AzureADPreview ADAL Helper library to your PowerShell Session so we can use it for AuthN to AzureAD | |
Add-Type -Path 'C:\Program Files\WindowsPowerShell\Modules\AzureADPreview\2.0.0.52\Microsoft.IdentityModel.Clients.ActiveDirectory.dll' | |
# This is the tenant id of you Azure AD. You can use tenant name instead if you want. | |
$tenantID = "customer.com.au" | |
$authString = "https://login.microsoftonline.com/$tenantID" | |
# The resource URI for your token. | |
$resource = "https://graph.windows.net/" | |
# This is the common client id. | |
$client_id = "1950a258-227b-4e31-a9cf-717495945fc2" | |
# ************** Differential Sync Settings ************************ | |
# Only interested in users | |
#$Searchfilter ="`$filter=isof('Microsoft.DirectoryServices.User') or isof('Microsoft.DirectoryServices.Group') or isof('Microsoft.DirectoryServices.Contact')" | |
$Searchfilter ="`$filter=isof('Microsoft.DirectoryServices.User')" | |
# Object Type (eg. users, groups, contacts, directoryObjects) | |
$object = "users" | |
# Output Directory and file for Differential Cookie | |
$downloadDirectory = "C:\PROGRA~1\MICROS~2\2010\SYNCHR~1\EXTENS~2\AzureA~2" | |
$cookieFile = "\AADUsersDeltaCookie.txt" | |
$cookiefilepath = $downloadDirectory +$cookieFile | |
# Reset results var | |
$query = $null | |
# Read in Delta Cookie if it exists, if not create the file for storing the cookie | |
if(!(Test-Path $cookiefilepath)) | |
{ | |
$cookie = New-Item -Path $cookiefilepath -ItemType File | |
"Creating new Cookie File" | Out-File $DebugFile -Append | |
} | |
else | |
{ | |
$cookie = Get-Item -Path $cookiefilepath | |
"Cookie File present" | Out-File $DebugFile -Append | |
} | |
# ****************************************************************** | |
# Check to see if we have already retreived our data | |
# If we haven't go and get our objects | |
# Subsequent runs of the import.ps1 will then skip this section | |
if (!$global:tenantObjects) { | |
# ********************** Authentication to Azure *************************** | |
# The username must be MFA disabled user Admin at least, and must not be a live id. | |
# Create a client credential with the above common client id, username and password from the Connectivity tab on the MA. | |
$creds = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential" ` | |
-ArgumentList $Username,$Password | |
# Create a authentication context with the above authentication string. | |
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" ` | |
-ArgumentList $authString | |
# Acquire access token from server. | |
$authenticationResult = $authContext.AcquireToken($resource,$client_id,$creds) | |
# Use the access token to setup headers for your http request. | |
$authHeader = $authenticationResult.AccessTokenType + " " + $authenticationResult.AccessToken | |
$headers = @{"Authorization"=$authHeader; "Content-Type"="application/json"} | |
# ************************************************************** | |
# Delta or Full Sync based on Run Type and Cookie File ? | |
if((Get-Item $cookie).length -gt 0kb -and $OperationType -eq "Delta"){ | |
# Delta cookie value exists. Get it | |
$url = Get-Content $cookie.FullName | |
$url += '&ocp-aad-dq-include-only-changed-properties=true&api-version=1.6' -f $authenticationResult.TenantId | |
"Delta Import" | Out-File $DebugFile -Append | |
} | |
else | |
{ | |
# no Delta Cookie, so first run OR Full Sync so return everything | |
$url = "https://graph.windows.net/{0}/$($object)?&$($Searchfilter)&api-version=1.6&deltaLink=" | |
"Full Import" | Out-File $DebugFile -Append | |
} | |
# *********************** IMPORT ********************************** | |
# Get first set of results | |
$query = Invoke-RestMethod -Method Get -Headers @{ | |
Authorization = $authenticationResult.CreateAuthorizationHeader() | |
'Content-Type' = "application/json" | |
} -Uri ($url -f $authenticationResult.TenantId) | |
"Retrieved Users " + $query.value.Count | Out-File $DebugFile -Append | |
$query.value | Out-GridView | |
# *************************************************************** | |
# Counter to know where we are up to processing the Import | |
# Starting at minus 1 as our first object is 0 and I'm incrementing at the start of the loop. | |
[int]$global:objectsImported = -1 | |
# An Array for the retuned objects to go into | |
$global:tenantObjects = @() | |
# Add in our first objects | |
$global:tenantObjects += $query.value | |
# For paging from AzureAD | |
$moreObjects = $query | |
# Get all the remaining objects in batches | |
if ($query.'aad.nextLink'){ | |
$moreObjects.'aad.nextLink' = $query.'aad.nextLink' | |
do | |
{ | |
$moreObjects = Invoke-RestMethod -Method Get -Headers @{ | |
Authorization = $authenticationResult.CreateAuthorizationHeader() | |
'Content-Type' = "application/json" | |
} -Uri ($moreObjects.'aad.nextLink'+'&api-version=1.6' -f $authenticationResult.TenantId) | |
"Retrieved Users " + $moreObjects.value.count | Out-File $DebugFile -Append | |
$global:tenantObjects += $moreObjects.value | |
} while ($moreObjects.'aad.nextLink') | |
} | |
"Total Users " + $global:tenantObjects.count | Out-File $DebugFile -Append | |
# store the DeltaLink in a file for next time we run the MA | |
$moreObjects.'aad.deltaLink' | Out-File $cookie | |
# We get Users and Directory Link Objects Back | |
# We only care about Users for our Org (not guests) and the ones that are current (not deleted) | |
$global:relevantObjects = @() | |
foreach ($object in $global:tenantObjects) { | |
if ($object.ObjectType -eq "User" -and !$object.'aad.isDeleted' -and $object.userType -eq "Member") { | |
$global:relevantObjects += $object | |
} | |
} | |
"Total Relevant Users " + $global:relevantObjects.count | Out-File $DebugFile -Append | |
} | |
# ********************* Process users into the MA ******************* | |
# Know where we are at in relation to the pagesize from the Run Profile | |
[int]$objectpagecount = 0 | |
# Process each object | |
foreach($global:user in $global:relevantObjects) | |
{ | |
# continue from where we go to from the previous page of objects processed | |
$global:user = $global:relevantObjects[$global:objectsImported + 1] | |
# if we are at the end then set MoreToImport to False and quit | |
if (!$global:user -or ($global:objectsImported +1 -eq $global:relevantObjects.count)) { | |
# nothing left to process | |
$global:MoreToImport = $false | |
break | |
} | |
$global:user | Out-File $DebugFile -Append | |
#Process our users and pass through to the MA | |
if ($global:user.objectId) { | |
$obj = @{} | |
$obj.Add("ID", $global:user.objectId) | |
$obj.Add("objectID", $global:user.objectId) | |
$obj.Add("objectClass", "AADUser") | |
$obj.Add("AADMailEnabled",$global:user.mailEnabled) | |
$obj.Add("AADSecurityEnabled",$global:user.securityEnabled) | |
$obj.Add("AADDirSyncEnabled",$global:user.dirSyncEnabled) | |
$obj.Add("AADDisplayName",$global:user.displayName) | |
$obj.Add("AADMail",$global:user.mail) | |
$obj.Add("AADMailNickname",$global:user.mailNickname) | |
$obj.Add("AADLastDirSyncTime",$global:user.lastDirSyncTime) | |
$obj.Add("AADonPremiseSID",$global:user.onPremisesSecurityIdentifier) | |
if ($global:user.proxyAddresses) | |
{ | |
$proxyAddresses = @() | |
foreach($address in $global:user.proxyAddresses) { | |
$proxyAddresses += $address | |
} | |
$obj.Add("AADProxyAddresses",($proxyAddresses)) | |
} | |
"UserObjectID: "+$global:user.objectId | Out-File $DebugFile -Append | |
# Pass the User Object to the MA | |
$obj | |
# Increase the object count | |
$objectpagecount++ | |
# for logging how many we've processed | |
$global:objectsImported++ | |
# Debug Logging where we are up to | |
"Page count: " + $objectpagecount | Out-File $DebugFile -Append | |
"Objects Imported count: " +$global:objectsImported | Out-File $DebugFile -Append | |
"Objects Remaining count: "+($global:relevantObjects.count - $global:objectsImported -1) | Out-File $DebugFile -Append | |
# Refresh token if it is going to expire in the next 5 minutes | |
if ($authenticationResult.ExpiresOn.AddMinutes(5).ToString("G") -lt (Get-Date -format G)) | |
{ | |
# Acquire a new access token from server. | |
$authenticationResult = $authContext.AcquireToken($resource,$client_id,$creds) | |
# Refresh the headers | |
$authHeader = $authenticationResult.AccessTokenType + " " + $authenticationResult.AccessToken | |
$headers = @{"Authorization"=$authHeader; "Content-Type"="application/json"} | |
} | |
# if number of processed objects equals the pagesize set MoreToImport as true and break | |
if ($objectpagecount -eq $pagesize) | |
{ | |
$global:MoreToImport = $true | |
"More to Import: " + $objectpagecount | Out-File $DebugFile -Append | |
break | |
} | |
} | |
} | |
# *********************************************************** | |
#endregion |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment