powershell sort-object and keeps window open - powershell

I have following script to check the installed software on local and remote machines.
Usually it works fine but i have two problems. It only works when i open it in ISE. If i open it in a normal powershell, the script close immediately. Even a pause or read-host command won't work.
For example here is my script for a local machine. Hope you guys can help me.
Function Get-InstalledSoftware {
Param(
[Alias('Computer','ComputerName','HostName')]
[Parameter(
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$true,
Position=1
)]
[string]$Name = $env:COMPUTERNAME
)
Begin{
$lmKeys = "Software\Microsoft\Windows\CurrentVersion\Uninstall","SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
$lmReg = [Microsoft.Win32.RegistryHive]::LocalMachine
$cuKeys = "Software\Microsoft\Windows\CurrentVersion\Uninstall"
$cuReg = [Microsoft.Win32.RegistryHive]::CurrentUser
}
Process{
if (!(Test-Connection -ComputerName $Name -count 1 -quiet)) {
Write-Error -Message "Unable to contact $Name. Please verify its network connectivity and try again." -Category ObjectNotFound -TargetObject $Computer
Break
}
$masterKeys = #()
$remoteCURegKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($cuReg,$computer)
$remoteLMRegKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($lmReg,$computer)
foreach ($key in $lmKeys) {
$regKey = $remoteLMRegKey.OpenSubkey($key)
foreach ($subName in $regKey.GetSubkeyNames()) {
foreach($sub in $regKey.OpenSubkey($subName)) {
$masterKeys += (New-Object PSObject -Property #{
"ComputerName" = $Name
"Name" = $sub.getvalue("displayname")
"SystemComponent" = $sub.getvalue("systemcomponent")
"ParentKeyName" = $sub.getvalue("parentkeyname")
"Version" = $sub.getvalue("DisplayVersion")
"UninstallCommand" = $sub.getvalue("UninstallString")
"InstallDate" = $sub.getvalue("InstallDate")
"RegPath" = $sub.ToString()
})
}
}
}
foreach ($key in $cuKeys) {
$regKey = $remoteCURegKey.OpenSubkey($key)
if ($regKey -ne $null) {
foreach ($subName in $regKey.getsubkeynames()) {
foreach ($sub in $regKey.opensubkey($subName)) {
$masterKeys += (New-Object PSObject -Property #{
"ComputerName" = $Name
"Name" = $sub.getvalue("displayname")
"SystemComponent" = $sub.getvalue("systemcomponent")
"ParentKeyName" = $sub.getvalue("parentkeyname")
"Version" = $sub.getvalue("DisplayVersion")
"UninstallCommand" = $sub.getvalue("UninstallString")
"InstallDate" = $sub.getvalue("InstallDate")
"RegPath" = $sub.ToString()
})
}
}
}
}
$woFilter = {$null -ne $_.name -AND $_.SystemComponent -ne "1" -AND $null -eq $_.ParentKeyName}
$props = 'Name','Version','ComputerName','Installdate','UninstallCommand','RegPath'
$masterKeys = ($masterKeys | Where-Object $woFilter | Select-Object $props | Sort-Object Name)
$masterKeys
}
End{}
}
Get-InstalledSoftware | select-object name | sort-object

Send your output somewhere, not just to the host window. I suspect the windows closes or your pause command is kicking in before its retrieved the results thus they aren't sent to the host window.
The following works fine for me run as a script:
Get-InstalledSoftware | Select-Object name | Sort-Object | Out-Gridview

Related

Powershell for retrieve local USB printer details and insert into WMI

