Skip to content

Instantly share code, notes, and snippets.

@darrenjrobinson
Last active June 26, 2019 00:29
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/3328bcd601194f24f8dfc331de790a84 to your computer and use it in GitHub Desktop.
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/
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