Windows Server How-To
How To Patch Nano Server, Part 2: Patching Multiple Servers
By modifying Microsoft's provided Powershell patching code, applying updates to multiple Nano Servers can be simplified.
In my first post in this series, I showed you a few blocks of PowerShell code that were provided by Microsoft for the purposes of patching Nano Server. Although this code gets the job done, it isn't perfect. As written, the code doesn't scale very well. In my lab environment, I found that it took 10 to 15 minutes to patch each Nano Server, and each server had to be patched separately. As such, I wanted to show you how you could modify the code to make it more efficient.
The first thing that I would recommend doing is to copy the code into a PowerShell script, and remove any unnecessary lines of code. This will not only help to automate the patching process, getting rid of unnecessary code will help the patching process to complete more quickly.
Believe it or not, the code can be shortened significantly. Much of the code used in the previous post existed for the purposes of listing the available updates or reporting on which updates have been applied. If you only want to apply any available updates, then you can do so by using this code:
Enter-PSSession -ComputerName (Read-Host "Enter Nano Server IP address") -Credential (Get-Credential)
$ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession
Invoke-CimMethod -InputObject $ci -MethodName ApplyApplicableUpdates
Restart-Computer; exit
OK, so we have simplified the code, and the code runs a bit faster because we aren't taking the time to display the available updates or to confirm the updates that have been installed. Even so, the code isn't exactly scalable in its current form because it still requires us to manually enter the server's IP address and a set of administrative credentials. Fortunately, we can change that.
Let's deal with the administrative credential problem first. Rather than requiring an administrator to enter a username and password, it is possible to save the credentials to an encrypted file and use that file to supply the credentials when necessary. When using this technique, there are two things to keep in mind. First, I am assuming that all of your Nano Servers use the same set of credentials. Second, you will need to store the password file in a secure location where it cannot be compromised. Although the file is encrypted, anyone with access to the file and a bit of technical know-how can use it to supply credentials to a server. So with that said, the command used for creating the password file is:
Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File C:\temp\Password.txt
This command creates a file named Password.txt in the C:\temp folder. You can see how this works, and the contents of the resulting file in Figure 1.
It takes two lines of code to import the account credentials into a script from the password file. Those lines of code are:
$Password = Get-Content C:\temp\password.txt | ConvertTo-SecureString
$Cred = New-Object -Typename System.Management.Automation.PSCredential -ArgumentList 'Administrator', $Password
These lines of code create a variable named $Cred that stores the username (I am using Administrator in this example), and the password from the password file. Now we can modify the first line of the script to use the $Cred variable rather than requiring the credentials to be entered manually. Here is what the new line of code looks like:
Enter-PSSession -ComputerName (Read-Host "Enter Nano Server IP Address") -Credential $Cred
OK, so we have dealt with the credential problem, but what can we do to make the script work with multiple servers? For that, we can create a For Each loop. Here is what such a loop might look like:
$Servers = @("192.168.0.1","192.168.0.2","192.168.0.3")
ForEach ($Server in $Servers)
{
Do something useful
}
The first line of code above creates an array called $Servers. This array contains the IP address for each of my Nano Servers. The second line of code sets up the loop. We are literally telling PowerShell to do something useful on each server in the list of servers.
OK, so it's time to put all of this together. Here is what the finished code looks like:
$Password = Get-Content C:\temp\password.txt | ConvertTo-SecureString
$Cred = New-Object -Typename System.Management.Automation.PSCredential -ArgumentList 'Administrator', $Password
$Servers = @("192.168.0.1","192.168.0.2","192.168.0.3")
ForEach ($Server in $Servers)
{
Exit-PSSession
Enter-PSSession -ComputerName $Server -Credential $Cred
$ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession
Invoke-CimMethod -InputObject $ci -MethodName ApplyApplicableUpdates
Restart-Computer -ComputerName $Server -Credential $Cred
}
About the Author
Brien Posey is a 22-time Microsoft MVP with decades of IT experience. As a freelance writer, Posey has written thousands of articles and contributed to several dozen books on a wide variety of IT topics. Prior to going freelance, Posey was a CIO for a national chain of hospitals and health care facilities. He has also served as a network administrator for some of the country's largest insurance companies and for the Department of Defense at Fort Knox. In addition to his continued work in IT, Posey has spent the last several years actively training as a commercial scientist-astronaut candidate in preparation to fly on a mission to study polar mesospheric clouds from space. You can follow his spaceflight training on his Web site.