EnvironmentVariableItems.psm1

<#
.SYNOPSIS
Adds an environment variable item for given Name, Value, Scope (default; 'Process') and Separator (';') and optional Index.
 
.EXAMPLE
 
Add 'C:\tmp' to $env:Path user environment variable
 
PS> Add-EnvironmentVariableItem -Name path -Value C:\tmp -Scope User -WhatIf
What if:
    Current Value:
        C:\Users\michaelf\AppData\Local\Microsoft\WindowsApps;C:\Users\michaelf\AppData\Local\Programs\Microsoft VS Code\bin
    New value:
        C:\Users\michaelf\AppData\Local\Microsoft\WindowsApps;C:\Users\michaelf\AppData\Local\Programs\Microsoft VS Code\bin;C:\tmp
 
.EXAMPLE
 
Insert 'C:\tmp' as first item in $env:Path user environment variable
 
PS> Add-EnvironmentVariableItem -Name path -Value C:\tmp -Scope User -Index 0 -WhatIf
What if:
    Current Value:
        C:\Users\michaelf\AppData\Local\Microsoft\WindowsApps;C:\Users\michaelf\AppData\Local\Programs\Microsoft VS Code\bin
    New value:
        C:\tmp;C:\Users\michaelf\AppData\Local\Microsoft\WindowsApps;C:\Users\michaelf\AppData\Local\Programs\Microsoft VS Code\bin
 
.EXAMPLE
 
Insert 'C:\tmp' as second last item in $env:Path process environment variable
 
PS> Add-EnvironmentVariableItem -Name path -Value C:\tmp -Scope Process -Index -2 -WhatIf
What if:
    Current Value:
        C:\Program Files\PowerShell\7;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;C:\Program Files (x86)\ATI Technologies\ATI.ACE\Core-Static;C:\ProgramData\chocolatey\bin;C:\Program Files\PowerShell\7\;C:\Program Files\Git\cmd;C:\Program Files\Microsoft VS Code\bin;C:\Users\michaelf\AppData\Local\Microsoft\WindowsApps
    New value:
        C:\Program Files\PowerShell\7;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;C:\Program Files (x86)\ATI Technologies\ATI.ACE\Core-Static;C:\ProgramData\chocolatey\bin;C:\Program Files\PowerShell\7\;C:\Program Files\Git\cmd;C:\Program Files\Microsoft VS Code\bin;C:\tmp;C:\Users\michaelf\AppData\Local\Microsoft\WindowsApps
 
.EXAMPLE
 
PS > Add 'cake' as second item of $env:foo user environment variable
 
PS> aevi foo cake -sc user -in 1 -se '#' -wh
What if:
    Current Value:
        foo#bar#cup
    New value:
        foo#cake#bar#cup
#>

function Add-EnvironmentVariableItem {
    [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='High')]
    param (
        [Parameter(
            Mandatory,
            Position = 0
        )]
            [ValidatePattern("[^=]+")]
            [String] $Name,        
        [Parameter(
            Mandatory,
            Position = 1
        )] 
            [String] $Value,        
        [Parameter()]
            [System.EnvironmentVariableTarget] $Scope = [System.EnvironmentVariableTarget]::Process,
        [Parameter()]
            [String] $Separator = ';',
        [Parameter()] 
            [int] $Index
    )    
    process {

        $evis = Get-EnvironmentVariableItems -Name $Name -Scope $Scope -Separator $Separator

        if ($PSBoundParameters.ContainsKey('Index')) {
            $result = $evis.AddItem($Value, $Index)
        } else {
            $result = $evis.AddItem($Value)
        }

        if ($result -eq $True) {
                $s = GetWhatIf
            if ($PSCmdlet.ShouldProcess($s, '', '')){
                #$evis.UpdateEnvironmentVariable()
                $evis.SetEnvironmentVariable($evis.Name, $evis.ToString(), $evis.Scope)
                $evis
            }
        } else { 
            return
        }
    }
}


<#
.SYNOPSIS
Gets an EnvironmentVariableItems object for a given Name, Scope (default; 'Process') and Separator (';').
 
.EXAMPLE
 
Get current process $env:Path EnvironmentVariableItems object
 
PS> Get-EnvironmentVariableItems -Name Path
 
Name : Path
Scope : Process
Separator : ;
Value : C:\Program Files\PowerShell\7;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.
            0\;C:\WINDOWS\System32\OpenSSH\;C:\Program Files (x86)\ATI
            Technologies\ATI.ACE\Core-Static;C:\ProgramData\chocolatey\bin;C:\Program Files\PowerShell\7\;C:\Program
            Files\Git\cmd;C:\Program Files\Microsoft VS Code\bin;C:\Users\michaelf\AppData\Local\Microsoft\WindowsApps
