## Sunday, May 28, 2017

### Access Windows Environment Variables from within Bash in WSL

I have been using my MacBook a lot now that it is my main computer at work. So much so that I found it necessary to invert my scroll wheel on my mouse on my windows desktop to behave like my MacBook. I've also been using bash a lot more since I can have similar experience between the 2 machines.

While in the process of writing the scripts to configure my bash environment on my Windows machine, I found the need to be able to access environment variables that are set in Windows. With WSL, the only environment variables that really come over to bash is PATH.

I googled around for a bit, but didn't find any way to actually do this. Then I remembered that WSL has interop between Windows and WSL. This means that I can execute a Windows executable and redirect the output back to bash. Which means I should be able to execute powershell.exe to get the information I need.

I first started with a test of just doing:

$echo$(powershell.exe -Command "gci ENV:")

And that gave me what I wanted back. Now there are some differences in the paths between WSL and Windows, so I knew I would also have to adjust for that.

What I did was put a file called ~/.env.ps1 in my home path.

#!~/bin/powershell
# Will return all the environment variables in KEY=VALUE format
function Get-EnvironmentVariables {
return (Get-ChildItem ENV: | foreach { "WIN_$(Get-LinuxSafeValue -Value ($_.Name -replace '$$|$$','').ToUpper())=$(Convert-ToWSLPath -Path$_.Value)" })
}

# converts the C:\foo\bar path to the WSL counter part of /mnt/c/foo/bar
function Convert-ToWSLPath {
param (
[Parameter(Mandatory=$true)]$Path
)
(Get-LinuxSafeValue -Value (($Path -split ';' | foreach { if ($_ -ne $null -and$_ -ne '' -and $_.Length -gt 0) { (( (Fix-Path -Path$_) -replace '(^[A-Za-z])\:(.*)', '/mnt/$1$2') -replace '\\','/')
}
} ) -join ':'));
}

function Fix-Path {
param (
[Parameter(Mandatory=$true)]$Path
)
if ( $Path -match '^[A-Z]\:' ) { return$Path.Substring(0,1).ToLower()+$Path.Substring(1); } else { return$Path
}
}

# Ouputs a string of exports that can be evaluated
function Import-EnvironmentVariables {
return (Get-EnvironmentVariables | foreach { "export $_;" }) | Out-String } # Just escapes special characters function Get-LinuxSafeValue { param ( [Parameter(Mandatory=$true)]
$Value ) process { return$Value -replace "(\s|'|"|\$|\#|&|!|~||\*|\?|$$|$$|\|)",'\$1';
}
}

Now in my .bashrc I have the following:

#!/usr/bin/env bash

source ~/.wsl_helper.bash
eval $(winenv) If I run env now, I get output like the following: WIN_ONEDRIVE=/mnt/d/users/rconr/onedrive PATH=~/bin:/foo:/usr/bin WIN_PATH=/mnt/c/windows:/mnt/c/windows/system32 Notice the environment variables that are prefixed with WIN_? These are environment variables directly from Windows. I can now add additional steps to my .bashrc using these variables. ln -s "$WIN_ONEDRIVE" ~/OneDrive

Additionally, I added a script to my ~/bin folder that is in my path called powershell. This will allow me to make "native" style calls to powershell from within bash scripts.
#!/usr/bin/env bash

# rename to powershell
# chmod +x powershell

. ~/.wsl_helper.bash

PS_WORKING_DIR=$(lxssdir) if [ -f "$1" ] && "$1" ~= ".ps1$"; then
powershell.exe  -NoLogo -ExecutionPolicy ByPass -Command "Set-Location '${PS_WORKING_DIR}'; Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Get-Content$1))) ${*:2}" elif [ -f "$1" ] && "$1" ~!= "\.ps1$"; then
powershell.exe -NoLogo -ExecutionPolicy ByPass -Command "Set-Location '${PS_WORKING_DIR}'; Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Get-Content$1))) ${*:2}" else powershell.exe -NoLogo -ExecutionPolicy ByPass${*:1}
fi
unset PS_WORKING_DIR

In the powershell file, you will see a call to source a file called .wsl_helper.bash. This script has some helper functions that will do things like transform a path from a Windows style path to a linux WSL path, and do the opposite as well.

#!/usr/bin/env bash
# This is the translated path to where the LXSS root directory is
export LXSS_ROOT=/mnt/c/Users/$USER/AppData/Local/lxss # translate to linux path from windows path function windir() { echo "$1" | sed -e 's|^$$[a-z]$$:$$.*$$|/mnt/\L\1\E\2|' -e 's|\\|/|g'
}

