I am attempting to get a list of installed Drivers and the date they were installed via PowerShell. I have attempted the following:
Get-WmiObject Win32_PnPSignedDriver| select devicename, InstallDate
And the InstallDate property is blank for 100% of the items...
I also tried:
Get-WindowsDriver -Online -All | Sort-Object Date
There is a date there, but it is not the install date. Probably the date the driver was compiled is my assumption.
As a misc note: MMC is not an alternative in this case. Long story.
I found the InstallDate associated with the driver files and not with the driver objects from WMI.
Please find the code bellow for an example:
# Initializing parameters (modify the Output folder by changing the $TargetDirectory define in the first line)
$TargetDirectory = "C:\temp\DriversINFTest\"
$hostname = $ENV:COMPUTERNAME
# Get Third Party drivers used, that are not provided by Microsoft and presumably included in the OS
$drivers = Get-WmiObject Win32_PNPSignedDriver | where {$_.DriverProviderName -ne "Microsoft"}
# Initialize the list of detected driver packages as an array
$DriverFolders = #()
foreach ($d in $drivers) {
# We initialize the list of driver files for each driver
$DriverFiles = #()
# For each driver instance from WMI class Win32_PNPSignedDriver, we compose the related WMI object name from the other WMI driver class, Win32_PNPSignedDriverCIMDataFile
$ModifiedDeviceID = $d.DeviceID -replace "\\", "\\"
$Antecedent = "\\" + $hostname + "\ROOT\cimv2:Win32_PNPSignedDriver.DeviceID=""$ModifiedDeviceID"""
# Get all related driver files for each driver listed in WMI class Win32_PNPSignedDriver
$DriverFiles += Get-WmiObject Win32_PNPSignedDriverCIMDataFile | where {$_.Antecedent -eq $Antecedent}
if ($DriverFiles -ne $null) {
foreach ($i in $DriverFiles) {
# We elliminate double backslashes from the file paths
$path = $i.Dependent.Split("=")[1] -replace '\\\\', '\'
# We elliminate the trailing and ending quotes from the file paths
$path2 = $path.Substring(1,$path.Length-2)
# On the first pass, we only process the INF files as there is a very low chance of existing more than one driver package on the same machine, with the same INF file legth
if ($path2.Split("\\")[$path2.Split("\\").Length-1].split(".")[1] -eq "inf") {
# We get the file attributes for each INF file
$CurrentDeviceID = $d.DeviceID
Write-Host "Current Device ID is $CurrentDeviceID"
$filedetails = $i.Dependent
Write-Host "Current inf file is $filedetails"
$where = $filedetails.split("=")[1]
$query = "select * from CIM_DataFile where Name=$where"
$InstallDate = Get-WmiObject -Namespace "ROOT\CIMV2" -Query $query
$ReadableDate = $InstallDate.ConvertToDateTime($InstallDate.InstallDate)
Write-Host "Installation date is:"
$ReadableDate
} # End of if ($path2.Split("\\")[$path2.Split("\\").Length-1].split(".")[1] -eq "inf")
} # End of foreach ($i in $DriverFiles)
} # End of if ($DriverFiles -ne $null)
} # End of foreach ($d in $drivers)
Related
I want optimize a simple task: pull server OS version into a neat table. However, some servers in our environment have Powershell disabled. Below you fill find my script, which works! However, it takes about 20 seconds or so per server, since it waits for the server to return the results of the invoked command before moving onto the next server in the list. I know there's a way to asynchronously pull the results from a PS command, but is this possible when I need to resort to cmd line syntax for servers that can't handle PS, as shown in the catch statement?
$referencefile = "ps_servers_to_query.csv"
$export_location = "ps_server_os_export.csv"
$Array = #()
$servers = get-content $referencefile
foreach ($server in $servers){
#attempt to query the server with Powershell.
try{
$os_version = invoke-command -ComputerName $server -ScriptBlock {Get-ComputerInfo -Property WindowsProductName} -ErrorAction stop
$os_version = $os_version.WindowsProductName
} # If server doesnt have PS installed/or is disabled, then we will resort to CMD Prompt, this takes longer however.. also we will need to convert a string to an object.
catch {
$os_version = invoke-command -ComputerName $server -ScriptBlock {systeminfo | find "OS Name:"} # this returns a string that represents the datetime of reboot
$os_version = $os_version.replace('OS Name: ', '') # Remove the leading text
$os_version = $os_version.replace(' ','') # Remove leading spaces
$os_version = $os_version.replace('Microsoft ','') # Removes Microsoft for data standardization
}
# Output each iteration of the loop into an array
$Row = "" | Select ServerName, OSVersion
$Row.ServerName = $Server
$Row.OSVersion = $os_version
$Array += $Row
}
# Export results to csv.
$Array | Export-Csv -Path $export_location -Force
Edit: Here's what I'd like to accomplish. Send the command out to all the servers (less than 30) at once, and have them all process the command at the same time rather than doing it one-by-one. I know I can do this if they all could take PowerShell commands, but since they can't I'm struggling. This script takes about 6 minutes to run in total.
Thank you in advance!
If I got it right something like this should be all you need:
$referencefile = "ps_servers_to_query.csv"
$export_location = "ps_server_os_export.csv"
$ComputerName = Get-Content -Path $referencefile
$Result =
Get-CimInstance -ClassName CIM_OperatingSystem -ComputerName $ComputerName |
Select-Object -Property Caption,PSComputerName
$Result
| Export-Csv -Path $export_location -NoTypeInformation
In the script attached I am trying to rename a PC if the PC has a certain hostname. However, the script is proceeding anyway and bypasses the if/else statement.
What am I doing wrong? I am kind of new with Windows Powershell.
Thanks!
# get current computername
$hostname = hostname.exe
#$env:computername
If ( $hostname = "CLNT3100" )
{
#Get all the computers with CLNT3* and sort them with the 'highest client' on top. Then put them in newlist.txt
Get-ADComputer -Filter 'SamAccountName -like "CLNT3*"' | Select -Exp Name | Sort-Object -Descending >> C:\newlist.txt
#Put the text file in a variable and show the top line
$Text = Get-Content -Path C:\newlist.txt
#$Text[0]
#Trim CLNT for numbering
$Text1 = $Text[0].TrimStart("CLNT")
#Add 1 number to the previous CLNT
$Text2 = 1 + $Text1
#Add CLNT again to the new variable
$Text3 = "CLNT" + $Text2
#Rename the computer
Rename-Computer –computername minint –newname $Text3
}
Else
{
Write-Host "Computernaam is niet minint!!!"
}
To compare if two values are equal in Powershell you have to use the -eqoperator.
Check the Powershell equality operators to see the others like -gt, -lt etc, or type man about_Comparison_Operators in the PS shell.
Also, to learn Powershell I found this free ebook to be very good.
I am trying to figure out a script that'll help me list all the Microsoft updates installed on my system.
I am using
Get-Hotfix
to do the same but i am not getting desired results. Neither is:
Get-WmiObject -Class "win32_quickfixengineering" |
where $_.name = "Microsoft"
this working for me.
Please help.
You could use this script (didn't find a way to display the description with Get-HotFix).
It lists the programs found in the Uninstall keys of the windows registry, and matches the name againt the $filter string.
You can remotely get this information from another computer by changing $computerName (currently the local host).
#store computer name in a variable
$computerName = $env:COMPUTERNAME
#store filter string in a variable
$filter = "KB"
#declare results array
$result = #()
#store registry key paths in an array
$keyList = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\',
'SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\'
#open registry hive HKLM
$hive = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $computerName)
#for each key path
foreach($key in $keyList) {
#open key
$uninstallKey = $hive.OpenSubKey($key)
#if key has been opened
if($uninstallKey) {
#list program keys
$programList = $uninstallKey.GetSubKeyNames()
#for each key
foreach($program in $programList) {
#get the program name
$programName = $uninstallKey.OpenSubKey($program).GetValue("DisplayName")
#if the program name is not null and matches our filter
if(($programName) -and ($programName -like "*$filter*")) {
#add program name to results array
$result += $programName
}
}
}
}
#sort and output results array
$result | Sort-Object
I am working on a side project and to make it easier for managment since almost all of out server names are 15 charactors long I started to look for an RDP managment option but none that I liked; so I started to write one and I am down to only one issue, what do I do to manage if the user types not enough for a search so two servers will match the Query. I think I will have to put it in an array and then let them select the server they meant. Here is what I have so far
function Connect-RDP
{
param (
[Parameter(Mandatory = $true)]
$ComputerName,
[System.Management.Automation.Credential()]
$Credential
)
# take each computername and process it individually
$ComputerName | ForEach-Object{
Try
{
$Computer = $_
$ConnectionDNS = Get-ADComputer -server "DomainController:1234" -ldapfilter "(name=$computer)" -ErrorAction Stop | Select-Object -ExpandProperty DNSHostName
$ConnectionSearchDNS = Get-ADComputer -server "DomainController:1234" -ldapfilter "(name=*$computer*)" | Select -Exp DNSHostName
Write-host $ConnectionDNS
Write-host $ConnectionSearchDNS
if ($ConnectionDNS){
#mstsc.exe /v ($ConnectionDNS) /f
}Else{
#mstsc.exe /v ($ConnectionSearchDNS) /f
}
}
catch
{
Write-Host "Could not locate computer '$Computer' in AD." -ForegroundColor Red
}
}
}
Basically I am looking for a way to manage if a user types server1
that it will ask does he want to connect to Server10 or Server11 since both of them match the filter.
Another option for presenting choices to the user is Out-GridView, with the -OutPutMode switch.
Borrowing from Matt's example:
$selection = Get-ChildItem C:\temp -Directory
If($selection.Count -gt 1){
$IDX = 0
$(foreach ($item in $selection){
$item | select #{l='IDX';e={$IDX}},Name
$IDX++}) |
Out-GridView -Title 'Select one or more folders to use' -OutputMode Multiple |
foreach { $selection[$_.IDX] }
}
else {$Selection}
This example allows for selection of multiple folders, but can you can limit them to a single folder by simply switching -OutPutMode to Single
I'm sure what mjolinor has it great. I just wanted to show another approach using PromptForChoice. In the following example we take the results from Get-ChildItem and if there is more than one we build a collection of choices. The user would select one and then that object would be passed to the next step.
$selection = Get-ChildItem C:\temp -Directory
If($selection.Count -gt 1){
$title = "Folder Selection"
$message = "Which folder would you like to use?"
# Build the choices menu
$choices = #()
For($index = 0; $index -lt $selection.Count; $index++){
$choices += New-Object System.Management.Automation.Host.ChoiceDescription ($selection[$index]).Name, ($selection[$index]).FullName
}
$options = [System.Management.Automation.Host.ChoiceDescription[]]$choices
$result = $host.ui.PromptForChoice($title, $message, $options, 0)
$selection = $selection[$result]
}
$selection
-Directory requires PowerShell v3 but you are using 4 so you would be good.
In ISE it would look like this:
In standard console you would see something like this
As of now you would have to type the whole folder name to select the choice in the prompt. It is hard to get a unique value across multiple choices for the shortcut also called the accelerator key. Think of it as a way to be sure they make the correct choice!
I'm new to PowerShell and I'm looking for a way to un-install multiple applications. I have a list of applications in a text file that i want to Un-install. Here's the code i have so far:
# Retrieve names of all softwares to un-install and places in variable $app
$App = Get-Content "C:\temp\un-installApps.txt"
# Cycle through each of the softwares to un-install and store in the WMI variable
Foreach ($AppName in $App)
{
$AppTmp = Get-WmiObject -query "Select * from win32_product WHERE Name like" + $AppName
$AppNames = $AppNames + $AppTmp
}
foreach ($Application in $AppNames )
{
msiexec /uninstall $Application.IdentifyingNumber
}
The following lines causes the issues
$AppTmp = Get-WmiObject -query "Select * from win32_product WHERE Name like" + $AppName
$AppNames = $AppNames + $AppTmp"
Any ideas how i can get this to work?
I think it's because there is no space between like and the application name, and there needs to be single quotes around the application name. That part should look like like '" + $AppName + "'".
However, you could do the whole script more simply like this:
$App = Get-Content "C:\temp\un-installApps.txt"
gwmi win32_product|
where { $App -contains $_.Name }|
foreach { $_.Uninstall() } | out-null