Public/Workstation_Reporting/Get-VBLoggedOnUserGpResult.ps1
|
#Requires -Version 5.1 # ============================================================ # FUNCTION : Get-VBLoggedOnUserGpResult # MODULE : VB.WorkstationReport # VERSION : 1.0.0 # AUTHOR : Vibhu Bhatnagar # PURPOSE : Parse gpresult /r /scope user for each logged-on user into PSCustomObjects # ENCODING : UTF-8 with BOM # ============================================================ <# .SYNOPSIS Returns user-scoped Group Policy results for every logged-on interactive user. .DESCRIPTION Calls Get-VBLoggedOnUser to discover all interactive sessions, then runs gpresult /r /scope user /user <name> for each user found. Returns one result object per user per computer. If no users are logged on, a single result object is returned with Status 'NoUsers' and all data properties as $null. Supports local and remote execution. Credentials are passed to both Get-VBLoggedOnUser and Invoke-Command when supplied. .PARAMETER ComputerName Target computer(s). Defaults to local machine. Accepts pipeline input. Aliases: CN, MachineName, PSComputerName .PARAMETER Credential Alternate credentials for remote execution. .EXAMPLE Get-VBLoggedOnUserGpResult Returns user GP results for all logged-on users on the local computer. .EXAMPLE Get-VBLoggedOnUserGpResult -ComputerName WS001, WS002 Returns user GP results from two remote workstations. .EXAMPLE 'WS001','WS002' | Get-VBLoggedOnUserGpResult -Credential $cred Pipeline input with alternate credentials. .OUTPUTS PSCustomObject with: ComputerName, Status, UserName, LastApplied, AppliedFrom, SlowLink, SlowLinkThreshold, DomainName, DomainType, AppliedGPOs, SecurityGroups, Error, CollectionTime .NOTES Version : 1.0.0 Author : Vibhu Bhatnagar Category : Group Policy Requirements : - PowerShell 5.1 or higher - gpresult.exe available on target (standard on all Windows SKUs) - PowerShell Remoting enabled for remote targets - Get-VBLoggedOnUser available in the same session REMOTING PREREQUISITES: Target: WinRM running, Enable-PSRemoting -Force, port 5985 open Domain: current token used if -Credential omitted Workgroup: requires -Credential or TrustedHosts configured Production: use HTTPS (port 5986) with valid certificate #> function Get-VBLoggedOnUserGpResult { [CmdletBinding()] [OutputType([PSCustomObject])] param( [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('CN', 'MachineName', 'PSComputerName')] [string[]]$ComputerName = $env:COMPUTERNAME, [Parameter()] [PSCredential]$Credential = [PSCredential]::Empty ) process { foreach ($computer in $ComputerName) { try { Write-Verbose "[$computer] Discovering logged-on users" $credSplat = if ($Credential -ne [PSCredential]::Empty) { @{ Credential = $Credential } } else { @{} } # Step 1 -- Discover logged-on interactive users via Get-VBLoggedOnUser $loggedOnParams = @{ ComputerName = $computer } if ($Credential -ne [PSCredential]::Empty) { $loggedOnParams['Credential'] = $Credential } $LoggedOnUsers = Get-VBLoggedOnUser @loggedOnParams | Where-Object { $_.Status -eq 'Success' -and $null -ne $_.Name } if ($null -eq $LoggedOnUsers -or @($LoggedOnUsers).Count -eq 0) { Write-Verbose "[$computer] No interactive users found" [PSCustomObject]@{ ComputerName = $computer Status = 'NoUsers' UserName = $null LastApplied = $null AppliedFrom = $null SlowLink = $null SlowLinkThreshold = $null DomainName = $null DomainType = $null AppliedGPOs = $null SecurityGroups = $null Error = $null CollectionTime = (Get-Date).ToString('dd-MM-yyyy HH:mm:ss') } continue } # Step 2 -- Run gpresult for each user foreach ($user in $LoggedOnUsers) { $userName = $user.Name Write-Verbose "[$computer] Capturing gpresult /r /scope user /user $userName" try { $captureBlock = { param([string]$TargetUser) $GpOutput = gpresult /r /scope user /user $TargetUser 2>$null if ($LASTEXITCODE -ne 0) { throw "gpresult exited with code $LASTEXITCODE for user $TargetUser" } $GpOutput | Where-Object { $_ -is [string] } } if ($computer -eq $env:COMPUTERNAME) { $Lines = & $captureBlock -TargetUser $userName } else { $Lines = Invoke-Command -ComputerName $computer @credSplat -ScriptBlock $captureBlock -ArgumentList $userName } # Step 3 -- Helper: extract single field value, returns $null if not found $fieldValue = { param([string[]]$L, [string]$Pattern) $m = $L | Select-String -Pattern $Pattern | Select-Object -First 1 if ($null -eq $m) { return $null } $m.Matches[0].Groups[1].Value.Trim() } # Step 4 -- Parse user info block $SlowLink = & $fieldValue -L $Lines -Pattern 'Connected over a slow link\?:\s+(.+)' $LastApplied = & $fieldValue -L $Lines -Pattern 'Last time Group Policy was applied:\s+(.+)' $AppliedFrom = & $fieldValue -L $Lines -Pattern 'Group Policy was applied from:\s+(.+)' $SlowThreshold = & $fieldValue -L $Lines -Pattern 'Group Policy slow link threshold:\s+(.+)' $DomainName = & $fieldValue -L $Lines -Pattern 'Domain Name:\s+(.+)' $DomainType = & $fieldValue -L $Lines -Pattern 'Domain Type:\s+(.+)' # Step 5 -- Parse Applied GPOs block $GpoStartLine = ($Lines | Select-String 'Applied Group Policy Objects' | Select-Object -First 1).LineNumber $GpoEndLine = ($Lines | Select-String 'The user is a part of' | Select-Object -First 1).LineNumber $AppliedGPOs = $Lines[($GpoStartLine)..($GpoEndLine - 2)] | Where-Object { $_.Trim() -ne '' -and $_ -notmatch '---' -and $_ -notmatch 'Applied Group Policy' } | ForEach-Object { [PSCustomObject]@{ ComputerName = $computer UserName = $userName GPOName = $_.Trim() } } # Step 6 -- Parse Security Groups block $SgStartLine = ($Lines | Select-String 'The user is a part of the following security groups' | Select-Object -First 1).LineNumber $SecurityGroups = $Lines[($SgStartLine)..($Lines.Count - 1)] | Where-Object { $_.Trim() -ne '' -and $_ -notmatch '---' -and $_ -notmatch 'security groups' } | ForEach-Object { [PSCustomObject]@{ ComputerName = $computer UserName = $userName SecurityGroup = $_.Trim() } } # Step 7 -- Emit result object for this user [PSCustomObject]@{ ComputerName = $computer Status = 'Success' UserName = $userName LastApplied = $LastApplied AppliedFrom = $AppliedFrom SlowLink = $SlowLink SlowLinkThreshold = $SlowThreshold DomainName = $DomainName DomainType = $DomainType AppliedGPOs = $AppliedGPOs SecurityGroups = $SecurityGroups Error = $null CollectionTime = (Get-Date).ToString('dd-MM-yyyy HH:mm:ss') } } catch { # Per-user failure -- emit failure object and continue to next user Write-Warning "[$computer] $userName -- $($_.Exception.Message)" [PSCustomObject]@{ ComputerName = $computer Status = 'Failed' UserName = $userName LastApplied = $null AppliedFrom = $null SlowLink = $null SlowLinkThreshold = $null DomainName = $null DomainType = $null AppliedGPOs = $null SecurityGroups = $null Error = $_.Exception.Message CollectionTime = (Get-Date).ToString('dd-MM-yyyy HH:mm:ss') } } } } catch { # Computer-level failure -- single failure object for the whole target Write-Warning "[$computer] $($_.Exception.Message)" [PSCustomObject]@{ ComputerName = $computer Status = 'Failed' UserName = $null LastApplied = $null AppliedFrom = $null SlowLink = $null SlowLinkThreshold = $null DomainName = $null DomainType = $null AppliedGPOs = $null SecurityGroups = $null Error = $_.Exception.Message CollectionTime = (Get-Date).ToString('dd-MM-yyyy HH:mm:ss') } } } } } |