Jun 022016
 

So I have not posted anything to my blog for quite some time. I’ve decided to fix that, and I’m also changing the focus of my blog slightly from a broad catch-all for my technology interests to a more focused blog on what I’m learning with PowerShell, operating systems, and hardware.

First up is a new script I created to generate a server uptime report. Is also uses Microsoft SCCM WMI classes to determine if a reboot is pending (useful if updates delivered via SCCM didn’t trigger a reboot).

I also decided that I didn’t want to maintain a list of servers to query, so I have my script querying Active Directory OUs to compile the server list.

There are some things that you need to set in order to get this operational. The first is the list of OUs you wish to search. The other is information for the email report. The email stuff can be skipped if you wish, but I’ve found it quite useful, as long as you have an open SMTP relay.

Some things I need to do:

  • Allow toggle of CCM query. Not everyone has SCCM running.
  • Add optional authentication to email for relays that are not unauthenticated.
  • Additional error handling.

So here you go, and as always, let me know if you see something I can improve upon. I am learning this stuff after all.

#requires -Version 2 -Modules ActiveDirectory

# Author: Robert Hollingshead 
# Version: 1 
# Version History: First version. 
# Purpose: Get a list of servers based on arbitrary OU's, and then check each server for uptime. 
# Also grab pending reboot flag from CCM. Send as a report to an email address. 
# To-Do List: Allow toggle of CCM reboot-pending. Add optional authentication to mail. Additional error handling.

# Uncomment this to disable progress bar.
# $ProgressPreference = 'SilentlyContinue'

# Enter OUs to search here. One per line, follow the format below, omit comma on last line.
# You definately want to edit this stuff.
$OUsToSearch = @(
  'OU=OU=Servers,DC=Contoso,DC=net', 
  'OU=Domain Controllers,DC=Contoso,DC=net'
)

# Enter your mail information here. 
$MailSender = '{sender email address here}'
$MailRecipients = '{recipient list here}'
$MailSubject = "Server UpTime Report for $(Get-Date -Format d)"
$MailBody = $MailSubject + ' is attached'
$MailServer = '{smtp server here}'

# Change nothing below this line. 
# ===============================

# Grab the class so that we can use convert to date fu nction. 
$WMI = [wmiclass]'\\.\root\cimv2:win32_operatingsystem'

# Set up some arrays. 
[system.array]$UpTimeReport = $null
[system.array]$Servers = $null

# Query Active Directory
ForEach ($OU in $OUsToSearch) 
{ 
  $OUResult = $null
  Try 
  { 
    $OUResult = Get-ADComputer -SearchBase $OU -Filter * -ErrorAction Stop
    $Servers = $Servers + $OUResult
  }
  catch 
  {
    Write-Warning -Message "Failed to get servers in $OU"
  }
}

# Set up for progress bar.
[long]$totalitems = $Servers.count
[long]$progress = 1
[long]$percentage = 0

