Extract Drivers

This script will extract all drivers from a computer and copy them to a folder structure organized by type. It is language dependent and currently works for Danish and English Operating System language.

Option Explicit

Dim WshShell, oExec
Set WshShell = CreateObject("WScript.Shell")

Set oExec = WshShell.Exec("Dism.exe /online /get-drivers")

WScript.Echo "Started, Get drivers"

Dim iCounter
iCounter = 0
Do While oExec.Status = 0 And iCounter < 600
	WScript.Sleep 100
	iCounter = iCounter + 1
Loop

WScript.Echo "Execute time: " & iCounter / 10 & " Sec."
WScript.Echo "Exit Code: " & oExec.ExitCode

Dim sText, sDriverList
Dim aDriverList()
Dim iDriversCount
iDriversCount = 0
Do While Not oExec.StdOut.AtEndOfStream
	sText = oExec.StdOut.ReadLine()
	If Left(sText,17) = "Published Name : " Or Left(sText,17) = "Udgivelsesnavn : " Then
		ReDim Preserve aDriverList(iDriversCount)
		aDriverList(iDriversCount) = Trim(Right(sText,Len(sText)-17))
		iDriversCount = iDriversCount + 1
	End If
Loop

Dim sElement
Dim aOEMDriverPaths(), aOEMDriverClasses(), aOEMDriverVersion()

WScript.Echo "Number of OEM Drivers: " & iDriversCount

iDriversCount = 0
For Each sElement In aDriverList
	
	Set oExec = WshShell.Exec("Dism.exe /online /get-driverInfo:" & sElement)

	WScript.Echo "Started, get info for driver: " & sElement

	iCounter = 0
	Do While oExec.Status = 0 And iCounter < 100
		WScript.Sleep 100
		iCounter = iCounter + 1
	Loop

	WScript.Echo "Execute time: " & iCounter / 10 & " Sec."
	WScript.Echo "Exit Code: " & oExec.ExitCode

	Do While Not oExec.StdOut.AtEndOfStream
		sText = oExec.StdOut.ReadLine()
		If Left(sText,20) = "Driver Store Path : " Then
			ReDim Preserve aOEMDriverPaths(iDriversCount)
			aOEMDriverPaths(iDriversCount) = Trim(Right(sText,Len(sText)-20))
		End If
		If Left(sText,22) = "Sti til driverlager : " Then
			ReDim Preserve aOEMDriverPaths(iDriversCount)
			aOEMDriverPaths(iDriversCount) = Trim(Right(sText,Len(sText)-22))
		End If
						    
		If Left(sText,13) = "Class Name : " Or Left(sText,13) = "Klassenavn : "Then
			ReDim Preserve aOEMDriverClasses(iDriversCount)
			aOEMDriverClasses(iDriversCount) = Trim(Right(sText,Len(sText)-13))
		End If
		If Left(sText,10) = "Version : " Then
			ReDim Preserve aOEMDriverVersion(iDriversCount)
			aOEMDriverVersion(iDriversCount) = Trim(Right(sText,Len(sText)-10))
			iDriversCount = iDriversCount + 1
		End If
	Loop
Next

Dim sScriptPath, sDriversDestinationPath
sScriptPath = Left(wscript.scriptfullname, Len(wscript.scriptfullname) - Len(wscript.scriptname))

Wscript.echo sScriptPath

Dim sComputer, oWMIService, cItems, oItem, sManufacturer, sModel
sComputer = "." 
Set oWMIService = GetObject("winmgmts:\\" & sComputer & "\root\CIMV2") 
Set cItems = oWMIService.ExecQuery("SELECT * FROM Win32_ComputerSystem",,48) 
For Each oItem in cItems
    sManufacturer = oItem.Manufacturer
    sModel = oItem.Model
Next

WScript.Echo "Number of OEM Drivers: " & iDriversCount

Dim sInfFileName, sDriverVersion, sDriverCopySource, sDriverCopyDistination, iWaitCounter


