Skip to content

Instantly share code, notes, and snippets.

@darrenjrobinson
Last active November 17, 2018 20:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save darrenjrobinson/7ec0c64f9aa618508052c519c20a4557 to your computer and use it in GitHub Desktop.
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/
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