Items : {C:\Program Files\PowerShell\7, C:\WINDOWS\system32, C:\WINDOWS, C:\WINDOWS\System32\Wbem…}
 
.EXAMPLE
 
Get user $env:Path EnvironmentVariableItems object
 
PS> Get-EnvironmentVariableItems -Name Path -Scope User
 
Name : Path
Scope : User
Separator : ;
Value : C:\tmp;C:\Users\michaelf\AppData\Local\Microsoft\WindowsApps
Items : {C:\tmp, C:\Users\michaelf\AppData\Local\Microsoft\WindowsApps}
 
.EXAMPLE
 
Get user $env:foo EnvironmentVariableItems object
 
PS> gevis foo -sc user -se '#'
 
Name : foo
Scope : User
Separator : #
Value : foo#cake#bar#cup
Items : {foo, cake, bar, cup}
 
#>

function Get-EnvironmentVariableItems {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
            [String] $Name,
        [Parameter()]
            #[System.EnvironmentVariableTarget] $Scope = [System.EnvironmentVariableTarget]::Process,
            [System.EnvironmentVariableTarget] $Scope,
        [Parameter()]
            [String] $Separator = ';'
    )    
    process {

        # preserve user provided Scope value before setting default
        $Script:ScopePreDefault = $Scope
        if ($null -eq $Scope) {
            $Scope = [System.EnvironmentVariableTarget]::Process
        } 

        $evis = New-EnvironmentVariableItems-Object $Name $Scope $Separator
        $evis.Value = $evis.GetEnvironmentVariable($Name, $Scope)
        $evis.SetItems($Name, $Scope, $Separator)

        $evis
    }
}

function GetWhatIf() {
    @"
 
    Current Value:
        $($evis.Value)
    New value:
        $($evis.ToString())
 
"@

}

