EnvironmentVariableItems.psm1

#Region '.\_PrefixCode.ps1' -1

# Code in here will be prepended to top of the psm1-file.
#EndRegion '.\_PrefixCode.ps1' 2
#Region '.\Classes\EnvironmentVariableItems.ps1' -1

class EnvironmentVariableItems {

    ### Class variables

    [ValidatePattern("^[^=]+$")] [String] $Name;
    [System.EnvironmentVariableTarget] $Scope;
    [String] $Separator;
    [String] $Value;
    [System.Collections.ArrayList] $Items;

    ### Hidden variables

    hidden $defaultSeparator = ';'
    hidden $defaultScope = [System.EnvironmentVariableTarget]::Process

    ### Constructors

    # Name
    EnvironmentVariableItems(
        [String] $Name
    ) {
        $this.Init($Name, $this.defaultScope, $this.defaultSeparator)
    } 

    # Name, Scope
    EnvironmentVariableItems(
        [String] $Name, 
        [System.EnvironmentVariableTarget] $Scope
    ) {
        $this.Init($Name, $Scope, $this.defaultSeparator)
    } 

    # Name, Separator
    EnvironmentVariableItems(
        [String] $Name, 
        [String] $Separator
    ) {
        $this.Init($Name, $this.defaultScope, $Separator)
    } 

    # Name, Scope, Separator
    EnvironmentVariableItems(
            [String] $Name, 
            [System.EnvironmentVariableTarget] $Scope,
            [String] $Separator
    ) {
        $this.Init($Name, $Scope, $Separator)
    }

    ### Methods
    
    ### Getter & setter methods

    # Name
    [String] GetName() {
        return $this.Name
    }

    [void] SetName(
        [String] $Name
    ) {
        $this.Name = $Name
    }

    # Scope
    [String] GetScope() {
        return $this.Scope
    }

    [void] SetScope(
        [System.EnvironmentVariableTarget] $Scope
    ) {
        $this.Scope = $Scope
    }

    # Separator
    [String] GetSeparator() {
        return $this.Separator
    }

    [void] SetSeparator(
        [String] $Separator
    ) {
        $this.Separator = $Separator
    }

    # Value
    [String] GetValue() {
        return $this.Value
    }

    [String] GetValue(
        [String] $Name,
        [System.EnvironmentVariableTarget] $Scope
    ) {
        $this.SetValue($Name, $Scope)
        return $this.Value
    }

    [void] SetValue(
        [String] $Value
    ) {
        $this.Value = $Value
    }

    [void] SetValue(
        [String] $Name,
        [System.EnvironmentVariableTarget] $Scope
    ) {
        $this.Value = $this.GetEnvironmentVariable($Name, $Scope)
    }

    # Items
    [System.Collections.ArrayList] GetItems() {
        return $this.Items
    }

    [void] SetItems(
        [String] $Name,
        [System.EnvironmentVariableTarget] $Scope,
        [String] $Separator
    ) {
        # tidy (trim) local copy of value
        $val = $this.GetValue($Name, $Scope)
        if ($null -ne $val) {$val = $val.Trim($Separator)}

        $this.Items = [System.Collections.ArrayList] @()
        if ($null -ne $val) {        
            $this.Items = $val -split $Separator
        }
    }

    ### Hidden methods

    hidden [bool] AddItem(
        [String] $Item
    ) {
        $this.GetItems().add($Item)
        return $True
    }

    hidden [bool] AddItem(
        [String] $Item,        
        [int] $Index
    ) {
        # Add 1 to items count reflecting length after addition
        $items_ = $this.GetItems()
        if (($ind = $this.GetPositiveIndex($Index, $items_.count + 1)) -is [int]) {
            $items_.insert($ind, $Item)
            return $True
        }                    
        return $False
    }

    hidden [String] GetEnvironmentVariable($Name, $Scope) {
        return [Environment]::GetEnvironmentVariable($Name, $Scope)
    }

