Last active
June 26, 2019 00:29
-
-
Save darrenjrobinson/3328bcd601194f24f8dfc331de790a84 to your computer and use it in GitHub Desktop.
Microsoft Identity Manager RACF PowerShell Management Agent. Associated blogpost https://blog.darrenjrobinson.com/a-rudimentary-racf-management-agent-for-microsoft-identity-manager/
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, | |
$OperationType, | |
[bool] $usepagedimport, | |
$pagesize, | |
$Credentials | |
) | |
$DebugFilePath = "C:\Program Files\Microsoft Forefront Identity Manager\2010\Synchronization Service\Extensions\RACF\Debug\racfUsersImport.txt" | |
# Debug Logging File | |
if (!(Test-Path $DebugFilePath)) { | |
$DebugFile = New-Item -Path $DebugFilePath -ItemType File | |
} | |
else { | |
$DebugFile = Get-Item -Path $DebugFilePath | |
} | |
" " | Out-File $DebugFile -Append | |
"-------------------------------------------------------" | Out-File $DebugFile -Append | |
"Starting Import as : " + $OperationType + " - " + (Get-Date) | Out-File $DebugFile -Append | |
"Paged Import : " + $usepagedimport | Out-File $DebugFile -Append | |
"PageSize : " + $pagesize | Out-File $DebugFile -Append | |
# wc3270 HTTPD Config from wc3270 Start Options e.g. wc3270 A:mainframe.customer.com.au -httpd 127.0.0.1:6001 | |
$wc3270HTTPDHost = "127.0.0.1" # e.g FQDN of host running it, or localhost DNS Name or IP | |
$wc3270HTTPDPort = "6001" | |
$racfHost = "yourRACFHost" | |
$tracelog = "C:\Program Files\Microsoft Forefront Identity Manager\2010\Synchronization Service\Extensions\RACF\gpd5tracelog.txt" | |
$wc3270Path = "C:\Program Files\wc3270" | |
$wc3270Args = "A:$($racfHost) -httpd $($wc3270HTTPDHost):$($wc3270HTTPDPort) -trace -tracefile `"$($tracelog)`" -utf8" | |
$Global:3270Term = $null | |
$Global:3270TermTrace = $null | |
Function startWC3270 { | |
if (!$Global:3270Term.ProcessName) { | |
$Global:3270Term = start-process ".\wc3270.exe" -ArgumentList $wc3270Args -WorkingDirectory $wc3270Path -PassThru | |
if ($Global:3270Term.Id) { | |
"wc3270 Started" | Out-File $DebugFile -Append | |
$Global:3270Term | Out-File $DebugFile -Append | |
# Wait for Tracing to Start | |
start-sleep -seconds 2 | |
$Global:3270TermTrace = Get-Process -Name catf | |
"$($Global:3270TermTrace.Name) $($Global:3270TermTrace.ID)" | Out-File $DebugFile -Append | |
return $Global:3270Term, $Global:3270TermTrace | |
} | |
} | |
} | |
Function stopWC3270 { | |
if (get-process -Id $Global:3270Term.Id) { | |
Stop-Process $Global:3270Term -PassThru | |
$Global:3270Term = $null | |
Stop-Process $Global:3270TermTrace -PassThru | |
$Global:3270TermTrace = $null | |
"wc3270 Stopped" | Out-File $DebugFile -Append | |
} | |
} | |
function TraceLogCheck($tracelog) { | |
$log = get-item -Path $tracelog | |
$logUpdate = Get-Content $tracelog -Tail 3 | |
$logStatus = @{ } | |
$logStatus.add("Length", $log.Length) | |
$logStatus.add("Trace", $logUpdate) | |
return $logStatus | |
} | |
function WaitforProcessing { | |
$currentStatus = TraceLogCheck($tracelog) | |
$oldStatus = $currentStatus | |
[int]$loop = 0 | |
do { | |
$loop++ | |
start-sleep -Seconds 1 | |
$newStatus = $null | |
$newStatus = TraceLogCheck($tracelog) | |
# Processing ? | |
if ($newStatus.Length -gt $oldStatus.Length) { | |
$oldStatus = $newStatus | |
Start-Sleep -Milliseconds 1000 | |
$loop = 0 | |
"[Waiting] $($newStatus.Length)" | Out-File $DebugFile -Append | |
"[Waiting] $($newStatus.Trace[2])" | Out-File $DebugFile -Append | |
} | |
} while ($loop -le 3) | |
} | |
# Check to see if we have our Groups of returned Mainframe Users to process. | |
if (!$Global:groups) { | |
<# | |
GET USERS | |
#> | |
$Global:startDate = get-date | |
$Global:startDate | Out-File $DebugFile -Append | |
"Starting wc3270 Client" | Out-File $DebugFile -Append | |
startWC3270 | |
Start-Sleep -Seconds 2 | |
WaitforProcessing | |
# Test to see if wc3270 Service is up | |
$status = Invoke-RestMethod -uri "http://$($wc3270HTTPDHost):$($wc3270HTTPDPort)/3270/rest/stext/Query()" -UseBasicParsing -Method Get | |
WaitforProcessing | |
if ($status.Contains("host $($racfHost)")) { | |
# Logon | |
$logon = Invoke-RestMethod -uri "http://$($wc3270HTTPDHost):$($wc3270HTTPDPort)/3270/rest/stext/String($($Username))" -UseBasicParsing -Method Get | |
"TN3270 LoginID" | Out-File $DebugFile -Append | |
WaitforProcessing | |
if ($logon.Contains("U U U C($($racfHost))")) { | |
$logon = $null | |
$logon = Invoke-RestMethod -uri "http://$($wc3270HTTPDHost):$($wc3270HTTPDPort)/3270/rest/stext/enter" -UseBasicParsing -Method Get | |
WaitforProcessing | |
if ($logon.Contains("U U U C($($racfHost))") ) { | |
$logon = $null | |
"TN3270 Password" | Out-File $DebugFile -Append | |
$logon = Invoke-RestMethod -uri "http://$($wc3270HTTPDHost):$($wc3270HTTPDPort)/3270/rest/stext/String($($Password))" -UseBasicParsing -Method Get | |
WaitforProcessing | |
if ($logon.Contains("U U U C($($racfHost))")) { | |
$logon = Invoke-RestMethod -uri "http://$($wc3270HTTPDHost):$($wc3270HTTPDPort)/3270/rest/stext/enter" -UseBasicParsing -Method Get | |
WaitforProcessing | |
if ($logon.Contains("U U U C($($racfHost))")) { | |
# GET USERS | |
"TN3270 Get Users" | Out-File $DebugFile -Append | |
$getUsers = Invoke-RestMethod -uri "http://$($wc3270HTTPDHost):$($wc3270HTTPDPort)/3270/rest/stext/string(%22SEARCH%20CLASS%20(USER)%22)" -UseBasicParsing -Method Get | |
#SEARCH CLASS(USER) | |
WaitforProcessing | |
if ($getUsers.Contains("U U U C($($racfHost))")) { | |
$getUsers = $null | |
$getUsers = Invoke-RestMethod -uri "http://$($wc3270HTTPDHost):$($wc3270HTTPDPort)/3270/rest/stext/enter" -UseBasicParsing -Method Get | |
WaitforProcessing | |
$exit = Invoke-RestMethod -uri "http://$($wc3270HTTPDHost):$($wc3270HTTPDPort)/3270/rest/stext/disconnect" -Method Get | |
} # GET USER | |
} # Password Process Confirm | |
} # Enter Password | |
} # Login ID Process | |
} #Login ID Enter | |
"Stopping wc3270 Client" | Out-File $DebugFile -Append | |
stopWC3270 | |
} # Connection | |
<# | |
Process | |
#> | |
# OPEN THE Trace and enumerate users | |
if (test-path -Path $tracelog) { | |
$traceFileDetails = Get-Item -path $tracelog | |
if ($traceFileDetails.LastWriteTime -gt $Global:startDate) { | |
$traceContent = get-content -path $tracelog -Encoding Ascii | |
"Tracefile found and has recently updated" | Out-File $DebugFile -Append | |
# look for start of User Dump | |
# TraceLog Date Pattern | |
$datePattern = "\d{1,8}\.\d{1,6}\.\d{1,4} " | |
$strLineNumber = 0 | |
$blnUserDump = $false | |
[string]$strLine = $null | |
$userListRaw = $null | |
"Parsing Tracelog for UserList" | Out-File $DebugFile -Append | |
foreach ($traceLine in $traceContent) { | |
$strLineNumber++ | |
$strLine = $traceLine.ToString() | |
if ($traceLine.Contains('S E A R C H C L A S S ( U S E R )')) { | |
$blnUserDump = $true | |
} | |
if (($strLine.contains("<.. ") -and $strLine.contains(" ...")) -or ($strLine.contains(" ... ") -and $strLine.contains(" ...")) -or ($strLine.contains(" ... ") -and $strLine.contains("^M^JREADY ^M^J")) -or $strLine.contains(" ... ") -and $strLine.contains("^M^J") -and $blnUserDump) { | |
# Is it our users? | |
if ($strLine.Contains(".. ") -and $strLine.Contains(" ...")) { | |
if ($strLine.Substring(0, 20) -match $datePattern) { | |
$strLine = $strLine.Remove(0, 20) | |
} | |
if ($strLine.Contains("<.. ")) { | |
$pacman = $null | |
$pacman = $strLine.IndexOf("<.. ") | |
$strLine = $strLine.Remove($pacman, 4) | |
} | |
if ($strLine.contains(" ...")) { | |
$dots = $null | |
$dots = $strLine.IndexOf(" ...") | |
$strLine = $strLine.Remove($dots, 4) | |
} | |
if ($strLine.contains("... ")) { | |
$dots = $null | |
$dots = $strLine.IndexOf("... ") | |
$strLine = $strLine.Remove($dots, 4) | |
} | |
$userListRaw += $strLine | |
} | |
} | |
} | |
$userlist = $userListRaw.replace("^M^J", "~") | |
$userlist = $userlist.replace("^M", "~") | |
$racfUserList = $userlist.split("~") | |
$racfUserList = $racfUserList | Select-Object -Unique | Sort-Object | |
$racfUsers = @() | |
foreach ($user in $racfUserList) { | |
$user = $user.racfID.trimStart(" ") | |
$user = $user.trimEnd(" ") | |
if ($user.Length -gt 0) { | |
$racfUserName = [PSCustomObject]@{ | |
racfID = $user | |
} | |
$racfUsers += $racfUserName | |
} | |
} | |
if ($racfUsers.count -gt 0) { | |
"$($racfUsers.count) users retrieved." | Out-File $DebugFile -Append | |
$Global:groups = @() | |
$parts = [math]::Ceiling($racfUsers.Length / $pagesize) | |
# Splitting the array to chunks of the same size | |
for ($i = 0; $i -le $parts; $i++) { | |
$start = $i * $pagesize | |
$end = (($i + 1) * $pagesize) - 1 | |
$Global:groups += , @($racfUsers[$start..$end]) | |
} | |
[int]$Global:groupsCount = $Global:groups.count | |
[int]$global:groupsProcessed = -1 | |
"$($Global:groupsCount) Groups to process" | Out-File $DebugFile -Append | |
$racfUsers = $null | |
$userlist = $null | |
$racfUserList = $null | |
$userListRaw = $null | |
} | |
} | |
else { | |
write-host "Tracefile is older than the runtime for this MA. Won't process it." | Out-File $DebugFile -Append | |
} | |
} | |
} | |
# Process the first batch | |
# Process Users in Batches based off Page Size | |
# Get the next batch | |
$global:racfBatch = $Global:groups[$global:groupsProcessed + 1] | |
# if we are at the end then set MoreToImport to False and quit | |
if (!$global:racfBatch -or ($global:groupsProcessed + 1 -eq $Global:groupsCount)) { | |
"End of Import as : " + $OperationType + " - " + (Get-Date) | Out-File $DebugFile -Append | |
$global:MoreToImport = $false | |
break | |
} | |
# Process the batch | |
$usersProcessed = 0 | |
foreach ($user in $global:racfBatch) { | |
$obj = @{ } | |
$obj.add("racfID", $user.racfID) | |
$obj.add("objectClass", "racfUser") | |
# Pass to the MA | |
$obj | |
$usersProcessed++ | |
} | |
"Users Processed: $($usersProcessed)" | Out-File $DebugFile -Append | |
# More Groups to Process? Let the Sync Engine know | |
$global:groupsProcessed++ | |
if ($global:groupsProcessed -lt $Global:groupsCount) { | |
"Groups Processed: $($groupsProcessed + 1) out of + $($Global:groupsCount.ToString())" | Out-File $DebugFile -Append | |
$global:MoreToImport = $true | |
} | |
else { | |
# Global:groups Variable exists | |
# Process Users in Batches based off Page Size | |
# Get the next batch | |
$global:racfBatch = $Global:groups[$global:groupsProcessed + 1] | |
# if we are at the end then set MoreToImport to False and quit | |
if (!$global:racfBatch -or ($global:groupsProcessed + 1 -eq $Global:groupsCount)) { | |
"End of Import as : " + $OperationType + " - " + (Get-Date) | Out-File $DebugFile -Append | |
$global:MoreToImport = $false | |
break | |
} | |
# Process the batch | |
$usersProcessed = 0 | |
foreach ($user in $global:racfBatch) { | |
$obj = @{ } | |
$obj.add("racfID", $user) | |
$obj.add("objectClass", "racfUser") | |
# Pass to the MA | |
$obj | |
$usersProcessed++ | |
} | |
"Users Processed: $($usersProcessed)" | Out-File $DebugFile -Append | |
# More Groups to Process? Let the Sync Engine know | |
$global:groupsProcessed++ | |
if ($global:groupsProcessed -lt $Global:groupsCount) { | |
"Groups Processed: $($groupsProcessed + 1) out of + $($Global:groupsCount.ToString())" | Out-File $DebugFile -Append | |
$global:MoreToImport = $true | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment