WMI

Lenovo BIOS Manipulation: Set-LenovoBIOSSetting

Changing a BIOS setting on a Lenovo, is a two step process:

  • Change the setting(s) in question
  • Commit (or Save) the all of your changes

Explanation

The script supports execution against remote systems to silently and easily toggle various BIOS settings.

By default the script relies on Get-LenovoBIOSSetting for validation purposes:

  1. It confirms the Setting you want to change is a valid setting.
  2. It confirms the count of the results returned from the previous command is equal to 1.
    This is done to ensure you specified an explicit setting like ‘Boot Up Num-Lock Status’ and not something generic like ‘Boot%’ which could return ‘Boot Agent’, ‘Boot Up Num-Lock Status’, ‘BootMode’, ‘BootOrder’ and so on.
  3. Finally, it will attempt to validate the Value you specified by evaluating the ‘OptionalValue’s returned by query.

Once it passes validation – or you bypass it – it then proceeds.

However there is a safety net and that is the requirement of the -Commit parameter to actually flip the bits.  If the -Commit parameter isn’t included, which is the default, it doesn’t make any changes.

Function Set-LenovoBIOSSetting


#region Function Set-LenovoBIOSSetting
Function Set-LenovoBIOSSetting
    {
        [cmdletbinding(SupportsShouldProcess=$True)]
        Param
            (
                [Parameter(Mandatory=$false)]
                    [string[]]$ComputerName = $env:computername,

                [Parameter(Mandatory=$true)]
                    [string]$Setting,

                [Parameter(Mandatory=$true)]
                [Alias('NewValue')]
                    [string]$Value,

                [Parameter(Mandatory=$false)]
                    [switch]$SkipCheck = $false,

                [Parameter(Mandatory=$false)]
                    [switch]$Commit
            )

        Begin { $NewValue = $Value }

        Process
            {
                foreach($Computer in $ComputerName)
                    {
                        if($SkipCheck -eq $false)
                            {
                                # Validate Setting - Part 1: Ensure this is a legitimate settng in the BIOS
                                $CurrentSettings = Get-LenovoBIOSSetting -ComputerName $Computer -Setting $Setting
                                if([string]::IsNullOrEmpty($CurrentSettings)) { Write-Output "ERROR INVALID SETTING SPECIFIED: [$Setting]"; throw }

                                # Validate Setting - Part 2: Ensure an 'exact' BIOS setting was specified like 'USB Port 1' not 'USB%' which could match several.
                                if(@($CurrentSettings).Count -gt 1) { Write-Output "ERROR SETTING [$Setting] RETURNED TOO MANY MATCHES: [$($CurrentSettings.Setting -join ', ')]"; throw }

                                # Validate New Value - If the OptionalValue isn't 'N/A' then we can verify the user supplied
                                if($CurrentSettings.OptionalValue -ne 'N/A')
                                    {
                                        if($($CurrentSettings.OptionalValue -split ',') -cnotcontains $Value) { Write-Output "ERROR: INVALID VALUE SPECIFIED [$Value] - MUST BE ONE OF: [$($CurrentSettings.OptionalValue)]"; throw }
                                    }

                                if($CurrentSettings.CurrentValue -eq $NewValue) { Write-Output "No change made for [$Setting] on [$Computer] because new and current values are identical:`r`nCurrent Value: [$($CurrentSettings.CurrentValue)]`r`nUpdated Value: [$NewValue]"; return }

                                $Action = "from [$($CurrentSettings.CurrentValue)] to [$NewValue]"
                            }
                        else { $Action = "to [$NewValue]" }

                        if($PSCmdlet.ShouldProcess("Setting [$Setting] on [$Computer] $Action"))
                            {
                                If($Commit -eq $true)
                                    {
                                        $OriginalEAP = $ErrorActionPreference
                                        $ErrorActionPreference = 'Stop'

                                        Try { $SetResult = (gwmi -class Lenovo_SetBiosSetting -namespace root\wmi -ComputerName $Computer -ErrorAction Stop).SetBiosSetting("$Setting,$NewValue") }
                                        Catch { Write-Output "ERROR: FAILED TO SET BIOS SETTING [$Setting] TO [$NewValue] ON [$Computer]: $_"; throw }

                                        $ErrorActionPreference = $OriginalEAP

                                        Switch($SetResult.return)
                                            {
                                                'Success' { Write-Output "$($SetResult.Return)fully set [$Setting] on [$Computer] $Action" }
                                                default
                                                    {
                                                        Write-Output "ERROR: FAILED TO SET [$Setting] TO [$NewValue] ON [$Computer]: [$($SetResult.Return)]"
                                                        if($Firmware -eq 'UEFI') { Write-Output "HINT: Firmware is UEFI [$Firmware] and you //may// first need to disable either 'OS Optimized Defaults' or 'SecureBoot' as these lock certain settings in the BIOS." }
                                                        throw
                                                    }
                                            }
                                    }
                                Else
                                    {
                                        Write-Output "WARNING: Commit is false [$Commit]; NOT Changing BIOS Setting [$Setting] on [$Computer] $Action!"
                                    }
                            }
                    }
            }

        End {  }
    }