# translate the path back to windows path
function wsldir() {
echo "$1" | sed -e 's|^/mnt/$$[a-z]$$/$$.*$$|\U\1\:\\\E\2|' -e 's|/|\\|g' } # gets the lxss path from windows function lxssdir() { if [$# -eq 0 ]; then
if echo "$PWD" | grep "^/mnt/[a-zA-Z]/" > /dev/null 2>&1; then echo "$PWD";
else
echo "$LXSS_ROOT$PWD";
fi
else
echo "$LXSS_ROOT$1";
fi
}

function printwinenv() {
_winenv --get
}

# this will load the output exports of the windows envrionment variables
function winenv() {
_winenv --import
}

function _winenv() {
if [ $# -eq 0 ]; then CMD_VERB="Get" else while test$# -gt 0; do
case "$1" in -g|--get) CMD_VERB="Get" shift ;; -i|--import) CMD_VERB="Import" shift ;; *) CMD_VERB="Get" break ;; esac done fi CMD_DIR=$(wsldir "$LXSS_ROOT$HOME/\.env.ps1")
echo $(powershell.exe -Command "Import-Module -Name$CMD_DIR; $CMD_VERB-EnvironmentVariables") | sed -e 's|\r|\n|g' -e 's|^[\s\t]*||g'; } ## Wednesday, May 17, 2017 ### Jenkins + NPM Install + Git I have been working on setting up Jenkins Pipelines for some projects and had an issue that I think others have had, but I could not find a clear answer on the way to handle it. We have some NPM Packages that are pulled from a private git repo, and all of the accounts have MFA enabled, including the CI user account. This means that SSH authentication is mandatory for CI user. If there is only one host that you need to ssh auth with jenkins, or you use the exact same ssh key for all hosts, then you can just put the private key on your Jenkins server at ~/.ssh/id_rsa. If you need to specify a key dependant upon the host, which is the situation I was in, it was not working to pull the package. The solution for this that I found was to use the ~/.ssh/config. In there you specify the hosts, the user, and what identity file to use. It can look something like this: Host github.com User git IdentityFile ~/.ssh/github.key Host bitbucket.org User git IdentityFile ~/.ssh/bitbucket.key Host tfs.myonprem-domain.com User my-ci-user IdentityFile ~/.ssh/onprem-tfs.key  So now, when running npm install, ssh will know what identity file to use. Bonus tip: Not everyone uses ssh, so in the package.json, it may not be configured to use ssh. You can put options in the global .gitconfig on the Jenkins server that will redirect the https protocol requests to ssh: [url "ssh://git@github.com/"] insteadOf = "https://github.com/" [url "ssh://git@bitbucket.org/"] insteadOf = "https://bitbucket.org/" [url "ssh://tfs.myonprem-domain.com:22/"] instadOf = "https://tfs.myonprem-domain.com/  So with that, when git detects an https request, it will switch to use ssh. ## Tuesday, January 10, 2017 ### Type 2 Diabetes Diagnosis I won't go through all the details, but on December 26th, 2016, I was diagnosed with Type 2 Diabetes. As you probably expect, it means a drastic life-style change for me. I have to give insulin injections, and monitor blood glucose, as well as monitor the amount of carbohydrates that I consume. To help me track that information I found an application on Google Play called Diabetes:M (Play Store). The application is free, but ad supported. You can purchase the ad-free license for ~$9 or so, which I purchased as soon as I was sure it was the application that I wanted to use.

This allows me to track everything that I need to be successful in controlling my diabetes. This includes information like my meals, my medications, blood pressure, insulin injections, etc. One of my favorite features of Diabetes:M is the ability for the application to sync the SQLite database, that contains all of the logged data, to a cloud storage provider like Dropbox. This, as a developer, got me thinking how I could leverage that so that I can provide my information to my wife so she is aware of what my current blood glucose level is.

To start this project I created an Express node application and put a copy of the SQLite database in the directory structure of the application. I then went to work on creating some SQL queries to pull information from the Diabetes:M database. To start, I queried my most recent glucose level.

One of the important steps to make the data current was for me to find a way to detect when a new version of the database was saved to my Dropbox. To do this, I found an application (for windows) called Directory Monitor. There is a free version of the application, which is what I am currently using, as the free version fits all my needs. I configured the application to run a shell script when the SQLite database file is modified on my system, which happens every time I log information in the Android application.

