DSC: configuring a proxy

What’s the first thing you do when you configure a new server?
After configuring the network stack, the time and allowing remote desktop, I make sure that the server has access to Internet so that all the other operations (like downloading drivers, packages, Windows updates, activating Windows,…) that depend on this link run smoothly.

In a corporate environment, accessing the Internet is usually done through a proxy server.
I’ll share with you an old trick I’m using since Windows XP: how to configure a proxy per machine with Desired State Configuration (DSC) and avoid other admins messing with it 😀

You can configure proxy settings via

  • a response file (unattend.xml) when the computer is provisioned with Microsoft-Windows-IE-ClientNetworkProtocolImplementation component
  • Group policies
  • That’s out-of-scope as we don’t know if the machine is domain or workgroup joined

  • IE branding
  • Remember the IEAK, it’s based on a INS file located in C:\Program Files (x86)\Internet Explorer\Custom and some registry settings

    The problem with this approach is that you need to indicate the version of Internet Explorer in the INS file. That’s why it’s not a suitable way to proceed.

  • the Registry
  • This method doesn’t depend on the version of Internet Explorer and whether the machine is workgroup or domain joined. It’s thus the most suitable way to go 🙂

I’ve created 5 small DSC configurations to illustrate 4 basic scenarios and 1 to restore to user based proxy settings. (Note that you mix some scenarios together to match your needs and your environment configuration)

  • NoProxy.ps1 will define proxy settings per machine and configure it to access directly Internet
  • ProxyAutodetect.ps1 will define proxy settings per machine and let autodetect enabled (~default user config but per machine)
  • ProxyURL.ps1 will define a proxy configuration script URL (set to http://myproxy.fqdn/proxy.pac in my example below)
  • Proxy.ps1 will define a proxy address set to myproxy.fqdn on port 8888 for every protocols and bypass proxy server for local addresses
  • RestorePerUserProxy.ps1 will delete all per machine settings and rely back on user settings

(I’ve stored the samples as Gist files, so that you can use the OneGet Gist provider made by Doug Finke to get them 😉 )

If you want to use these configurations, you may need to modify my sample configurations and replace the binary value for both DefaultConnectionSettings and SavedLegacySettings. You actually need to capture them on a working computer where you configured manually Internet Explorer settings for your environment.
Then you can extract the binary value from the HKCU hive with the following code:

$regkey = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections"
-join ( 
(Get-ItemProperty -Path  $regkey -Name DefaultConnectionSettings).DefaultConnectionSettings |
 Foreach-Object { '{0:X2}' -f $_ })
-join ( 
(Get-ItemProperty -Path $regkey -Name SavedLegacySettings).SavedLegacySettings |
 Foreach-Object { '{0:X2}' -f $_ })

Here are the 5 small DSC samples with what you get in UI as a result. Enjoy 😎

  • NoProxy.ps1

  • ProxyAutodetect.ps1

  • ProxyURL.ps1

  • Proxy.ps1

  • RestorePerUserProxy.ps1

Retrieving scheduled tasks history

I had to check the history of custom scheduled tasks after OS upgrades on remote sites servers.

The scheduled task appeared to be working before the upgrade, looked good after the upgrade but was miserably failing at launch time:

Task Scheduler failed to load task “\My TaskName” at service startup. Additional Data: Error Value: 2147944189.
Whenever I launched the task manually, the schtasks.exe returned
ERROR: The system cannot find the file specified.

The solution consisted in deleting the task and creating it back. Creating it by overwriting the existing one didn’t make it actually.

I’ve used PowerShell to track precisely where the task was failing and where it succeeded after the above fix or not.
Because the bandwidth was quite low for the majority of remote sites servers, I had to limit the number of events and properties returned over remoting.

I’ve created the following XML query used with the Get-WinEvent cmdlet to query the history of my custom scheduled task (named ‘My TaskName’ below) in fairly decent amount of time.

Get-Content servers.txt | ForEach-Object { 
 $pc = $_
 try {
  invoke-command -ComputerName "$($_).my.fqdn" -ErrorAction Stop -ScriptBlock {
   try {
    $events = @(
     Get-WinEvent  -FilterXml @'
      <Query Id="0" Path="Microsoft-Windows-TaskScheduler/Operational">
       <Select Path="Microsoft-Windows-TaskScheduler/Operational">
        *[EventData/Data[@Name='TaskName']='\My TaskName']
'@  -ErrorAction Stop -MaxEvents 2
   } catch {
     Write-Warning -Message "Failed to query $($env:computername) because $($_.Exception.Message)"
   if ($events) {
    $events | Select MachineName,TimeCreated,Id,TaskDisplayName
  } # end of scriptblock
 } catch {
  Write-Warning -Message "Failed to contact $pc because $($_.Exception.Message)"
} | 
Select MachineName,TimeCreated,Id,TaskDisplayName |
Format-Table -AutoSize

Here is what it looks like when I query the history over the last 24 hours
(replace line 11 with this one in the above snippet and remove -MaxEvents 2 on line 15)

*[EventData/Data[@Name='TaskName']='\My TaskName'] and *[System[TimeCreated[timediff(@SystemTime) &lt;= 86400000]]]

Cleanup WSUS

I’ve never had to perform a WSUS cleanup for years, shame on me 😉

Lawrence Garvin, who answers all the WSUS maintenance related questions on the patchmanagement.org mailing list made me change my mind recently.

His post explains what are the benefits of performing maintenance tasks on a regular basis:

Source: http://marc.info/?l=patchmanagement&m=142075193424332&w=2

Having WSUS built-in Windows 2012 R2 servers makes it very easy with PowerShell 😀

Here’s what I do a few days later, after performing my usual Patch Tuesday duties (~synchronize and approve required updates):

  • Step 1: Decline all updates that have been superseded and not approved
$UpdateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$allupdates = (Get-WsusServer).GetUpdates($UpdateScope)
$allupdates | Where {
 ($_.IsSuperseded) -and
 -not($_.isApproved) -and
} | ForEach-Object -Process {            
  • Step 2: Use the built-in cmdlet to perform the maintenance and display results
$cleanupResults = Get-WsusServer | 
Invoke-WsusServerCleanup -CleanupObsoleteUpdates `
-CleanupUnneededContentFiles -CompressUpdates `
-DeclineExpiredUpdates -DeclineSupersededUpdates:$false -Verbose

'Diskspace Freed: {0:N2} GB' -f (
(($cleanupResults | sls -Pattern "^DiskSpace").Line -split ":")[1] / 1GB 

To summarize, I’ve now:

  • Updates being displayed faster in the console, when I select ‘Any Except Declined’
  • Compressed updates (less to transport over the wire)
  • Made some free space on the server (~ 1,5 GB in my case, the first time)
  • A cleanup maintenance task performing faster next month

PowerShell and WSUS, that rocks! – no doubt 😎 and a huge thanks to Lawrence Garvin for his excellent input on this subject 😀

Quicktips: is a reboot required after Windows updates installation

I’ve came across a nice tips when looking for something about the Windows Update Agent (WUA) API.

Let’s say you’ve performed updates installation but forgot to track the global result of the installation process.

There’s actually a way to query it afterward 😀

(New-Object -ComObject "Microsoft.Update.SystemInfo").

You can have it as a simple function as well:

Function Test-WUARebootRequired {
    try {
        (New-Object -ComObject "Microsoft.Update.SystemInfo").RebootRequired
    } catch {
        Write-Warning -Message "Failed to query COM object because $($_.Exception.Message)"