Public/Get-EnvironmentVariable.ps1

function Get-EnvironmentVariable {
    <#
    .SYNOPSIS
    Retrieves the value of an environment variable.
 
    .DESCRIPTION
    The Get-EnvironmentVariable function retrieves the value of the specified environment variable or displays all environment variables.
 
    .PARAMETER Name
    The name of the environment variable to retrieve.
 
    .PARAMETER Pattern
    A regex pattern to match environment variable names against.
 
    .PARAMETER Target
    The target (Process, Machine, User) to pull environment variables from. The default is process. Multiple targets may be specified.
 
    .PARAMETER All
    Optionally get all environment variables from all targets. Process ID and process name will be included for process environment variables.
 
    .EXAMPLE
    Get-EnvironmentVariable -Name 'UserName'
    Retrieves the value of the "UserName" environment variable from the process target.
 
    .EXAMPLE
    Get-EnvironmentVariable -Name 'Path' -Target 'Machine'
    Retrieves the value of the PATH environment variable from the machine target.
 
    .EXAMPLE
    Get-EnvironmentVariable -Pattern '^u'
    Get environment variables with names that begin with the letter "u" in any target.
 
    .EXAMPLE
    Get-EnvironmentVariable -Pattern 'git' | Format-Table Name,Target,PID,ProcessName,Value
 
    Get all process environment variables that match the pattern "git" and return the results as a table.
 
    .EXAMPLE
    Get-EnvironmentVariable -Pattern 'path' -Target Machine,Process,User | Format-Table Name,Target,PID,ProcessName,Value
 
    Return all environment variables that match the pattern "path" from all targets and format the results as a table.
 
    .NOTES
    Author: Sam Erde
    Version: 0.1.0
    Modified: 2024/10/8
 
    To Do: Return environment variables if -Target is used without either -Name or -Pattern.
 
 
    About Environment Variables:
 
    Variable names are case-sensitive on Linux and macOS, but not on Windows.
 
    Why is 'Target' used by .NET instead of the familiar 'Scope' parameter name? @IISResetMe (Mathias R. Jessen) explains:
    "Scope" would imply some sort of integrated hierarchy of env variables - that's not really the case.
    Target=Process translates to kernel32!GetEnvironmentVariable (which then in turn reads the PEB from
    the calling process), whereas Target={User,Machine} causes a registry lookup against environment
    data in either HKCU or HKLM.
 
    The relevant sources for the User and Machine targets are in the registry at:
    - HKEY_CURRENT_USER\Environment
    - HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
 
    See more at <https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables>.
 
    #>

    [Alias('gev')]
    [CmdletBinding(HelpUri = 'https://day3bits.com/PSPreworkout/Get-EnvironmentVariable')]
    [OutputType('PSObject')]
    param (
        # The name of the environment variable to retrieve. If not specified, all environment variables are returned.
        [Parameter(Position = 0)]
        #[Parameter(Position = 0, ParameterSetName = 'LookupByName')]
        [string]$Name,

        # A regex pattern to search variable names by
        [Parameter()]
        #[Parameter(Position = 0, ParameterSetName = 'LookupByRegexPattern')]
        [string]
        $Pattern,

        # The target of the environment variable to retrieve: Process (default), User, or Machine.
        [Parameter()]
        [System.EnvironmentVariableTarget[]]
        $Target,

        # Switch to show environment variables in all target scopes.
        [Parameter()]
        [switch]
        $All
    )

    begin {
        # Initialize the collection of environment variables that will be returned to the pipeline at the end.
        [System.Collections.Generic.List[PSObject]]$EnvironmentVariables = @()

        # Get environment variables from all targets if no parameters are specified
        if (
            'Name' -notin $PSBoundParameters.Keys -and
            'Pattern' -notin $PSBoundParameters.Keys -and
            'Target' -notin $PSBoundParameters.Keys
        ) {
            $All = $true
            $Target = @([System.EnvironmentVariableTarget]::Process, [System.EnvironmentVariableTarget]::User, [System.EnvironmentVariableTarget]::Machine)
        }

        # If a Name or a Pattern is specified with no Target, get the name/pattern matches from all targets.
        if ( ( $PSBoundParameters.ContainsKey('Name') -or $PSBoundParameters.ContainsKey('Pattern') ) -and
            -not $PSBoundParameters.ContainsKey('Target')
        ) {
            $Target = @('Process', 'User', 'Machine')
        }

        # Handle -All when used with or without a name, pattern, or target parameter
        if ( $PSBoundParameters.ContainsKey('All') ) {

            # Get all matches for a specific name or pattern
            if ( $PSBoundParameters.ContainsKey('Name') -or $PSBoundParameters.ContainsKey('Pattern') ) {
                # Don't need to change anything (yet?)
            }

            # Get all variables from a specific target if a name or pattern are not specified
            if ( $PSBoundParameters.ContainsKey('Target') -and
                -not ( $PSBoundParameters.ContainsKey('Name') -or $PSBoundParameters.ContainsKey('Pattern') )
            ) {
                # Don't change the target. Don't need to do anything here?
            }

        }
        Write-Debug -Message "Parameters: $($PSBoundParameters.GetEnumerator())`n`n`t Name: $Name`n`tPattern: $Pattern`n`t Target: $Target`n`t All: $All" -ErrorAction SilentlyContinue
    } # end begin block

    process {
        foreach ($thisTarget in $Target) {

            if ( $PSBoundParameters.ContainsKey('Name') -and -not $PSBoundParameters.ContainsKey('Pattern') ) {
                # If a variable name was specified, get that environment variable.
                # Temporarily using this -and -not condition because I couldn't get exclusive Name/Pattern parameter sets to work.
                $ThisEnvironmentVariable = [ordered]@{
                    Name        = $Name
                    Value       = [Environment]::GetEnvironmentVariable($Name, $thisTarget)
                    Target      = $thisTarget[0]
                    PID         = if ($thisTarget -eq 'Process') { $PID } else { $null }
                    ProcessName = if ($thisTarget -eq 'Process') { (Get-Process -Id $PID).Name } else { $null }
                }
                $item = New-Object -TypeName psobject -Property $ThisEnvironmentVariable
                $EnvironmentVariables.Add($item)

            } elseif ( $PSBoundParameters.ContainsKey('Pattern') ) {
                if ($Name) {
                    Write-Verbose -Message 'A value for the Name parameter was specified, but it is being ignored because a Pattern was also provided.'
                }
                # If a pattern is specified, get environment variables with names that match the pattern.
                $Result = [Environment]::GetEnvironmentVariables($thisTarget).GetEnumerator() | Where-Object { $_.Key -match $pattern }
                foreach ($PatternResult in $Result) {
                    $ThisEnvironmentVariable = [ordered]@{
                        Name        = $PatternResult.Name
                        Value       = $PatternResult.Value
                        Target      = $thisTarget[0]
                        PID         = if ($thisTarget -eq 'Process') { $PID } else { $null }
                        ProcessName = if ($thisTarget -eq 'Process') { (Get-Process -Id $PID).Name } else { $null }
                    }
                    $item = New-Object -TypeName psobject -Property $ThisEnvironmentVariable
                    $EnvironmentVariables.Add($item)
                }

            } elseif ( $PSBoundParameters.ContainsKey('Target') -and
                -not ( $PSBoundParameters.ContainsKey('Name') -or $PSBoundParameters.ContainsKey('Pattern') )
            ) {
                foreach ( $ev in ([Environment]::GetEnvironmentVariables([System.EnvironmentVariableTarget]::$thisTarget)).GetEnumerator() ) {
                    $ThisEnvironmentVariable = [ordered]@{
                        Name        = $ev.Name
                        Value       = $ev.Value
                        Target      = $thisTarget
                        PID         = if ($thisTarget -eq 'Process') { $PID } else { $null }
                        ProcessName = if ($thisTarget -eq 'Process') { (Get-Process -Id $PID).Name } else { $null }
                    }
                    $item = New-Object -TypeName psobject -Property $ThisEnvironmentVariable
                    $EnvironmentVariables.Add($item)
                }

            } else {
                # Get all environment variables.
                foreach ( $ev in ([Environment]::GetEnvironmentVariables([System.EnvironmentVariableTarget]::$thisTarget).GetEnumerator()) ) {
                    $ThisEnvironmentVariable = [ordered]@{
                        Name        = $ev.Name
                        Value       = $ev.Value
                        Target      = $thisTarget[0]
                        PID         = if ($thisTarget -eq 'Process') { $PID } else { $null }
                        ProcessName = if ($thisTarget -eq 'Process') { (Get-Process -Id $PID).Name } else { $null }
                    }
                    $item = New-Object -TypeName psobject -Property $ThisEnvironmentVariable
                    $EnvironmentVariables.Add($item)
                }
            }
        } # end foreach target
    } # end process block

    end {
        $EnvironmentVariables
    } # end end block
} # end function