26 July 2017

Dell BIOS Reporting Tool

Recently, we ran into a problem when we discovered some of the newer laptops were not automatically disabling the WiFi when connected to ethernet. What made the task even more difficult was that all of our Dell Latitude 7480 systems were already deployed. Being in the legal industry, it is more difficult to ask for time to troubleshoot problems when attorneys bill by the hour.

We knew there was either a new BIOS setting for the 7480 or it had been taken away. To get a list of all the BIOS settings for the 7480, I wrote the script below that uses the Dell Command | Configure to get the BIOS options, settings, and descriptions. You can use the Dell Command | Configure GUI application, but that also requires getting time on the remote machine. This script will grab the info in the background without any interruption to the user.

The script first gets a list of all the available BIOS settings and filters out the following items since I did not see the need for these in the reports:


  • help
  • version
  • infile
  • logfile
  • outfile
  • ovrwrt
  • setuppwd
  • sysdefaults
  • syspwd
The next thing it does it to grab the set value for each setting and then it retrieves the description of the setting. The script formats this data into a table that is exported to a .CSV file for easy viewing. In future models, there will likely be new data, so the script will likely need to be updated. There may also be some data the script did not have access to as the firm I work at only has 8 models of Dell systems. 

The first thing you need to do is to get a list of all systems with their BIOS version. You will want to run this in SCCM in order to find the systems with the latest BIOS version to generate the report on. Here is the WQL code for performing a query in SCCM. 

 select SMS_G_System_COMPUTER_SYSTEM.Manufacturer, SMS_G_System_COMPUTER_SYSTEM.Model, SMS_G_System_PC_BIOS.SMBIOSBIOSVersion, SMS_R_System.Name from SMS_R_System inner join SMS_G_System_PC_BIOS on SMS_G_System_PC_BIOS.ResourceID = SMS_R_System.ResourceId inner join SMS_G_System_COMPUTER_SYSTEM on SMS_G_System_COMPUTER_SYSTEM.ResourceId = SMS_R_System.ResourceId order by SMS_G_System_COMPUTER_SYSTEM.Manufacturer, SMS_G_System_PC_BIOS.SMBIOSBIOSVersion, SMS_G_System_COMPUTER_SYSTEM.Model  

Once you get a list of the systems and choose which one to execute the script on, you have some options. You could either deploy the script through SCCM or you could execute it remotely using PSEXEC. Personally, I used PSEXEC. The only parameter you will need to define is the FilePath, which is the location where the .CSV will be written to.

Here is an example of a .CSV file I ran on my own machine. Some values are left blank because the output exceeded a reasonable amount for this spreadsheet, such as hddinfo. Some are also blank due to security, such as hddpwd.



