NAP, DHCP and Windows 10 – Nope!

I just spent a good hour trying to figure out why my Windows 10 clients were not getting assigned an IP Address from my DHCP servers once I enabled NAP integration on the scope. The reason of course is obvious: NAP was deprecated in Windows Server 2012 R2.

The NAP client is not available on Windows 10 computers. You can’t even see the Network Access Policy node when you edit a GPO using Windows 10 RSAT:

NAP on Windows 10? Nope.
So if you’re wanting to configure your Windows 10 computers with DHCP and you’re using NAP, you’ll need to disable it or create a special scope without NAP enabled with a DHCP Scope Policy for your Windows 10 clients. As this technology has been deprecated you’re probably better off removing NAP entirely. Pity I’m having to spend time studying it for my 70.411 exam.

Install an SSL WS-Management Listener with GPO

Introduction

One of the things I like to do when ever I install a new server is to enable an HTTPS/SSL WS-Management Listener on it so that I can disable the more insecure HTTP WS-Management listener. For more information on WS-Management Listeners see this MSDN article.

There are many benefits to using a secure HTTPS/SSL WS-Management Listener:

  1. Security – the communication channel between client and server is encrypted using SSL.
  2. Authentication – the server is authenticated to the client so you can trust you’re talking to the server you think you’re talking to.

The downside to this is that you need a valid and trusted server authentication certificate on the server to enable this – but if you have a PKI then this is no big deal as you’ll probably have certificate autoenrollment enabled. If you don’t have a PKI, then you should look into installing one.

Installing these listeners manually is fairly straight forward and requires only a single command:

New-WSManInstance -ResourceURI winrm/config/Listener `
-SelectorSet @{Address='*';Transport="HTTPS"} -ValueSet @{Hostname="SERVER01.CONTOSO.COM";CertificateThumbprint="09 49 93 24 53 81 32 16 b7 44 8b 47 ca af 56 3a ef 9f 10 2d"}

All you need to do is enter the appropriate hostname and certificate thumbprint for a server authentication certificate that exists on the server. But who wants to do this manually, right?

Installing with GPO

The slightly tricky part of installing this automatically onto your servers with a GPO is detecting which certificate to use. The certificate must:

  • exist in the local computer personal certificate store.
  • have an Extended Key Usage of Server Authentication.
  • be issued by a CA trusted by any client connecting to the server.
  • The Subject must contain a Common Name that contains either the FQDN computer name or the flat computer name (e.g. CN=SERVER1.CONTOSO.COM or CN=SERVER1).

It is easy to ensure a certificate meets these criteria by using a GPO enabling certificate autoenrollment for computer certificates and that the Computer autoenrollment certificate template will create certificates meeting these requirements. See this page for some basic information on certificate autoenrollment. There are some much more detailed instructions on this around the net if you’re happy to search.

Once you’ve ensured all your computers have been issued such a certificate you would normally need to lookup the certificate on each computer and get the certificate thumbprint and execute the command I showed earlier. This would be a complete pain on any more than 10 computers, and probably pure insanity at a lot of facilities.

So, I put together the following PowerShell commands that could be used to automatically pull the certificate thumbprint for an appropriate certificate:

[String] $Issuer = 'CN=CONTOSO.COM Issuing CA, DC=CONTOSO, DC=COM'
[String] $HostName = [System.Net.Dns]::GetHostByName($ENV:computerName).Hostname
[String] $Thumbprint = (get-childitem -Path Cert:\localmachine\my | Where-Object { 
		        ($_.Extensions.EnhancedKeyUsages.FriendlyName -contains 'Server Authentication') -and
		        ($_.IssuerName.Name -eq $Issuer) -and
		        ($HostName -in $_.DNSNameList.Unicode) -and
                ($_.Subject -eq "CN=$HostName") } | Select-Object -First 1
            ).Thumbprint

All you’d need to set was the Issuer to whatever the Distinguished Name of your issuing CA is – which should be the same for all computers. This simplifies things a lot because the same code could be run on any computer and should always return the correct thumbprint.

The next step was to put it into a script where you could just pass the Distinguished Name of the issuing CA as a parameters. I did this and also added some other optional parameters and uploaded the result to Microsoft Script Center. So you can download this script and put it into a GPO Startup Script:

Installing an HTTPS WS-Management Listener with GPO

Installing an HTTPS WS-Management Listener with GPO

The script is actually a little bit smarter than the above command. If a certificate with a subject can’t be found that matches the FQDN of the computer it will automatically look for one that just uses the flat computer name. You can control this behavior by setting the DNSNameType parameter.

There are some other optional parameters that control other the behavior of the script as well:

DNSNameType

The allowed DNS Name types that will be used to find a matching certificate. If set to FQDN then the script will only try to find a certificate with a subject matching the FQDN of the computer. If set to ComputerName it will only match on the computer name of the computer. By default this is set to Both which will try to match on FQDN first and then ComputerName if it can’t find one matching the FQDN.

MatchAlternate

The certificate found must also have an alternate subject name containing the DNS name found in
the subject as well. This places additional restrictions on the certificate that is used, but is not usually required. This defaults to False.

Port

This parameter lets you specify an alternate port to install the HTTPS/SSL listener on. If you don’t specify it, it will use the default port of 5986.

Don’t Forget your Firewall

It is important to remember that by default this listener is installed onto port 5986, which is not usually open inbound. So you’ll want to add a firewall rule to ensure this port can be reached – another job for GPO. You could even add this setting into the GPO that installs the HTTPS/SSL listener.

Installing with DSC

In theory it should be possible to adapt this code to run in a DSC Script Resource. I haven’t tried this yet, but you can be assured that I will fairly soon. If there was some interest in this I could convert it into a proper DSC Resource (unless there was one already – I haven’t checked). If you are interested, let me know.

Links

If you want to make a copy of the repository, you’ll find it here:

https://github.com/PlagueHO/WSManGPOTools

Right, that is me out for another Sunday. Thanks for reading.

Installing Windows Management Framework 5.0 with a GPO

The Need

Last week I decided I needed to get to know the new features that come with DSC in the new Windows Management Framework 5.0 (aka PowerShell 5.0) April Preview release (available to download here). I figured I’d also need to look at updating my DSCTools module to use the new features as well. But first up I’d need to update all my lab machines with the WMF 5.0 update. Being a proper lazy nerd I thought I’d just automate it.

The Problem

After downloading the .MSU file for installing WMF 5.0 from Microsoft I decided that WSUS would be the proper way to get this out to all my machines. Nope. Wrong! WSUS only supports pushing updates that appear in the Microsoft Update Catalogue (looking for Windows Management Framework) – but no update packages greater than Windows Management Frameworks 2.0 are available in the catalogue:

Where is PowerShell 5.0? Or 4.0? Or 3.0?

Where is PowerShell 5.0? Or 4.0? Or 3.0?

As an aside after doing a bit of reading on the reason for this it appears that updating to later versions of PS can cause problems with some applications and so it is kept as a manual process.

Next on the drawing board was push the update out using GPO’s software installation policies – except it only supports MSI files. So that won’t work (although converting an MSU to an MSI might be possible according to some sources).

The Solution

Finally I settled on pushing the MSU onto the machines by using a GPO Startup Script – of the PowerShell flavour of course. It seemed like I could just adapt the PS script I wrote for installing Office 2013 via GPO. After a bit of coding I had the update installing (in silent mode of course).

The next problem was that I needed some sort of registry key that could be checked to see if the update was already installed so it didn’t try to repeatedly reinstall every time the computer started up. I spent a few hours hunting around for information on where in the registry a record of installed updates was kept and couldn’t seemed to find any.

So instead I just used a simple WMI query to find out if the update was installed:

get-wmiobject -Class win32_QuickFixEngineering -Filter "HotfixID = 'KB2908075'"

The above command will return a list of KBs installed with a matching HotfixID. There will be 0 if the KB is not installed, so to use it I needed to count the objects returned:

(get-wmiobject -Class win32_QuickFixEngineering -Filter "HotfixID = 'KB2908075'" | Measure-Object).Count -gt 0

If the MSU or EXE (did I mention this script will work with hotfixes in EXE form) did indeed need installing the script just builds a command line and calls it:

If ([io.path]::GetExtension($InstallerPath) -eq '.msu') {
    [String]$Command="WUSA.EXE $InstallerPath /quiet /norestart)"
    [String]$Type="MSU $KBID"
} else {
    [String]$Command="$InstallerPath /quiet /norestart"
    [String]$Type="EXE $KBID"
}

# Call the product Install.
& cmd.exe /c "$Command"
[Int]$ErrorCode = $LASTEXITCODE

The Script

So after putting all the pieces together I had a finished script that seemed to do exactly what I needed. It can be downloaded from the Microsoft Script Center PowerShell Scripts to Install Application (EXE) or Update (MSU) using GPO. It is also available in my GitHub repo (edit: I moved this out of my generic PowerShell tools repo and into it’s own repo).

Warning

Before getting started, I strongly suggest you read my post about the problems encountered using PowerShell parameters in GPO here. There are some really annoying issues with PowerShell script parameters in GPO that will have you tearing your hair out – luckily I tore my hair out for you.

Using It

  1. Download the PowerShell Scripts to Install Application (EXE) or Update (MSU) using GPO from Microsoft Script Center.
  2. Download the MSU/EXE update file and put it in a shared folder all of your machines can get to. I used \\plague-pdc\Software$\Updates\WindowsBlue-KB3055381-x64.msu
  3. Create a new GPO that will be used to install the update – I called mine Install WMF 5.0 Policy:New Policy
  4. Locate the Startup setting under Computer Configuration/Policies/Windows Settings/Scripts:The Startup Script of a Computer GPO.
  5. Double click the Startup item to edit the scripts and select the PowerShell Scripts tab:
    Setting the Startup PowerShell Scripts
  6. Click the Show Files button and copy the Install-Update.ps1 file that was in the zip file downloaded from Microsoft Script Center into this location:GPO Folder containing script
  7. Close the folder and go back to the Startup Properties window and click the Add button.
  8. Set the Script Name to Install-Update.ps1.
  9. Set the Script Parameters to be the following (customized the bold sections to yourenviroment):
     -InstallerPath "\\plague-pdc\Software$\Updates\WindowsBlue-KB3055381-x64.msu" -KBID "KB3055381" -LogPath \\plague-pdc\LogFiles$\

    Note: You can leave off the -LogPath parameter if you don’t want to create logs stating if the update was installed correctly on each machine.

    Add the script and parameters

  10. Click OK.
  11. Click OK on the Startup Properties window.
  12. Close the GPME window and assign the policy to whichever OUs you want to try WMF 5.0 out on.

That was probably a lot more detail than most people needed, but I thought I throw it in there just in case.

If you require more details on the parameters available in the script, use the PowerShell cmdlet Get-Help .\Install-Update.ps1 in the folder that Install-Update.ps1 is installed into – or just look at the PowerShell Scripts to Install Application (EXE) or Update (MSU) using GPO Microsoft Script Center page.

Installing an Application Script

While I was at it I also decided that I wanted to install applications contained in an EXE by this method too (Notepad++ was my excuse). This required a slightly different approach to detect if the application was already installed. Basically depending on the application a registry key and/or value needs to be checked to see if the application needs installation. These registry entries differ for every application being installed so which keys to check for an app need to be passed in via parameters to the PowerShell script.

For example, for Notepad++ version 6.7.8.2 the registry key HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Notepad++ must exist and requires a string value called DisplayVersion to be equal to “6.7.8.2”.

I also wrote a script, Install-Application.ps1 to do all this as well, and it is available in the same package as the Install-Update.ps1 script. I will write a separate blog post on using this one as it can be a little bit trickier thanks to the limitations with passing parameters to PS scripts in GPOs. So I’ll leave this post till next week.

Thanks for reading!

PowerShell Paramters in GPO Scripts

introduction

This morning I decided I wanted to update all my lab servers to Windows Management Framework 5.0 so I could do some work on the new DSC features that come with it. To do this, I though I’d use a GPO with a startup PowerShell script that would perform the installation of the WMF 5.0 April hotfix (available here).

A GPO Startup PowerShell script with parameters.

A GPO Startup PowerShell script with parameters.

On thinking about this I decided it might also be a good idea to modify the PowerShell script designed to install Microsoft Office 2013 products via GPO (see the post here). After producing the new scripts and testing them by manually running them to ensure they worked correctly, I put them into some GPOs.  And that is when things started to go wrong!

Parameter Problems

The issue I ran into was that the parameters set in the GPO PowerShell script parameters seemed to misbehave in several ways. After about 6 hours of investigating and testing I’ve found the following things cause problems when you do them.

Parameter Length Limit

There seems to be a maximum number of characters that will be used in the Script Parameters setting in a GPO Startup/Shutdown/Logon/Logoff PowerShell script. The limit appears to be 207 but I can’t find official documentation of this. If script parameters longer than this limit is entered the additional characters will simply be ignored, leading to the script either failing to run or running incorrectly.

If you do run into this issue, one way around it is to edit the script and wrap all the code in a function definition and then add a call with the parameters to the end of the script after the end of the function definition. For Example:

Function Install-Application {
... Existing script code here ...
}
Install-Application -InstallerPath "\\Server\Software$\Notepad++\npp.6.7.8.2.Installer.exe" -InstallerParameters "/S" -RegistryKey "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Notepad++" -RegistryName "DisplayVersion" -RegistryValue "6.7.8.2" -LogPath \\Server\Logfiles$\

The PowerShell script parameters in the GPO can then be dropped as they are contained in the script itself – this is not ideal, but unless Microsoft lifts this limitation it may be required.

Parameter Quotes

There is also some odd behaviour passing parameters with quotes (single or double) to the PowerShell scripts in a GPO. I have run into several situations where the use of quotes causes the parameters to either not be passed to the script or passed with additional quotes in them. I recommend the following:

  1. DO NOT use single quotes around any parameters – use double quotes around string parameters only.
  2. DO NOT end the parameter list with a quoted parameter – this seems to cause the last parameter content to contain an extra quote.

Summary

In short, if you stick to the above when calling PowerShell scripts with parameters from GPO then you might save yourself a lot of time scratching your head.

As a quick aside, the scripts I wrote as part of this (for installing Windows QFE Hotfixes and Applications via GPO) are available on Microsoft Script Center here. I will be writing a full post on these scripts later in the week.

Danrus

Using PowerShell to Install/Uninstall Microsoft Office Products by Group Policy

I’ve been recently doing some experimentation with AD RMS templates and AD FS integration in my lab environment. Clearly, I lead a very exciting life. Of course to test AD RMS templates one needs a copy of Office installed into the lab environment. This, I thought, would be a good opportunity to configure Office (Office 2013 Pro Plus to be precise) to be installed via Group Policy.

I of course read the Deploy Office 2013 by using Group Policy computer startup scripts documentation on TechNet, which directed me to use GPOs which called batch files on computer start-up. This is all simple enough and works well, but being a bit of a PowerShell fiend I’d prefer to use it whenever I can. Since Windows Server 2008 R2 and above supports PowerShell scripts for GPO startup and shutdown I thought I’d write some general purpose PowerShell scripts that could be used in place of the old batch file scripts:

PowerShell Scripts to Install/Uninstall Office 2013 products using GPO

As a side note – I wonder why Microsoft doesn’t support installing Office 2013 products using the standard Software Deployment policies in GPO (assigning, publishing etc). But that is a question only Microsoft can answer.

These PowerShell scripts accept parameters that allow key elements of the installation process (product, source folder, config file, log file location) to be specified in the GPO Script parameters themselves:

GPO PowerShell Startup Script

For example, to install a copy of Microsoft Office 2013 Pro Plus from the \\server\software$\Office15.ProPlus\ share using the file \\server\software$\Office15.ProPlus\ProPlus.ww\SilentInstall.xml to control the configuration the following parameters could be used:

-ProductId "Office15.ProPlus" -SourcePath "\\server\software$\Office15.ProPlus\" -ConfigFile "\\server\software$\Office15.ProPlus\ProPlus.ww\SilentInstall.xml"

The full script parameters are documented in the PowerShell scripts as well as on the Microsoft Script Repository page along with additional examples. The scripts can also accept a parameter for controlling where a brief log file can be written to so that it is easy to see if the product has been successfully installed on each machine that has been assigned the GPO.

Creating a GPO using the PowerShell Scripts

  1. In Group Policy Management Console, create a new GPO:ss_gpmc_new_gpo
  2. Enter a name for the installation policy and click OK.ss_gpmc_new_gpo_name
  3. Right click on the new policy and select Edit:ss_gpmc_gpo_edit
  4. Select the Computer Configuration\Policies\Windows Settings\Scripts (Startup/Shutdown) node:ss_gpmc_edit_startup_script
  5. Double click the Startup item:ss_gpmc_startup_properties_scripts
  6. Select the PowerShell Scripts tab:ss_gpmc_startup_powershell_scripts
  7. Click Add to add a new startup script:ss_gpmc_startup_powershell_addascript
  8. Click the Browse button to locate the folder where the policies scripts should be stored:ss_gpmc_startup_scripts_location
  9. You will need to copy the PowerShell scripts downloaded from the Microsoft Script Repository into this folder and select the one that should be used with this policy.
  10. Enter the PowerShell script parameters into the Script Parameters box:ss_gpmc_startup_powershell_script_properties
  11. Click OK to save the PowerShell startup script:ss_gpmc_startup_powershell_scrpts_configured
  12. Click OK to save the Startup Properties.
  13. Close the GPME and apply the new GPO.

Important Note about PowerShell Script Parameters

There appears to be a limit to the maximum number of characters supported in the GPO Startup/Shutdown PowerShell script parameters. This limit seems to be 207 characters, but I haven’t been able to confirm this anywhere. Although more than this number of characters can be entered into the Parameter text box, anything above 207 does not get passed through to the script, which either causes the script to run incorrectly or not at all.

If you do encounter this limit, but still need additional parameters passed, you could use positional parameters to reduce the overhead or create another script that calls these scripts with the defined parameters.

Hopefully, someone will find this useful. If you have any comments or requests for improvements of the scripts, don’t hesitate to let me know.