For iCounter = 0 To iDriversCount-1

	sInfFileName = Right(aOEMDriverPaths(iCounter),Len(aOEMDriverPaths(iCounter))-InstrRev(aOEMDriverPaths(iCounter),"\",-1,vbTextCompare))
	sInfFileName = Left(sInfFileName,Len(sInfFileName)-4)
	sDriverVersion = aOEMDriverVersion(iCounter)
	sDriversDestinationPath = sScriptPath & "Drivers\" & sManufacturer & "\" & sModel & "\"
	sDriverCopySource = Left(aOEMDriverPaths(iCounter),InstrRev(aOEMDriverPaths(iCounter),"\",-1,vbTextCompare))
	sDriverCopyDistination = sDriversDestinationPath & aOEMDriverClasses(iCounter) & "\" & sInfFileName & "_" & sDriverVersion

	WScript.echo "Copy Driver: " & aOEMDriverClasses(iCounter) & " " & sInfFileName & " " & sDriverVersion

	CopyFolder sDriverCopySource, sDriverCopyDistination, "Precompiled Setup Information"
Next

Function CopyFolder(sSourceFolder, sDestinationFolder, sExcludeType)
	SearchSubFolders sSourceFolder, sDestinationFolder, sExcludeType
End Function

Sub SearchSubFolders(sSourceFoldersPath, sDestFoldersPath, sExcludeType)

	Dim oFileSystem, oFolder, oFile, aExcludeType, bExclude, sElement
	Set oFileSystem = CreateObject("Scripting.FileSystemObject")
	aExcludeType = Split(sExcludeType,",",-1,vbTextCompare)

	'** Søg i under mapper ************************************************************
	Set oFolder = oFileSystem.getFolder(sSourceFoldersPath)
	For Each ofile in oFolder.SubFolders
		SearchSubFolders sSourceFoldersPath & "\" & oFile.name, sDestFoldersPath & "\" & oFile.name, sExcludeType
	Next

	'** Kopier filer i mappe **********************************************************
	For Each oFile in oFolder.Files

		bExclude = False
		For Each sElement In aExcludeType
			If UCase(oFile.Type) = UCase(sElement) Then
				bExclude = True
			End If
		Next
		If Not bExclude Then
			CopyFile sSourceFoldersPath, sDestFoldersPath, oFile.name
		End If
	Next
	Set oFolder = Nothing
	Set oFileSystem = nothing

End Sub


Function CopyFile(sSourceFilePath, sDestFilePath, sFile)

	Dim oFileSystem, oCopyFile, oCopyFileDest, iSecondsDateDiff
	Set oFileSystem = CreateObject("Scripting.FileSystemObject")

	'** Kontroller om destinations filen findes ****************************************
	If oFileSystem.FileExists(sDestFilePath & "\" & sFile) Then

		'** Destinations fil eksisterer, kontroller om kilde fil er nyere **********
		Set oCopyFile = oFileSystem.GetFile(sSourceFilePath & "\" & sFile)
		Set oCopyFileDest = oFileSystem.GetFile(sDestFilePath & "\" & sFile)
		iSecondsDateDiff = DateDiff("s", oCopyFile.DateLastModified, oCopyFileDest.DateLastModified)
		If iSecondsDateDiff < 0 Then

			'** Kilde Fil er nyere, kopier fil *********************************
			On Error resume Next
			oCopyFile.Copy (sDestFilePath & "\" & sFile)
			If Err.Number = 0 Then
				'wscript.echo "File copied: "  & sSourceFilePath & "\" & sFile
			Else
				'** Fejl i kopiering, log fejl *****************************
				wscript.echo "ERROR " & Err.Number & " copy file: " & sSourceFilePath & "\" & sFile
			End If
			On Error Goto 0

		End If
		Set oCopyFile = nothing
		Set oCopyFileDest = nothing
	Else

		'** Destination findes ikke, kopier fil ************************************
		Set oCopyFile = oFileSystem.GetFile(sSourceFilePath & "\" & sFile)
		CreateFolderTree(sDestFilePath)
		On Error resume Next
		oCopyFile.Copy (sDestFilePath & "\" & sFile)

		'** Slet kilde fil, hvis kopiering gik godt ********************************
		If Err.Number = 0 Then
			'wscript.echo "File copied: "  & sSourceFilePath & "\" & sFile
		Else
			'** Fejl i kopiering, log fejl *************************************
			wscript.echo  "Error " & Err.Number & " copy file: " & sSourceFilePath & "\" & sFile

		End If
		On Error Goto 0
		Set oCopyFile = nothing
	End If
	Set oFileSystem = nothing
End Function


'****************************************************************************** 
Function CreateFolderTree(strInput)
	Dim objFileSystemObject
	Set objFileSystemObject = CreateObject("Scripting.FileSystemObject")
	If Not objFileSystemObject.FolderExists(strInput) Then 
		Dim arrFolderPath,intFolder,strFolderTree
		arrFolderPath = Split(strInput,"\")
		For intFolder = 0 To UBound(arrFolderPath)
			strFolderTree = strFolderTree & arrFolderPath(intFolder) & "\"
			If intFolder > 1 Then 
				If Not objFileSystemObject.FolderExists(strFolderTree) Then
					objFileSystemObject.CreateFolder(strFolderTree)
				End If
			End If
		Next
		CreateFolderTree = True
	Else
		CreateFolderTree = False
	End If
	Set objFileSystemObject = nothing
End Function

Create OU Based Collections

I recently set up a whole new SCCM 2012 environment and we needed to create collections for a lot of OUs containing the computers.The script below will run through an OU structure and create device collections for each OU and sub OU’s.

To run the script open your SCCM console and press the drop down menu next to the home button and choose “Connect via Windows Powershell”.

This will establish a connection to the SCCM and allow you to use some special CM powershell functions which is utilized in this script.

To avoid warnings about the script not  being digitally signed run this command prior to executing the script it self.

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass

Now you can run the script.

It takes 1 mandatory argument and 3 optional which if not specified will default to the values they have in the script.

The mandatory argument is the SearchBase which is the top level OU from which you want to create the OU based Collections. The Optional arguments are LimitingCollection, SearchScope and RefreshType.

To run the script in its most simple form:

New-OUDeviceCollection.ps1 -SearchBase “OU=Computers,OU=Company,DC=OMG,DC=local”

This will create a collection for each of the OU’s under omg.local\Company\Computers – excluding the Computers container it self.

New-OUDeviceCollection.ps1 -SearchBase “OU=Computers,OU=Company,DC=OMG,DC=local” -LimitingCollection “All Windows 10 Computers” -SearchScope Subtree -RefreshType 2

This will create a collection for each of the OU’s under omg.local\Company\Computers – including the Computers container it self. It will use “All Windows 10 Computers” as the limiting Collection and it will use periodic update.

# Name: New-OUDeviceCollection
# Arguments: 4 (1 Mandatory, 3 Optional)
#   1. SearchBase (String, Mandatory): The top level OU from where you want to create OU Collections. The DistinguisedName attribute of the OU from AD is used.
#   2. LimitingCollection (String, Optional): The Collection to limit the new collections to. Default: All Desktop and Server Clients
#   3. SearchScope (String, Optional): Subtree, OneLevel or Base. Default: OneLevel
#   4. RefreshType (String, Optional): 1 (Manual),2 (Periodic), 4(CONSTANT_UPDATE) - Default: 4
#
# Description: This script will run through an OU and create device collections for each OU and sub OU's 
#              depending on what you specify in SearchScope.


param(
[string]$SearchBase,
[string]$LimitingCollection = 'All Desktop and Server Clients',
[string]$SearchScope = 'OneLevel',
[string]$RefreshType = '4'
)


$OUS = Get-ADOrganizationalUnit -SearchBase $SearchBase -SearchScope $SearchScope -Filter * -Properties Canonicalname
foreach ($OU in $OUS) 
 {
    $OUName=$OU.Name
    $Canonical=$OU.CanonicalName
    New-CMDeviceCollection -Name "$OUName" -LimitingCollectionName $LimitingCollection -RefreshType $RefreshType
    Add-CMDeviceCollectionQueryMembershipRule -CollectionName "$OUName" -QueryExpression "select SMS_R_SYSTEM.ResourceID, SMS_R_SYSTEM.ResourceType,
    SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.SystemOUName
   = '$Canonical'" -RuleName "$OUName OU"
 }

Rebuild a corrupt WMI repository

If i run into client computers that have issues with WMI i usually run through these steps:

Run this command to verify WMI:

winmgmt /verifyrepository

If the above command returns “inconsistent” then i would run the following command:

winmgmt /salvagerepository

Then i would use verifyrepository again to check the state. If it is still “inconsistent” then i would run the following command:

winmgmt /resetrepository

This will stop the WMI service and rename the repository (C:\Windows\System32\wbem\repository) and start the WMI service.

This should resolve the WMI issues.

In case you are in a SCCM environment the affected computer would need its SCCM client reinstalled.

Uninstall Microsoft Software Update deployed with SCCM

I recently had to uninstall a Software Update deployed by SCCM 2012 on a Windows 10 client. Before when running SCCM 2007 i used to run WUSA with the following syntax:

wusa.exe / uninstall / kb: <KBnumber> /quiet /norestart

This didnt work  with the /quiet parameter so instead i had to use DISM as i need to be able to uninstall it silently.

DISM takes the packageName of the Software Update as input which can be found by running the following command on a client which have the Software Update installed:

dism /online /get-packages /format:table

Deployment Image Servicing and Management tool
Version: 10.0.10586.0

Image Version: 10.0.10586.0

Packages listing:


--------------------------------------------------------------------------------------------------------- | --------------- | --------------- | ----------------
Package Identity                                                                                          | State           | Release Type    | Install Time    
--------------------------------------------------------------------------------------------------------- | --------------- | --------------- | ----------------
Microsoft-Windows-Client-LanguagePack-Package~31bf3856ad364e35~amd64~da-DK~10.0.10586.0                   | Installed       | Language Pack   | 13-02-2016 16:58
Microsoft-Windows-DiagTrack-Internal-Package~31bf3856ad364e35~amd64~~10.0.10586.0                         | Installed       | Feature Pack    | 30-10-2015 07:26
Microsoft-Windows-Foundation-Package~31bf3856ad364e35~amd64~~10.0.10586.0                                 | Installed       | Foundation      | 30-10-2015 07:26
Microsoft-Windows-InsiderHub-Package~31bf3856ad364e35~amd64~~10.0.10586.0                                 | Installed       | OnDemand Pack   | 13-02-2016 17:02
Microsoft-Windows-LanguageFeatures-Basic-da-dk-Package~31bf3856ad364e35~amd64~~10.0.10586.0               | Installed       | OnDemand Pack   | 13-02-2016 17:02
Microsoft-Windows-LanguageFeatures-Basic-en-us-Package~31bf3856ad364e35~amd64~~10.0.10586.0               | Installed       | OnDemand Pack   | 13-02-2016 17:02
Microsoft-Windows-LanguageFeatures-Handwriting-da-dk-Package~31bf3856ad364e35~amd64~~10.0.10586.0         | Installed       | OnDemand Pack   | 13-02-2016 17:02
Microsoft-Windows-LanguageFeatures-OCR-da-dk-Package~31bf3856ad364e35~amd64~~10.0.10586.0                 | Installed       | OnDemand Pack   | 13-02-2016 17:02
Microsoft-Windows-LanguageFeatures-OCR-en-us-Package~31bf3856ad364e35~amd64~~10.0.10586.0                 | Installed       | OnDemand Pack   | 13-02-2016 17:02
Microsoft-Windows-NetFx3-OnDemand-Package~31bf3856ad364e35~amd64~~10.0.10586.0                            | Installed       | OnDemand Pack   | 11-04-2016 14:54
Microsoft-Windows-Prerelease-Client-Package~31bf3856ad364e35~amd64~da-DK~10.0.10586.0                     | Installed       | Language Pack   | 13-02-2016 16:58
Microsoft-Windows-Prerelease-Client-Package~31bf3856ad364e35~amd64~~10.0.10586.0                          | Installed       | Feature Pack    | 30-10-2015 07:26
Microsoft-Windows-Security-SPP-Component-SKU-Enterprise-GVLK-Package~31bf3856ad364e35~amd64~~10.0.10586.0 | Installed       | Feature Pack    | 13-02-2016 17:20
Package_for_KB3135173~31bf3856ad364e35~amd64~~10.0.1.2                                                    | Installed       | Security Update | 13-02-2016 17:13
Package_for_KB3136561~31bf3856ad364e35~amd64~~10.0.1.0                                                    | Installed       | Update          | 13-02-2016 17:13
Package_for_KB3140741~31bf3856ad364e35~amd64~~10.0.1.0                                                    | Installed       | Update          | 11-04-2016 14:35
Package_for_KB3144756~31bf3856ad364e35~amd64~~10.0.1.0                                                    | Installed       | Security Update | 11-04-2016 14:35
Package_for_KB3149135~31bf3856ad364e35~amd64~~10.0.1.1                                                    | Installed       | Update          | 24-06-2016 15:03
Package_for_KB3154132~31bf3856ad364e35~amd64~~10.0.1.0                                                    | Installed       | Security Update | 24-06-2016 10:01
Package_for_KB3157993~31bf3856ad364e35~amd64~~10.0.1.0                                                    | Installed       | Security Update | 24-06-2016 09:49
Package_for_RollupFix~31bf3856ad364e35~amd64~~10586.218.1.4                                               | Superseded      | Security Update |                 
Package_for_RollupFix~31bf3856ad364e35~amd64~~10586.318.1.7                                               | Install Pending | Security Update | 28-06-2016 12:39

The operation completed successfully.

From the list find the Package Identity Name of the Software Update.

Run this command to uninstall the KB3144756 silently:

DISM.exe /Online /Remove-Package /PackageName:Package_for_KB3144756~31bf3856ad364e35~amd64~~10.0.1.0 /quiet /norestart

It might require a reboot for the update to be fully removed.

Run Hardware or Software Inventory

This function can be used to trigger a Hardware or Software inventory.

To trigger Hardware inventory call the function using:

RunInventory(“Hardware”)

To trigger Software inventory call the function using:

RunInventory(“Software”)

Sub RunInventory(StrType)

	If StrType = "Hardware" Then
		StrType = "Hardware Inventory Collection Cycle"
	ElseIf StrType = "Software" Then
		StrType = "Software Inventory Collection Cycle"
	Else
		Exit Sub
	End If

    ' Create the CPAppletMgr instance.
    Dim controlPanelAppletManager
    Set controlPanelAppletManager = CreateObject("CPApplet.CPAppletMgr")

    ' Get the available ClientActions object.
    Dim clientActions
    Set clientActions = controlPanelAppletManager.GetClientActions()

    ' Loop through the available client actions. Run the matching client action when it is found.
    Dim clientAction
    For Each clientAction In clientActions
        If clientAction.Name =  StrType  Then
            clientAction.PerformAction  
        End If
    Next
    

End Sub

Uninstall MSI if Installed for the Current User

This function takes an MSI product code as input and uninstalls the software if its installed for the current user (ProductState =5)

Sub UnInstPkg (StrProdCode)

Dim oWSHShell : Set oWSHShell = CreateObject("WScript.Shell")
Dim oMSI : Set oMSI = CreateObject("WindowsInstaller.Installer")
Dim UnInstVal : UnInstVal = 1

 Do While UnInstVal &lt;&gt; 0
  If oMSI.ProductState (StrProdCode) = 5 Then
   ReturnVal = oWSHShell.Run("MSIExec.exe /X " &amp; StrProdCode &amp; " /QB!-", 0, True)
   if Not (ReturnVal = 0 OR ReturnVal = 3010) then WScript.Quit ReturnVal
   UnInstPkg (StrProdCode)
  Else
   UnInstVal = 0
  End If

  Loop
End Sub

Prompt to set OSDComputerName

I use this script to set the computer name on virtual computers during task sequence.

The computer naming script in the task sequence is using the serial number which for virtual computers is too long and will cause the task sequence to fail. Therefore its needed to somehow change the computer name. I have placed this script on a network share which i map after the computer have been given the long and faulty serial-number name. The script will prompt for a new computer name which will then overwrite the OSDComputerName task sequence variable.

Set env = CreateObject("Microsoft.SMS.TSEnvironment")
 Set SWBemlocator = CreateObject("WbemScripting.SWbemLocator")
 Set objWMIService = SWBemlocator.ConnectServer(strComputer,"root\CIMV2",UserName,Password)
 Set colItems = objWMIService.ExecQuery("Select * from Win32_BIOS",,48)
 For Each objItem in colItems
   env("OSDComputerName") = InputBox("Please enter a Computer Name:", "Computer Name")
 Next

Find all files with specific content

This Script will search c:\temp to find all files called test.txt that contains the string “server2”.

Get-ChildItem -Path c:\temp -File -Recurse -include test.txt | Select-String -pattern "server2" | Group path | Select Name

 

Replace Text in a File

This script will replace the occurence of “server1” with “server2” in all files called config.ini placed under c:\temp

Get-ChildItem -Path c:\temp -File -Recurse -include config.ini | %{(Get-Content $_) -replace "server1","server2" | Set-Content $_.fullname}

 

When was the Last Password Change

 

It can be handy to be able to look up when a User / System Account last changed password – this can be done using DSQuery

dsquery * “CN=<User name>,OU=<name of OU>,DC=<domain>,DC=<com> -Attr PwdLastSet

this will generate a long number which with the help of w32tm.exe can be converted to a date:

w32tm.exe /ntte <number generated above>

2015-06-11_09-41-51