#endregion Function Set-LenovoBIOSSetting

One Final Tidbit: Save-LenovoBIOSSettings

This one is important and I intentionally put this down here at the bottom.  Like I mentioned in the beginning, making BIOS change is a two step process, and so far we’ve only done one of the two.

The final step is to save the changes so that they’re committed.  Yes, I realize I’m using the parameter -Commit above which may be a little confusing but that’s just to make sure you don’t accidentally shoot yourself in the foot: write a change you didn’t mean to write and then forget what you had previously, so you struggle to get back to where you were before.

I intentionally didn’t include the ‘save’ code in the function above because I prefer to set all the changes first:

  • Switch to UEFI via ‘Boot Mode’ and ‘Boot Priority’ or ‘SecureBoot’
  • Set boot device order
  • Enable/Disable USB ports
  • Enable/Disable Bluetooth
  • Enable/Disable IPv4NetworkStack and/or IPv6NetworkStack
  • And so on…

Once all the changes are staged and no errors were encountered, I save the settings once via a call to a function which also requires the -Commit parameter to help prevent accidental foot shooting:


#region Function Set-LenovoBIOSSetting
Function Set-LenovoBIOSSetting
    {
        [cmdletbinding(SupportsShouldProcess=$True)]
        Param
            (
                [Parameter(Mandatory=$false)]
                    [string[]]$ComputerName = $env:computername,

                [Parameter(Mandatory=$true)]
                    [string]$Setting,

                [Parameter(Mandatory=$true)]
                [Alias('NewValue')]
                    [string]$Value,

                [Parameter(Mandatory=$false)]
                    [switch]$SkipCheck = $false,

                [Parameter(Mandatory=$false)]
                    [switch]$Commit
            )

        Begin { $NewValue = $Value }

        Process
            {
                foreach($Computer in $ComputerName)
                    {
                        if($SkipCheck -eq $false)
                            {
                                # Validate Setting - Part 1: Ensure this is a legitimate settng in the BIOS
                                $CurrentSettings = Get-LenovoBIOSSetting -ComputerName $Computer -Setting $Setting
                                if([string]::IsNullOrEmpty($CurrentSettings)) { Write-Output "ERROR INVALID SETTING SPECIFIED: [$Setting]"; throw }

                                # Validate Setting - Part 2: Ensure an 'exact' BIOS setting was specified like 'USB Port 1' not 'USB%' which could match several.
                                if(@($CurrentSettings).Count -gt 1) { Write-Output "ERROR SETTING [$Setting] RETURNED TOO MANY MATCHES: [$($CurrentSettings.Setting -join ', ')]"; throw }

                                # Validate New Value - If the OptionalValue isn't 'N/A' then we can verify the user supplied
                                if($CurrentSettings.OptionalValue -ne 'N/A')
                                    {
                                        if($($CurrentSettings.OptionalValue -split ',') -cnotcontains $Value) { Write-Output "ERROR: INVALID VALUE SPECIFIED [$Value] - MUST BE ONE OF: [$($CurrentSettings.OptionalValue)]"; throw }
                                    }

                                if($CurrentSettings.CurrentValue -eq $NewValue) { Write-Output "No change made for [$Setting] on [$Computer] because new and current values are identical:`r`nCurrent Value: [$($CurrentSettings.CurrentValue)]`r`nUpdated Value: [$NewValue]"; return }

                                $Action = "from [$($CurrentSettings.CurrentValue)] to [$NewValue]"
                            }
                        else { $Action = "to [$NewValue]" }

                        if($PSCmdlet.ShouldProcess("Setting [$Setting] on [$Computer] $Action"))
                            {
                                If($Commit -eq $true)
                                    {
                                        $OriginalEAP = $ErrorActionPreference
                                        $ErrorActionPreference = 'Stop'

                                        Try { $SetResult = (gwmi -class Lenovo_SetBiosSetting -namespace root\wmi -ComputerName $Computer -ErrorAction Stop).SetBiosSetting("$Setting,$NewValue") }
                                        Catch { Write-Output "ERROR: FAILED TO SET BIOS SETTING [$Setting] TO [$NewValue] ON [$Computer]: $_"; throw }

                                        $ErrorActionPreference = $OriginalEAP

                                        Switch($SetResult.return)
                                            {
                                                'Success' { Write-Output "$($SetResult.Return)fully set [$Setting] on [$Computer] $Action" }
                                                default
                                                    {
                                                        Write-Output "ERROR: FAILED TO SET [$Setting] TO [$NewValue] ON [$Computer]: [$($SetResult.Return)]"
                                                        if($Firmware -eq 'UEFI') { Write-Output "HINT: Firmware is UEFI [$Firmware] and you //may// first need to disable either 'OS Optimized Defaults' or 'SecureBoot' as these lock certain settings in the BIOS." }
                                                        throw
                                                    }
                                            }
                                    }
                                Else
                                    {
                                        Write-Output "WARNING: Commit is false [$Commit]; NOT Changing BIOS Setting [$Setting] on [$Computer] $Action!"
                                    }
                            }
                    }
            }

        End {  }
    }