# Query each server for uptime. 
foreach ($Server in $Servers) 
{
  # Calculate percent complete and write-progress (if enabled)
  $percentage = ($progress * 100) / $totalitems
  Write-Progress -Activity 'Querying servers.' -PercentComplete $percentage `
  -Status "$percentage% complete..." -CurrentOperation "Checking $($Server.name)."

  # Attempt to get last bootup time and local date/time, then calculate. If fails, handle error. 
  try 
  { 
    $Stopwatch = Measure-Command -Expression {
      $OSProps = Get-WmiObject -ComputerName $Server.name -Class win32_operatingsystem -Property LocalDateTime, LastBootupTime
      $UpTime = $WMI.ConvertToDateTime($OSProps.LocalDateTime) - $WMI.ConvertToDateTime($OSProps.LastBootUpTime)
      $LastBootTime = $WMI.ConvertToDateTime($OSProps.LastBootUpTime)

      # Use CCM to determine if a reboot is pending.
      $CCMWmi = [wmiclass]"\\$($Server.name)\ROOT\ccm\ClientSDK:CCM_ClientUtilities"
      $Reboot = ($CCMWmi.DetermineIfRebootPending()).RebootPending
    }
    $ServerUptime = "$($UpTime.days)+$($UpTime.Hours):$($UpTime.Minutes):$($UpTime.Seconds).$($UpTime.Milliseconds)"
    $TimeToRetrieve = "$($Stopwatch.Minutes):$($Stopwatch.Seconds).$($Stopwatch.Milliseconds)"
  }
  catch 
  {
    $ServerUptime = 'Not Available'
    $LastBootTime = 'Not Available'
    $TimeToRetrieve = 'Failed To Retrieve'
    $Reboot = $false
  }

  # Create object to be added to report. 
  $CurrentServer = New-Object -TypeName PSObject
  $CurrentServer | Add-Member -MemberType NoteProperty -Name 'ServerName' -Value $($Server.name) 
  $CurrentServer | Add-Member -MemberType NoteProperty -Name 'UpTime' -Value $($ServerUptime)
  $CurrentServer | Add-Member -MemberType NoteProperty -Name 'LastBootTime' -Value $($LastBootTime)
  $CurrentServer | Add-Member -MemberType NoteProperty -Name 'TimeToRetrieve' -Value $($TimeToRetrieve)
  $CurrentServer | Add-Member -MemberType NoteProperty -Name 'TimeRetrieveFinished' -Value $(Get-Date -Format T)
  $CurrentServer | Add-Member -MemberType NoteProperty -Name 'RebootPending' -Value $($Reboot)
  
  # Add to report object.
  $UpTimeReport = $UpTimeReport + $CurrentServer
  
  # Increment progress counter for progress bar. 
  $progress++
}

$ReportPath = $env:Temp + '\ServerUpTime.csv'
$UpTimeReport | Export-Csv $ReportPath
Send-MailMessage -From $MailSender -To $MailRecipients -Subject $MailSubject -Body $MailBody -Attachments $ReportPath -SmtpServer $MailServer
Dec 222014
 

This is an experimental PowerShell script that looks at all the physical network adapters on a machine and verifies that they are at or above an arbitrary link speed set in the script. It works by querying the MSNdis_LinkSpeed class in WMI, filtering out non-physical adapters, and then checking each one to make sure the link speed is above the $MinSpeed value that you set. It then returns TRUE or FALSE.

I believe it is useful to use a greator than or equal to comparison instead of saying that all links must equal the minimum speed, especially in server environments where you may have a mixed environment of some servers with gigabit links for instance, and then another set of servers or virtual machines with 10gig links.

I don’t see this being useful in a PC environment because you have less control over what a desktop is hooked in to at any given point in time. Though it may still be useful if you want to enforce such a policy, like trading floor must be at gigabit or higher, and you want to catch non-compliance.

Obviously I can’t account for every InstanceName that isn’t a physical adapter. If there is a better way, by all means let me know and I’ll modify the script, but here it is!

# Robert's experimental NIC speed compliance script. 
# Emphasis on EXPERIMENTAL. 
# It will not break anything but it may not give back a sane result. 
# Manually check suspect compliance, bad compliance will probably come
# from non-physical nics where I didn't account for the InstanceName
# below when it came from MSNdis_LinkSpeed. 

# Minimum allowed speed in megabits. 
$MinSpeed = '1000' 

# This is used for compliancy.
$NonCompliant = 0

# Get link speed for all physical network adapters. 
$nics = Get-WmiObject -Namespace root\wmi -Class MSNdis_LinkSpeed | where {`
$_.InstanceName -notlike '*miniport*' -and `
$_.InstanceName -notlike '*WAN*' -and `
$_.InstanceName -notlike '*1394*' -and `
$_.InstanceName -notlike '*ISATAP*' -and `
$_.InstanceName -notlike '*Bluetooth*' -and `
$_.InstanceName -notlike '*RAS*' -and `
$_.InstanceName -notlike 'Direct Parallel' -and `
$_.InstanceName -notlike '*tunnel*' -and `
$_.InstanceName -notlike '*6to4*' -and `
$_.InstanceName -notlike '*Deterministic*' -and `
$_.InstanceName -notlike '*miniport*' -and `
$_.InstanceName -notlike '*kernel*'
}

# Go through list of NICS and make sure speed is above $MinSpeed
foreach ($nic in $nics) {
    
    #Make the link speed in megabits instead of bits.
    $LinkSpeed = $nic.NdisLinkSpeed/10000

    #See if this NIC is compliant.
    if ($LinkSpeed -lt $MinSpeed) {
        $NonCompliant++
    }
}

# Am I compliant?
if ($NonCompliant -eq 0) {
    write-host TRUE
    } else {
    write-host FALSE
}
Nov 232014
 

The following detection script accomplishes the following.

  • Determines if virtual memory is automatically managed. The desired configuration according to the script is that the pagefile should be managed manually (true can be changed to false if you want to go the automagic route).
  • If the pagefile is not automatically managed, the script determines if the size of the page file is at least double the amount of visible physical memory.

I’m working on a remediation script, but for now I figure’d I would share the love.

# This script simply checks to see if Windows is handling the page file
# automagically. Then if no, it verifies to make sure that the swap file
# is set at or over twice the available memory. 

$system = get-wmiobject -Class win32_ComputerSystem

if ($system.AutomaticManagedPagefile -eq $true) {
    write-host FALSE
    } else {  

    $mem = get-wmiobject -Class win32_OperatingSystem | select-object TotalVisibleMemorySize,TotalVirtualMemorySize

    [int64]$vismem = $mem.TotalVisibleMemorySize
    [int64]$vrtmem = $mem.TotalVirtualMemorySize

    if ($vrtmem -ge ($vismem * 2)) {
        write-host TRUE
        } else {
        write-host FALSE
    }
}

 

Nov 112014
 

Here is a way for you to keep your EMIE site list up to date using CI. Enterprise Mode IE is Microsoft’s method for allowing backwards compatibility with sites that do not fully support Internet Explorer 11’s Edge mode. I don’t go into detail about how to set up EMIE, more information on how to set it up can be found via MSDN.

First we start with the detection script. This script returns the version number of your site list XML. Be sure to replace the $file string listed with the correct path and file name of your EMIE list XML.

 
# EMIE XML Version Check (Detection) Script for CM12 Compliance Settings
# by Robert Hollingshead

# November 11th, 2014
# Find more CM12 Compliance Setting scripts at occurative.com!

# Supress error messages.
$ErrorActionPreference = "SilentlyContinue"

# Enter the path and name of the XML file here. 
$file = '{enter path and filename here}'

# Get the XML Content and then unwrap its tags into individual lines.
# We could leave out the split method and just go by the entire InnerXml,
# but IMHO, this way makes more sense and is friendly to ANY 
# odd modifications of the remediation script should they be needed. 

Try {
    [xml]$sites = Get-Content $file
    $unwrappedsites = $sites.innerxml.split('</>')

    # Look for the rules version, split out the line, 
    # and output the second string which will be the
    # version itself. 

    ForEach($line in $unwrappedsites) {
        if ($line -like "*rules version*") {
            write-host $line.split('"')[1]
            break
        }
    }

    # If the split of $sites failed, we'll catch it and
    # spit out versin 0 to indicate the file doesn't exist.

    } catch {   
    write-host 0
}

The detection script is going to return the current version of the XML file on the client, not a true or false. So your compliance rule will be equal to the version you want all your clients to be at. How to get that updates can be done via this remediation script. First thing, there are two values that must be changed, the $version, and the $file strings. $version will be the equal to the version you want all your clients to be at. Make sure  you have it set to the same version as in your compliance rule or you’ll never be compliant. The second is the $file string, which will be the same as in the detection script.

Next  you will add all the domains. There are four lines beginning with “$domain = New-Object PSOBject.” We are building an array of Domains as well as if they are to be excluded or included in EMIE. Each domain will be a TLD. True means “exclude this site from EMIE” and is useful for preventing an intranet site from running in compatibility mode. False means “include this site in EMIE” and is useful for placing a site on the internet in compatibility mode, or if the site is setting its compatibility via META tag and you wish to override. Here is the script, stay tuned after the script for a few notes.

# EMIE XML Update (Remediation) Script for CM12 Compliance Settings
# by Robert Hollingshead

# November 11th, 2014
# Find more CM12 Compliance Setting scripts at occurative.com!

# This script keeps your EMIE (Enterprise Mode IE 11) XML up to date.

# Enter the resultant version here. This is your XML list version.
# Increment when there are changes. 
# Make sure that you change the compliance rule to match. 
$version = "{enter version number here}"

# Enter the path and name of the XML file here. 
$file = '{enter path and filename here}'

# Setup Array for domains and the resulting xml 

[system.array]$domains = $null
[system.array]$sitesxml = $null

#Add domain here.
#False indicates that EMIE should run for non-intranet site. 
#False also indicates that EMIE should run for intranet sites that are saying they are
#compatible via server meta tag.
#True indicates that EMIE should not run for intranet site. 
# Copy the following four lines for each new domain. Don't forget to increment the version
# if you make any changes. 
$domain = New-Object PSObject
$domain | Add-Member -membertype noteproperty -Name Domain -Value "{tld like microsoft.com}"
$domain | Add-Member -membertype noteproperty -Name Exclude -Value "{false or true}"
$domains = $domains + $domain


#Now let's write out the XML. We build an array that will be written out to file. 

# Compile the version line and the opening EMIE tag.
$line = New-Object PSObject
$line = ''
$sitesxml = $line
$line = '  '
$sitesxml = $sitesxml + $line

# Compile the domain tags.
ForEach ($domain in $domains) {
    $line = '    ' + $domain.Domain + ''
    $sitesxml = $sitesxml + $line
}

# Compile the closing tags.
$line = '  '
$sitesxml = $sitesxml + $line
$line = ''
$sitesxml = $sitesxml + $line

# Output to the file. 
$sitesxml | out-file $file

write-host TRUE

Obviously there is some room for improvement in this script. While I can include TLD’s right now, I cannot include subdomains just yet, that ability will be forthcoming in another update of this script, or if you wish to update it yourself and contribute back that is fine too.

Enjoy!

Nov 052014
 

EDIT 11/10/14: I found and corrected a bug in the detection script. There is a chance in certain configurations that the detection script might miss cards. I modified it so that wouldn’t happen.

The following detection and remediation scripts are designed to be placed into as compliance settings in a configuration item in CM12. They are heavily modified from an original powershell function published on the TechNet gallery. Where the original powershell script is a run-once affair, these two scripts will enable you to establish compliance on all desktops, laptops, and especially servers where it is generally not a good idea to power manage your NICs.

I recommend testing before deployment. Note that the remediation script will run and the detection script will show compliance, but where the original script could force a reboot, the remediation script here does no such thing. This is intentional as I believe it would be better in practice to let the compliant machines reboot through other means, such as during a patch cycle or when the end user shuts down for the evening.

The detection script (copy and paste, the lines will remain intact):

# NIC Power Management Detection Script for CM12 Compliance Settings

# Based off of the script found at https://gallery.technet.microsoft.com/scriptcenter/Disable-turn-off-this-f74e9e4a
# Modified by Robert Hollingshead
# November 10th, 2014
# Find more CM12 Compliance Setting scripts at occurative.com!

# This script detects power management status for all physical NICs.

#Original scripts comments:
#find only physical network,if value of properties of adaptersConfigManagerErrorCode is 0,  it means device is working properly. 
#even covers enabled or disconnected devices.
#if the value of properties of configManagerErrorCode is 22, it means the adapter was disabled. 

# This is to calculate compliance. If both of these are equal at the end then all NICs are compliant.
$SettingChecksum = 0
$NICCount = 0

$PhysicalAdapters = Get-WmiObject -Class Win32_NetworkAdapter|Where-Object{$_.PNPDeviceID -notlike "ROOT\*" `
	-and $_.Manufacturer -ne "Microsoft" -and $_.ConfigManagerErrorCode -eq 0 -and $_.ConfigManagerErrorCode -ne 22} 
	
