Showing posts with label powershell. Show all posts
Showing posts with label powershell. Show all posts

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';
}

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';

 

image

Now my errors are readable.

To see what values you can set them to just execute: [system.consolecolor]::GetNames("consolecolor").

Tuesday, April 12, 2016

Running kitchen converge on Windows 2012r2 image on OpenStack

I have been working with my team to get Windows 2012r2 image chef cookbook to converge in an OpenStack environment. We have run in to a lot of issues, and I am confident that some of those were because of my lack of experience with chef/windows. One of the big issues that we encountered was kitchen unable to WINRM in to the box. It would just keep throwing [winrm] HTTPClient::ConnectionTimeoutException. We noticed that if someone manually logs on to the machine, and they are presented with the User must change password for the account. After they enter the password, they are prompted with Choose the type of network for the machine.

This had me thinking. If the user account is set to require the password to be changed, Windows will not allow anyone to remotely logon to the machine with that account. So I tested this theory. I spun up the Windows2012r2 image in OpenStack and attempted to connect with Enter-PSSession. I was getting an error that the connection could not be established. Specifically:

WinRM cannot complete the operation. Verify that the specified computer name is valid, that the computer is accessible over the network, 
and that a firewall exception for the WinRM service is enabled and allows access from this computer. By default, the WinRM firewall
exception for public profiles limits access to remote computers within the same local subnet.

This lead me to determine that because the network type has not been selected, the second thing the user is presented with after manually logging in, WinRM is not letting the connection happen. I then figured that because this machine has CloudInit, there has to be a way to set the network type to be private. If this machine was added to the Domain, this would not be an issue, but this is a ephemeral machine just to test the changes to the cookbook and verify it works so it is not on the Domain. After some research I came across Setting Network Location to Private blog post by Vladimir Averkin. This post has a block of PowerShell that enumerates all networks, post-Vista machines that are not joined to a Domain, and sets them to Private.

# Skip network location setting for pre-Vista operating systems
if([environment]::OSVersion.version.Major -lt 6) { return } # Skip network location setting if local machine is joined to a domain.
if(1,3,4,5 -contains (Get-WmiObject win32_computersystem).DomainRole) { return } # Get network connections
$networkListManager = [Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]"{DCB00C01-570F-4A9B-8D69-199FDBA5723B}"))
$connections = $networkListManager.GetNetworkConnections() # Set network location to Private for all networks
$connections | % {$_.GetNetwork().SetCategory(1)}

Perfect!

I launched a new instance on OpenStack, this time in the Post-Creation I added the script above, making sure to specify #ps1_sysnative at the top to let cloudinit know that we are running some PowerShell. Then waited…

Fired up PowerShell and ran Enter-PSSession... Failed, Access is denied. This has to be the issue with the user being prompted to change the password. Went back into OpenStack, launched another instance. This time at the end of the above script, I added:

net user KitchenCIUser KitchenCIUserPassword

Then I waited again… I started running Test-WSMan until it came back successful. Then I ran Enter-PSSession one more time. SUCCESS! I was now connected to the machine via PSRemoting.

Now one last hurdle, how do I specify the CloudInit script with the .kitchen.yml. Well, as it turns out, the kitchen-openstack driver supports this.

driver_config:
  user_data: cloud-init.txt

Then just have to create the cloud-init.txt file in the same path as the .kitchen.yml. I saved the changes, committed to repository and watched as jenkins kicked off the build. Oh, and I waited…

Success! Kitchen was able to use WinRM and converge the box, now on to the next problem that I am sure we will encounter.

Friday, November 6, 2015

Persistent Command History in PowerShell

If you are familiar with .bash_history or just wish you had a history of your powershell commands persist between sessions here is a handy little script. It will persist command history when you exit a powershell session to a file in %USERPROFILE% called .ps_history.

Copy the code below and save to a file called “Microsoft.PowerShell_profile.ps1” and save it in “%USERPROFILE%\Documents\WindowsPowerShell”

$HistoryFilePath = Join-Path -Path ([Environment]::GetFolderPath('UserProfile')) -ChildPath ".ps_history"; 
function Persist-History {
Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action {
Get-History | Select-Object -Unique | Export-Clixml -Path $HistoryFilePath;
} | Out-Null;
if (Test-path -Path $HistoryFilePath) {
Import-Clixml -Path $HistoryFilePath | Add-History;
}
Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward;
Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward;
}
Persist-History;
 

Disclaimer: I have only tested this with PowerShell 5


If it doesn’t work, you could probably make it work by installing the PackageManagement modules for PowerShell and install the “PSReadline” module.

Sunday, October 4, 2015

Simplify standing up an IE Virtual Machine with PSIEVM