#endregion Function Set-LenovoBIOSSetting

Obviously you’re welcome to do as you wish in your environment, even roll the execution of the WMI method to the Set-LenovoBIOSSettings function.

Just be safe and sure of what you’re doing 🙂

Good Providence to you as you reconfigure your BIOS’!

Lenovo BIOS Manipulation: Get-LenovoBIOSSetting

First An Apology

I owe you an apology because I forgot to follow up: A year ago I posted about getting ‘pretty’ BIOS data via PowerShell+WMI but never followed up with a function/script; which I’ve been using for sometime now.  I lost sight of this so sorry about that.

Explanation

There really isn’t much to this: Execute Get-BIOSSetting without any parameters to pull ALL the BIOS settings from the current machine.

If you’re on a ThinkPad Laptop (T440 through T470, X240 through X270) it’ll output data like this


ComputerName    Setting                             CurrentValue                          OptionalValue
------------    -------                             ------------                          -------------
Leno-T470s AdaptiveThermalManagementAC         MaximizePerformance                   N/A
Leno-T470s AdaptiveThermalManagementBattery    Balanced                              N/A
Leno-T470s AlwaysOnUSB                         Enable                                N/A
Leno-T470s AMTControl                          Enable                                N/A
Leno-T470s BIOSPasswordAtBootDeviceList        Disable                               N/A
Leno-T470s BIOSPasswordAtReboot                Disable                               N/A
Leno-T470s BIOSPasswordAtUnattendedBoot        Enable                                N/A
Leno-T470s BIOSUpdateByEndUsers                Enable                                N/A
Leno-T470s BluetoothAccess                     Enable                                N/A
Leno-T470s BootDeviceListF12Option             Enable                                N/A
Leno-T470s BootDisplayDevice                   LCD                                   N/A
Leno-T470s BootMode                            Quick                                 N/A
Leno-T470s BootOrder                           USBCD:USBFDD:NVMe0:HDD0:USBHDD:PCILAN N/A
...
Leno-T470s USBPortAccess                       Enable                                N/A
Leno-T470s VirtualizationTechnology            Disable                               N/A
Leno-T470s VTdFeature                          Disable                               N/A
Leno-T470s WakeByThunderbolt                   Enable                                N/A
Leno-T470s WakeOnLAN                           ACOnly                                N/A
Leno-T470s WakeOnLANDock                       Enable                                N/A
Leno-T470s WiGig                               Enable                                N/A
Leno-T470s WiGigWake                           Disable                               N/A
Leno-T470s WindowsUEFIFirmwareUpdate           Enable                                N/A
Leno-T470s WirelessAutoDisconnection           Disable                               N/A
Leno-T470s WirelessLANAccess                   Enable                                N/A
Leno-T470s WirelessWANAccess                   Enable                                N/A