The shell script goes and extracts the dbz file, which is just a gzipped (or some other compression) SQLite file. It takes that extracted database file and puts it in the /data directory in the express application. It then does a git commit and git push of the git repository. This commit triggers a deploy in Azure. Within seconds of me taking my glucose level, the database is updated on Azure, so when my wife wants to check to see how I am doing, she can see the data in near real-time.

So when she opens the site on her phone she would see something like the following:

She can even see a history of all the events that I have entered into Diabetes:M

And, because I could, I added a dashboard with some charts

This is just the beginning of what I have done. I have other things I want to add so my wife, and family, can support me in managing this so I can live a healthier life. Right now, the code for this is not publicly available because it is still very tied to my information and configuration. I will work towards getting this in a place that I can share the code so others can use this.

Some other ideas that I think will be fun, and useful:

• Alexa Skill – Wife will be able to ask Alexa what my glucose is, and will respond with something like "about 32 minutes ago, Ryan's glucose was 132"
• iOS/Android 'support' app to be able to receive push notifications when there is a reason for concern, like high, or low glucose levels.
• More detailed reports and the ability to change the timespan of the data.
• Support for other systems to monitor the database changes. I have multiple Raspberry PI's that are always running, one of them could monitor the database for changes in Dropbox and the rPi could sync the database.
• Nightscout (CGM in the Cloud) – Integration / Support for Nightscout and all the work that the community has already done here.

## Sunday, June 12, 2016

### Installing Git on a Jenkins Windows Slave

Recently working on getting a Jenkins Windows build slave up and running, I noticed that the builds would hang, and timeout. The build would get stuck during the clone of the repository. Furthermore, if the build tried to run again, it would error out while trying to clean up the workspace.

We are using Chef and the Jenkins Cookbook to set up the Windows slave. Git for Windows is installed via Chocolatey. After a couple days of trial and error, here is what I found, and how I fixed it.

The latest versions of Git for Windows installer, we are installing 2.8.4, installs the Credential Manager by default. I have been unable to find a way to disable this option during the install, if you know a way, I would love to hear the solution to that.

(Here is a screenshot I found of the option, though it is from the 2.7.4 installer)

The credential manager stores your git user/password for your repositories, so you don’t have to enter them every time. Well, we are using Jenkins, and Jenkins does not get mad about having to enter the credentials every time. Also, the credential manager pops up this dialog when you try to clone a repository that requires authentication

When this pops up, the Jenkins build executor is stuck because it does not know how to enter input into this dialog.

We need to tell git that we do no want to use this credential helper, since we have no way to prevent it from being installed. To do this, with chef, I created a file in files/default called base.gitconfig.

[core]
autocrlf = true


Next, in the recipe, I put the file on the system in c:\program files\git\mingw64\etc\ named gitconfig.

cookbook_file "C:/Program Files/Git/mingw64/etc/gitconfig" do
source 'base.gitconfig'
owner node['jenkins']['os']['windows']['user']
group node['jenkins']['os']['windows']['group']
end

Now when the Windows slave attempts to do a git clone, it no longer will use the credential manager and pop up the dialog, and everything works flawlessly.

## Thursday, April 14, 2016

### Change Colors in PowerShell Console