Foreach($PhysicalAdapter in $PhysicalAdapters) {
	$PhysicalAdapterName = $PhysicalAdapter.Name
	
    #check the unique device id number of network adapter in the currently environment.
	$DeviceID = $PhysicalAdapter.DeviceID
	If([Int32]$DeviceID -lt 10) {
		$AdapterDeviceNumber = "000"+$DeviceID
		} Else {
		$AdapterDeviceNumber = "00"+$DeviceID
	}

	#check whether the registry path exists.
	$KeyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\$AdapterDeviceNumber"
	
    If(Test-Path -Path $KeyPath) {
		$PnPCapabilitiesValue = (Get-ItemProperty -Path $KeyPath).PnPCapabilities
		If($PnPCapabilitiesValue -eq 0){
			#This adapter isn't compliant!
            $SettingChecksum++		
		}
		If($PnPCapabilitiesValue -eq $null) {
            #This adapter isn't compliant!
		    $SettingChecksum++				
		}
    }			
}


# Are we compliant?
If ($SettingChecksum -eq 0) {
    write-host TRUE
    } else {
    write-host FALSE
}

The remediation script (copy and paste, the lines will remain intact):

# NIC Power Management Remediation Script for CM12 Compliance Settings

# Based off of the script found at https://gallery.technet.microsoft.com/scriptcenter/Disable-turn-off-this-f74e9e4a
# Modified by Robert Hollingshead
# November 5h, 2014
# Find more CM12 Compliance Setting scripts at occurative.com!