However if you’re on a ThinkCentre, like a M93, M900, M910 it’ll output something like this


ComputerName    Setting                                   CurrentValue                                                                                                              OptionalValue
------------    -------                                   ------------                                                                                                              -------------
Leno-M910 After Power Loss                          Last State                                                                                                                      Power On,Power Off,Last State
Leno-M910 Alarm Date(MM/DD/YYYY)                    [01/01/2016][Status:ShowOnly]                                                                                                   N/A
Leno-M910 Alarm Day of Week                         Sunday                                                                                                                          Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,SaturdayStatus:ShowOnly
Leno-M910 Alarm Time(HH:MM:SS)                      [00:00:00][Status:ShowOnly]                                                                                                     N/A
Leno-M910 Allow Flashing BIOS to a Previous Version Yes                                                                                                                             No,Yes
Leno-M910 Automatic Boot Sequence                   Network 1:M.2 Drive 1:PCIE4X_1 Drive:PCIE16X_1 Drive:SATA 1:SATA 2:SATA 3:SATA 4:Other Device                                   Network 2:Network 3:Network 4:USB FDD:USB HDD:USB CDROM:USB KEY
Leno-M910 Boot Agent                                PXE                                                                                                                             Disabled,PXE
Leno-M910 Boot Up Num-Lock Status                   On                                                                                                                              Off,On
...
Leno-M910 User Defined Alarm Time                   [00:00:00][Status:ShowOnly]                                                                                                     N/A
Leno-M910 VT-d                                      Enabled                                                                                                                         Disabled,Enabled
Leno-M910 Wake from Serial Port Ring                Primary                                                                                                                         Primary,Automatic,Disabled
Leno-M910 Wake on LAN                               Automatic                                                                                                                       Primary,Automatic,Disabled
Leno-M910 Wake Up on Alarm                          Disabled                                                                                                                        Single Event,Daily Event,Weekly Event,Disabled,User Defined
Leno-M910 Wednesday                                 Disabled                                                                                                                        Disabled,EnabledStatus:ShowOnly
Leno-M910 Windows UEFI Firmware Update              Enabled                                                                                                                         Disabled,Enabled

I like that the Desktop’s provide valid configuration settings and I hope this is something that will one day make it over to the laptops.

My two favorite parts about this script:

  • Supports querying remote systems
  • Supports querying for all, single or multiple specific settings

The script isn’t perfect but it works for our needs on our systems but I’m interested in hearing from you.

Function: Get-LenovoBIOSSetting

Usage

Get-LenovoBIOSSetting

Get-LenovoBIOSSetting -Setting Wi%

Get-LenovoBIOSSetting -ComputerName Leno-T470 -Setting 'Fingerprint%'

Get-LenovoBIOSSetting -ComputerName Leno-M900,Leno-M910 -Setting 'USB%','C State Support','Intel(R) Virtualization Technology'

Full code below


