Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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/
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
You can’t perform that action at this time.