function New-EnvironmentVariableItems-Object {
    param (
        [String] $Name,
        [System.EnvironmentVariableTarget] $Scope = [System.EnvironmentVariableTarget]::Process,
        [String] $Separator = ';'
    )
    process {
        $obj = [PSCustomObject]@{}

        $obj | Add-Member ScriptMethod AddItem { 
            param (
                [String] $Value,        
                [int] $Index
            )    
            process {
                if ($PSBoundParameters.ContainsKey('Index')) {
                    # Add 1 to items count reflecting length after addition
                    if (($ind = $this.GetPositiveIndex($Index, $this.Items.count + 1)) -is [int]) {
                        $this.Items.insert($ind, $Value)
                        return $True
                    }                    
                } else {
                    $this.Items.add($Value)
                    return $True
                }
            }
         } -Force
        
         $obj | Add-Member ScriptMethod GetEnvironmentVariable { 
            param (
                [String] $Name,        
                [System.EnvironmentVariableTarget] $Scope = [System.EnvironmentVariableTarget]::Process
            )
            process {
                [Environment]::GetEnvironmentVariable($Name, $Scope)
            }
         }

         $obj | Add-Member ScriptMethod GetItems { 
            param (
                [String] $Name,
                [System.EnvironmentVariableTarget] $Scope = [System.EnvironmentVariableTarget]::Process,
                [String] $Separator = ';'
            )
            process {
                $value = $this.GetEnvironmentVariable($Name, $Scope)

                if ($null -ne $value) {$value = $value.Trim($Separator)}
        
                $items = @()
                if ($null -ne $value) {        
                    $items = $value -split $Separator
                }

                $items
            }
         }
            
         # check index is within range and return (as positive value if required)
        $obj | Add-Member ScriptMethod GetPositiveIndex { 
            param (
                [int] $Index,
                [int] $ItemsCount
            )

            if ($Index -lt $ItemsCount -and $(-($Index) -le $ItemsCount)) {
                if ($Index -lt 0) {
                    $ItemsCount + $Index
                } else {
                    $Index
                }
            } else {
                Write-Host
                Write-Host  -ForegroundColor Red "Index $Index is out of range"
                Write-Host
            }
            
        } -Force

        $obj | Add-Member ScriptMethod RemoveItemByIndex { 
            param (
                [int] $Index
            )    
            process {
                if (($ind = $this.GetPositiveIndex($Index, $this.Items.count)) -is [int]) {
                    $this.Items.RemoveAt($ind)
                } else {
                    return $False
                }                    
            }
         } -Force
        
         $obj | Add-Member ScriptMethod RemoveItemByValue { 
            param (
                [String] $Value
            )    
            process {
                if (($this.Items.IndexOf($Value)) -ge 0) {
                    $this.Items.Remove($Value)
                } else {
                    Write-Host
                    Write-Host  -ForegroundColor Red "Value $Value not found"
                    Write-Host
                    return $False
                }                    
            }
         } -Force

         $obj | Add-Member ScriptMethod SetItems { 
            param (
                [String] $Name = $this.Name,
                [System.EnvironmentVariableTarget] $Scope = $this.Scope,
                [String] $Separator = $this.Separator
            )
            process {
                $this.Items = [System.Collections.ArrayList] @($this.GetItems($Name, $Scope, $Separator))
            }
         }

        $obj | Add-Member ScriptMethod ShowIndex { 

            process {
                Write-Host 
                if ($null -eq $Script:ScopePreDefault) {
                    $this.ShowIndexForScope([System.EnvironmentVariableTarget]::Machine)
                    $this.ShowIndexForScope([System.EnvironmentVariableTarget]::User)
                    $this.ShowIndexForScope([System.EnvironmentVariableTarget]::Process)
                } else {
                    $this.ShowIndexForScope($this.Scope)
                }
                Write-Host
                Write-Host
            }
         } -Force

         $obj | Add-Member ScriptMethod ShowIndexForScope { 
            param (
                [System.EnvironmentVariableTarget] $Scope
            )
            process {
                Write-Host $Scope
                $items = @($this.GetItems($this.Name, $Scope, $this.Separator))
                for ($i = 0; $i -lt $items.count; $i++) {
                    Write-Host -ForegroundColor Blue "${i}: $($items[$i].ToString())"
                }
                Write-Host
            }
         } -Force



         $obj | Add-Member ScriptMethod SetEnvironmentVariable { 
            param (
                [String] $Name,        
                [String] $Value,        
                [System.EnvironmentVariableTarget] $Scope = [System.EnvironmentVariableTarget]::Process
            )
            process {
                [Environment]::SetEnvironmentVariable($Name, $Value, $Scope)
            }
         }

         $obj | Add-Member ScriptMethod ToString { 
            $s = ''
            for ($i = 0; $i -lt $this.Items.count; $i++) {
                if ($i) { $s += $this.Separator}
                $s += $this.Items[$i]
            }
            $s
         } -Force

         $obj | Add-Member -NotePropertyName Name -NotePropertyValue $Name
         $obj | Add-Member -NotePropertyName Scope -NotePropertyValue $Scope
         $obj | Add-Member -NotePropertyName Separator -NotePropertyValue $Separator

         $obj | Add-Member -NotePropertyName Value -NotePropertyValue $Value

     $items = [System.Collections.ArrayList]@()
        $obj | Add-Member -NotePropertyName Items -NotePropertyValue $items 
 
        return $obj
    }
}

<#
.SYNOPSIS
Removes an environment variable item for given Name, Value and Scope (default; 'Process') and Separator (';') and optional Index.
 
.EXAMPLE
 
Remove 'C:\tmp' from $env:Path user environment variable
 
PS> Remove-EnvironmentVariableItem -Name path -Value 'C:\tmp' -Scope User -WhatIf
 
What if:
    Current Value:
        C:\Users\michaelf\AppData\Local\Microsoft\WindowsApps;C:\tmp;C:\Users\michaelf\AppData\Local\Programs\Microsoft VS Code\bin
    New value:
        C:\Users\michaelf\AppData\Local\Microsoft\WindowsApps;C:\Users\michaelf\AppData\Local\Programs\Microsoft VS Code\bin
 
.EXAMPLE
 
Remove last item from $env:Path user environment variable
 
PS> Remove-EnvironmentVariableItem -Name path -Scope User -Index -1 -WhatIf
 
What if:
 
    Current Value:
        C:\Users\michaelf\AppData\Local\Microsoft\WindowsApps;C:\Users\michaelf\AppData\Local\Programs\Microsoft VS Code\bin
    New value:
        C:\Users\michaelf\AppData\Local\Microsoft\WindowsApps
 
.EXAMPLE
 
Remove second item from $env:foo user environment variable
 
PS> sevis foo
 
Machine
0: mat#mop
 
User
0: foo#cake#bar#cup
 
Process
0: foo#cake#bar#cup
 
PS> sevis foo -sc user -se '#'
 
User
0: foo
1: cake
2: bar
3: cup
 
