Private/Set-NixEnvironmentVariable.ps1

# use all caps for the $ENV: prefix to avoid capitalization issues
# this function will strictly be used in linux environments
# this function will set an environment variable in the /etc/environment file
# this private function will not be called, as the logic is handled in the public function Set-EnvironmentVariable

if ($IsLinux) {
  function Set-EnvironmentVariable {
    param(
      [Parameter(Mandatory = $true)]
      [string]$Name,
      [Parameter(Mandatory = $true)]
      [string]$Value
    )
    begin {
      Write-Debug "[Begin Set-NixEnvironmentVariable]"
      $environmentFile = "/etc/environment"

      #region functions

      # function to test for root

      function Test-Root {
        $isRoot = $false
        $userId = . id -u
        if ($userId -eq 0) {
          $isRoot = $true
        }
        return $isRoot
      }

      # function to parse te environment file

      function Read-EnvironmentFile {
        param(
          [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
          [string]$EnvironmentFile
        )
        $environmentVariables = [System.Collections.Specialized.OrderedDictionary]::new()
        $environmentFileContent = Get-Content -Path $EnvironmentFile
        foreach ($line in $environmentFileContent) {
          if ($line -match "^(?<name>[A-Z_]+)=(?<value>.*)$") {
            $environmentVariables.Add($matches.name, $matches.value)
            # $environmentVariables[$matches.name] = $matches.value
          }
        }
        return $environmentVariables
      }

      # function to convert a hashtable to a valid environment file

      function ConvertTo-EnvironmentFile {
        [CmdletBinding()]
        param(
          [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
          [System.Collections.Specialized.OrderedDictionary]$EnvironmentVariables
        )
        $stringBuilder = [System.Text.StringBuilder]::new()
        # iterate over the keys in the order they are listed in the ordered dictionary, and add the resulting environment file content to the string builder
        foreach ($key in $EnvironmentVariables.Keys) {
          $null = $stringBuilder.AppendLine("$key=$($EnvironmentVariables[$key])")
        }
        return $stringBuilder.ToString()
        # foreach ($key in $EnvironmentVariables.Keys) {
        # $environmentFileContent += "$key=$($EnvironmentVariables[$key])"
        # }
        # return $environmentFileContent
      }

      # function to test if the environment variable already exists

      function Test-EnvironmentVariableExists {
        [CmdletBinding()]
        param(
          [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
          [string]$EnvironmentFile,
          [Parameter(Mandatory = $true)]
          [string]$Name
        )
        $environmentFileContent = Get-Content -Path $EnvironmentFile
        foreach ($line in $environmentFileContent) {
          if ($line -match "^(?<name>[A-Z_]+)=(?<value>.*)$") {
            if ($matches.name -eq $Name) {
              Write-Warning "The environment variable $Name already exists in $EnvironmentFile"
              return $true
            }
          }
        }
        return $false
      }

      # function to add the environment variable to the environment file

      function Add-EnvironmentVariable {
        [CmdletBinding()]
        param(
          [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
          [string]$EnvironmentFile,
          [Parameter(Mandatory = $true)]
          [string]$Name,
          [Parameter(Mandatory = $true)]
          [string]$Value
        )
        $environmentFileContent = Get-Content -Path $EnvironmentFile
        $environmentFileContent += "`n$Name=$Value"
        Set-Content -Path $EnvironmentFile -Value $environmentFileContent
      }

      # function to update the environment variable in the environment file

      function Update-EnvironmentVariable {
        [CmdletBinding()]
        param(
          [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
          [string]$EnvironmentFile,
          [Parameter(Mandatory = $true)]
          [string]$Name,
          [Parameter(Mandatory = $true)]
          [string]$Value
        )
        $environmentFileContent = Get-Content -Path $EnvironmentFile
        foreach ($line in $environmentFileContent) {
          if ($line -match "^(?<name>[A-Z_]+)=(?<value>.*)$") {
            if ($matches.name -eq $Name) {
              $line = "$Name=$Value"
            }
          }
        }
        Set-Content -Path $EnvironmentFile -Value $environmentFileContent
      }


      # a function to check the differences between two environment files

      function Compare-EnvironmentFiles {
        [CmdletBinding()]
        param(
          [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
          [string]$EnvironmentFile1,
          [Parameter(Mandatory = $true)]
          [string]$EnvironmentFile2
        )
        $environmentFile1Content = Get-Content -Path $EnvironmentFile1
        $environmentFile2Content = Get-Content -Path $EnvironmentFile2
        $environmentFile1Content | Compare-Object -ReferenceObject $environmentFile2Content
      }


      #endregion functions

      if (!(Test-Root)) {
        Write-Error "This script must be run as root"
        break
      }
      # create a backup in the user's home directory
      $backupFile = "$ENV:HOME/environment.bak"
      try {
        Copy-Item -Path $environmentFile -Destination $backupFile -Force
      } catch {
        Write-Warning "Unable to create backup of $environmentFile"
        break
      }

      # read the environment file into an ordered dictionary
      $environmentVariables = Read-EnvironmentFile -EnvironmentFile $environmentFile

      # test if the environment variable already exists

      $environmentVariableExists = Test-EnvironmentVariableExists -EnvironmentFile $environmentFile -Name $Name

    }
    process {
      Write-Debug "[Process Set-NixEnvironmentVariable]"
      # if the environment variable already exists, update it
      if ($environmentVariableExists) {
        try {
          Update-EnvironmentVariable -EnvironmentFile $environmentFile -Name $Name -Value $Value
        } catch {
          Write-Warning "Unable to update environment variable $Name in $environmentFile"
          Write-Warning $PSItem.Exception.Message
          Write-Warning "if the environment variable is not set correctly, you can restore the backup file at $backupFile"
          break
        }
      } else {
        # if the environment variable does not exist, add it
        try {
          Add-EnvironmentVariable -EnvironmentFile $environmentFile -Name $Name -Value $Value

        } catch {
          Write-Warning "Unable to add environment variable $Name in $environmentFile"
          Write-Warning $PSItem.Exception.Message
          Write-Warning "if the environment variable is not set correctly, you can restore the backup file at $backupFile"
          break
        }
      }


    }
    end {
      Write-Debug "[End Set-NixEnvironmentVariable]"
      # clean up backup file if the only difference is the new variable
      $differences = Compare-EnvironmentFiles -EnvironmentFile1 $environmentFile -EnvironmentFile2 $backupFile
      if ($differences.Count -eq 1) {
        Remove-Item -Path $backupFile
      } else {
        Write-Warning "The environment file $environmentFile has been updated"
        Write-Warning "if the environment variable is not set correctly, you can restore the backup file at $backupFile"
      }
    }

  }

}