Over the weekend I was bored and decided to start a project to automate the process of standing up an IE VM with powershell. I would like to introduce PSIEVM. A powershell module that allows you to stand up an IE VM in VirtualBox (other hosts coming in the future) in one line.

PS> Get-IEVM –OS Win7 –IEVersion 9

That is the simplest form of the command.

Installation

To install, download from the github project releases page and extract to $env:USERPROFILE\WindowsPowerShell\Modules. Then to use it just import it

PS> Import-Module psievm
PS> Get-IEVM –OS Win7 –IEVersion 10 –AlternateVMLocation "G:\VMs\" –VMRootPath "G:\VMs\"

 

The parameters for Get-IEVM:

Name

Required

Type

Default

OS

YES

String

[Empty]

The OS to use:

  • WinXP, WindowsXP, Windows XP, XP
  • WinVista, WindowsVista, Windows Vista, Vista
  • Win7, Windows7, Windows 7, 7
  • Win8, Windows8, Windows 8, 8
  • Win8.1, Windows8.1, Windows 8.1, 8.1
  • Win10, Windows10, Windows 10, 10

IEVersion

YES

String

[Empty]

The version of IE. This is dependant on the version of Windows (See the table below).

  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • Edge

Shares

NO

String[]

[Empty]

These are the paths to add as a share in the VM through the VMHost. May not be fully supported in all VMHosts.

AlternateVMLocation

NO

String

[Empty]

By default the VM images will be downloaded from Microsoft, but if you have the images downloaded already, you can use this to tell the script where to get them. This can be a URL, Network Share, or a local path. The images need to follow the following pattern:

IE: IE<IEVersion>.Win<OSVersion>.For.Windows.<VMHost>.zip
Edge: Microsoft%20Edge.Win10.For.Windows.<VMHost>.zip

VMHost

NO

String

VirtualBox

The VM Host to use. Only VirtualBox is currently supported but here is the full list that will be supported (and maybe others).

  • VirtualBox
  • HyperV
  • Vagrent
  • VPC
  • VMWare

IgnoreInvalidMD5

NO

Boolean

$False

After the zip image is downloaded, the file MD5 hash is validated. If you use your own images, you will want to set this to $True.

VMRootPath

NO

String

$PWD

This is the path to download the zip image to and create the VM image folder where the VM will reside. By default it will use the Current Working Directory.

OS / IEVersion

This is the version of the OS that you want hosted and the version of IE you want with it. Here are the supported values:

 

IE 6

IE 7

IE 8

IE 9

IE 10

IE 11

MS Edge

Requires 64-bit Emulation

XP

X

 

X

 

 

 

 

 

Vista

 

X

 

 

 

 

 

 

Win7

 

 

X

X

X

X

 

 

Win8

 

 

 

 

X

 

 

 

Win8.1

 

 

 

 

 

X

 

X

Win10

 

 

 

 

 

 

X

X

Download PSIEVM

Monday, August 3, 2015

How to enable Use default gateway on remote network with Windows 10

There are a lot of tutorials out there that are for previous versions of Windows that say to right click on the VPN and choose properties, then select IPv4 and click properties. The problem is with Windows 10, at least for me, nothing happens when you click the properties button.

 

The way I have found to enable this functionality is via PowerShell.

 

Start PowerShell as an administrator then run Get-VpnConnection

1

Next run Set-VpnConnection –Name MY_VPN_NAME –SplitTunneling $true
Replace MY_VPN_NAME with the name of your VPN connection.

2

Sunday, April 13, 2014

Really Disable UAC on Windows 8.1 Update 1

The control panel item to disable UAC does not actually disable UAC, it puts it in to a “Silent Mode”. This will really disable UAC on Windows 8, Windows 8.1, and Windows 8.1 Update 1.

Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -Name "EnableLUA" -Value "0"

Friday, April 4, 2014

Powershell Script to download //BUILD/ 2014 sessions

Here is a powershell script that downloads all the mp4 video sessions available on Channel9. It first pulls all the HD videos, then checks the normal quality videos. It names the videos based on the session title. It will only download the video if it does not exist in the destination directory.


Update: Blogger doesn't want to show my GIST, so here is a direct link to it.

Saturday, July 2, 2011

PowerShell Script to pull Album Art for missing directories

I recently went and downloaded Album Art Downloader to get some missing album art for some of the albums I have. But I quickly noticed that it would be a manual process to find the directories that needed the art added, and I am lazy.

Luckily there is a command line interface for AAD. But that alone would not accomplish what I wanted to do. I figured now would be a good time to try my hand at a powershell script.

This script will take each artist/album and use AAD to get album art and save it as folder.jpg. The script expects the root directory to contain “artist/album” hierarchy.