I have my powershell console window have a black background and slightly transparent. The default colors here work for most things, except errors. Errors were hard to read, especially when projecting to a secondary monitor. I found on the web that you can change the foreground color for powershell errors by setting $host.privatedata.ErrorForegroundColor. But the problem with these solutions that I found was that they just said “from a PS prompt, set the color”. I wanted something a little more permanent. What I came up with was to set the values that I want in my PowerShell profile file that is located at [Environment]::GetFolderPath("MyDocuments")/WindowsPowerShell/Microsoft.PowerShell_profile.ps1. Here is what I put in mine: $host.privatedata.ErrorForegroundColor = 'Magenta';
$host.privatedata.ErrorBackgroundColor = 'Black';$host.privatedata.WarningForegroundColor = 'Yellow';
$host.privatedata.WarningBackgroundColor = 'Black';$host.privatedata.DebugForegroundColor = 'DarkGray';
$host.privatedata.DebugBackgroundColor = 'Black';$host.privatedata.VerboseForegroundColor = 'White';
$host.privatedata.VerboseBackgroundColor = 'Black';$host.privatedata.ProgressForegroundColor = 'DarkBlue';
$host.privatedata.ProgressBackgroundColor = 'DarkCyan'; Now my errors are readable. To see what values you can set them to just execute: [system.consolecolor]::GetNames("consolecolor"). ### Access Windows Environment Variables from within Bash in WSL I have been using my MacBook a lot now that it is my main computer at work. So much so that I found it necessary to invert my scroll wheel on my mouse on my windows desktop to behave like my MacBook. I've also been using bash a lot more since I can have similar experience between the 2 machines. While in the process of writing the scripts to configure my bash environment on my Windows machine, I found the need to be able to access environment variables that are set in Windows. With WSL, the only environment variables that really come over to bash is PATH. I googled around for a bit, but didn't find any way to actually do this. Then I remembered that WSL has interop between Windows and WSL. This means that I can execute a Windows executable and redirect the output back to bash. Which means I should be able to execute powershell.exe to get the information I need. I first started with a test of just doing: $ echo $(powershell.exe -Command "gci ENV:") And that gave me what I wanted back. Now there are some differences in the paths between WSL and Windows, so I knew I would also have to adjust for that. What I did was put a file called ~/.env.ps1 in my home path. #!~/bin/powershell # Will return all the environment variables in KEY=VALUE format function Get-EnvironmentVariables { return (Get-ChildItem ENV: | foreach { "WIN_$(Get-LinuxSafeValue -Value ($_.Name -replace '$$|$$','').ToUpper())=$(Convert-ToWSLPath -Path $_.Value)" }) } # converts the C:\foo\bar path to the WSL counter part of /mnt/c/foo/bar function Convert-ToWSLPath { param ( [Parameter(Mandatory=$true)]
$Path ) (Get-LinuxSafeValue -Value (($Path -split ';' | foreach {
if ($_ -ne$null -and $_ -ne '' -and$_.Length -gt 0) {
(( (Fix-Path -Path $_) -replace '(^[A-Za-z])\:(.*)', '/mnt/$1$2') -replace '\\','/') } } ) -join ':')); } function Fix-Path { param ( [Parameter(Mandatory=$true)]
$Path ) if ($Path -match '^[A-Z]\:' ) {
return $Path.Substring(0,1).ToLower()+$Path.Substring(1);
} else {
return $Path } } # Ouputs a string of exports that can be evaluated function Import-EnvironmentVariables { return (Get-EnvironmentVariables | foreach { "export$_;" }) | Out-String
}

# Just escapes special characters
function Get-LinuxSafeValue {
param (
[Parameter(Mandatory=$true)]$Value
)
process {
return $Value -replace "(\s|'|"|\$|\#|&|!|~||\*|\?|$$|$$|\|)",'\$1'; } } Now in my .bashrc I have the following: #!/usr/bin/env bash source ~/.wsl_helper.bash eval$(winenv)

If I run env now, I get output like the following:

WIN_ONEDRIVE=/mnt/d/users/rconr/onedrive
PATH=~/bin:/foo:/usr/bin
WIN_PATH=/mnt/c/windows:/mnt/c/windows/system32

Notice the environment variables that are prefixed with WIN_? These are environment variables directly from Windows. I can now add additional steps to my .bashrc using these variables.

ln -s "$WIN_ONEDRIVE" ~/OneDrive Additionally, I added a script to my ~/bin folder that is in my path called powershell. This will allow me to make "native" style calls to powershell from within bash scripts. #!/usr/bin/env bash # rename to powershell # chmod +x powershell . ~/.wsl_helper.bash PS_WORKING_DIR=$(lxssdir)
if [ -f "$1" ] && "$1" ~= ".ps1$"; then powershell.exe -NoLogo -ExecutionPolicy ByPass -Command "Set-Location '${PS_WORKING_DIR}'; Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Get-Content $1)))${*:2}"
elif [ -f "$1" ] && "$1" ~!= "\.ps1$"; then powershell.exe -NoLogo -ExecutionPolicy ByPass -Command "Set-Location '${PS_WORKING_DIR}'; Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Get-Content $1)))${*:2}"
else
powershell.exe -NoLogo -ExecutionPolicy ByPass ${*:1} fi unset PS_WORKING_DIR In the powershell file, you will see a call to source a file called .wsl_helper.bash. This script has some helper functions that will do things like transform a path from a Windows style path to a linux WSL path, and do the opposite as well. #!/usr/bin/env bash # This is the translated path to where the LXSS root directory is export LXSS_ROOT=/mnt/c/Users/$USER/AppData/Local/lxss

# translate to linux path from windows path
function windir() {
echo "$1" | sed -e 's|^$$[a-z]$$:$$.*$$|/mnt/\L\1\E\2|' -e 's|\\|/|g' } # translate the path back to windows path function wsldir() { echo "$1" | sed -e 's|^/mnt/$$[a-z]$$/$$.*$$|\U\1\:\\\E\2|' -e 's|/|\\|g'
}

# gets the lxss path from windows
function lxssdir() {
if [ $# -eq 0 ]; then if echo "$PWD" | grep "^/mnt/[a-zA-Z]/" > /dev/null 2>&1; then
echo "$PWD"; else echo "$LXSS_ROOT$PWD"; fi else echo "$LXSS_ROOT$1"; fi } function printwinenv() { _winenv --get } # this will load the output exports of the windows envrionment variables function winenv() { _winenv --import } function _winenv() { if [$# -eq 0 ]; then
CMD_VERB="Get"
else
while test $# -gt 0; do case "$1" in
-g|--get)
CMD_VERB="Get"
shift
;;
-i|--import)
CMD_VERB="Import"
shift
;;
*)
CMD_VERB="Get"
break
;;
esac
done
fi
CMD_DIR=$(wsldir "$LXSS_ROOT$HOME/\.env.ps1") echo$(powershell.exe -Command "Import-Module -Name $CMD_DIR;$CMD_VERB-EnvironmentVariables") | sed -e 's|\r|\n|g' -e 's|^[\s\t]*||g';
}