    hidden [System.Collections.ArrayList] GetItemsForScope($Scope) {
        if ($Scope -eq $this.GetScope()) {
            return $this.GetItems()
        } else {
            return [EnvironmentVariableItems]::new($this.Name, $Scope, $this.Separator).GetItems()
        }
    }


    # check index is within range and return (as positive value if required)
    hidden [int] GetPositiveIndex(
        [int] $Index,
        [int] $ItemsCount
    ) {
        if ($Index -lt $ItemsCount -and $(-($Index) -le $ItemsCount)) {
            if ($Index -lt 0) {
                return $ItemsCount + $Index
            } else {
                return $Index
            }
        } else {
            Write-Host
            Write-Host  -ForegroundColor Red "Index $Index is out of range"
            Write-Host
        }
        return $False
    }

    hidden [void] Init(
            [String] $Name, 
            [System.EnvironmentVariableTarget] $Scope,
            [String] $Separator
    ) {
        #$this.Name = $Name
        $this.SetName($Name)
        $this.SetScope($Scope)
        $this.SetSeparator($Separator)
        $this.SetValue($this.Name, $this.Scope)
        $this.SetItems($this.Name, $this.Scope, $this.Separator)
    }

    hidden [bool] RemoveItemByIndex(
            [int] $Index
    ) {
        $items_ = $this.GetItems()
        if (($ind = $this.GetPositiveIndex($Index, $items_.count)) -is [int]) {
            $items_.RemoveAt($ind)
            return $True
        }                    
        return $False
    }
    
    hidden [bool] RemoveItemByItem(
            [String] $Item
    ) {
        $items_ = $this.GetItems()
        if (($items_.IndexOf($Item)) -ge 0) {
            $items_.Remove($Item)
            return $True
        }
        Write-Host
        Write-Host  -ForegroundColor Red "Item $Item not found"
        Write-Host
        return $False
    }

    hidden [void] SetEnvironmentVariable(
            [String] $Name,        
            [String] $Value,        
            [System.EnvironmentVariableTarget] $Scope = [System.EnvironmentVariableTarget]::Process
    ) {
        [Environment]::SetEnvironmentVariable($Name, $Value, $Scope)
        $this.SetValue($Value)
    }


    ### Public methods

    [void] ShowIndex(
            [System.EnvironmentVariableTarget] $Scope
    ) {
        $items_ = $this.GetItemsForScope($Scope)
        $this.ShowIndex($Scope, $items_)
    }

    [void] ShowIndex(
        [System.EnvironmentVariableTarget] $Scope,
        [System.Collections.ArrayList] $items_
    ) {
        Write-Host $Scope
        for ($i = 0; $i -lt $items_.count; $i++) {
            Write-Host -ForegroundColor Blue "${i}: $($items_[$i].ToString())"
        }
        Write-Host
    }

    [void] ShowIndexes() {
        Write-Host 
        $this.ShowIndex([System.EnvironmentVariableTarget]::Machine)
        $this.ShowIndex([System.EnvironmentVariableTarget]::User)
        $this.ShowIndex([System.EnvironmentVariableTarget]::Process)
        Write-Host
        Write-Host
    }

    [String] ToString() { 
        $s = ''
        $items_ = $this.GetItems()
        for ($i = 0; $i -lt $items_.count; $i++) {
            if ($i) { $s += $this.Separator}
            $s += $items_[$i]
        }
        return $s
    }
}
#EndRegion '.\Classes\EnvironmentVariableItems.ps1' 274
#Region '.\Private\ConfirmAction.ps1' -1