You will have to change the “root” and possibly the location of the aad.exe.

Showing posts with label powershell. Show all posts
Showing posts with label powershell. Show all posts

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';
}

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';

 

image

Now my errors are readable.

To see what values you can set them to just execute: [system.consolecolor]::GetNames("consolecolor").

Running kitchen converge on Windows 2012r2 image on OpenStack

I have been working with my team to get Windows 2012r2 image chef cookbook to converge in an OpenStack environment. We have run in to a lot of issues, and I am confident that some of those were because of my lack of experience with chef/windows. One of the big issues that we encountered was kitchen unable to WINRM in to the box. It would just keep throwing [winrm] HTTPClient::ConnectionTimeoutException. We noticed that if someone manually logs on to the machine, and they are presented with the User must change password for the account. After they enter the password, they are prompted with Choose the type of network for the machine.

This had me thinking. If the user account is set to require the password to be changed, Windows will not allow anyone to remotely logon to the machine with that account. So I tested this theory. I spun up the Windows2012r2 image in OpenStack and attempted to connect with Enter-PSSession. I was getting an error that the connection could not be established. Specifically:

WinRM cannot complete the operation. Verify that the specified computer name is valid, that the computer is accessible over the network, 
and that a firewall exception for the WinRM service is enabled and allows access from this computer. By default, the WinRM firewall
exception for public profiles limits access to remote computers within the same local subnet.

This lead me to determine that because the network type has not been selected, the second thing the user is presented with after manually logging in, WinRM is not letting the connection happen. I then figured that because this machine has CloudInit, there has to be a way to set the network type to be private. If this machine was added to the Domain, this would not be an issue, but this is a ephemeral machine just to test the changes to the cookbook and verify it works so it is not on the Domain. After some research I came across Setting Network Location to Private blog post by Vladimir Averkin. This post has a block of PowerShell that enumerates all networks, post-Vista machines that are not joined to a Domain, and sets them to Private.

# Skip network location setting for pre-Vista operating systems
if([environment]::OSVersion.version.Major -lt 6) { return } # Skip network location setting if local machine is joined to a domain.
if(1,3,4,5 -contains (Get-WmiObject win32_computersystem).DomainRole) { return } # Get network connections
$networkListManager = [Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]"{DCB00C01-570F-4A9B-8D69-199FDBA5723B}"))
$connections = $networkListManager.GetNetworkConnections() # Set network location to Private for all networks
$connections | % {$_.GetNetwork().SetCategory(1)}

Perfect!

I launched a new instance on OpenStack, this time in the Post-Creation I added the script above, making sure to specify #ps1_sysnative at the top to let cloudinit know that we are running some PowerShell. Then waited…

Fired up PowerShell and ran Enter-PSSession... Failed, Access is denied. This has to be the issue with the user being prompted to change the password. Went back into OpenStack, launched another instance. This time at the end of the above script, I added:

net user KitchenCIUser KitchenCIUserPassword

Then I waited again… I started running Test-WSMan until it came back successful. Then I ran Enter-PSSession one more time. SUCCESS! I was now connected to the machine via PSRemoting.

Now one last hurdle, how do I specify the CloudInit script with the .kitchen.yml. Well, as it turns out, the kitchen-openstack driver supports this.

driver_config:
  user_data: cloud-init.txt

Then just have to create the cloud-init.txt file in the same path as the .kitchen.yml. I saved the changes, committed to repository and watched as jenkins kicked off the build. Oh, and I waited…

Success! Kitchen was able to use WinRM and converge the box, now on to the next problem that I am sure we will encounter.

Persistent Command History in PowerShell

If you are familiar with .bash_history or just wish you had a history of your powershell commands persist between sessions here is a handy little script. It will persist command history when you exit a powershell session to a file in %USERPROFILE% called .ps_history.

Copy the code below and save to a file called “Microsoft.PowerShell_profile.ps1” and save it in “%USERPROFILE%\Documents\WindowsPowerShell”

$HistoryFilePath = Join-Path -Path ([Environment]::GetFolderPath('UserProfile')) -ChildPath ".ps_history"; 
function Persist-History {
Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action {
Get-History | Select-Object -Unique | Export-Clixml -Path $HistoryFilePath;
} | Out-Null;
if (Test-path -Path $HistoryFilePath) {
Import-Clixml -Path $HistoryFilePath | Add-History;
}
Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward;
Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward;
}
Persist-History;
 

Disclaimer: I have only tested this with PowerShell 5


If it doesn’t work, you could probably make it work by installing the PackageManagement modules for PowerShell and install the “PSReadline” module.

Simplify standing up an IE Virtual Machine with PSIEVM