### Jenkins + NPM Install + Git

I have been working on setting up Jenkins Pipelines for some projects and had an issue that I think others have had, but I could not find a clear answer on the way to handle it.

We have some NPM Packages that are pulled from a private git repo, and all of the accounts have MFA enabled, including the CI user account. This means that SSH authentication is mandatory for CI user.

If there is only one host that you need to ssh auth with jenkins, or you use the exact same ssh key for all hosts, then you can just put the private key on your Jenkins server at ~/.ssh/id_rsa. If you need to specify a key dependant upon the host, which is the situation I was in, it was not working to pull the package.

The solution for this that I found was to use the ~/.ssh/config. In there you specify the hosts, the user, and what identity file to use. It can look something like this:

Host github.com
User git
IdentityFile ~/.ssh/github.key

Host bitbucket.org
User git
IdentityFile ~/.ssh/bitbucket.key

Host tfs.myonprem-domain.com
User my-ci-user
IdentityFile ~/.ssh/onprem-tfs.key


So now, when running npm install, ssh will know what identity file to use.

Bonus tip: Not everyone uses ssh, so in the package.json, it may not be configured to use ssh. You can put options in the global .gitconfig on the Jenkins server that will redirect the https protocol requests to ssh:

[url "ssh://git@github.com/"]
[url "ssh://git@bitbucket.org/"]
[url "ssh://tfs.myonprem-domain.com:22/"]


So with that, when git detects an https request, it will switch to use ssh.

