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.