Workers/Get-LocalGroupMembers.Worker.ps1
|
<#
.SYNOPSIS Worker script for Get-LocalGroupMembers.ps1 .DESCRIPTION This script is intended to be dot-sourced by Get-LocalGroupMembers.ps1 and should not be invoked directly. It contains the main implementation logic for enumerating local group members with optional recursion into nested groups, and enrichment of account details using Get-LocalGroupMember where possible. .PARAMETER Group The local group to enumerate members of. Default is 'Administrators'. .PARAMETER Recurse If specified, recursively expand nested groups. Default is $false. .PARAMETER MaxDepth Maximum recursion depth for nested groups. Default is 5. .PARAMETER IncludeGroups If specified, include groups in the output. Default is $true. .NOTES #> [CmdletBinding()] param( [Parameter()] [string] $Group = 'Administrators', [Parameter()] [switch] $Recurse, [Parameter()] [ValidateRange(0, 50)] [int] $MaxDepth = 5, [Parameter()] [bool] $IncludeGroups = $true ) Set-StrictMode -Version 2.0 function ConvertTo-SidString { param([object]$SidBytes) try { if ($null -eq $SidBytes) { return $null } $bytes = if ($SidBytes -is [byte[]]) { $SidBytes } else { [byte[]]$SidBytes } return ([System.Security.Principal.SecurityIdentifier]::new($bytes, 0)).Value } catch { return $null } } function Test-TranslateSidToNtAccount { param([string]$Sid) if (-not $Sid) { return $null } try { $sidObj = [System.Security.Principal.SecurityIdentifier]::new($Sid) return $sidObj.Translate([System.Security.Principal.NTAccount]).Value } catch { return $null } } function Get-AdsiMembersOfWinNTGroup { param( [Parameter(Mandatory)] [string] $WinntGroupAdsPath ) $groupObj = [ADSI]$WinntGroupAdsPath @($groupObj.psbase.Invoke('Members')) | ForEach-Object { $_.GetType().InvokeMember('ADsPath', 'GetProperty', $null, $_, $null) } } function Convert-AdsiPathToDisplayName { param([string]$AdsPath) if (-not $AdsPath) { return $null } # Common shapes: # WinNT://COMPUTER/Administrator # WinNT://DOMAIN/Domain Admins if ($AdsPath -match '^WinNT://([^/]+)/(.+)$') { $dom = $matches[1] $obj = $matches[2] $obj = $obj -replace '/', '\' return "$dom\$obj" } return $AdsPath } function Get-PrincipalFromAdsiPath { param( [Parameter(Mandatory)] [string] $AdsPath ) $obj = $null try { $obj = [ADSI]$AdsPath } catch { $obj = $null } $name = Convert-AdsiPathToDisplayName -AdsPath $AdsPath $class = $null $sidBytes = $null $sid = $null if ($obj) { try { $class = $obj.Class } catch { $class = $null } try { $sidBytes = $obj.psbase.InvokeGet('ObjectSID') } catch { $sidBytes = $null } $sid = ConvertTo-SidString -SidBytes $sidBytes } $resolved = Test-TranslateSidToNtAccount -Sid $sid # Heuristics $computer = $env:COMPUTERNAME $isLocal = ($resolved -like "$computer\*") -or ($name -like "$computer\*") $isAzure = ($resolved -like "AzureAD\*") -or ($name -like "AzureAD\*") -or ($sid -like "S-1-12-1-*") [pscustomobject]@{ Name = $name AdsPath = $AdsPath ADSIClass = $class SID = $sid ResolvedNTAccount = $resolved IsGroup = ($class -eq 'Group') IsLocal = $isLocal IsAzureAD = $isAzure } } function Test-GetLocalAccountsEnrichment { param( [Parameter(Mandatory)] [string] $GroupName ) $cmd = Get-Command Get-LocalGroupMember -ErrorAction SilentlyContinue if (-not $cmd) { return @{} } $map = @{} try { foreach ($m in (Get-LocalGroupMember -Group $GroupName -ErrorAction Stop)) { $sid = $null try { $sid = $m.SID.Value } catch { $sid = $null } if (-not $sid) { continue } $map[$sid] = [pscustomobject]@{ ObjectClass = $m.ObjectClass PrincipalSource = $m.PrincipalSource LocalName = $m.Name } } } catch { # Ignore; ADSI remains authoritative } return $map } function Test-ExpandDomainGroupMembers { param( [Parameter(Mandatory)] [string] $GroupSid, [Parameter(Mandatory)] [int] $Depth, [Parameter(Mandatory)] [int] $MaxDepth, [Parameter(Mandatory)] [hashtable] $SeenGroupSids ) if (-not $GroupSid) { return @() } if ($Depth -ge $MaxDepth) { return @() } if ($SeenGroupSids.ContainsKey($GroupSid)) { return @() } $SeenGroupSids[$GroupSid] = $true # Use AccountManagement to avoid requiring the AD module try { Add-Type -AssemblyName System.DirectoryServices.AccountManagement -ErrorAction Stop } catch { return @() } $out = New-Object System.Collections.Generic.List[object] try { $ctx = New-Object System.DirectoryServices.AccountManagement.PrincipalContext ` ([System.DirectoryServices.AccountManagement.ContextType]::Domain) $gp = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity( $ctx, [System.DirectoryServices.AccountManagement.IdentityType]::Sid, $GroupSid ) if (-not $gp) { return @() } foreach ($m in $gp.GetMembers($false)) { $msid = $null try { $msid = $m.Sid.Value } catch { $msid = $null } $mname = $null try { if ($m.ContextType -eq 'Domain') { $mname = "$($m.Context.Name)\$($m.SamAccountName)" } else { $mname = $m.Name } } catch { $mname = $m.Name } $isGroup = $false try { $isGroup = ($m.StructuralObjectClass -eq 'group') } catch { $isGroup = $false } $resolved = Test-TranslateSidToNtAccount -Sid $msid $out.Add([pscustomobject]@{ Name = $mname AdsPath = $null ADSIClass = if ($isGroup) { 'Group' } else { 'User' } SID = $msid ResolvedNTAccount = $resolved IsGroup = $isGroup IsLocal = $false IsAzureAD = ($msid -like "S-1-12-1-*") }) | Out-Null if ($isGroup -and $msid) { foreach ($child in (Test-ExpandDomainGroupMembers -GroupSid $msid -Depth ($Depth + 1) -MaxDepth $MaxDepth -SeenGroupSids $SeenGroupSids)) { $out.Add($child) | Out-Null } } } } catch { return @() } return $out } function Add-Row { param( [Parameter(Mandatory)] [pscustomobject] $Principal, [Parameter(Mandatory)] [bool] $IsDirect, [Parameter(Mandatory)] [string] $ParentGroup, [Parameter(Mandatory)] [int] $Depth ) $sidKey = if ($Principal.SID) { "SID:$($Principal.SID)" } else { "PATH:$($Principal.AdsPath)" } $dedupeKey = "$IsDirect|$ParentGroup|$Depth|$sidKey" if ($seenMemberKeys.ContainsKey($dedupeKey)) { return } $seenMemberKeys[$dedupeKey] = $true $objClass = $null $src = $null $localName = $null if ($Principal.SID -and $enrichBySid.ContainsKey($Principal.SID)) { $objClass = $enrichBySid[$Principal.SID].ObjectClass $src = $enrichBySid[$Principal.SID].PrincipalSource $localName = $enrichBySid[$Principal.SID].LocalName } $rows.Add([pscustomobject]@{ Name = $Principal.Name ComputerName = $computer Group = $Group ParentGroup = $ParentGroup IsDirect = $IsDirect ADSIClass = $Principal.ADSIClass AdsPath = $Principal.AdsPath IsGroup = $Principal.IsGroup IsLocal = $Principal.IsLocal IsAzureAD = $Principal.IsAzureAD WhenQueried = $when }) | Out-Null } function Get-LocalGroupMembers { [CmdletBinding()] param( [Parameter()] [string] $Group = 'Administrators', [Parameter()] [switch] $Recurse, [Parameter()] [ValidateRange(0, 50)] [int] $MaxDepth = 5, [Parameter()] [bool] $IncludeGroups = $true ) $computer = $env:COMPUTERNAME $when = Get-Date $enrichBySid = Test-GetLocalAccountsEnrichment -GroupName $Group # Direct members from local group (authoritative) $groupAdsPath = "WinNT://./$Group,group" Write-Log -Level Info -Message "Enumerating direct members via ADSI: $groupAdsPath" $directMemberPaths = Get-AdsiMembersOfWinNTGroup -WinntGroupAdsPath $groupAdsPath # Output rows list $rows = New-Object System.Collections.Generic.List[object] # For recursion $seenGroupSids = @{} # group-sid cycle protection $seenMemberKeys = @{} # de-dupe (SID preferred, else AdsPath) foreach ($path in $directMemberPaths) { $p = Get-PrincipalFromAdsiPath -AdsPath $path # Always include direct members (including groups) Add-Row -Principal $p -IsDirect $true -ParentGroup $Group -Depth 0 if ($Recurse -and $p.IsGroup -and $p.SID) { # If it's a local group (WinNT://COMPUTER/Group), expand via WinNT $isLocalGroupPath = ($p.AdsPath -match '^WinNT://\./') -or ($p.Name -like "$computer\*") if ($isLocalGroupPath) { if (-not $seenGroupSids.ContainsKey($p.SID)) { $seenGroupSids[$p.SID] = $true Write-Log -Level Info -Message "Expanding local nested group: $($p.Name) [$($p.SID)]" $nestedGroupPath = $p.AdsPath if (-not $nestedGroupPath) { continue } # Enumerate local group members try { foreach ($childPath in (Get-AdsiMembersOfWinNTGroup -WinntGroupAdsPath $nestedGroupPath)) { $cp = Get-PrincipalFromAdsiPath -AdsPath $childPath if ($IncludeGroups -or (-not $cp.IsGroup)) { Add-Row -Principal $cp -IsDirect $false -ParentGroup $p.Name -Depth 1 } } } catch { # ignore } } } else { # Domain group expansion using AccountManagement (recursive) Write-Log -Level Info -Message "Expanding domain nested group via AccountManagement: $($p.Name) [$($p.SID)]" foreach ($child in (Test-ExpandDomainGroupMembers -GroupSid $p.SID -Depth 0 -MaxDepth $MaxDepth -SeenGroupSids $seenGroupSids)) { if ($IncludeGroups -or (-not $child.IsGroup)) { Add-Row -Principal $child -IsDirect $false -ParentGroup $p.Name -Depth 1 } } } } } # Sort stable & return $rows | Sort-Object @{Expression = 'IsDirect'; Descending = $true }, @{Expression = 'Depth'; Ascending = $true }, @{Expression = 'Name'; Ascending = $true } } Get-LocalGroupMembers -Group $Group -Recurse:$Recurse -MaxDepth $MaxDepth -IncludeGroups:$IncludeGroups # SIG # Begin signature block # MIIfAgYJKoZIhvcNAQcCoIIe8zCCHu8CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAYmPklwP6tIaJW # 0lVMw1ZBzpaXUQfzUXSe7KmR2aP9TKCCGEowggUMMIIC9KADAgECAhAR+U4xG7FH # qkyqS9NIt7l5MA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNVBAMME1ZBRFRFSyBDb2Rl # IFNpZ25pbmcwHhcNMjUxMjE5MTk1NDIxWhcNMjYxMjE5MjAwNDIxWjAeMRwwGgYD # VQQDDBNWQURURUsgQ29kZSBTaWduaW5nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A # MIICCgKCAgEA3pzzZIUEY92GDldMWuzvbLeivHOuMupgpwbezoG5v90KeuN03S5d # nM/eom/PcIz08+fGZF04ueuCS6b48q1qFnylwg/C/TkcVRo0WFcKoFGT8yGxdfXi # caHtapZfbSRh73r7qR7w0CioVveNBVgfMsTgE0WKcuwxemvIe/ptmkfzwAiw/IAC # Ib0E0BjiX4PySbwWy/QKy/qMXYY19xpRItVTKNBtXzADUtzPzUcFqJU83vM2gZFs # Or0MhPvM7xEVkOWZFBAWAubbMCJ3rmwyVv9keVDJChhCeLSz2XR11VGDOEA2OO90 # Y30WfY9aOI2sCfQcKMeJ9ypkHl0xORdhUwZ3Wz48d3yJDXGkduPm2vl05RvnA4T6 # 29HVZTmMdvP2475/8nLxCte9IB7TobAOGl6P1NuwplAMKM8qyZh62Br23vcx1fXZ # TJlKCxBFx1nTa6VlIJk+UbM4ZPm954peB/fIqEacm8LkZ0cPwmLE5ckW7hfK4Trs # o+RaudU1sKeA+FvpOWgsPccVRWcEYyGkwbyTB3xrIBXA+YckbANZ0XL7fv7x29hn # gXbZipGu3DnTISiFB43V4MhNDKZYfbWdxze0SwLe8KzIaKnwlwRgvXDMwXgk99Mi # EbYa3DvA/5ZWikLW9PxBFD7Vdr8ZiG/tRC9I2Y6fnb+PVoZKc/2xsW0CAwEAAaNG # MEQwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQW # BBRfYLVE8caSc990rnrIHUjoB7X/KjANBgkqhkiG9w0BAQsFAAOCAgEAiGB2Wmk3 # QBtd1LcynmxHzmu+X4Y5DIpMMNC2ahsqZtPUVcGqmb5IFbVuAdQphL6PSrDjaAR8 # 1S8uTfUnMa119LmIb7di7TlH2F5K3530h5x8JMj5EErl0xmZyJtSg7BTiBA/UrMz # 6WCf8wWIG2/4NbV6aAyFwIojfAcKoO8ng44Dal/oLGzLO3FDE5AWhcda/FbqVjSJ # 1zMfiW8odd4LgbmoyEI024KkwOkkPyJQ2Ugn6HMqlFLazAmBBpyS7wxdaAGrl18n # 6bS7QuAwCd9hitdMMitG8YyWL6tKeRSbuTP5E+ASbu0Ga8/fxRO5ZSQhO6/5ro1j # PGe1/Kr49Uyuf9VSCZdNIZAyjjeVAoxmV0IfxQLKz6VOG0kGDYkFGskvllIpQbQg # WLuPLJxoskJsoJllk7MjZJwrpr08+3FQnLkRuisjDOc3l4VxFUsUe4fnJhMUONXT # Sk7vdspgxirNbLmXU4yYWdsizz3nMUR0zebUW29A+HYme16hzrMPOeyoQjy4I5XX # 3wXAFdworfPEr/ozDFrdXKgbLwZopymKbBwv6wtT7+1zVhJXr+jGVQ1TWr6R+8ea # tIOFnY7HqGaxe5XB7HzOwJKdj+bpHAfXft1vUoiKr16VajLigcYCG8MdwC3sngO3 # JDyv2V+YMfsYBmItMGBwvizlQ6557NbK95EwggWNMIIEdaADAgECAhAOmxiO+dAt # 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa # Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD # ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E # MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy # unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF # xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1 # 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB # MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR # WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6 # nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB # YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S # UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x # q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB # NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP # TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC # AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp # Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0 # aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB # LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc # Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov # Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy # oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW # juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF # mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z # twGpn1eqXijiuZQwgga0MIIEnKADAgECAhANx6xXBf8hmS5AQyIMOkmGMA0GCSqG # SIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx # GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy # dXN0ZWQgUm9vdCBHNDAeFw0yNTA1MDcwMDAwMDBaFw0zODAxMTQyMzU5NTlaMGkx # CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4 # RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYg # MjAyNSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC0eDHTCphB # cr48RsAcrHXbo0ZodLRRF51NrY0NlLWZloMsVO1DahGPNRcybEKq+RuwOnPhof6p # vF4uGjwjqNjfEvUi6wuim5bap+0lgloM2zX4kftn5B1IpYzTqpyFQ/4Bt0mAxAHe # HYNnQxqXmRinvuNgxVBdJkf77S2uPoCj7GH8BLuxBG5AvftBdsOECS1UkxBvMgEd # gkFiDNYiOTx4OtiFcMSkqTtF2hfQz3zQSku2Ws3IfDReb6e3mmdglTcaarps0wjU # jsZvkgFkriK9tUKJm/s80FiocSk1VYLZlDwFt+cVFBURJg6zMUjZa/zbCclF83bR # VFLeGkuAhHiGPMvSGmhgaTzVyhYn4p0+8y9oHRaQT/aofEnS5xLrfxnGpTXiUOeS # LsJygoLPp66bkDX1ZlAeSpQl92QOMeRxykvq6gbylsXQskBBBnGy3tW/AMOMCZIV # NSaz7BX8VtYGqLt9MmeOreGPRdtBx3yGOP+rx3rKWDEJlIqLXvJWnY0v5ydPpOjL # 6s36czwzsucuoKs7Yk/ehb//Wx+5kMqIMRvUBDx6z1ev+7psNOdgJMoiwOrUG2Zd # SoQbU2rMkpLiQ6bGRinZbI4OLu9BMIFm1UUl9VnePs6BaaeEWvjJSjNm2qA+sdFU # eEY0qVjPKOWug/G6X5uAiynM7Bu2ayBjUwIDAQABo4IBXTCCAVkwEgYDVR0TAQH/ # BAgwBgEB/wIBADAdBgNVHQ4EFgQU729TSunkBnx6yuKQVvYv1Ensy04wHwYDVR0j # BBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1Ud # JQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0 # cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0 # cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8E # PDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz # dGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw # DQYJKoZIhvcNAQELBQADggIBABfO+xaAHP4HPRF2cTC9vgvItTSmf83Qh8WIGjB/ # T8ObXAZz8OjuhUxjaaFdleMM0lBryPTQM2qEJPe36zwbSI/mS83afsl3YTj+IQhQ # E7jU/kXjjytJgnn0hvrV6hqWGd3rLAUt6vJy9lMDPjTLxLgXf9r5nWMQwr8Myb9r # EVKChHyfpzee5kH0F8HABBgr0UdqirZ7bowe9Vj2AIMD8liyrukZ2iA/wdG2th9y # 1IsA0QF8dTXqvcnTmpfeQh35k5zOCPmSNq1UH410ANVko43+Cdmu4y81hjajV/gx # dEkMx1NKU4uHQcKfZxAvBAKqMVuqte69M9J6A47OvgRaPs+2ykgcGV00TYr2Lr3t # y9qIijanrUR3anzEwlvzZiiyfTPjLbnFRsjsYg39OlV8cipDoq7+qNNjqFzeGxcy # tL5TTLL4ZaoBdqbhOhZ3ZRDUphPvSRmMThi0vw9vODRzW6AxnJll38F0cuJG7uEB # YTptMSbhdhGQDpOXgpIUsWTjd6xpR6oaQf/DJbg3s6KCLPAlZ66RzIg9sC+NJpud # /v4+7RWsWCiKi9EOLLHfMR2ZyJ/+xhCx9yHbxtl5TPau1j/1MIDpMPx0LckTetiS # uEtQvLsNz3Qbp7wGWqbIiOWCnb5WqxL3/BAPvIXKUjPSxyZsq8WhbaM2tszWkPZP # ubdcMIIG7TCCBNWgAwIBAgIQCoDvGEuN8QWC0cR2p5V0aDANBgkqhkiG9w0BAQsF # ADBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNV # BAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hB # MjU2IDIwMjUgQ0ExMB4XDTI1MDYwNDAwMDAwMFoXDTM2MDkwMzIzNTk1OVowYzEL # MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJE # aWdpQ2VydCBTSEEyNTYgUlNBNDA5NiBUaW1lc3RhbXAgUmVzcG9uZGVyIDIwMjUg # MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANBGrC0Sxp7Q6q5gVrMr # V7pvUf+GcAoB38o3zBlCMGMyqJnfFNZx+wvA69HFTBdwbHwBSOeLpvPnZ8ZN+vo8 # dE2/pPvOx/Vj8TchTySA2R4QKpVD7dvNZh6wW2R6kSu9RJt/4QhguSssp3qome7M # rxVyfQO9sMx6ZAWjFDYOzDi8SOhPUWlLnh00Cll8pjrUcCV3K3E0zz09ldQ//nBZ # ZREr4h/GI6Dxb2UoyrN0ijtUDVHRXdmncOOMA3CoB/iUSROUINDT98oksouTMYFO # nHoRh6+86Ltc5zjPKHW5KqCvpSduSwhwUmotuQhcg9tw2YD3w6ySSSu+3qU8DD+n # igNJFmt6LAHvH3KSuNLoZLc1Hf2JNMVL4Q1OpbybpMe46YceNA0LfNsnqcnpJeIt # K/DhKbPxTTuGoX7wJNdoRORVbPR1VVnDuSeHVZlc4seAO+6d2sC26/PQPdP51ho1 # zBp+xUIZkpSFA8vWdoUoHLWnqWU3dCCyFG1roSrgHjSHlq8xymLnjCbSLZ49kPmk # 8iyyizNDIXj//cOgrY7rlRyTlaCCfw7aSUROwnu7zER6EaJ+AliL7ojTdS5PWPsW # eupWs7NpChUk555K096V1hE0yZIXe+giAwW00aHzrDchIc2bQhpp0IoKRR7YufAk # prxMiXAJQ1XCmnCfgPf8+3mnAgMBAAGjggGVMIIBkTAMBgNVHRMBAf8EAjAAMB0G # A1UdDgQWBBTkO/zyMe39/dfzkXFjGVBDz2GM6DAfBgNVHSMEGDAWgBTvb1NK6eQG # fHrK4pBW9i/USezLTjAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYB # BQUHAwgwgZUGCCsGAQUFBwEBBIGIMIGFMCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz # cC5kaWdpY2VydC5jb20wXQYIKwYBBQUHMAKGUWh0dHA6Ly9jYWNlcnRzLmRpZ2lj # ZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRpbWVTdGFtcGluZ1JTQTQwOTZTSEEy # NTYyMDI1Q0ExLmNydDBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vY3JsMy5kaWdp # Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRUaW1lU3RhbXBpbmdSU0E0MDk2U0hB # MjU2MjAyNUNBMS5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB # MA0GCSqGSIb3DQEBCwUAA4ICAQBlKq3xHCcEua5gQezRCESeY0ByIfjk9iJP2zWL # pQq1b4URGnwWBdEZD9gBq9fNaNmFj6Eh8/YmRDfxT7C0k8FUFqNh+tshgb4O6Lgj # g8K8elC4+oWCqnU/ML9lFfim8/9yJmZSe2F8AQ/UdKFOtj7YMTmqPO9mzskgiC3Q # YIUP2S3HQvHG1FDu+WUqW4daIqToXFE/JQ/EABgfZXLWU0ziTN6R3ygQBHMUBaB5 # bdrPbF6MRYs03h4obEMnxYOX8VBRKe1uNnzQVTeLni2nHkX/QqvXnNb+YkDFkxUG # tMTaiLR9wjxUxu2hECZpqyU1d0IbX6Wq8/gVutDojBIFeRlqAcuEVT0cKsb+zJNE # suEB7O7/cuvTQasnM9AWcIQfVjnzrvwiCZ85EE8LUkqRhoS3Y50OHgaY7T/lwd6U # Arb+BOVAkg2oOvol/DJgddJ35XTxfUlQ+8Hggt8l2Yv7roancJIFcbojBcxlRcGG # 0LIhp6GvReQGgMgYxQbV1S3CrWqZzBt1R9xJgKf47CdxVRd/ndUlQ05oxYy2zRWV # FjF7mcr4C34Mj3ocCVccAvlKV9jEnstrniLvUxxVZE/rptb7IRE2lskKPIJgbaP5 # t2nGj/ULLi49xTcBZU8atufk+EMF/cWuiC7POGT75qaL6vdCvHlshtjdNXOCIUjs # arfNZzGCBg4wggYKAgEBMDIwHjEcMBoGA1UEAwwTVkFEVEVLIENvZGUgU2lnbmlu # ZwIQEflOMRuxR6pMqkvTSLe5eTANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3 # AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisG # AQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCDhTAnfIeRM # dZ1ZTRVTPErIzaszOML1Dgml2tQ4Wo5IhzANBgkqhkiG9w0BAQEFAASCAgC5xugr # FK4GNRJ+GXnPXvdrKGwQjdLd30tuf6YEup9iLLzH46qrT9z985eoSKZbsVd/04w+ # e8KSLpw/6Kf1/F7JHYG0ZqZhB6YVEIbjDBFXgIVO8uEOUzHH16kmwQ8yzkcekFRh # +hBBHFECjZbMfQnHixm8Gvp33GpqqHTheHOgr/UR/PfOj59aWfatAGTEaJ+7VlLc # x6ufabFs5uj9gE2UwbvBVqUnMxIaV0dtP9S4Ol7261tyD1WbNMhbT12C/xwCcDoE # GtwD6SQprR/n4Z7Z/NvS2pdkLYgxaGVIE+OALUbJFCNedxF7WJs7uSI0IXmG2oyr # R5kqyPrXGE3lusNn66Aff98lo5He+IFxxZVg/kokuWt5otYUulFWaWXXuY1NTrf4 # fB30C/+Flf5b4sYlTGX3HHubcGAkXTlYQJSslLC1+coeHsI7dWDoJ2BMY6ofZnoY # 1+cyheHgmliCpBBiHyfMeEotb7QkBKLhwTiAssR9ikYqLMP8eK+n6fM3FwO8K3L5 # hltlMFz1R+J4+5oSpgBRH3oD9reHWyEwJxz5ot2KBIsDERvCUkJsOBjgnwokMuXa # In/3zr5pGyeKrQzkcxYYPF9Ngyy65tulP68quOqIjnTNFREEY7SHKNYaBrX+aitm # LMMp4dd9u0iMqs9R42xgA82f3JDWnGWvvuC4yqGCAyYwggMiBgkqhkiG9w0BCQYx # ggMTMIIDDwIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg # SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcg # UlNBNDA5NiBTSEEyNTYgMjAyNSBDQTECEAqA7xhLjfEFgtHEdqeVdGgwDQYJYIZI # AWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJ # BTEPFw0yNjA0MjcxNTMyMzRaMC8GCSqGSIb3DQEJBDEiBCA8C5mxgf6RLMsVTb0P # BA8baFU+1xTVsoYX/iwX9rn3hDANBgkqhkiG9w0BAQEFAASCAgArErW6RP/uiPl9 # T3Hbsm+SZ7wylCkXkxGwR15dDE6kKqd7Aw5dA8GcF38CZS8VO0gpDolONZ793okG # nqwCbzLzLN9CPMuht4MAzmKC4jKFV2fzKrxvAFOyAIB3xEUekBcefkwiq5R/5xW9 # FpPaYLqGFZSxrhCXc3Vfj/PkZfNZblZkto8EDT0K6KiKBjDq7xNunNW+YwbtNydy # q9QY+r80HXp5kOrKGnnuO9Zbr0qHHVBiv9khLyAjxON89Cd9EqeegGj0C55eXTmv # YB5IjETXJafcRWYagXLR8PiaIRE6Y5hpNRTAB2XV8TxgDCOXm3PULLnR2yO4BF6K # JclEKw01PB9Lt6wCQAgOrx06zykzV1+1frN4ptJZBEVUzW1uPtvKvQp92dnyyT+m # cLT5lrJm7mwjJNsGkWO5GHn1SyL5rkUVtjaTXsoGvZdYg/1Rw50xtDphuYmfz8oL # xG3QoU3Detia1IZ18rpC3J1HXhK9wpBx3e5I9pWKDo/XZfSCfKlWnDDC53ALkuYo # Ils0jQ2qD0QK8UfbTIk2dJMLQ1wWkz43It0QPbwZMtr9/wZiV2C7vjLaUqxNvuIs # 5w6GX5kmlDhTaeEkpwVgxwsTVeLTNf9zJXagN3sBXz71/Nd+VzzZHh8RcnCvewvp # LVjOogqO7kTK6xdXlVHfWi52NiCoDA== # SIG # End signature block |