PS> revi foo -in 1 -sc user -se '#'
 
Confirm
Are you sure you want to perform this action?
 
    Current Value:
        foo#cake#bar#cup
    New value:
        foo#bar#cup
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): y
 
Name : foo
Scope : User
Separator : #
Value : foo#cake#bar#cup
Items : {foo, bar, cup}
 
PS> sevis foo
 
Machine
0: mat#mop
 
User
0: foo#bar#cup
 
Process
0: foo#cake#bar#cup
 
PS> $env:foo
foo#cake#bar#cup
 
PS> [Environment]::GetEnvironmentVariable('foo', 'User')
foo#bar#cup
#>

function Remove-EnvironmentVariableItem {
    [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='High')]
    param (
        [Parameter(
            Mandatory,
            Position = 0
        )]
            [ValidatePattern("[^=]+")]
            [String] $Name,        
        [Parameter(
            Mandatory,
            ParameterSetName = 'ByValue',
            Position = 1 
        )] 
            [String] $Value,        
        [Parameter(
            ParameterSetName = 'ByIndex',
            Position = 1, 
            Mandatory
        )] [int] $Index,
        [Parameter()]
            [System.EnvironmentVariableTarget] $Scope = [System.EnvironmentVariableTarget]::Process,
        [Parameter()] 
            [String] $Separator = ";"

    ) 
    process {

        $evis = Get-EnvironmentVariableItems -Name $Name -Scope $Scope -Separator $Separator

        if ($PSCmdlet.ParameterSetName -eq 'ByIndex') {
            $result = $evis.RemoveItemByIndex($Index) -ne $False
        } elseif ($PSCmdlet.ParameterSetName -eq 'ByValue') {
            $result = $evis.RemoveItemByValue($Value) -ne $False
        }

        if ($result -ne $False) {
            $s = GetWhatIf
            if ($PSCmdlet.ShouldProcess($s, '', '')){
                #$evis.UpdateEnvironmentVariable()
                $evis.SetEnvironmentVariable($evis.Name, $evis.ToString(), $evis.Scope)
                $evis
            }
        } else { 
            return
        }


    }
}

<#
.SYNOPSIS
Show indexed list of environment variable items for given Name, Scope and Separator (default: ';'). Omitting Scope parameter shows list for all, ie., Machine, User and Process.
 
.EXAMPLE
 
Show $env:PSModulePath items
 
PS> Show-EnvironmentVariableItems PSModulePath
 
Machine
0: C:\Program Files\WindowsPowerShell\Modules
1: C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules
2: N:\lib\pow\mod
 
User
0: H:\lib\pow\mod
 
Process
0: C:\Users\michaelf\Documents\PowerShell\Modules
1: C:\Program Files\PowerShell\Modules
2: c:\program files\powershell\7\Modules
3: H:\lib\pow\mod
4: C:\Program Files\WindowsPowerShell\Modules
5: C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules
6: N:\lib\pow\mod
 
.EXAMPLE
 
Show PSModulePath system variable items
 
PS> Show-EnvironmentVariableItems PSModulePath -Scope Machine
 
Machine
0: C:\Program Files\WindowsPowerShell\Modules
1: C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules
2: N:\lib\pow\mod
 
.EXAMPLE
 
Show system, user and process items for $env:TMP environment variable
 
PS> Show-EnvironmentVariableItems TMP
 
Machine
0: C:\WINDOWS\TEMP
 
User
0: C:\Users\michaelf\AppData\Local\Temp
 
Process
0: C:\Users\michaelf\AppData\Local\Temp
#>

function Show-EnvironmentVariableItems {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
            [String] $Name,
        [Parameter()]
            #[System.EnvironmentVariableTarget] $Scope = [System.EnvironmentVariableTarget]::Process,
            [System.EnvironmentVariableTarget] $Scope,
        [Parameter()]
            [String] $Separator = ';'
    )    
    process {

        if ($null -eq $Scope) {
            $evis = Get-EnvironmentVariableItems -Name $Name -Separator $Separator
        } else {
            $evis = Get-EnvironmentVariableItems -Name $Name -Scope $Scope -Separator $Separator
        }

        'sevis'
        $evis.ShowIndex()
    }
}

New-Alias -Name aevi -Value Add-EnvironmentVariableItem
New-Alias -Name gevis -Value Get-EnvironmentVariableItems
New-Alias -Name revi -Value Remove-EnvironmentVariableItem
New-Alias -Name sevis -Value Show-EnvironmentVariableItems

Export-ModuleMember -Alias * -Function *