I am trying to get USB printer model and serial from the Win32_PnPEntity, then insert these info into a custom WMI namespace, so that I can use the Hardware inventory function in SCCM to collect the info for reporting.
I noticed that the location storing these info varies from one manufacturer to another.
such as the FujiXerox stores in "USB Printing Support" while HP and Brother stores in "USB Composite Device"
what weird is after I somehow got the below code working on my computer, when I try it on other computers, it only returns the first character, such as:
Results on my computer (two USB printers connected)
Model
Serial
HEWLETT-PACKARDHP_LASERJET_400_M401D
VNH3G0XXXX
FUJI_XEROXDOCUPRINT_P355_DB
YWG-50XXXX
Results on other computers (eg. a HP printer with serial no. starting with V)
Model
Serial
H
V
I am a system administrator managing SCCM, occasionally using PowerShell to help on my tasks, I just completely stuck at the moment as I didn't find any hint that will trim the results.
Thanks
Andrew
$ModelInfo = #()
$SerialInfo = #()
$FullInfo = #{}
$Final=#()
$USBPrinterModels = Get-WmiObject Win32_PnPEntity | Where-Object {$_.DeviceID -Match "USBPRINT"}|select DeviceID
$USBPrinterSerials2 = Get-WmiObject Win32_PnPEntity | Where-Object {$_.Description -Match "USB 列印支援" -or $_.Description -Match "USB Printing Support"}|select DeviceID
$USBPrinterSerials = Get-WmiObject Win32_PnPEntity | Where-Object {$_.Description -Match "USB Composite Device"}|select DeviceID
Foreach ($USBPrinterModel in $USBPrinterModels)
{
$ModelFull = $USBPrinterModel.DeviceID
$Model = #{}
$Model.model += ($ModelFull.Split("\"))[1]
$ModelInfo += $Model
}
Foreach ($USBPrinterSerial in $USBPrinterSerials)
{
$SerialFull = $USBPrinterSerial.DeviceID
$Serial = #{}
$Serial.serial += $SerialFull.Split("\")[2]
If($Serial.serial -notmatch "&")
{
$SerialInfo += $Serial
}
}
Foreach ($USBPrinterSerial2 in $USBPrinterSerials2)
{
$SerialFull2 = $USBPrinterSerial2.DeviceID
$Serial2 = #{}
$Serial2.serial += $SerialFull2.Split("\")[2]
If($Serial2.serial -notmatch "&")
{
$SerialInfo += $Serial2
}
}
$MaxLength = [Math]::Max($ModelInfo.Length, $SerialInfo.Length)
for ($loop_index = 0; $loop_index -lt $MaxLength; $loop_index++)
{
$Final += new-object psobject -Property #{
Model=$ModelInfo.model[$loop_index]
Serial=$SerialInfo.serial[$loop_index]
}
# $Final+=$ModelInfo[$loop_index]
# $Final+=$SerialInfo[$loop_index]
}
$Class = Get-WmiObject Win32_USBPrinterDetails -ErrorAction SilentlyContinue
If ($Class) {Remove-WmiObject -Class Win32_USBPrinterDetails}
$WMIClass = New-Object System.Management.ManagementClass("root\cimv2", [String]::Empty, $null);
$WMIClass["__CLASS"] = "Win32_USBPrinterDetails";
$WMIClass.Qualifiers.Add("Static", $true)
$WMIClass.Properties.Add("Model", [System.Management.CimType]::String, $false)
$WMIClass.Properties["Model"].Qualifiers.Add("read", $true)
$WMIClass.Properties.Add("Serial", [System.Management.CimType]::String, $false)
$WMIClass.Properties["Serial"].Qualifiers.Add("key", $true)
$WMIClass.Properties["Serial"].Qualifiers.Add("read", $true)
$WMIClass.Put()
ForEach ($FInfo in $Final) {
[void](Set-WmiInstance -Path \\.\root\cimv2:Win32_USBPrinterDetails -Arguments #{Model=$FInfo.model; Serial=$FInfo.serial})
}
I am so stupid,
When there is only ONE USB printer, the $ModelInfo and $SerialInfo become "String" type and caused the [loop_index] return the first character of the string instead of the first entry of the array.
I added a gettype check on the variables (surely not perfect).
$ModelInfo = #()
$SerialInfo = #()
$FullInfo = #{}
$Final=#()
$USBPrinterModels = Get-WmiObject Win32_PnPEntity | Where-Object {$_.DeviceID -Match "USBPRINT"}|select DeviceID
$USBPrinterSerials2 = Get-WmiObject Win32_PnPEntity | Where-Object {$_.Description -Match "USB 列印支援" -or $_.Description -Match "USB Printing Support"}|select DeviceID
$USBPrinterSerials = Get-WmiObject Win32_PnPEntity | Where-Object {$_.Description -Match "USB Composite Device"}|select DeviceID
Foreach ($USBPrinterModel in $USBPrinterModels)
{
$ModelFull = $USBPrinterModel.DeviceID
$Model = #{}
$Model.model += ($ModelFull.Split("\"))[1]
$ModelInfo += $Model
}
Foreach ($USBPrinterSerial in $USBPrinterSerials)
{
$SerialFull = $USBPrinterSerial.DeviceID
$Serial = #{}
$Serial.serial += $SerialFull.Split("\")[2]
If($Serial.serial -notmatch "&")
{
$SerialInfo += $Serial
}
}
Foreach ($USBPrinterSerial2 in $USBPrinterSerials2)
{
$SerialFull2 = $USBPrinterSerial2.DeviceID
$Serial2 = #{}
$Serial2.serial += $SerialFull2.Split("\")[2]
If($Serial2.serial -notmatch "&")
{
$SerialInfo += $Serial2
}
}
If ($ModelInfo.model.GetType().name -eq "String") {
$Final += new-object psobject -Property #{
Model=$ModelInfo.model
Serial=$SerialInfo.serial
}
}
ElseIf ($ModelInfo.model.GetType().name -ne "String"){
$MaxLength = [Math]::Max($ModelInfo.Length, $SerialInfo.Length)
for ($loop_index = 0; $loop_index -lt $MaxLength; $loop_index++)
{
$Final += new-object psobject -Property #{
Model=$ModelInfo.model[$loop_index]
Serial=$SerialInfo.serial[$loop_index]
}
}
}
$Class = Get-WmiObject Win32_USBPrinterDetails -ErrorAction SilentlyContinue
If ($Class) {Remove-WmiObject -Class Win32_USBPrinterDetails}
$WMIClass = New-Object System.Management.ManagementClass("root\cimv2", [String]::Empty, $null);
$WMIClass["__CLASS"] = "Win32_USBPrinterDetails";
$WMIClass.Qualifiers.Add("Static", $true)
$WMIClass.Properties.Add("Model", [System.Management.CimType]::String, $false)
$WMIClass.Properties["Model"].Qualifiers.Add("read", $true)
$WMIClass.Properties.Add("Serial", [System.Management.CimType]::String, $false)
$WMIClass.Properties["Serial"].Qualifiers.Add("key", $true)
$WMIClass.Properties["Serial"].Qualifiers.Add("read", $true)
$WMIClass.Put()
ForEach ($FInfo in $Final) {
[void](Set-WmiInstance -Path \\.\root\cimv2:Win32_USBPrinterDetails -Arguments #{Model=$FInfo.model; Serial=$FInfo.serial})
}
$final|FT

Why do I get different output in Powershell from start-job vs just running the code?

The script runs correctly when outside of Start-Job but when in a scriptblock I get incorrect results. Where am I going wrong?
I need the Start-Job functionality since I have servers where the remote commands will hang (separate problem - WMI is borked) and I need to timeout and move to the next server.
I've tried every variation I can find in Google and still don't have the results I'm looking for.
I am really at my wits end with this as I don't understand what is happening... Help?
Thanks!
$timeoutSeconds = 90
ForEach($server in $servers) {
#$ErrorActionPreference = "inquire"
#$WarningPreference = "inquire"
$ErrorActionPreference = "silentlycontinue"
$WarningPreference = "silentlycontinue"
write-host $SERVER
$code = {
param($SERVER,$LOGto,$outputPath)
$ping = (Test-Connection -ComputerName $SERVER -Count 2 -Quiet )
if($ping -eq $true)
{
$pingVerbose = (Test-Connection -ComputerName $SERVER -Count 1)
$IP = $pingVerbose.IPV4Address.IPAddressToString
$osname2 = (Get-WMIObject -computerName $SERVER win32_operatingsystem).name
if($osname2 -match "|")
{
$osname,$osname1 = $osname2.Split('|')
} else {
$osname = $osname2
}
$lastinstalled = (Get-HotFix -computerName $SERVER | where -property InstalledOn -ne $null)
if($lastinstalled.InstalledOn)
{
$lastinstalledOn1 = ($lastinstalled.InstalledOn | Sort-Object -Property InstalledOn )[-1]
$lastinstalledOn = $lastinstalledOn1
}
$lastQFE = (get-wmiobject -class win32_quickfixengineering -computerName $SERVER | where -property InstalledOn -ne $null)
if($lastQFE.InstalledOn -ne $null)
{
$lastQFEon = ($lastQFE.InstalledOn | Sort-Object -Property InstalledOn)[-1]
$lastQFEon = $lastQFEon
}
if(($lastinstalledOn) -or ($lastQFEon))
{
if(($lastinstalledOn) -and ($lastinstalledOn -gt $lastQFEon))
{
$installedOn = $lastinstalledOn.tostring("MM/dd/yyyy")
$HotFixNumber = ($lastinstalled.HotFixID | Sort-Object -Property HotFixID)[-1]
} else {
$installedOn = $lastQFEon.tostring("MM/dd/yyyy")
$HotFixNumber = ($lastQFE.HotFixID | Sort-Object -Property HotFixID)[-1]
}
} else {
$installedOn = ''
$HotFixNumber = ''
}
}
#add entries to the log file
ac $outputPath\$LOGto "$Server,$ip,$installedOn,$HotFixNumber,$ping,$osname "
Write-Host "$Server,$ip,$installedOn,$HotFixNumber,$ping,$osname "
}
$runCode = Start-Job -ScriptBlock $code -ArgumentList $server,$LOGto,$outputPath
if(Wait-Job $runCode -Timeout $timeoutSeconds)
{
Receive-Job $runCode
} else {
Remove-Job -Force $runCode
ac $($outputPath + "\error.txt") "$Server"
}
}
When running in the scriptblock I receive the wrong date and KB.
SERVERNAME
SERVERNAME,10.1.XX.XX,03/13/2015,KB3022777,True,Microsoft Windows Server 2012 R2 Standard
vs.
SERVERNAME
SERVERNAME,10.1.XX.XX,05/15/2017,KB4012213,True,Microsoft Windows Server 2012 R2 Standard
For posterity more than anything else...
The error I was making was using sort-object against a string instead of a numerical value.
The code below will work correctly once you replace the domain name and file path information.
Thanks,
-Luke
#change this to a directory of your choice
$outputPath = "C:\Users\username\Desktop"
cd $outputPath
#create a default file name
$LOGto = "AllWinHotFixDateDC_$((Get-Date).ToString('yyyyMMdd')).csv"
#create the headers
sc .\$LOGto "Server,IPAddress,InstalledOn,HotFixNumber,Ping,OS_Name"
#get the server names from AD
Get-ADComputer -Filter {(Enabled -eq "True") -and (OperatingSystem -like "*Windows*") } -SearchBase "OU=Servers,DC=mydomain,DC=net" -server 'mydomain.net' -SearchScope Subtree | Select Name -ExpandProperty name | Sort-Object | Out-File .\servers.txt
$servers = get-content .\servers.txt
$timeoutSeconds = 90
ForEach($server in $servers) {
$ErrorActionPreference = "inquire"
$WarningPreference = "inquire"
#$ErrorActionPreference = "silentlycontinue"
#$WarningPreference = "silentlycontinue"
write-host $SERVER
$code = {
param($SERVER,$LOGto,$outputPath)
$ping = (Test-Connection -ComputerName $SERVER -Count 2 -Quiet )
if($ping -eq $true)
{
$pingVerbose = (Test-Connection -ComputerName $SERVER -Count 1)
$IP = $pingVerbose.IPV4Address.IPAddressToString
$osname2 = (Get-WMIObject -computerName $SERVER win32_operatingsystem).name
if($osname2 -match "|")
{
$osname,$osname1 = $osname2.Split('|')
} else {
$osname = $osname2
}
$getinstalled = (Get-HotFix -computerName $SERVER)
if($getinstalled)
{
if($getinstalled.HotFixID -ne $null)
{
$validinstalled = ($getinstalled.HotFixID -match "KB*")
$KB = (($validinstalled -replace 'KB','') | Sort-Object {[int]($_ -replace '(\d+).*', '$1')})[-1]
$lastinstalledOnHotFix = ("KB$KB")
}
if ($getinstalled.InstalledOn -ne $null)
{
$lastInstalled = ($getinstalled | Sort-Object -Property InstalledOn)[-1]
$lastInstalledlist = $lastInstalled.InstalledOn | Sort-Object {[int]($_ -replace '(\d+).*', '$1')}
$lastInstalledon = $lastInstalledlist.tostring("MM/dd/yyyy")
} else {
$lastinstalledOn = "0"
}
}
Write-Host $lastinstalledOn
Write-Host $lastinstalledOnHotFix
$getQFE = (get-wmiobject -class win32_quickfixengineering -computerName $SERVER )
if($getQFE)
{
if($getQFE.HotFixID -ne $null)
{
$validQFE = ($getQFE.HotFixID -match 'KB')
$KB = (($validQFE -replace 'KB','') | Sort-Object {[int]($_ -replace '(\d+).*', '$1')})[-1]
$lastQFEonHotFix = ("KB$KB")
}
if($getQFE.InstalledOn -ne $null)
{
$lastQFE = ($getQFE | Sort-Object -Property InstalledOn)[-1]
$lastQFElist = $lastQFE.InstalledOn | Sort-Object {[int]($_ -replace '(\d+).*', '$1')}
$lastQFEon = $lastQFElist.tostring("MM/dd/yyyy")
} else {
$lastQFEon = "0"
}
}
Write-Host $lastQFEon
Write-Host $lastQFEonHotFix
if(($lastinstalledOn -ne $null) -or ($lastQFEon -ne $null))
{
if(($lastInstalledlist -ne $null) -and ($lastInstalledlist -gt $lastQFElist))
{
$installedOn = $lastinstalledOn
$HotFixNumber = $lastinstalledOnHotFix
} elseif($lastQFEon -ne $null)
{
$installedOn = $lastQFEon
$HotFixNumber = $lastQFEonHotFix
}
} else {
$installedOn = '0'
$HotFixNumber = $lastQFEonHotFix
}
}
#add entries to the log file
ac $outputPath\$LOGto "$Server,$ip,$installedOn,$HotFixNumber,$ping,$osname "
Write-Host "$Server,$ip,$installedOn,$HotFixNumber,$ping,$osname "
}
$runCode = Start-Job -ScriptBlock $code -ArgumentList $server,$LOGto,$outputPath
if(Wait-Job $runCode -Timeout $timeoutSeconds)
{
Receive-Job $runCode
} else {
Remove-Job -Force $runCode
ac $($outputPath + "\error.txt") "$Server"
}
}

PowerShell Workflow scoping issue

Recently we started exploring Workflows in PowerShell. It greatly enhances the execution speed, but it adds an extra level of complexity too.
The code below has a scoping issue. The variables in the Parallel clause ($Workflow:Ports and $Workflow:Drivers) ar apparently shared over the different $ComputerNames instead of being specific to one $ConputerName. When checking technet I can't seem to figure out how to make the variables $Ports and $Drivers specific to that computer ($C).
When using $Workflow:Ports they are shared between all computers, and this is not what we want. When using $Ports it's not available in the InlineScript clause.
The code:
Workflow Get-PrintersInstalledHC {
Param (
[String[]]$ComputerName
)
Foreach -Parallel ($S in $ComputerName) {
$Computer = InlineScript {
[PSCustomObject]#{
ComputerName = $Using:S
ComputerStatus = $null
Printers = $null
RetrievalDate = Get-Date
}
}
# $VerbosePreference = [System.Management.Automation.ActionPreference]$Using:VerbosePreference
Try {
$Printers = Get-Printer -ComputerName $S -Full -EA Stop
if ($Printers) {
Write-Verbose "$S Found $($Printers.Count) printers"
Parallel {
$Workflow:Ports = Get-PrinterPort -ComputerName $S
$Workflow:Drivers = Get-PrinterDriver -ComputerName $S
}
$CimConfig = InlineScript {
Try {
#region CmdLets that require admin permissions
$Params = #{
ComputerName = $Using:S
ClassName = 'Win32_PrinterConfiguration'
Property = '*'
ErrorAction = 'Stop'
Verbose = $false
}
$Config = Get-CimInstance #Params
Foreach ($P in $Using:Printers) {
Foreach($C in $Config) {
if ($P.Name -eq $C.Name) {
#{
PrinterName = $P.Name
CimStatus = 'Ok'
DriverVersionCim = $C.DriverVersion
Collate = $C.Collate
Color = $C.Color
Copies = $C.Copies
Duplex = $C.Duplex
PaperSize = $C.PaperSize
Orientation = $C.Orientation
PrintQuality = $C.PrintQuality
MediaType = $C.MediaType
DitherType = $C.DitherType
}
Break
}
}
}
#endregion
}
Catch {
Foreach ($P in $Using:Printers) {
#{
PrinterName = $P.Name
CimStatus = 'No admin permissions'
}
}
}
}
Foreach -parallel ($P in $Printers) {
$PrinterConfig = InlineScript {
$P = $Using:P
$Port = $Using:Ports | Where {$_.Name -eq $P.PortName}
$Driver = $Using:Drivers | Where {$_.Name -eq $P.DriverName}
Write-Verbose "$Using:S Printer '$($P.Name)'"
$DriverManufacturer = if ($Driver.Manufacturer) {$Driver.Manufacturer} else {
if ($Driver.Name -like '*Microsoft*') {'Microsoft'}
}
$DriverVersion = if ($Driver.DriverVersion -eq '0') {$null} else {
$Driver.DriverVersion
}
#{
Online_Hostname = if ($P.Name) {Test-Connection $P.Name -Quiet -EA Ignore} else {$null}
Online_PortHostAddress = if ($Port.PrinterHostAddress) {Test-Connection $Port.PrinterHostAddress -Quiet -EA Ignore} else {$null}
PortHostAddress = if ($Port.PrinterHostAddress) {$Port.PrinterHostAddress} else {$null}
PortDescription = if ($Port.Description) {$Port.Description} else {$null}
DriverType = $Driver.PrinterEnvironment -join ','
DriverManufacturer = ($DriverManufacturer | Select -Unique) -join ','
DriverVersion = ($DriverVersion | Select -Unique) -join ','
}
}
$Cim = $CimConfig | Where-Object -FilterScript {$P.Name -eq $_.PrinterName}
$P | Add-Member -NotePropertyMembers ($PrinterConfig + $Cim) -TypeName NoteProperty
}
InlineScript {
$Computer = $Using:Computer
$Computer.Printers = $Using:Printers
$Computer.ComputerStatus = 'Ok'
$Computer
}
}
else {
Write-Verbose "$S No printers found"
}
}
Catch {
InlineScript {
$Computer = $Using:Computer
if (Test-Connection $Using:S -Count 2 -Quiet) {
$Computer.ComputerStatus = $Using:_.Message
}
else {
$Computer.ComputerStatus = 'Offline'
}
$Computer
}
}
}
}

How to output from a Powershell hashtable to a output file

I am trying to get the .NetFramwork version from all the windows servers. I am using powershell script. I can get the output displayed but unable to get the output from the hashtable to a output file. Also how would I get rid of the "..." from VersionDetails : {1.0.3705, 1.1.4322, 2.0.50727, 3.0...} and show the full content.
Any help will be greatly appreciated
here is the code I am using:
$username = "username"
$password = "Password"
$secstr = New-Object -TypeName System.Security.SecureString
$password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr
$query = "select name from win32_directory where name like 'c:\\windows\\microsoft.net\\framework\\v%'"
$ComputerNames = Get-Content "d:\Scripts\serverList.txt"
foreach ($ComputerName in $ComputerNames)
{
write-host "ComputerName = $ComputerName"
$ComputerName | ForEach-Object {
$res = Get-WmiObject -query $query -Credential $cred -ComputerName $ComputerName | ForEach-Object {
Split-Path $_.name -Leaf } | # returns directories
Where-Object { $_ -like 'v*' } | # only include those that start with v
ForEach-Object { [system.version]( $_ -replace "^v" ) }
# remove "v" from the string and convert to version object
# Create hashtable with computername and version details
$prop = #{
ComputerName = $ComputerName
#V1_Present = &{ if ( $res | Where-Object { $_.Major -eq 1 -and $_.Minor -eq 0 } ) { $true } }
#V1_1Present = &{ if ( $res | Where-Object { $_.Major -eq 1 -and $_.Minor -eq 1 } ) { $true } }
V2_Present = &{ if ( $res | Where-Object { $_.Major -eq 2 -and $_.Minor -eq 0 } ) { $true } }
V3_Present = &{ if ( $res | Where-Object { $_.Major -eq 3 -and $_.Minor -eq 0 } ) { $true } }
V3_5Present = &{ if ( $res | Where-Object { $_.Major -eq 3 -and $_.Minor -eq 5 } ) { $true } }
V4_Present = &{ if ( $res | Where-Object { $_.Major -eq 4 -and $_.Minor -eq 0 } ) { $true } }
VersionDetails = $res
}
# Create and output PSobject using hashtable
New-Object PSObject -Property $prop
}
=========================================================
Output dispalys
PS D:\Scripts> .\GetDotNetFrameworkver.ps1
in for loop ComputerName = XXXXXXX
V4_Present : True
V3_5Present : True
V2_Present : True
V3_Present : True
ComputerName : XXXXX
VersionDetails : {1.0.3705, 1.1.4322, 2.0.50727, 3.0...}
Based on the answer of link there is a "simpler" (and faster) solution to fetch the versions.
Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -recurse | Get-ItemProperty -name Version,Release -ErrorAction Ignore | Where { $_.PSChildName -match '^(?!S)\p{L}'} | Select PSChildName, Version, Release
If you want to get the versions of different remote machines you can use PowerShell remoting. Be aware that you've to enable PS remoting .If your OS version is WIN10/WIN2012R2 it is enabled per default. If you're using an older OS you've to call Enable-PSRemoting on the remote machine. See this link for details.
Example:
$result = Invoke-Command -ComputerName computer1.domain, computer1.domain -Credential (Get-Credential ) -ScriptBlock {
Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -recurse | Get-ItemProperty -name Version,Release -ErrorAction Ignore | Where { $_.PSChildName -match '^(?!S)\p{L}'} | Select PSChildName, Version, Release
}
$hash = $result | group PSComputerName -AsHashTable # Group the .Net versions by computername
$hash.'computer1.domain' # Print all .Net version of computer1
$hash.'computer1.domain'.Version # Only print the version
Hope that helps.

Issues with powershell invoke-command, Unexpected token '-ArgumentList' in expression or statement

I am quite new to powershell. I am trying to remote to PCs in a csv file and extract the status of a registry key. However I get the error
"Unexpected token '-ArgumentList' in expression or statement." when I try to execute. And I am not pretty sure on the syntax of the Invoke-Command, can one of your pls verify if it is correct? Appreciate your help.
So basically, what I intend to do is, get computer names, specify an output path which I will be then using inside the invoke command. Testing the online status of the PC, checking the required registry value and writing it to the file.
$computers = Get-Content "C:\Temp\AutoSug\Computers.txt"
$output_path = "C:\Temp\AutoSug\output.csv"
foreach($computer in $computers)
{
Test-Connection -computername $computer -Quiet
If (Test-Connection $computer -count 1 -quiet)
{
Invoke-Command -computer $computer -ScriptBlock
{
param(
$output_path
)
$hostname = (Get-CIMInstance CIM_ComputerSystem).Name
$objExcel = New-Object -ComObject Excel.Application
if ($objExcel.Version -eq "15.0")
{
$HKEY_USERS = Get-ChildItem REGISTRY::HKEY_USERS | where-object { ($_.Name -like "*S-1-5-21*") -and ($_.Name -notlike "*_Classes") }
$Users = #()
$value = #()
foreach ($User in $HKEY_USERS)
{
$PROFILESID = Get-ChildItem REGISTRY::"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Where-Object { $_.name -like "*" + $USER.PSChildName + "*" }
$SID = $PROFILESID.PSChildName
foreach ($value in $SID)
{
$key = Get-Item REGISTRY::HKEY_USERS\$VALUE\Software\Microsoft\Office\15.0\Outlook\Preferences -ErrorAction SilentlyContinue
$gold = $key.property
if($gold -like 'ShowAutoSug')
{
$grail = (Get-ItemProperty REGISTRY::HKEY_USERS\$VALUE\Software\Microsoft\Office\15.0\Outlook\Preferences).ShowAutoSug
$objSID = New-Object System.Security.Principal.SecurityIdentifier($value)
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
$hostname, $objUser, $value , $grail | Add-Content $output_path
}
else
{
$objSID = New-Object System.Security.Principal.SecurityIdentifier($value)
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
$hostname,$objUser, $value , "The Auto Complete is not disabled" | Add-Content $output_path
}
}
}
}
if ($objExcel.Version -eq "14.0")
{
$HKEY_USERS = Get-ChildItem REGISTRY::HKEY_USERS | where-object { ($_.Name -like "*S-1-5-21*") -and ($_.Name -notlike "*_Classes") }
$Users = #()
$value = #()
foreach ($User in $HKEY_USERS)
{
$PROFILESID = Get-ChildItem REGISTRY::"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Where-Object { $_.name -like "*" + $USER.PSChildName + "*" }
$SID = $PROFILESID.PSChildName
foreach ($value in $SID)
{
$key = Get-Item REGISTRY::HKEY_USERS\$VALUE\Software\Microsoft\Office\14.0\Outlook\Preferences -ErrorAction SilentlyContinue
$gold = $key.property
if($gold -like 'ShowAutoSug')
{
$grail = (Get-ItemProperty REGISTRY::HKEY_USERS\$VALUE\Software\Microsoft\Office\14.0\Outlook\Preferences).ShowAutoSug
$objSID = New-Object System.Security.Principal.SecurityIdentifier($value)
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
$hostname, $objUser, $value , $grail | Add-Content -path $output_path
}
else
{
$objSID = New-Object System.Security.Principal.SecurityIdentifier($value)
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
$hostname,$objUser, $value , "The Auto Complete is not disabled" | Add-Content $output_path
}
}
}
}
if ($objExcel.Version -eq "12.0")
{
$HKEY_USERS = Get-ChildItem REGISTRY::HKEY_USERS | where-object { ($_.Name -like "*S-1-5-21*") -and ($_.Name -notlike "*_Classes") }
$Users = #()
$value = #()
foreach ($User in $HKEY_USERS)
{
$PROFILESID = Get-ChildItem REGISTRY::"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Where-Object { $_.name -like "*" + $USER.PSChildName + "*" }
$SID = $PROFILESID.PSChildName
foreach ($value in $SID)
{
$key = Get-Item REGISTRY::HKEY_USERS\$VALUE\Software\Microsoft\Office\12.0\Outlook\Preferences -ErrorAction SilentlyContinue
$gold = $key.property
if($gold -like 'ShowAutoSug')
{
$grail = (Get-ItemProperty REGISTRY::HKEY_USERS\$VALUE\Software\Microsoft\Office\12.0\Outlook\Preferences).ShowAutoSug
$objSID = New-Object System.Security.Principal.SecurityIdentifier($value)
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
$hostname, $objUser, $value , $grail | Add-Content $output_path
}
else
{
$objSID = New-Object System.Security.Principal.SecurityIdentifier($value)
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
$hostname,$objUser, $value , "The Auto Complete is not disabled" |Add-Content $output_path
}
}
}
}
} -ArgumentList $output_path
}
else
{
$status = 'Offline'
$computer , $status | Add-Content $output_path
}
}
To fix your error, simply cut -ArgumentList $output_path and put it before -ScriptBlock like:
Invoke-Command -computer $computer -ArgumentList $output_path -ScriptBlock ....