#region Function Get-LenovoBIOSSetting
Function Get-LenovoBIOSSetting
    {
        [cmdletbinding()]
        Param
            (
                [Parameter(Mandatory=$false)]
                    [string[]]$ComputerName = $env:computername,

                [Parameter(Mandatory=$false)]
                    [string[]]$Setting
            )

        Begin { [System.Collections.ArrayList]$BIOSSetting = @() }

        Process
            {
                foreach($Computer in $ComputerName)
                    {
                        if($Setting)
                            {
                                Foreach($Item in $Setting)
                                    {
                                        Try
                                            {
                                                $arrCurrentBIOSSetting = gwmi -class Lenovo_BiosSetting -namespace root\wmi -Filter "CurrentSetting like '$Item,%'" -ComputerName $Computer -ErrorAction Stop | ? { $_.CurrentSetting -ne "" } | Select -ExpandProperty CurrentSetting
                                                Foreach($CurrentBIOSSetting in $arrCurrentBIOSSetting)
                                                    {
                                                        [string]$CurrentItem = $CurrentBIOSSetting.SubString(0,$($CurrentBIOSSetting.IndexOf(',')))
                                                
                                                        if($CurrentBIOSSetting.IndexOf(';') -gt 0) { [string]$CurrentValue = $CurrentBIOSSetting.SubString($($CurrentBIOSSetting.IndexOf(',')+1),$CurrentBIOSSetting.IndexOf(';')-$($CurrentBIOSSetting.IndexOf(',')+1)) }
                                                        else { [string]$CurrentValue = $CurrentBIOSSetting.SubString($($CurrentBIOSSetting.IndexOf(',')+1)) }
                                                
                                                        if($CurrentBIOSSetting.IndexOf(';') -gt 0)
                                                            {
                                                                [string]$OptionalValue = $CurrentBIOSSetting.SubString($($CurrentBIOSSetting.IndexOf(';')+1))
                                                                [string]$OptionalValue = $OptionalValue.Replace('[','').Replace(']','').Replace('Optional:','').Replace('Excluded from boot order:','')
                                                            }
                                                        Else { [string]$OptionalValue = 'N/A' }
                                                                                
                                                        $BIOSSetting += [pscustomobject]@{ComputerName=$Computer;Setting=$CurrentItem;CurrentValue=$CurrentValue;OptionalValue=$OptionalValue;}
                                                
                                                        Remove-Variable CurrentItem,CurrentValue,OptionalValue -ErrorAction SilentlyContinue -WhatIf:$false
                                                    }
                                                Remove-Variable arrCurrentBIOSSetting -ErrorAction SilentlyContinue -WhatIf:$false
                                            }
                                        Catch { Write-Output "ERROR: UNABLE TO QUERY THE BIOS VIA CLASS [Lenovo_BiosSeting] ON [$Computer] - POSSIBLE INVALID SETTING SPECIFIED [$Item][$CurrentItem]: $_"; throw }
                                    }
                            }
                        Else
                            {
                                Try
                                    {
                                        $arrCurrentBIOSSetting = gwmi -class Lenovo_BiosSetting -namespace root\wmi -ComputerName $Computer -ErrorAction Stop | ? { $_.CurrentSetting -ne "" } | Select -ExpandProperty CurrentSetting
                                        Foreach($CurrentBIOSSetting in $arrCurrentBIOSSetting)
                                            {
                                                [string]$CurrentItem = $CurrentBIOSSetting.SubString(0,$($CurrentBIOSSetting.IndexOf(',')))
                                        
                                                if($CurrentBIOSSetting.IndexOf(';') -gt 0) { [string]$CurrentValue = $CurrentBIOSSetting.SubString($($CurrentBIOSSetting.IndexOf(',')+1),$CurrentBIOSSetting.IndexOf(';')-$($CurrentBIOSSetting.IndexOf(',')+1)) }
                                                else { [string]$CurrentValue = $CurrentBIOSSetting.SubString($($CurrentBIOSSetting.IndexOf(',')+1)) }
                                                                        
                                                if($CurrentBIOSSetting.IndexOf(';') -gt 0)
                                                    {
                                                        [string]$OptionalValue = $CurrentBIOSSetting.SubString($($CurrentBIOSSetting.IndexOf(';')+1))
                                                        [string]$OptionalValue = $OptionalValue.Replace('[','').Replace(']','').Replace('Optional:','').Replace('Excluded from boot order:','')
                                                    }
                                                Else { [string]$OptionalValue = 'N/A' }
                                                                                                                        
                                                $BIOSSetting += [pscustomobject]@{ComputerName=$Computer;Setting=$CurrentItem;CurrentValue=$CurrentValue;OptionalValue=$OptionalValue;}
                                        
                                                Remove-Variable CurrentItem,CurrentValue,OptionalValue -ErrorAction SilentlyContinue -WhatIf:$false
                                            }
                                        Remove-Variable arrCurrentBIOSSetting -ErrorAction SilentlyContinue -WhatIf:$false
                                    }
                                Catch { Write-Output "ERROR: UNABLE TO QUERY THE BIOS VIA CLASS [Lenovo_BiosSeting] ON [$Computer]: $_"; throw }
                            }
                    }
            }
        
        End { $BIOSSetting }
    }