function ConfirmAction {
    param (
        [String] $Message,
        [switch] $NoConfirmationRequired
    )
    Write-Host $Message
    if ($NoConfirmationRequired) { return $True }
    $choices = [System.Management.Automation.Host.ChoiceDescription[]] @(
        [System.Management.Automation.Host.ChoiceDescription]::new('&Yes'),
        [System.Management.Automation.Host.ChoiceDescription]::new('&No')
    )
    $Host.UI.PromptForChoice('Confirm', 'Are you sure you want to perform this action?', $choices, 0) -eq 0
}
#EndRegion '.\Private\ConfirmAction.ps1' 14
#Region '.\Private\GetWhatIf.ps1' -1

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

}

#EndRegion '.\Private\GetWhatIf.ps1' 12
#Region '.\Public\Add-EnvironmentVariableItem.ps1' -1

<#
.SYNOPSIS
Adds an environment variable item for given Name, Item, Scope (default; 'Process') and Separator (';') and optional Index.
 
.PARAMETER Name
Environment variable name
 
.PARAMETER Item
An item of an environment variable (eg., 'C:\foo' in $env:Path of 'C:\foo;C:\bar')
 
.PARAMETER Scope
Environment variable scope (.NET enum System.EnvironmentVariableTarget)
 
.PARAMETER Separator
Environment variable item separator (eg., ';' in $env:Path of 'C:\foo;C:\bar')
 
.PARAMETER Index
Item index position (negative values work backwards through collection,-1 being the last item)
 
.EXAMPLE
 
Add 'C:\tmp' to $env:Path user environment variable
 
PS> Add-EnvironmentVariableItem -Name path -Item 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 -Item 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 -Item 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
 
.INPUTS
 
.OUTPUTS
EnvironmentVariableItems PSCustomObject
 
#>

function Add-EnvironmentVariableItem {
    [CmdletBinding()]
    [Alias('aevi')]
    param (
        [Parameter(
            Mandatory,
            Position = 0
        )]
        [ValidatePattern("^[^=]+$")]
            [String] $Name,
        [Parameter(
            Mandatory,
            Position = 1
        )]
            [String] $Item,
        [Parameter()]
            [System.EnvironmentVariableTarget] $Scope = [System.EnvironmentVariableTarget]::Process,
        [Parameter()]
            [String] $Separator = ';',
        [Parameter()]
            [int] $Index,
        [Parameter()]
            [switch] $NoConfirmationRequired
    )
    process {
        $evis = [EnvironmentVariableItems]::new($Name, $Scope, $Separator)

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

        if ($result -eq $True) {
            if (ConfirmAction -Message (GetWhatIf) -NoConfirmationRequired:$NoConfirmationRequired) {
                $evis.SetEnvironmentVariable($evis.Name, $evis.ToString(), $evis.Scope)
                $evis
            }
        } else {
            return
        }
    }
}
#EndRegion '.\Public\Add-EnvironmentVariableItem.ps1' 113
#Region '.\Public\Get-EnvironmentVariableItems.ps1' -1


<#
.SYNOPSIS
_Gets an EnvironmentVariableItems PSCustomObject for a given Name, Scope (default; 'Process') and Separator (';').
 
.PARAMETER Name
Environment variable name
 
.PARAMETER Scope
Environment variable scope (.NET enum System.EnvironmentVariableTarget)
 
.PARAMETER Separator
Environment variable item separator (eg., ';' in $env:Path of 'C:\foo;C:\bar')
 
.EXAMPLE
 
Get current process $env:Path EnvironmentVariableItems PSCustomObject
 
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 PSCustomObject
 
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 PSCustomObject
 
PS> gevis foo -sc user -se '#'
 
Name : foo
Scope : User
Separator : #
Value : foo#cake#bar#cup
Items : {foo, cake, bar, cup}
 
.INPUTS
 
.OUTPUTS
EnvironmentVariableItems PSCustomObject
#>