Over the weekend I was bored and decided to start a project to automate the process of standing up an IE VM with powershell. I would like to introduce PSIEVM. A powershell module that allows you to stand up an IE VM in VirtualBox (other hosts coming in the future) in one line.

PS> Get-IEVM –OS Win7 –IEVersion 9

That is the simplest form of the command.

Installation

To install, download from the github project releases page and extract to $env:USERPROFILE\WindowsPowerShell\Modules. Then to use it just import it

PS> Import-Module psievm
PS> Get-IEVM –OS Win7 –IEVersion 10 –AlternateVMLocation "G:\VMs\" –VMRootPath "G:\VMs\"

 

The parameters for Get-IEVM:

Name

Required

Type

Default

OS

YES

String

[Empty]

The OS to use:

  • WinXP, WindowsXP, Windows XP, XP
  • WinVista, WindowsVista, Windows Vista, Vista
  • Win7, Windows7, Windows 7, 7
  • Win8, Windows8, Windows 8, 8
  • Win8.1, Windows8.1, Windows 8.1, 8.1
  • Win10, Windows10, Windows 10, 10

IEVersion

YES

String

[Empty]

The version of IE. This is dependant on the version of Windows (See the table below).

  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • Edge

Shares

NO

String[]

[Empty]

These are the paths to add as a share in the VM through the VMHost. May not be fully supported in all VMHosts.

AlternateVMLocation

NO

String

[Empty]

By default the VM images will be downloaded from Microsoft, but if you have the images downloaded already, you can use this to tell the script where to get them. This can be a URL, Network Share, or a local path. The images need to follow the following pattern:

IE: IE<IEVersion>.Win<OSVersion>.For.Windows.<VMHost>.zip
Edge: Microsoft%20Edge.Win10.For.Windows.<VMHost>.zip

VMHost

NO

String

VirtualBox

The VM Host to use. Only VirtualBox is currently supported but here is the full list that will be supported (and maybe others).

  • VirtualBox
  • HyperV
  • Vagrent
  • VPC
  • VMWare

IgnoreInvalidMD5

NO

Boolean

$False

After the zip image is downloaded, the file MD5 hash is validated. If you use your own images, you will want to set this to $True.

VMRootPath

NO

String

$PWD

This is the path to download the zip image to and create the VM image folder where the VM will reside. By default it will use the Current Working Directory.

OS / IEVersion

This is the version of the OS that you want hosted and the version of IE you want with it. Here are the supported values:

 

IE 6

IE 7

IE 8

IE 9

IE 10

IE 11

MS Edge

Requires 64-bit Emulation

XP

X

 

X

 

 

 

 

 

Vista

 

X

 

 

 

 

 

 

Win7

 

 

X

X

X

X

 

 

Win8

 

 

 

 

X

 

 

 

Win8.1

 

 

 

 

 

X

 

X

Win10

 

 

 

 

 

 

X

X

Download PSIEVM

How to enable Use default gateway on remote network with Windows 10

There are a lot of tutorials out there that are for previous versions of Windows that say to right click on the VPN and choose properties, then select IPv4 and click properties. The problem is with Windows 10, at least for me, nothing happens when you click the properties button.

 

The way I have found to enable this functionality is via PowerShell.

 

Start PowerShell as an administrator then run Get-VpnConnection

1

Next run Set-VpnConnection –Name MY_VPN_NAME –SplitTunneling $true
Replace MY_VPN_NAME with the name of your VPN connection.

2

Really Disable UAC on Windows 8.1 Update 1

The control panel item to disable UAC does not actually disable UAC, it puts it in to a “Silent Mode”. This will really disable UAC on Windows 8, Windows 8.1, and Windows 8.1 Update 1.

Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -Name "EnableLUA" -Value "0"

Powershell Script to download //BUILD/ 2014 sessions

Here is a powershell script that downloads all the mp4 video sessions available on Channel9. It first pulls all the HD videos, then checks the normal quality videos. It names the videos based on the session title. It will only download the video if it does not exist in the destination directory.


Update: Blogger doesn't want to show my GIST, so here is a direct link to it.

PowerShell Script to pull Album Art for missing directories

I recently went and downloaded Album Art Downloader to get some missing album art for some of the albums I have. But I quickly noticed that it would be a manual process to find the directories that needed the art added, and I am lazy.

Luckily there is a command line interface for AAD. But that alone would not accomplish what I wanted to do. I figured now would be a good time to try my hand at a powershell script.

This script will take each artist/album and use AAD to get album art and save it as folder.jpg. The script expects the root directory to contain “artist/album” hierarchy.

You will have to change the “root” and possibly the location of the aad.exe.