You can download the script from my GitHub repository located here.


 <#  
      .SYNOPSIS  
           BIOS Reporting Tool  
        
      .DESCRIPTION  
           This script will query the BIOS of Dell machines using the Dell Command | Configure to report the data to SCCM via WMI.  
        
      .PARAMETER FilePath  
           UNC path where to write the file output  
        
      .NOTES  
           ===========================================================================  
           Created with:     SAPIEN Technologies, Inc., PowerShell Studio 2017 v5.4.141  
           Created on:       7/18/2017 9:31 AM  
           Created by:       Mick Pletcher  
           Filename:         DellBIOSReportingTool.ps1  
           ===========================================================================  
 #>  
 [CmdletBinding()]  
 param  
 (  
      [ValidateNotNullOrEmpty()][string]$FilePath  
 )  
   
 function Get-Architecture {  
 <#  
      .SYNOPSIS  
           Get-Architecture  
        
      .DESCRIPTION  
           Returns whether the system architecture is 32-bit or 64-bit  
        
      .EXAMPLE  
           Get-Architecture  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()][OutputType([string])]  
      param ()  
        
      $OSArchitecture = (Get-WmiObject -Class Win32_OperatingSystem | Select-Object OSArchitecture).OSArchitecture  
      Return $OSArchitecture  
      #Returns 32-bit or 64-bit  
 }  
   
 function Get-RelativePath {  
 <#  
      .SYNOPSIS  
           Get the relative path  
        
      .DESCRIPTION  
           Returns the location of the currently running PowerShell script  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()][OutputType([string])]  
      param ()  
        
      $Path = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent) + "\"  
      Return $Path  
 }  
   
 function Get-CCTK {  
 <#  
      .SYNOPSIS  
           Find CCTK.EXE  
        
      .DESCRIPTION  
           Find the Dell CCTK.EXE file.  
        
      .EXAMPLE  
                     PS C:\> Get-CCTK  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()]  
      param ()  
        
      $Architecture = Get-Architecture  
      If ($Architecture -eq "64-bit") {  
           $Directory = ${env:ProgramFiles(x86)} + "\Dell\"  
           $File = Get-ChildItem -Path $Directory -Filter cctk.exe -Recurse | Where-Object { $_.Directory -like "*_64*" }  
      } else {  
           $Directory = $env:ProgramFiles + "\Dell\"  
           $File = Get-ChildItem -Path $Directory -Filter cctk.exe -Recurse | Where-Object { $_.Directory -like "*x86" }  
      }  
      Return $File  
 }  
   
 function Get-ListOfBIOSSettings {  
 <#  
      .SYNOPSIS  
           Retrieve List of BIOS Settings  
        
      .DESCRIPTION  
           This will get a list of all BIOS settings  
        
      .PARAMETER Executable  
           CCTK.exe  
        
      .EXAMPLE  
           PS C:\> Get-ListOfBIOSSettings  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()]  
      param  
      (  
           [ValidateNotNullOrEmpty()]$Executable  
      )  
        
      #Get the path this script is executing from  
      $RelativePath = Get-RelativePath  
      #Get list of exclusions to omit from list of BIOS settings  
      $File = $RelativePath + "BIOSExclusions.txt"  
      $BIOSExclusions = Get-Content -Path $File | Sort-Object  
      #Rewrite list of sorted exclusion back to text file  
      $BIOSExclusions | Out-File -FilePath $File -Force  
      #Get list of BIOS settings -- Script must be executed on a local machine and not from a UNC path  
      $Output = cmd.exe /c $Executable.FullName  
      #Remove instructional information  
      $Output = $Output | Where-Object { $_ -like "*--*" } | Where-Object { $_ -notlike "*cctk*" }  
      #Format Data and sort it  
      $Output = ($Output.split("--") | Where-Object { $_ -notlike "*or*" } | Where-Object{ $_.trim() -ne "" }).Trim() | Where-Object { $_ -notlike "*help*" } | Where-Object { $_ -notlike "*version*" } | Where-Object { $_ -notlike "*infile*" } | Where-Object { $_ -notlike "*logfile*" } | Where-Object { $_ -notlike "*outfile*" } | Where-Object { $_ -notlike "*ovrwrt*" } | Where-Object { $_ -notlike "*setuppwd*" } | Where-Object { $_ -notlike "*sysdefaults*" } | Where-Object { $_ -notlike "*syspwd*" } | ForEach-Object { $_.Split("*")[0] } | Where-Object { $_ -notin $BIOSExclusions }  
      #Add bootorder back in as -- filtered it out since it does not have the -- in front of it  
      $Output = $Output + "bootorder" | Sort-Object  
      Return $Output  
 }  
   
 function Get-BIOSSettings {  
 <#  
      .SYNOPSIS  
           Retrieve BIOS Settings Values  
        
      .DESCRIPTION  
           This will retrieve the value associated with the BIOS Settings  
        
      .PARAMETER Settings  
           List of BIOS Settings  
        
      .PARAMETER Executable  
           CCTK.exe file  
        
      .EXAMPLE  
           PS C:\> Get-BIOSSettings  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()]  
      param  
      (  
           [ValidateNotNullOrEmpty()]$Settings,  
           [ValidateNotNullOrEmpty()]$Executable  
      )  
        
      #Create Array  
      $BIOSArray = @()  
      foreach ($Setting in $Settings) {  
           switch ($Setting) {  
                "advbatterychargecfg" {  
                     $Arguments = [char]34 + $Executable.FullName + [char]34 + [char]32 + "--" + $Setting  
                     $Value = (cmd.exe /c $Arguments).split("=")[1]  
                     $Arguments = [char]34 + $Executable.FullName + [char]34 + [char]32 + "-h" + [char]32 + "--" + $Setting  
                     $Description = (cmd.exe /c $Arguments | Where-Object { $_.trim() -ne "" }).split(":")[1].Trim()  
                }  
                "advsm" {  
                     $Value = ""  
                     $Arguments = [char]34 + $Executable.FullName + [char]34 + [char]32 + "-h" + [char]32 + $Setting  
                     $Description = ((cmd.exe /c $Arguments) | where-object {$_.trim() -ne ""}).split(":")[1].Trim().split(".")[0]  
                }  
                "bootorder" {  
                     $Arguments = [char]34 + $Executable.FullName + [char]34 + [char]32 + $Setting  
                     $Output = (((((cmd.exe /c $Arguments | Where-Object { $_ -like "*Enabled*" } | Where-Object { $_ -notlike "*example*" }) -replace 'Enabled', '').Trim()) -replace '^\d+', '').Trim()) | ForEach-Object { ($_ -split ' {2,}')[1] }  
                     $Output2 = "bootorder="  
                     foreach ($item in $Output) {  
                          [string]$Output2 += [string]$item + ","  
                     }  
                     $Value = $Output2.Substring(0,$Output2.Length-1)  
                     $Arguments = [char]34 + $Executable.FullName + [char]34 + [char]32 + "-h" + [char]32 + $Setting  
                     $Description = ((cmd.exe /c $Arguments) | where-object { $_.trim() -ne "" }).split(":")[1].Trim().split(".")[0]  
                }  
                "hddinfo" {  
                     $Value = ""  
                     $Arguments = [char]34 + $Executable.FullName + [char]34 + [char]32 + "-h" + [char]32 + $Setting  
                     $Description = ((cmd.exe /c $Arguments) | where-object {$_.trim() -ne ""}).split(":")[1].trim().split(".")[0]  
                }  
                "hddpwd" {  
                     $Value = ""  
                     $Arguments = [char]34 + $Executable.FullName + [char]34 + [char]32 + "-h" + [char]32 + $Setting  
                     $Description = ((cmd.exe /c $Arguments) | Where-Object {$_.trim() -ne ""}).split(":")[1].split(".")[0].trim()  
                }  
                "pci" {  
                     $Value = ""  
                     $Arguments = [char]34 + $Executable.FullName + [char]34 + [char]32 + "-h" + [char]32 + $Setting  
                     $Description = ((cmd.exe /c $Arguments) | Where-Object { $_.trim() -ne "" }).split(":")[1].split(".")[0].trim()  
                }  
                "propowntag" {  
                     $Arguments = [char]34 + $Executable.FullName + [char]34 + [char]32 + "--" + $Setting  
                     $Value = ((cmd.exe /c $Arguments).split("=")[1]).trim()  
                     $Arguments = [char]34 + $Executable.FullName + [char]34 + [char]32 + "-h" + [char]32 + $Setting  
                     $Description = ((cmd.exe /c $Arguments) | Where-Object { $_.trim() -ne "" }).split(":")[1].trim()  
                }  
                "secureboot" {  
                     $Arguments = [char]34 + $Executable.FullName + [char]34 + " --" + $Setting  
                     $Output = cmd.exe /c $Arguments  
                     if ($Output -like "*not enabled*") {  
                          $Value = "disabled"  
                     } else {  
                          $Value = "enabled"  
                     }  
                     $Arguments = [char]34 + $Executable.FullName + [char]34 + [char]32 + "-h" + [char]32 + $Setting  
                     $Description = ((cmd.exe /c $Arguments) | where-object { $_.trim() -ne "" }).split(":")[1].Trim().split(".")[0]  
                }  
                default {  
                     #Get BIOS setting  
                     $Output = $null  
                     $Arguments = [char]34 + $Executable.FullName + [char]34 + [char]32 + "--" + $Setting  
                     $Output = cmd.exe /c $Arguments  
                     #Get BIOS Description  
                     $Arguments = [char]34 + $Executable.FullName + [char]34 + [char]32 + "-h" + [char]32 + "--" + $Setting  
                     $Description = ((cmd.exe /c $Arguments) | Where-Object { $_.trim() -ne "" }).split(":").Trim()[1]  
                     $Value = $Output.split("=")[1]  
                }  
           }  
           #Add Items to object array  
           $objBIOS = New-Object System.Object  
           $objBIOS | Add-Member -MemberType NoteProperty -Name Setting -Value $Setting  
           $objBIOS | Add-Member -MemberType NoteProperty -Name Value -Value $Value  
           $objBIOS | Add-Member -MemberType NoteProperty -Name Description -Value $Description  
           $BIOSArray += $objBIOS  
      }  
      Return $BIOSArray  
 }  
 #Find the CCTK.exe file  
 $CCTK = Get-CCTK  
 #Get List of BIOS settings  
 $BIOSList = Get-ListOfBIOSSettings -Executable $CCTK  
 #Get all BIOS settings  
 $BIOSSettings = Get-BIOSSettings -Executable $CCTK -Settings $BIOSList  
 #Add Computer Model to FileName  
 $FileName = ((Get-WmiObject -Class win32_computersystem -Namespace root\cimv2).Model).Trim()  
 #Add BIOS version and .CSV extension to computer name  
 $FileName += [char]32 + ((Get-WmiObject -Class win32_bios -Namespace root\cimv2).SMBIOSBIOSVersion).Trim() + ".CSV"  
 #Get full path to the output .CSV file  
 If ($FilePath[$FilePath.Length - 1] -ne "\") {  
      $FileName = $FilePath + "\" + $FileName  
 } else {  
      $FileName = $FilePath + $FileName  
 }  
 #Delete old .CSV if it exists  
 If ((Test-Path $FileName) -eq $true) {  
      Remove-Item -Path $FileName -Force  
 }  
 #Screen output  
 $BIOSSettings  
 #File output  
 $BIOSSettings | Export-Csv -Path $FileName -NoTypeInformation -Force  
   

2 comments:

  1. Hi - Can you share the download link for the script and cctk.exe file please.

    Thanks
    RL

    ReplyDelete
    Replies
    1. Done. The CCTK.exe can be extracted from the Dell Command | Configure.

      Delete