Richard J Green

List Updates on Windows Nano Server 2016

Windows Server 2016 introduced the new SKU, Nano Server. Nano Server is an extremely low footprint operating system designed for micro services and rapid deployment and provisioning and currently supports roles including Failover Clustering, Hyper-V, File Server, Web Server and DNS Server.

With Nano Server being completely headless and at this moment in time, not supporting a Configuration Manager agent for managing operating system patches, there needs to be a way for you to to track and manage patching on them. At home I run two Nano Server hosts using Hyper-V to host some virtual machines and a third running inside a VM for some testing workloads. I decided I wanted to script a way of at least going some way to automate the patching.

The first script below lists the updates that your Nano Server has installed already for reporting purposes. The second lists the updates which are available and require installation. It’s worth noting that for this to work, your Nano Server machines will need access to an update service to find out what updates are available, be it Microsoft Update or WSUS. If you are reading this thinking that you didn’t know Nano Server could use WSUS, well sure it can, you just need to populate the same registry keys you would on a normal Windows machine.

The code for returning the list of updates comes direct from the Microsoft Blog at https://blogs.technet.microsoft.com/nanoserver/2016/10/07/updating-nano-server/ however this assumes a manual process so I have wrapped this up to provide a level of automation.

List Currently Installed Updates

# Get the Admin Credentials
$cred = Get-Credential

# List the Nano Hosts
$nanoHosts = "HOST1","HOST2"

ForEach ($nanoHost in $nanoHosts) {
    $session = New-PSSession -Credential $cred -ComputerName $nanoHost
    
    Invoke-Command -Session $session -ScriptBlock {
        # List Updates which are Available to the Host
        $cim = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession
        $requiredUpdates = $cim | Invoke-CimMethod -MethodName ScanForUpdates -Arguments @{SearchCriteria="IsInstalled=1";OnlineScan=$true}
        $requiredUpdates.Updates | Select Title
    }

    Remove-PSSession $session
}

Currently, I have a manually defined parameter called $nanoHosts which lists the servers however read on for another option.

List Updates Not Installed (Missing)

Here is the second script and the eagle eyed will spot it is actually the same except for the change from IsInstalled=1 to IsInstalled=0. Quite simply, we are saying either show the updates which are installed or those which are not.

# Get the Admin Credentials
$cred = Get-Credential

# List the Nano Hosts
$nanoHosts = "HOST1","HOST2"

ForEach ($nanoHost in $nanoHosts) {
    $session = New-PSSession -Credential $cred -ComputerName $nanoHost
    
    Invoke-Command -Session $session -ScriptBlock {
        # List Updates which are Available to the Host
        $cim = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession
        $requiredUpdates = $cim | Invoke-CimMethod -MethodName ScanForUpdates -Arguments @{SearchCriteria="IsInstalled=0";OnlineScan=$true}
        $requiredUpdates.Updates | Select Title
    }

    Remove-PSSession $session
}

Replace the Host List with an Active Directory Group

Wanting to take this further, I started thinking how instead of using a static hosts list, I could automate that step too. My first thought was to use Active Directory and the operatingSystem attribute within a Get-ADComputer query however Nano Server returns the same Windows Server 2016 value as a normal Server Core or GUI installation which threw that out. Chatting it over with my friend @LupoLoopy, he came up with an idea for an auto-populating group in Active Directory.

To use this method, we need to run a script somewhere on the network at regular intervals. This script, would consist of something along these lines. Whilst I have tested this functionality in parallel to the above scripts, I would hasten to add, anyone with a large environment may want to consider this twice as the script will be performing a PowerShell Remoting login to each one of your Windows Server 2016 machines which could generate some extra load.

# Get the Nano Server Group Name
$nanoServerGroup = "Nano_Server_Systems_GG"

# Get the Admin Credentials
$cred = Get-Credential

# Try and Import the Active Directory PowerShell Module
$ErrorActionPreference = "Stop"
Try {
    Import-Module ActiveDirectory
} Catch {
    Write-Error "Unable to import the Active Directory PowerShell module." -RecommendedAction "Check that the module is installed and available."
}

# Get all Computers from Active Directory which are Windows Server 2016
$domain = Get-ADDomain | Select DistinguishedName
$servers = Get-ADComputer -Filter {operatingSystem -like "Windows Server 2016*"} -SearchBase $domain.DistinguishedName

# Get the Nano Servers from the list of Windows Server 2016 servers.
ForEach ($server in $servers) {
    $result = Invoke-Command -ComputerName $server.Name -Credential $cred -ScriptBlock {
        Get-ComputerInfo | Select OsServerLevel
    }

    If ($result.OsServerLevel -like "NanoServer") {
        Write-Host "Host $server is a Nano Server, adding it to the Active Directory Group." -ForegroundColor Green
        Add-ADGroupMember -Credential $cred -Identity $nanoServerGroup -Members $server.SamAccountName 
    } Else {
        Write-Host "Host $server is not a Nano Server. Skipping this Host." -ForegroundColor Yellow
    }
}

If you like this option when in the first two scripts which report the installed and missing updates, replace the $nanoHosts parameter with the following codeblock. This will perform a lookup in Active Directory for the group members instead of using the static list.

$nanoServerGroup = "Nano_Server_Systems_GG"

# Try and Import the Active Directory PowerShell Module
$ErrorActionPreference = "Stop"
Try {
Import-Module ActiveDirectory
} Catch {
Write-Error "Unable to import the Active Directory PowerShell module." -RecommendedAction "Check that the module is installed and available."
}

# Collect the Active Directory Group Members
Get-ADGroupMember $nanoServerGroup | Select Name

I know this has been quite a coderific post so I hope that someone out there running Nano Servers in their environment finds it useful.

Exit mobile version