I won't go through all the details, but on December 26th, 2016, I was diagnosed with Type 2 Diabetes. As you probably expect, it means a drastic life-style change for me. I have to give insulin injections, and monitor blood glucose, as well as monitor the amount of carbohydrates that I consume. To help me track that information I found an application on Google Play called Diabetes:M (Play Store). The application is free, but ad supported. You can purchase the ad-free license for ~$9 or so, which I purchased as soon as I was sure it was the application that I wanted to use. This allows me to track everything that I need to be successful in controlling my diabetes. This includes information like my meals, my medications, blood pressure, insulin injections, etc. One of my favorite features of Diabetes:M is the ability for the application to sync the SQLite database, that contains all of the logged data, to a cloud storage provider like Dropbox. This, as a developer, got me thinking how I could leverage that so that I can provide my information to my wife so she is aware of what my current blood glucose level is. To start this project I created an Express node application and put a copy of the SQLite database in the directory structure of the application. I then went to work on creating some SQL queries to pull information from the Diabetes:M database. To start, I queried my most recent glucose level. One of the important steps to make the data current was for me to find a way to detect when a new version of the database was saved to my Dropbox. To do this, I found an application (for windows) called Directory Monitor. There is a free version of the application, which is what I am currently using, as the free version fits all my needs. I configured the application to run a shell script when the SQLite database file is modified on my system, which happens every time I log information in the Android application. The shell script goes and extracts the dbz file, which is just a gzipped (or some other compression) SQLite file. It takes that extracted database file and puts it in the /data directory in the express application. It then does a git commit and git push of the git repository. This commit triggers a deploy in Azure. Within seconds of me taking my glucose level, the database is updated on Azure, so when my wife wants to check to see how I am doing, she can see the data in near real-time. So when she opens the site on her phone she would see something like the following: She can even see a history of all the events that I have entered into Diabetes:M And, because I could, I added a dashboard with some charts This is just the beginning of what I have done. I have other things I want to add so my wife, and family, can support me in managing this so I can live a healthier life. Right now, the code for this is not publicly available because it is still very tied to my information and configuration. I will work towards getting this in a place that I can share the code so others can use this. Some other ideas that I think will be fun, and useful: • Alexa Skill – Wife will be able to ask Alexa what my glucose is, and will respond with something like "about 32 minutes ago, Ryan's glucose was 132" • iOS/Android 'support' app to be able to receive push notifications when there is a reason for concern, like high, or low glucose levels. • More detailed reports and the ability to change the timespan of the data. • Support for other systems to monitor the database changes. I have multiple Raspberry PI's that are always running, one of them could monitor the database for changes in Dropbox and the rPi could sync the database. • Nightscout (CGM in the Cloud) – Integration / Support for Nightscout and all the work that the community has already done here. ### Installing Git on a Jenkins Windows Slave Recently working on getting a Jenkins Windows build slave up and running, I noticed that the builds would hang, and timeout. The build would get stuck during the clone of the repository. Furthermore, if the build tried to run again, it would error out while trying to clean up the workspace. We are using Chef and the Jenkins Cookbook to set up the Windows slave. Git for Windows is installed via Chocolatey. After a couple days of trial and error, here is what I found, and how I fixed it. The latest versions of Git for Windows installer, we are installing 2.8.4, installs the Credential Manager by default. I have been unable to find a way to disable this option during the install, if you know a way, I would love to hear the solution to that. (Here is a screenshot I found of the option, though it is from the 2.7.4 installer) The credential manager stores your git user/password for your repositories, so you don’t have to enter them every time. Well, we are using Jenkins, and Jenkins does not get mad about having to enter the credentials every time. Also, the credential manager pops up this dialog when you try to clone a repository that requires authentication When this pops up, the Jenkins build executor is stuck because it does not know how to enter input into this dialog. We need to tell git that we do no want to use this credential helper, since we have no way to prevent it from being installed. To do this, with chef, I created a file in files/default called base.gitconfig. [core] autocrlf = true askpass = true  Next, in the recipe, I put the file on the system in c:\program files\git\mingw64\etc\ named gitconfig. cookbook_file "C:/Program Files/Git/mingw64/etc/gitconfig" do source 'base.gitconfig' owner node['jenkins']['os']['windows']['user'] group node['jenkins']['os']['windows']['group'] end  Now when the Windows slave attempts to do a git clone, it no longer will use the credential manager and pop up the dialog, and everything works flawlessly. ### Change Colors in PowerShell Console I have my powershell console window have a black background and slightly transparent. The default colors here work for most things, except errors. Errors were hard to read, especially when projecting to a secondary monitor. I found on the web that you can change the foreground color for powershell errors by setting$host.privatedata.ErrorForegroundColor. But the problem with these solutions that I found was that they just said “from a PS prompt, set the color”. I wanted something a little more permanent.

What I came up with was to set the values that I want in my PowerShell profile file that is located at [Environment]::GetFolderPath("MyDocuments")/WindowsPowerShell/Microsoft.PowerShell_profile.ps1.

Here is what I put in mine:

$host.privatedata.ErrorForegroundColor = 'Magenta';$host.privatedata.ErrorBackgroundColor = 'Black';
$host.privatedata.WarningForegroundColor = 'Yellow';$host.privatedata.WarningBackgroundColor = 'Black';
$host.privatedata.DebugForegroundColor = 'DarkGray';$host.privatedata.DebugBackgroundColor = 'Black';
$host.privatedata.VerboseForegroundColor = 'White';$host.privatedata.VerboseBackgroundColor = 'Black';
$host.privatedata.ProgressForegroundColor = 'DarkBlue';$host.privatedata.ProgressBackgroundColor = 'DarkCyan';