Powershell looping through message queues - powershell

I'm trying to return tables with message queues from three different environments. I could copy and paste the existing code for all three, but I want to make it cleaner and more reusable.
Is there a way to loop through each message queue and return them in separate tables (i.e.: Dev, Dev2, Dev3 queues)?
[object]$dev3Queues = gwmi -class Win32_PerfFormattedData_msmq_MSMQQueue -computerName myServer | Where{$_.Name -like "*dev3*" } | select Name,MessagesInQueue #| Out-File "C:\test.txt"
[object]$dev2Queues = gwmi -class Win32_PerfFormattedData_msmq_MSMQQueue -computerName myServer | Where{$_.Name -like "*dev2*" } | select Name,MessagesInQueue #| Out-File "C:\test2.txt"
[object]$devQueues = gwmi -class Win32_PerfFormattedData_msmq_MSMQQueue -computerName myServer |
Where{$_.Name -notlike "*dev2*" -AND $_.Name -notlike "*dev3*" -AND $_.Name -notlike "*private*" -AND $_.Name -notlike "*Computer Queues*" -AND $_.Name -notlike "*uat*"} | select Name,MessagesInQueue #| Out-File "C:\test3.txt"
$Html = "<html><head>Whoo Queues</head><body><table border=1>"
foreach($element in $devQueues)
{
$Html += "<tr><td>" + $element.Name + "</td><td>"+ $element.MessagesInQueue + "</td> </tr>"
}
$Html += "</table></body></html>"
$Html | out-file C:\temp\DEVQueues.html
#environmentloop - dev,dev2,dev3
#{
#queue loop + html
#}

You can use the ConvertTo-Html cmdlet with the option -Fragment to convert a list of objects to an HTML table of the object properties.
Get-WmiObject -Class Win32_PerfFormattedData_msmq_MSMQQueue |
select Name, MessagesInQueue |
ConvertTo-Html -Fragment
Also, when running Get-WmiObject against a remote server using a WMI filter provides better performance than retrieving all results and filtering them on the local host with Where-Object.
$computer = 'myServer'
$filter = 'Name LIKE "%dev3%"'
Get-WmiObject -Class Win32_PerfFormattedData_msmq_MSMQQueue -Computer $computer `
-Filter $filter
However, since you want to filter the same dataset for various criteria, in your case the best approach might be to first fetch all relevant data from the remote host with a more general WMI filter (to avoid multiple remote connections), and then process them locally with several Where-Object filters:
$server = 'myServer'
$wmiFilter = 'NOT (Name LIKE "%private%" OR Name LIKE "%Computer Queues%" ' +
'OR Name LIKE "%uat%")'
$psFilters = { $_.Name -like "*dev3*" },
{ $_.Name -like "*dev2*" },
{ $_.Name -notlike "*dev2*" -and $_.Name -notlike "*dev3*" }
$data = Get-WmiObject -Class Win32_PerfFormattedData_msmq_MSMQQueue `
-Computer $server -Filter $wmiFilter
'<html><head>Whoo Queues</head><body>'
foreach ($filter in $psFilters) {
$data | ? $filter | select Name, MessagesInQueue | ConvertTo-Html -Fragment
}
'</body></html>'

Related

i trying to collect different info with Get-ADComputer, Get-WmiObject and Get-windowsfeature