function Get-EnvironmentVariableItems {
    [CmdletBinding()]
    [Alias('gevis')]
    param (
        [Parameter(Mandatory)]
        [ValidatePattern("^[^=]+$")]
            [String] $Name,
        [Parameter()]
            [System.EnvironmentVariableTarget] $Scope = [System.EnvironmentVariableTarget]::Process,
        [Parameter()]
            [String] $Separator = ';'
    )    
    process {
        [EnvironmentVariableItems]::new($Name, $Scope, $Separator)
    }
}
#EndRegion '.\Public\Get-EnvironmentVariableItems.ps1' 75
#Region '.\Public\Remove-EnvironmentVariableItem.ps1' -1

<#
.SYNOPSIS
Removes an environment variable item for given Name, Item or Index, and Scope (default; 'Process') and Separator (';').
 
.PARAMETER Name
Environment variable name
 
.PARAMETER Item
An item of an environment variable (eg., 'C:\foo' in $env:Path of 'C:\foo;C:\bar')
 
.PARAMETER Scope
Environment variable scope (.NET enum System.EnvironmentVariableTarget)
 
.PARAMETER Separator
Environment variable item separator (eg., ';' in $env:Path of 'C:\foo;C:\bar')
 
.PARAMETER Index
Item index position (negative values work backwards through collection,-1 being the last item)
 
.EXAMPLE
 
Remove 'C:\tmp' from $env:Path user environment variable
 
PS> Remove-EnvironmentVariableItem -Name path -Item '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#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
 
.INPUTS
 
.OUTPUTS
EnvironmentVariableItems PSCustomObject
#>

function Remove-EnvironmentVariableItem {
    [CmdletBinding()]
    [Alias('revi')]
    param (
        [Parameter(
            Mandatory,
            Position = 0
        )]
        [ValidatePattern("^[^=]+$")]
            [String] $Name,
        [Parameter(
            Mandatory,
            ParameterSetName = 'ByItem',
            Position = 1
        )]
            [String] $Item,
        [Parameter(
            ParameterSetName = 'ByIndex',
            Position = 1,
            Mandatory
        )] [int] $Index,
        [Parameter()]
            [System.EnvironmentVariableTarget] $Scope = [System.EnvironmentVariableTarget]::Process,
        [Parameter()]
            [String] $Separator = ";",
        [Parameter()]
            [switch] $NoConfirmationRequired

    )
    process {
        $evis = [EnvironmentVariableItems]::new($Name, $Scope, $Separator)

        if ($PSCmdlet.ParameterSetName -eq 'ByIndex') {
            $result = $evis.RemoveItemByIndex($Index) -ne $False
        } elseif ($PSCmdlet.ParameterSetName -eq 'ByItem') {
            $result = $evis.RemoveItemByItem($Item) -ne $False
        }

        if ($result -ne $False) {
            if (ConfirmAction -Message (GetWhatIf) -NoConfirmationRequired:$NoConfirmationRequired) {
                $evis.SetEnvironmentVariable($evis.Name, $evis.ToString(), $evis.Scope)
                $evis
            }
        } else {
            return
        }
    }
}
#EndRegion '.\Public\Remove-EnvironmentVariableItem.ps1' 155
#Region '.\Public\Show-EnvironmentVariableItems.ps1' -1

<#
.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.
 
.PARAMETER Name
Environment variable name
 
.PARAMETER Scope
Environment variable scope (.NET enum System.EnvironmentVariableTarget)
 
.PARAMETER Separator
Environment variable item separator (eg., ';' in $env:Path of 'C:\foo;C:\bar')
 
.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()]
    [Alias('sevis')]
    param (
        [Parameter(Mandatory)]
        [ValidatePattern("^[^=]+$")]
            [String] $Name,
        [Parameter()]
            [System.EnvironmentVariableTarget] $Scope,
        [Parameter()]
            [String] $Separator = ';'
    )    
    process {
        if ($null -eq $Scope) {
            [EnvironmentVariableItems]::new($Name, $Separator).ShowIndexes()
        } else {
            [EnvironmentVariableItems]::new($Name, $Scope, $Separator).ShowIndex($Scope)
        }
    }
}
#EndRegion '.\Public\Show-EnvironmentVariableItems.ps1' 83