# This script turns off power management for all physical NICs.

#Original scripts comments:
#find only physical network,if value of properties of adaptersConfigManagerErrorCode is 0,  it means device is working properly. 
#even covers enabled or disconnected devices.
#if the value of properties of configManagerErrorCode is 22, it means the adapter was disabled. 

$PhysicalAdapters = Get-WmiObject -Class Win32_NetworkAdapter|Where-Object{$_.PNPDeviceID -notlike "ROOT\*" `
-and $_.Manufacturer -ne "Microsoft" -and $_.ConfigManagerErrorCode -eq 0 -and $_.ConfigManagerErrorCode -ne 22} 
	
Foreach($PhysicalAdapter in $PhysicalAdapters) {
    
    $InterfaceChecksum++
	$PhysicalAdapterName = $PhysicalAdapter.Name
		
    # Check the NIC ID Number.
	
    $DeviceID = $PhysicalAdapter.DeviceID
	
	If([Int32]$DeviceID -lt 10) {
		$AdapterDeviceNumber = "000"+$DeviceID
	} Else {
		$AdapterDeviceNumber = "00"+$DeviceID
	}
		
	# See if the registry path exists.
	$KeyPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\$AdapterDeviceNumber"
		
    If(Test-Path -Path $KeyPath) {
		$PnPCapabilitiesValue = (Get-ItemProperty -Path $KeyPath).PnPCapabilities
		If($PnPCapabilitiesValue -eq 0){
			#setting the value of properties of PnPCapabilites to 24, it will disable save power option.
			Set-ItemProperty -Path $KeyPath -Name "PnPCapabilities" -Value 24 | Out-Null			
		}
		If($PnPCapabilitiesValue -eq $null) {
                #setting the value of properties of PnPCapabilites to 24, it will disable save power option.
				New-ItemProperty -Path $KeyPath -Name "PnPCapabilities" -Value 24 -PropertyType DWord | Out-Null				
		}
	}		
}

write-host TRUE

 

I am always open to improvements to these scripts. If you find something that could use improvement just let me know via the comments below!

Jul 182014
 

A while back I wrote a MS SQL query to gather vitals for a PC Fleet in SCCM 2012. I have now updated it to include some more information like Make and information for the system partition (size/free/% used). I’ve also changed some of the columns to use the more Appealing Giga units of measurement instead of Mega.

This is so that if anyone ever asks what you have out there,  you can run a quick query and say “HERE YOU GO!” Enjoy! Written in Microsoft SQL Server Management Studio 2012.

NOTE: Search and replace {domain\} with your domain name or the query will fail, just FYI. Also, when you copy and paste, the lines will remain intact.

select distinct

CS.Manufacturer0 [Make],
CS.Model0 [Model],
CS.Name0 [Hostname], 
replace(CS.UserName0,'{domain\}','') [Primary User],
OS.Caption0 [OS],
OS.InstallDate0 [Image Date],
cast(CPU.MaxClockSpeed0/1000.00 as decimal(10,2)) [Speed (MHz)],
CPU.Name0 [CPU],
CPU.NumberOfCores0 [Cores],
CPU.IsHyperthreadCapable0 [Hyperthread],
DSK.Caption0 [HDD],
cast(DSK.Size0/1000.00 as decimal(10,2)) [HDD Capacity (GB)],
cast(LDSK.Size0/1000.00 as decimal(10,2)) [C Size (GB)],
cast(LDSK.FreeSpace0/1000.00 as decimal(10,2)) [C Free (GB)],
cast((LDSK.FreeSpace0 * 100.00)/(LDSK.Size0 * 1.00) as decimal(10,2)) [C %Free],
cast(OS.TotalVirtualMemorySize0/1000.000 as decimal(10,3)) [Virtual Memory (GB)],
cast(OS.TotalVisibleMemorySize0/1000.000 as decimal(10,3)) [Visible Memory (GB)]

from v_GS_COMPUTER_SYSTEM CS

left join v_GS_PROCESSOR CPU on CS.ResourceID = CPU.ResourceID
left join v_GS_DISK DSK on CS.ResourceID = DSK.ResourceID
left join v_GS_OPERATING_SYSTEM OS on CS.ResourceID = OS.ResourceID
left join v_GS_SYSTEM SYS on CS.ResourceID = SYS.ResourceID
left join v_GS_LOGICAL_DISK LDSK on CS.ResourceID = LDSK.ResourceID

where 
SYS.SystemRole0 like 'Workstation'  and
LDSK.Caption0 like 'C:' and
DSK.DeviceID0 like '\\.\PHYSICALDRIVE0' 

order by Make, Model

 

Feb 262014
 

Here’s a nifty little query that will return the physical memory configuration of each computer registered in SCCM 2012. I’m excluding the System ROM (where layeth the BIOS) because we don’t really care about that here. We want the RAM! Written in SQL Server Management Studio 2012.

select 

v_GS_PHYSICAL_MEMORY.DeviceLocator0 as [Slot],
v_GS_PHYSICAL_MEMORY.Capacity0 as [Size],
v_GS_PHYSICAL_MEMORY.Manufacturer0 as [Manufacturer],
v_GS_PHYSICAL_MEMORY.PartNumber0 as [Part Number],
v_GS_Computer_System.Model0 as [Model],
v_GS_Computer_System.Name0 as [Hostname],
v_GS_Computer_System.UserName0 as [Username]

from v_GS_PHYSICAL_MEMORY

left join v_GS_COMPUTER_SYSTEM on v_GS_PHYSICAL_MEMORY.ResourceID=v_GS_COMPUTER_SYSTEM.ResourceID

/* We don't care about the system rom. */
where v_GS_PHYSICAL_MEMORY.DeviceLocator0 not like 'SYSTEM ROM'

/* Sort by hostname. */
order by v_GS_Computer_System.Name0
Feb 242014
 

Here is a SQL Query to generate a list of each Computer System in the SCCM 2012 Database, and then list CPU/Memory/Disk information. I wrote this to display Physical Disk 0. There are two different where clauses. The first one, commented out, can be used to pull up specific computers based upon the conditions you set. The second one just uses Physical Disk 0 as a condition. Written using SQL Server Management Studio 2012.

select distinct

v_GS_COMPUTER_SYSTEM.Model0 as [Model],
v_GS_COMPUTER_SYSTEM.Name0 as [Hostname], 
v_GS_COMPUTER_SYSTEM.UserName0 as [Primary User],
v_GS_PROCESSOR.MaxClockSpeed0 as [Speed],
v_GS_PROCESSOR.Name0 as [CPU],
v_GS_PROCESSOR.NumberOfCores0 as [Cores],
v_GS_PROCESSOR.IsHyperthreadCapable0 as [Hyperthread],
v_GS_DISK.Caption0 as [HDD],
v_GS_DISK.Size0 as [HDD Capacity],
v_GS_OPERATING_SYSTEM.TotalVirtualMemorySize0 as [Virtual Memory],
v_GS_OPERATING_SYSTEM.TotalVisibleMemorySize0 as [Visible Memory]

from v_GS_COMPUTER_SYSTEM

left join v_GS_PROCESSOR on v_GS_COMPUTER_SYSTEM.ResourceID = v_GS_PROCESSOR.ResourceID
left join v_GS_DISK on v_GS_COMPUTER_SYSTEM.ResourceID = v_GS_DISK.ResourceID
left join v_GS_OPERATING_SYSTEM on v_GS_COMPUTER_SYSTEM.ResourceID = v_GS_OPERATING_SYSTEM.ResourceID

/* where (v_GS_COMPUTER_SYSTEM.Name0 like '{yourstringhere' ) and
v_GS_DISK.DeviceID0 like '\\.\PHYSICALDRIVE0' */

where v_GS_DISK.DeviceID like '\\.\PHYSICALDRIVE0'

order by [Visible Memory]
May 072013
 

This PowerShell script uses Dism to find all OEM entries and then parses them into a text file. Run as administrator.

###########################################################################
#
# Get-Drivers.ps1
#
# Use Dism to create a list of OEM driver entries. This can also be 
# changed to output other entries as well. Run as administrator.
#
# by Robert Hollingshead
# Contributed to the public domain. Do with it what you will. 
#
###########################################################################

$fileStream = [System.IO.StreamWriter] "C:\Drivers.txt"

[array]$arrayDismParsed = @()

$arrayDismParsed = "Parsed output from Get-Drivers.ps1"

# Populate the array. 
$arrayDismOutput = & dism /online /get-drivers /all /format:table

$arrayDismParsed = $arrayDismParsed + $arrayDismOutput[11]
$arrayDismParsed = $arrayDismParsed + $arrayDismOutput[12]
$arrayDismParsed = $arrayDismParsed + $arrayDismOutput[13]

for ($arrayPos=0;$arrayPos -le $arrayDismOutput.length;$arrayPos++)
{
    if ($arrayDismOutput[$arrayPos] -like "*OEM*") # Find the OEM entries. ;
    {
        $arrayDismParsed = $arrayDismParsed + $arrayDismOutput[$arrayPos]
    }
}

for ($arrayPos=0;$arrayPos -le $arrayDismParsed.length;$arrayPos++)
{
    $fileStream.WriteLine($arrayDismParsed[$arrayPos])
}

$fileStream.Close()
May 072013
 

This script will take a list of print servers and then query each individual server on the list for the installed print drivers. It will then output a report in csv format of all drivers encountered.

###########################################################################
#
# Query listed print servers and report on all drivers discovered.  
#
# by Robert Hollingshead
# Contributed to the public domain. Do with it what you will. 
#
###########################################################################

# Place your server list CSV file here, including path. 
$ServerCSV = "{output csv and path}"

# Place the output CSV file here, including path. 
$OutputCSV = "{output csv and path}"

#Change nothing below this line. 

$credential = Get-Credential
$driver = ""
[system.array]$report = $null

$servers = Import-Csv $ServerCSV

foreach ($server in $servers) 
{

    Write-Host ' ' 
    Write-Host $server.Servername -NoNewLine

    $drivers = Get-WMIObject -class Win32_PrinterDriver -computer $server.Servername -credential $credential

    foreach($driver in $drivers) 
    {

        Write-Host "." -NoNewLine

        $path = $driver.DriverPath.Substring(0,1)

        $output = New-Object PSObject
        $output | Add-Member -membertype noteproperty -Name Servername -Value $server.Servername
        $output | Add-Member -membertype noteproperty -Name Drivername -Value $driver.Name

        $servername = $server.Servername

        $output | Add-Member -membertype noteproperty -Name Driverversion -Value (Get-ItemProperty ($driver.DriverPath.Replace("$path`:","\\$servername\$path`$"))).VersionInfo.ProductVersion

        $report = $report + $output

    }

}

$report | export-csv $OutputCSV -notype