I am trying to collect different properties from all computer listed from my AD but I can't get format to recolected querys
I run get-adcomputer query
$computerList = Get-ADComputer -Filter 'Name -like "SCPW*" -and ObjectClass -eq "Computer"' | Select-Object -Property Name,objectClass`
I collect info from al computer listed in $computerlist
$list = foreach ($computer in $computerList) {Get-WmiObject -Class Win32_Product | Select-Object -Property Name }
$WFeature = foreach ($computer in $computerList) {Get-windowsfeature | Where {$_.installed -Eq $true} | Select-Object -Property Name}
$Wrole = foreach ($computer in $computerList) {Get-WmiObject -Class Win32_ServerFeature -Property * | select pscomputername,name }
Finally I try to list all info collected in a unique table with
%{[PSCustomObject]#{
'ComputerName' = $computer.Name
'features' = $WFeature.name
'Role' = $Wrole.name
'list' = $list.name
} } | Format-table
but the table listed is only in one line
ComputerName features Role list
------------ -------- ---- ----
SCPWEXC0101 {File-Services, FS-FileServer, Remote-Desktop-Services, RDS-RD-Server...} {Web Server (IIS), File Services, Print and Document Services, Remote Desktop Services...} {Microsoft Ap...
the table listed only in one line (screenshot)
I really will be much gratefull if somebody give me some help, I'm learning PS and I really lossed with this query. I've been running Ps 5.1
Thanks!!
You will have to put the queries inside a loop for each single computer to keep the correlation between the collected information and the computer name. Something like this should get you started:
$computerList = Get-ADComputer -Filter 'Name -like "SCPW*" -and ObjectClass -eq "Computer"'
$Result =
foreach ($computer in $computerList) {
$CimSession = New-CimSession -ComputerName $computer.name
[PSCustomObject]#{
ComputerName = $computer.Name
WindowsFeatureList = (Get-windowsfeature -ComputerName $computer.name | Where-Object { $_.installed } | Select-Object -Property Name) -join ', '
W32ServerFeatureList = (Get-CimInstance -CimSession $CimSession -ClassName Win32_ServerFeature | Select-Object -Property Name) -join ', '
W32ProductList = (Get-CimInstance -CimSession $CimSession -ClassName Win32_Product | Select-Object -Property Name) -join ', '
}
}
$Result

Trouble with retrieving certificate information in Powershell?

I'm trying to build a dashboard to retrieve certificate information on all our servers, but I'm struggling with the powershell object handling. I believe it's the way objects are getting passed inside and outside a loop. I have 3 iterations of my code.
In the first, all certificates are retrieved, but the FriendlyName is blanked out on every object:
$serverCert = $null
$servers=get-adcomputer -filter { ( OperatingSystem -like '*server*') -AND ( Name -notlike '*-DT0094' ) } | sort Name
foreach ( $server in $servers ) {
$ServerName=$server.Name
$ServerName="$ServerName.DOMAINSUFFIX"
$serverCert += Invoke-Command -ComputerName $ServerName -Scriptblock {
return $(Get-ChildItem Cert:\LocalMachine\My)
}
}
$serverCert | Select-Object PSComputerName, Thumbprint, FriendlyName, NotAfter, #{N="Template";E={($_.Extensions | ?{$_.oid.Friendlyname -match "Certificate Template Information"}).Format(0) -replace "(.+)?=(.+)\((.+)?", '$2'}}, #{N="IssuedBy";E={($_.IssuerName.Name -split ',*..=')[1]}}, #{N="Subject";E={($_.Subject -split ',*..=')[1]}} | Sort Thumbprint | Format-Table -Wrap
In this iteration, the Extensions come through like this:
PS C:\WINDOWS> $serverCert[0] | Select-Object -Property Extensions
Extensions
----------
{System.Security.Cryptography.Oid, System.Security.Cryptography.Oid}
In the second, I solved this by explicitly passing the FriendlyName through as a new property called "Description"...unfortunately, now the Template doesn't display:
$serverCert = $null
$servers=get-adcomputer -filter { ( OperatingSystem -like '*server*') -AND ( Name -notlike '*-DT0094' ) } | sort Name
foreach ( $server in $servers ) {
$ServerName=$server.Name
$ServerName="$ServerName.DOMAINSUFFIX"
$serverCert += Invoke-Command -ComputerName $ServerName -Scriptblock {
return $(Get-ChildItem Cert:\LocalMachine\My | Select-Object *, #{N="Description";E={$_.FriendlyName}})
}
}
$serverCert | Select-Object PSComputerName, Thumbprint, Description, NotAfter, #{N="Template";E={($_.Extensions | ?{$_.oid.Friendlyname -match "Certificate Template Information"}).Format(0) -replace "(.+)?=(.+)\((.+)?", '$2'}}, #{N="IssuedBy";E={($_.IssuerName.Name -split ',*..=')[1]}}, #{N="Subject";E={($_.Subject -split ',*..=')[1]}} | Sort Thumbprint | Format-Table -Wrap
In this iteration, the Extensions come through like this, and I can't get template name to display:
PS C:\WINDOWS> $serverCert[0] | Select-Object -Property Extensions
Extensions
----------
{System.Security.Cryptography.X509Certificates.X509EnhancedKeyUsageExtension, System.Security.Cryptography.X509Certificates.X509KeyUsageExtension}
Now the third. This time I try to pass the template information forward as a property like the "Description." Problem is, the template information doesn't come through clearly; rather than the friendly name, other info is displayed.
$serverCert = $null
$servers=get-adcomputer -filter { ( OperatingSystem -like '*server*') -AND ( Name -notlike '*-DT0094' ) } | sort Name
foreach ( $server in $servers ) {
$ServerName=$server.Name
$ServerName="$ServerName.DOMAINSUFFIX"
$serverCert += Invoke-Command -ComputerName $ServerName -Scriptblock {
return $(Get-ChildItem Cert:\LocalMachine\My | Select-Object *, #{N="Description";E={$_.FriendlyName}}, #{N="Template";E={($_.Extensions | ?{$_.oid.Friendlyname -match "Certificate Template Information"}).Format(0) -replace "(.+)?=(.+)\((.+)?", '$2'}})
}
}
$serverCert | Select-Object PSComputerName, Thumbprint, Description, NotAfter, Template, #{N="IssuedBy";E={($_.IssuerName.Name -split ',*..=')[1]}}, #{N="Subject";E={($_.Subject -split ',*..=')[1]}} | Sort Thumbprint | Format-Table -Wrap
Template information for some certificates (I can't correlate on template, OS version...anything) looks like this:
Template=1.3.6.1.4.1.311.21.8.16245382.12313948.10571683.3565079.1665071.100.15924968.15384388, Major Version Number=100, Minor Version Number=4
I'm stumped. I am still getting comfortable in powershell, but I don't understand object manipulation well enough to know how to fix this. Any help is appreciated!
I think FriendlyName not getting returned in your first iteration has to do with the way PowerShell is deserializing data when the -ComputerName parameter is used.
In your first iteration, try changing this line:
return $(Get-ChildItem Cert:\LocalMachine\My)
to:
return $(Get-ChildItem Cert:\LocalMachine\My | Select-Object *)
To illustrate the issue, run these three commands, which you would expect to all include the same object properties. Note that FriendlyName is only included in the output of the first two commands:
Invoke-Command -ScriptBlock { gci Cert:\LocalMachine\My } | Select-Object FriendlyName
Invoke-Command -ScriptBlock { gci Cert:\LocalMachine\My | Select-Object * } -ComputerName . | Select-Object FriendlyName
Invoke-Command -ScriptBlock { gci Cert:\LocalMachine\My } -ComputerName . | Select-Object FriendlyName
Edit: This is how I might do it:
Function Get-Cert-Info($ComputerName) {
Invoke-Command -Computer $ComputerName -ScriptBlock {
$certs = Get-ChildItem cert:\localmachine\my
foreach($cert in $certs) {
[pscustomobject]#{
ComputerName = $env:COMPUTERNAME
Thumbprint = $cert.Thumbprint
Description = $cert.FriendlyName
TemplateName = $(
$Template = $cert.Extensions | Where-Object { $_.oid.FriendlyName -match "Certificate Template Information" }
if($Template) {
($Template.Format(0) -split "\(")[0] -replace "Template=", ""
}
)
SAN=$(
try {
$cert.Extensions | Where-Object {$_.Oid.FriendlyName -eq "subject alternative name"} | ForEach {
$SANString = "{0}" -f $_.Format(0)
$SANS = $SANString -split ','
foreach($SAN in $SANS) {
($SAN -split "=")[1]
}
}
} catch {
"n/a"
}
)
IssuedBy=$(($cert.IssuerName.Name -split ',')[0] -replace 'CN=', '')
Subject=$(($cert.Subject -split ',')[0] -replace 'CN=', '')
NotAfter=$cert.NotAfter
}
}
}
}
$servers=get-adcomputer -filter { ( OperatingSystem -like '*server*') -AND ( Name -notlike '*-DT0094' ) } | sort Name
foreach ( $server in $servers ) {
Get-Cert-Info -ComputerName $server.Name
}
I found a way to get the template name though, but lookup would fail, so I did something akin to a replace when I got the ID:
# Get all certificates from all servers
clear-host
$serverCert = $null
ipconfig /flushdns
$servers=get-adcomputer -filter { OperatingSystem -like '*server*' } | sort Name
foreach ( $server in $servers ) {
$ServerName=$server.Name
$ServerName="$ServerName.DOMAINSUFFIX"
$serverCert += Invoke-Command -ComputerName $ServerName -Scriptblock {
return $(Get-ChildItem Cert:\LocalMachine\My | Select-Object *, #{N="Description";E={$_.FriendlyName}}, #{N="TemplateName";E={($_.Extensions | ?{$_.oid.Friendlyname -match "Certificate Template Information"}).Format(0) -replace "(.+)?=(.+)\((.+)?", '$2' -replace 'Template=', '' -replace '1.3.6.1.4.1.311.21.8.16245382.12313948.10571683.3565079.1665071.100.15924968.15384388.*', 'SCCM Client Certificate' -replace '1.3.6.1.4.1.311.21.8.16245382.12313948.10571683.3565079.1665071.100.9941395.14900143.*','IIS Web Servers' -replace '1.3.6.1.4.1.311.21.8.16245382.12313948.10571683.3565079.1665071.100.1979823.4984146.*','WSUS Web Server Certificate'}})
}
}
$serverCert | Select-Object PSComputerName, Thumbprint, Description, NotAfter, TemplateName, #{N="IssuedBy";E={($_.IssuerName.Name -split ',*..=')[1]}}, #{N="Subject";E={($_.Subject -split ',*..=')[1]}} | Sort NotAfter, PSComputerName | Format-Table -Wrap

Reboot check is showing all reboots not just last

I have a reboot check script that is run post MW, I need it to pull just the last reboot to verify the servers have been rebooted, currently they pull all reboot history. Below is my script:
$DHCP = (Get-Content -Path "\\termserv\d$\SERVER1\SERVER2\Scripts\morescripts\DHCPServers.txt")
foreach ($Server in $DHCP) {
Get-Service -ComputerName $Server -DisplayName "DHCP Server" |
ConvertTo-Html -Title "PScomputername" -Body "<H3> SERVER2 Uptime Report </H3> " -Property PSComputername >> \\termserv\d$\SERVER1\SERVER2\Server3\SERVER2.html
Get-Service -ComputerName $Server -DisplayName "DHCP Server" |
ConvertTo-Html -Property MachineName,Status,ServiceName |
foreach {
if ($_ -like "*<td>Running</td>*") {
$_ -replace "<tr>", "<tr bgcolor=green>"
} elseif ($_ -like "*<td>Stopped</td>*") {
$_ -replace "<tr>", "<tr bgcolor=red>"
} else {
$_
}
} >> \\termserv\d$\SERVER1\SERVER2\Server3\SERVER2.html
Get-WmiObject win32_operatingsystem -ComputerName $Server |
Select PSComputername, #{n='BootTime';e={$_.ConvertToDateTime($_.LastBootupTime)}} |
ConvertTo-Html -Property PSComputerName,BootTime >> \\termserv\d$\SERVER1\SERVER2\Server3\SERVER2.html
ConvertTo-Html -Property PSComputerName,Installedon,Description,caption >> \\termserv\d$\SERVER1\SERVER2\Server3\SERVER2.html
}
$Print = (Get-Content -Path "\\termserv\d$\SERVER1\SERVER2\Scripts\morescripts\PrintServers.txt")
foreach ($Server in $Print) {
Get-Service -ComputerName $Server -DisplayName "Print Spooler" |
ConvertTo-Html -Property MachineName,Status,ServiceName |
foreach {
if ($_ -like "*<td>Running</td>*") {
$_ -replace "<tr>", "<tr bgcolor=green>"
} elseif ($_ -like "*<td>Stopped</td>*") {
$_ -replace "<tr>", "<tr bgcolor=red>"
} else {
$_
}
} >> \\termserv\d$\SERVER1\SERVER2\Server3\SERVER2.html
Get-WmiObject win32_operatingsystem -ComputerName $Server |
Select PSComputername, #{n='BootTime';e={$_.ConvertToDateTime($_.LastBootupTime)}} |
ConvertTo-Html -Property PSComputerName,BootTime >> \\termserv\d$\SERVER1\SERVER2\Server3\SERVER2.html
ConvertTo-Html -Property PSComputerName,Installedon,Description,caption >> \\termserv\d$\SERVER1\SERVER2\Server3\SERVER2.html
}
You just need to Sort-Object with a calculated property and then tell Select-Object to pick the first item:
Get-WmiObject -ClassName Win32_OperatingSystem -ComputerName $Server |
Sort-Object -Property #{e={$_.ConvertToDateTime($_.LastBootupTime)}} -Descending |
Select-Object -First 1 -Property [...] |
ConvertTo-Html [...]
It should be noted that the final ConvertTo-Html call in each main foreach loop doesn't have anything to convert as far as I can tell. They're going to create an empty HTML document with an empty table and append that to your file.
Also, you're appending multiple HTML documents to the server2.html file. If they're going to be in the same document, you should start the entire file with <html><head><title /></head><body>, then use the -Fragment parameter on all of your ConvertTo-Html calls, and finally close the document with </body></html>. Your current method may work, but you're generating an explicitly invalid HTML document. A browser that renders correctly should only display the first table.

Find out the current version and update of an installed application

I have some requirement where I have to find out the current version and update details of an installed application (highlighted):
And I have this PowerShell snippet for modification:
$server="XXXXXXXXXX"
$ServiceInfo = Get-WmiObject win32_service -ComputerName $server -ExpandProperty Version | Where-Object {$_.Name -eq "VSTTAgent"}
if($ServiceInfo.State -eq "Running")
{
$userAccount = $ServiceInfo.DisplayName.ToString()
Write-Host ("VSTTAgent service is Running on $server and $userAccount ")
}
To get the product version, you can use the Get-Item cmdlet using the PathName property of your $ServiceInfo object:
$ServiceInfo.PathName.Trim('"') | Get-Item | select -expand VersionInfo | select ProductVersion
Essentially the same as #MartinBrandl, but the WMI-only version.
Get-WmiObject win32_service -Filter 'Name="VSTTAgent"' -ComputerName $server | ForEach-Object {
$filter = 'Name="{0}"' -f $_.PathName -replace '\\', '\\'
$version = (Get-WmiObject CIM_DataFile -Filter $filter -ComputerName $server).Version
if ($_.State -eq 'Running') {
$userAccount = $ServiceInfo.DisplayName.ToString()
Write-Host ("VSTTAgent ($version) service is Running on $server and $userAccount")
}
}

Powershell: Pipe variable $_ in if statement?

I have the following short script to grab serial numbers of computers and monitors in an OU, which works fine:
Import-Module ActiveDirectory
$searchbase = "OU=some,OU=organisational,OU=units,DC=somedomain,DC=local"
Write-Host ""
Write-Host "Serial Numbers for Computers and Monitors in" $searchbase
Write-Host "--"
Get-ADComputer -SearchBase $searchbase -Filter '*' | `
Select-Object -Expand Name | %{Write-Host ""; echo $_ ; Get-WMIObject -Class Win32_BIOS -ComputerName $_ | Select-Object -Expand SerialNumber; `
$monitor = gwmi WmiMonitorID -Namespace root\wmi -computername $_; ($monitor.SerialNumberID | foreach {[char]$_}) -join ""};
This script doesn't check to see if the computer is online before attempting to fetch the WMIObject, so if a computer is offline it takes ages before the RPC call times out.
I tried to modify the script to use the Test-Connection cmdlet before trying to get the WMIObject:
Import-Module ActiveDirectory
$searchbase = "OU=some,OU=organisational,OU=units,DC=somedomain,DC=local"
Write-Host ""
Write-Host "Serial Numbers for Computers and Monitors in" $searchbase
Write-Host "--"
Get-ADComputer -SearchBase $searchbase -Filter '*' | `
Select-Object -Expand Name | `
if (Test-Connection -ComputerName $_ -Quiet) {
%{Write-Host ""; echo $_ ; Get-WMIObject -Class Win32_BIOS -ComputerName $_ | Select-Object -Expand SerialNumber; `
$monitor = gwmi WmiMonitorID -Namespace root\wmi -computername $_; ($monitor.SerialNumberID | foreach {[char]$_}) -join ""};}
}
else {
Write-Host ""; Write-Host $_ "is offline";
}
I'm sure I'm doing something syntactically stupid. Can someone point me in the right direction?
You can't pipe directly to an if statement, only to cmdlets.
Put the if statement inside the ForEach-Object block (% is an alias for ForEach-Object):
... | Select-Object -Expand Name | `
%{
if (Test-Connection -ComputerName $_ -Quiet) {
# Get-WmiObject in here
}
else {
Write-Host ""; Write-Host $_ "is offline";
}
}
If you don't care about writing each machine's status to the host, you could also filter out offline computers with Where-Object(alias ?):
... | Select-Object -Expand Name | ?{
Test-Connection $_ -Quiet
} | % {
Get-WmiObject -ComputerName $_
}
In addition to the answer from #Mathias R. Jessen, you can get rid of the backticks for line continuation.
They are not needed if the end of the line infers there is another block of code required for the statement. Like | or { or (.
"foo", "bar" |
% {$_}
works just fine...