Src/Private/SharedUtilsFunctions.ps1
function ConvertTo-TextYN { <# .SYNOPSIS Used by As Built Report to convert true or false automatically to Yes or No. .DESCRIPTION .NOTES Version: 0.3.0 Author: LEE DAILEY .EXAMPLE .LINK #> [CmdletBinding()] [OutputType([String])] Param ( [Parameter ( Position = 0, Mandatory)] [AllowEmptyString()] [string] $TEXT ) switch ($TEXT) { "" { "--"; break } " " { "--"; break } $Null { "--"; break } "True" { "Yes"; break } "False" { "No"; break } default { $TEXT } } } # end function ConvertTo-FileSizeString { <# .SYNOPSIS Used by As Built Report to convert bytes automatically to GB or TB based on size. .DESCRIPTION .NOTES Version: 0.1.0 Author: Jonathan Colon .EXAMPLE .LINK #> [CmdletBinding()] [OutputType([String])] Param ( [Parameter ( Position = 0, Mandatory)] [int64] $Size ) $Unit = Switch ($Size) { { $Size -gt 1PB } { 'PB' ; Break } { $Size -gt 1TB } { 'TB' ; Break } { $Size -gt 1GB } { 'GB' ; Break } { $Size -gt 1Mb } { 'MB' ; Break } Default { 'KB' } } return "$([math]::Round(($Size / $("1" + $Unit)), 0)) $Unit" } # end function ConvertTo-EmptyToFiller { <# .SYNOPSIS Used by As Built Report to convert empty culumns to "--". .DESCRIPTION .NOTES Version: 0.5.0 Author: Jonathan Colon .EXAMPLE .LINK #> [CmdletBinding()] [OutputType([String])] Param ( [Parameter ( Position = 0, Mandatory)] [AllowEmptyString()] [string]$TEXT ) switch ([string]::IsNullOrEmpty($TEXT)) { $true { "--"; break } default { $TEXT } } } function Convert-IpAddressToMaskLength { <# .SYNOPSIS Used by As Built Report to convert subnet mask to dotted notation. .DESCRIPTION .NOTES Version: 0.4.0 Author: Ronald Rink .EXAMPLE .LINK #> [CmdletBinding()] [OutputType([String])] Param ( [Parameter ( Position = 0, Mandatory)] [string] $SubnetMask ) [IPAddress] $MASK = $SubnetMask $octets = $MASK.IPAddressToString.Split('.') foreach ($octet in $octets) { while (0 -ne $octet) { $octet = ($octet -shl 1) -band [byte]::MaxValue $result++; } } return $result; } function ConvertTo-ADObjectName { <# .SYNOPSIS Used by As Built Report to translate Active Directory DN to Name. .DESCRIPTION .NOTES Version: 0.4.0 Author: Jonathan Colon .EXAMPLE .LINK #> param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] $DN, $Session ) $ADObject = @() foreach ($Object in $DN) { $ADObject += Invoke-Command -Session $Session { Get-ADObject $using:Object | Select-Object -ExpandProperty Name } } return $ADObject; }# end Function Get-LocalGroupMembership { <# .SYNOPSIS Recursively list all members of a specified Local group. .DESCRIPTION Recursively list all members of a specified Local group. This can be run against a local or remote system or systems. Recursion is unlimited unless specified by the -Depth parameter. Alias: glgm .NOTES Version: 0.5.4 Author: Boe Prox (Updated by Graham Flynn (26/07/2024) Changes: Updated to ouput PrincipalSource and ObjectClass so output is similar to Microsoft's Get-LocalGroupMember for compatibility .PARAMETER Computername Local or remote computer/s to perform the query against. Default value is the local system. .PARAMETER Group Name of the group to query on a system for all members. Default value is 'Administrators' .PARAMETER Depth Limit the recursive depth of a query. Default value is 2147483647. .PARAMETER Throttle Number of concurrently running jobs to run at a time Default value is 10 .EXAMPLE Get-LocalGroupMembership Name ParentGroup isGroup ObjectClass PrincipalSource Computername Depth ---- ----------- ------- ---- ------------ ----- Administrator Administrators False User Local DC1 1 boe Administrators False User Domain DC1 1 testuser Administrators False User Local DC1 1 bob Administrators False User Domain DC1 1 proxb Administrators False User Domain DC1 1 Enterprise Admins Administrators True Group Domain DC1 1 Sysops Admins Enterprise Admins True Group Domain DC1 2 Domain Admins Enterprise Admins True Group Domain DC1 2 Administrator Enterprise Admins False User Domain DC1 2 Domain Admins Administrators True Group Domain DC1 1 proxb Domain Admins False User Domain DC1 2 Administrator Domain Admins False User Domain DC1 2 Sysops Admins Administrators True Group Domain DC1 1 Org Admins Sysops Admins True Group Domain DC1 2 Enterprise Admins Sysops Admins True Group Domain DC1 2 Description ----------- Gets all of the members of the 'Administrators' group on the local system. .EXAMPLE Get-LocalGroupMembership -Group 'Administrators' -Depth 1 Name ParentGroup isGroup ObjectClass PrincipalSource Computername Depth ---- ----------- ------- ---- ------------ ----- Administrator Administrators False User Local DC1 1 boe Administrators False User Domain DC1 1 testuser Administrators False User Local DC1 1 bob Administrators False User Domain DC1 1 proxb Administrators False User Domain DC1 1 Enterprise Admins Administrators True Group Domain DC1 1 Domain Admins Administrators True Group Domain DC1 1 Sysops Admins Administrators True Group Domain DC1 1 Description ----------- Gets the members of 'Administrators' with only 1 level of recursion. .LINK Original Script: https://github.com/proxb/PowerShell_Scripts/blob/master/Get-LocalGroupMembership.ps1 #> [cmdletbinding()] Param ( [parameter(ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('CN', '__Server', 'Computer', 'IPAddress')] [string[]]$Computername = $env:COMPUTERNAME, [parameter()] [string]$Group = "Administrators", [parameter()] [int]$Depth = ([int]::MaxValue), [parameter()] [Alias("MaxJobs")] [int]$Throttle = 10 ) Begin { $PSBoundParameters.GetEnumerator() | ForEach-Object { Write-Verbose $_ } # region Extra Configurations Write-Verbose ("Depth: {0}" -f $Depth) # endregion Extra Configurations # Define hash table for Get-RunspaceData function $runspacehash = @{} # Function to perform runspace job cleanup Function Get-RunspaceData { [cmdletbinding()] param( [switch]$Wait ) Do { $more = $false Foreach ($runspace in $runspaces) { If ($runspace.Runspace.isCompleted) { $runspace.powershell.EndInvoke($runspace.Runspace) $runspace.powershell.dispose() $runspace.Runspace = $null $runspace.powershell = $null } ElseIf ($runspace.Runspace -ne $null) { $more = $true } } If ($more -AND $PSBoundParameters['Wait']) { Start-Sleep -Milliseconds 100 } # Clean out unused runspace jobs $temphash = $runspaces.clone() $temphash | Where-Object { $_.runspace -eq $Null } | ForEach-Object { Write-Verbose ("Removing {0}" -f $_.computer) $Runspaces.remove($_) } } while ($more -AND $PSBoundParameters['Wait']) } # region ScriptBlock $scriptBlock = { Param ($Computer, $Group, $Depth, $NetBIOSDomain, $ObjNT, $Translate) $Script:Depth = $Depth $Script:ObjNT = $ObjNT $Script:Translate = $Translate $Script:NetBIOSDomain = $NetBIOSDomain Function Get-LocalGroupMemberObj { [cmdletbinding()] Param ( [parameter()] [System.DirectoryServices.DirectoryEntry]$LocalGroup ) # Invoke the Members method and convert to an array of member objects. $Members = @($LocalGroup.psbase.Invoke("Members")) | ForEach-Object { ([System.DirectoryServices.DirectoryEntry]$_) } $Counter++ ForEach ($Member In $Members) { Try { $Name = $Member.InvokeGet("Name") $Path = $Member.InvokeGet("AdsPath") # Check if this member is a group. $isGroup = ($Member.InvokeGet("Class") -eq "group") # Remove the domain from the computername to fix the type comparison when supplied with FQDN IF ($Computer.Contains('.')) { $Computer = $computer.Substring(0, $computer.IndexOf('.')) } If (($Path -like "*/$Computer/*")) { $Type = 'Local' } Else { $Type = 'Domain' } # Add Objectclass to match Get-LocalGroupMember output if ($isGroup) { $ObjectClass = 'Group' } elseif ($isGroup -eq $false) { $ObjectClass = 'User' } else { 'Unknown' } New-Object PSObject -Property @{ Computername = $Computer Name = $Name PrincipalSource = $Type ParentGroup = $LocalGroup.Name[0] isGroup = $isGroup ObjectClass = $ObjectClass Depth = $Counter Group = $Group } If ($isGroup) { # Check if this group is local or domain. # $host.ui.WriteVerboseLine("(RS)Checking if Counter: {0} is less than Depth: {1}" -f $Counter, $Depth) If ($Counter -lt $Depth) { If ($Type -eq 'Local') { If ($Groups[$Name] -notcontains 'Local') { $host.ui.WriteVerboseLine(("{0}: Getting local group members" -f $Name)) $Groups[$Name] += , 'Local' # Enumerate members of local group. Get-LocalGroupMemberObj $Member } } Else { If ($Groups[$Name] -notcontains 'Domain') { $host.ui.WriteVerboseLine(("{0}: Getting domain group members" -f $Name)) $Groups[$Name] += , 'Domain' # Enumerate members of domain group. Get-DomainGroupMember $Member $Name $True } } } } } Catch { $host.ui.WriteWarningLine(("GLGM{0}" -f $_.Exception.Message)) } } } Function Get-DomainGroupMember { [cmdletbinding()] Param ( [parameter()] $DomainGroup, [parameter()] [string]$NTName, [parameter()] [string]$blnNT ) Try { If ($blnNT -eq $True) { # Convert NetBIOS domain name of group to Distinguished Name. $objNT.InvokeMember("Set", "InvokeMethod", $Null, $Translate, (3, ("{0}{1}" -f $NetBIOSDomain.Trim(), $NTName))) $DN = $objNT.InvokeMember("Get", "InvokeMethod", $Null, $Translate, 1) $ADGroup = [ADSI]"LDAP://$DN" } Else { $DN = $DomainGroup.distinguishedName $ADGroup = $DomainGroup } $Counter++ ForEach ($MemberDN In $ADGroup.Member) { $MemberGroup = [ADSI]("LDAP://{0}" -f ($MemberDN -replace '/', '\/')) # Add Objectclass to match Get-LocalGroupMember output if ($MemberGroup.Class -eq "group") { $ObjectClass = 'Group' } else { $ObjectClass = 'User' } New-Object PSObject -Property @{ Computername = $Computer Name = $MemberGroup.name[0] PrincipalSource = 'Domain' ParentGroup = $NTName isGroup = ($MemberGroup.Class -eq "group") ObjectClass = $ObjectClass Depth = $Counter Group = $Group } # Check if this member is a group. If ($MemberGroup.Class -eq "group") { If ($Counter -lt $Depth) { If ($Groups[$MemberGroup.name[0]] -notcontains 'Domain') { Write-Verbose ("{0}: Getting domain group members" -f $MemberGroup.name[0]) $Groups[$MemberGroup.name[0]] += , 'Domain' # Enumerate members of domain group. Get-DomainGroupMember $MemberGroup $MemberGroup.Name[0] $False } } } } } Catch { $host.ui.WriteWarningLine(("GDGM{0}" -f $_.Exception.Message)) } } # region Get Local Group Members $Script:Groups = @{} $Script:Counter = 0 # Bind to the group object with the WinNT provider. $ADSIGroup = [ADSI]"WinNT://$Computer/$Group,group" Write-Verbose ("Checking {0} membership for {1}" -f $Group, $Computer) $Groups[$Group] += , 'Local' Get-LocalGroupMemberObj -LocalGroup $ADSIGroup # endregion Get Local Group Members } # endregion ScriptBlock Write-Verbose ("Checking to see if connected to a domain") Try { $Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() $Root = $Domain.GetDirectoryEntry() $Base = ($Root.distinguishedName) # Use the NameTranslate object. $Script:Translate = New-Object -ComObject "NameTranslate" $Script:objNT = $Translate.GetType() # Initialize NameTranslate by locating the Global Catalog. $objNT.InvokeMember("Init", "InvokeMethod", $Null, $Translate, (3, $Null)) # Retrieve NetBIOS name of the current domain. $objNT.InvokeMember("Set", "InvokeMethod", $Null, $Translate, (1, "$Base")) [string]$Script:NetBIOSDomain = $objNT.InvokeMember("Get", "InvokeMethod", $Null, $Translate, 3) } Catch { Out-Null } # region Runspace Creation Write-Verbose ("Creating runspace pool and session states") $sessionstate = [system.management.automation.runspaces.initialsessionstate]::CreateDefault() $runspacepool = [runspacefactory]::CreateRunspacePool(1, $Throttle, $sessionstate, $Host) $runspacepool.Open() Write-Verbose ("Creating empty collection to hold runspace jobs") $Script:runspaces = New-Object System.Collections.ArrayList # endregion Runspace Creation } Process { ForEach ($Computer in $Computername) { # Create the powershell instance and supply the scriptblock with the other parameters $powershell = [powershell]::Create().AddScript($scriptBlock).AddArgument($computer).AddArgument($Group).AddArgument($Depth).AddArgument($NetBIOSDomain).AddArgument($ObjNT).AddArgument($Translate) # Add the runspace into the powershell instance $powershell.RunspacePool = $runspacepool # Create a temporary collection for each runspace $temp = "" | Select-Object PowerShell, Runspace, Computer $Temp.Computer = $Computer $temp.PowerShell = $powershell # Save the handle output when calling BeginInvoke() that will be used later to end the runspace $temp.Runspace = $powershell.BeginInvoke() Write-Verbose ("Adding {0} collection" -f $temp.Computer) $runspaces.Add($temp) | Out-Null Write-Verbose ("Checking status of runspace jobs") Get-RunspaceData @runspacehash } } End { Write-Verbose ("Finish processing the remaining runspace jobs: {0}" -f (@(($runspaces | Where-Object { $_.Runspace -ne $Null }).Count))) $runspacehash.Wait = $true Get-RunspaceData @runspacehash # region Cleanup Runspace Write-Verbose ("Closing the runspace pool") $runspacepool.close() $runspacepool.Dispose() # endregion Cleanup Runspace } } function ConvertTo-HashToYN { <# .SYNOPSIS Used by As Built Report to convert array content true or false automatically to Yes or No. .DESCRIPTION .NOTES Version: 0.1.0 Author: Jonathan Colon .EXAMPLE .LINK #> [CmdletBinding()] [OutputType([Hashtable])] Param ( [Parameter (Position = 0, Mandatory)] [AllowEmptyString()] [Hashtable] $TEXT ) $result = [ordered] @{} foreach ($i in $inObj.GetEnumerator()) { try { $result.add($i.Key, (ConvertTo-TextYN $i.Value)) } catch { $result.add($i.Key, ($i.Value)) } } if ($result) { return $result } else { return $TEXT } } # end |