#endregion Function Get-LenovoBIOSSetting

Wrap It Up!

If you decide to try this and run into problems or have questions, please let me know as I’m generally interested in use cases and constructive feedback.

Thanks and Good Providence to you!

Task Sequence Fails to Start 0x80041032

Recommended reading: https://blogs.msdn.microsoft.com/steverac/2014/08/25/policy-flow-the-details/

After preparing a 1730 upgrade Task Sequence for our 1607 machines, I kicked it off on a box via Software Center and walked away once the status changed to ‘Installing thinking I’d come back to an upgraded machine an hour later.  To my surprise, I was still logged on and the TS was still ‘running’.  A few hours later the button was back to ‘Install’ with a status of ‘Available’.  Thinking I goofed I tried again and the same thing happened.

I assumed it was unique to this one machine so I tried it on another and it behaved the same way.  I then ran the upgrade sequence on 5 other machines and they all exhibited the same behavior.  I knew the Task Sequence was sound because others were using it, so it was definitely something unique to my machines but what?

Since I was doing this on 1607 machines I tried upgrading form 1511 to 1703 and 1511 to 1607 but they too failed the same way confirming it was not Task Sequence specific but again unique to my machines.  After spending a quite a few cycles on this, my original machine started failing differently: I was now seeing a ‘Retry’ button with a status of ‘Failed’.  I checked the smsts.log but it didn’t have a recent date/time stamp so it never got that far.  Hmm…

Check the TSAgent.log

Opening the TSAgent.log file I could see some 80070002 errors about not being able to delete HKLM\Software\Microsoft\SMS\Task Sequence\Active Request Handle but the real cause was a bit further up.

TSAgentLog

The lines of interest:


Getting assignments from local WMI. TSAgent 9/1/2017 12:58:27 PM 1748 (0x06D4)
pIWBEMServices->;;ExecQuery (BString(L"WQL"), BString (L"select * from XXX_Policy_Policy4"), WBEM_FLAG_FORWARD_ONLY, NULL, &pWBEMInstanceEnum), HRESULT=80041032 (e:\nts_sccm_release\sms\framework\osdmessaging\libsmsmessaging.cpp,3205) TSAgent 9/1/2017 1:03:55 PM 1748 (0x06D4)
Query for assigned policies failed. 80041032 TSAgent 9/1/2017 1:03:55 PM 1748 (0x06D4)oPolicyAssignments.RequestAssignmentsLocally(), HRESULT=80041032 (e:\cm1702_rtm\sms\framework\tscore\tspolicy.cpp,990) TSAgent 9/1/2017 1:03:55 PM 1748 (0x06D4)
Failed to get assignments from local WMI (Code 0x80041032) TSAgent 9/1/2017 1:03:55 PM 1748 (0x06D4)

The source of error code 80041032 is Windows Management (WMI) and translates to ‘Call cancelled’ which presumably happened while running the query select * from XXX_Policy_Policy4, where XXX is the site code.

I ran a similar query on my machine to get a feel for the number of items in there:


(gwmi -Class xxx_policy_policy4 -Namespace root\xxx\Policy\machine\RequestedConfig).Count

Which ended up failing with a Quota violation error suggesting I’ve reached the WMI memory quota.

Increase WMI MemoryPerHost & MemoryAllHosts

Fortunately, there’s a super helpful TechNet Blog post about this.  Since all of my test machines were running into this, I decided to make life easier for myself and use PowerShell to accomplish the task on a few of them thinking I’d have to raise the limit once.


$PHQC = gwmi -Class __providerhostquotaconfiguration -Namespace root
$PHQC.MemoryPerHost = 805306368
# Below is optional but mentioned in the article
#$PHQC.MemoryAllHosts = 2147483648
$PHQC.Put()
Restart-Computer

After the machine came up I ran the same query again, and after 2 minutes and 38 seconds it returned over 1800 items.  Great!  I ran it again and after 5 minutes it failed with the same quota violation error.  Boo urns.  I kept raising MemoryPerHost and MemoryAllHosts to insane levels to get the query to run back to back successfully.

The good news is that I made progress suggesting I’m definitely hitting some sort of memory ceiling that has now been raised.

The bad news is why me and not others?  Hmm…

Root Cause Analysis

I checked the deployments available to that machine and wowzers it was super long.  I won’t embarrass myself by posting an image of that but it was very long.  This helped to identify areas of improvement in the way we’re managing collections & deploying things, be it Applications, Packages, Software Updates and so on.

On my patient zero machine I ran the following to clear out the policies stored in WMI:


gwmi -namespace root\xxx\softmgmtagent -query "select * from ccm_tsexecutionrequest" | remove-wmiobject

gwmi -namespace root\xxx -query "select * from sms_maintenancetaskrequests" | remove-wmiobject

restart-service -name ccmexec

I then downloaded the policies and tried to image – it worked!  I decided to let that machine go and focus on some administrative cleanup in SCCM.  After tidying things up a bit, the rest of my 1607 machines started the 1703 upgrade Task Sequence without issue and the 1511 machines ran the 1607 upgrade Task Sequence as well.

As we continue to phase out Windows 7, we’ll hopefully update our methods to help avoid problems like this and perform that routine maintenance a little more frequently.

 

Get Gateway IP Address

I created this function ages ago as part of a larger script that needed to be executed completely unattended.

I needed a method to determine which office a particular asset was in at the time.  I settled on using the gateway IP address since I already had a list of them from our BootStrap.ini and CustomSettings.ini.

Originally I was using this query:

[array]$arrDefaultGatewayIP = gwmi Win32_NetworkAdapterConfiguration -ErrorAction Stop | select -ExpandProperty DefaultIPGateway

But this proved problematic on machines running older versions of PowerShell so I ended up using this instead.

[array]$arrDefaultGatewayIP = gwmi Win32_NetworkAdapterConfiguration -ErrorAction Stop | Where { $_.DefaultIPGateway -ne $null } | select -ExpandProperty DefaultIPGateway

Alternatively I suppose I could have Try’ied the first then executed the latter if PoSH Caught any exceptions.

And converted it to a function

Function Get-GatewayIP

Function Get-GatewayIP
{
try { return [string](gwmi Win32_networkAdapterConfiguration -Filter 'IPEnabled = True' -ErrorAction Stop | ? { $_.DefaultIPGateway -ne $null } | select -ExpandProperty DefaultIPGateway | Select -First 1) }
catch { return $_ }
}

From there it was just a matter of validating that the return result looked like an IP before feeding them through a hash table array to determine the office location.

So Why is this Relevant?

My original plan was to rely on the computer name since, in our environment, machine names contain an abbreviated version of the office name – the office code for lack of a better term.  For example,

  • a machine based out of the New York office might be called SOME-NY-THING
  • another in our Nation’s Capital might be OTHER-DC-THING etc.

However some machines don’t follow this format because:

  1. they’re running an older build imaged prior to the new naming convention
  2. some overzealous/tenacious individuals have renamed their machines

I had also considered querying AD to get the machine’s current OU but since this script didn’t always run under the context of an AD account (e.g.: in WinPE) it meant baking the credentials into the script, and I really don’t like doing that any more than I have to.
(Hint: Webservices is the answer to that problem)

I’ve been using this approach for well over a year now and its come in handy countless times.  Hopefully you’ll find some value for this as well.

Good Providence!