Powershell on Windows 2019 doesn't wait for a statement - powershell

I've created a powershell script to prepare server disks some years ago and it was functioning all the time, but now using Windows 2019 I got a problem. This part of the script
#$disk_list = (Get-Disk | where { ($_.OperationalStatus -eq "Offline") -and ($_.IsClustered -eq $False) })
if ($disk_list)
{
Write-Host "$($disk_list.count) disks with partition style RAW are found" -ForegroundColor Green
$overview_disks = [pscustomobject]#{ }
foreach ($d in $disk_list)
{
$LUN = Get-WmiObject Win32_DiskDrive | where { ($_.InterfaceType -eq "SCSI") -and ($_.index -eq $d.Number) } | select Name, SCSILogicalUnit, Size
$overview_disks | Add-Member -Name $LUN.Name -Value $("`tLUN:" + $LUN.SCSILogicalUnit + " `tDiskID: " + $d.Number + " `tSize: " + $([math]::Round($LUN.Size /1GB, 1)) + "GB") -MemberType NoteProperty
}
$overview_disks
doesn't work anymore, delivering an empty object $overview_disks.
I got it functioning using Measure-Command:
$disk_list = (Get-Disk | where { ($_.OperationalStatus -eq "Offline") -and ($_.PartitionStyle -eq "RAW") })
#$disk_list = (Get-Disk | where { ($_.OperationalStatus -eq "Offline") -and ($_.IsClustered -eq $False) })
if ($disk_list)
{
Write-Host "$($disk_list.count) disks with partition style RAW are found" -ForegroundColor Green
$overview_disks = [pscustomobject]#{ }
foreach ($d in $disk_list)
{
Measure-Command -Expression { $LUN = Get-WmiObject Win32_DiskDrive | where { ($_.InterfaceType -eq "SCSI") -and ($_.index -eq $d.Number) } | select Name, SCSILogicalUnit, Size}
$overview_disks | Add-Member -Name $LUN.Name -Value $("`tLUN:" + $LUN.SCSILogicalUnit + " `tDiskID: " + $d.Number + " `tSize: " + $([math]::Round($LUN.Size /1GB, 1)) + "GB") -MemberType NoteProperty
}
$overview_disks
Measure-Command lets $LUN= part to wait till it finishes.
I've searched a lot, but there is no information why it doesn't wait in a standard syntax of the script as it was the case for Windows 2012 and 2016.
Do one of you have an idea?

Related

How to display the variable result as table in email body or console screen?

The below script to get the logon users and send as email was working great but only on the console output only.
I am trying to get the result as a table so the result in the console and the email body will be like:
Server, ConnectionType, User, ID, State
PRDSVR16, rdp-tcp#44, SVC-SQL, 4, Active
PRDSVR10, rdp-tcp#27, Admin.domain, 6, Disc
SVR25-VM,console,domain.admin,8,Active
Open in new window
This is the script:
$Today = Get-Date -Format 'F'
$SessionList = "`n`nRDP Session List - " + $Today + "`n`n"
$CurrentSN = 0
# Get a list of servers from Active Directory
write-progress -activity "Getting list of servers from Active Directory" -status "... please wait ..."
$Servers = (Get-ADComputer -Filter { Enabled -eq $True -and OperatingSystem -like "*Server*" } -Properties OperatingSystem -SearchBase "OU=Data Center,DC=Company,DC=com") |
Where-Object { Test-Connection $_.Name -Count 1 -Quiet } |
Select-Object -ExpandProperty Name
$NumberOfServers = $Servers.Count
# Iterate through the retrieved list to check RDP sessions on each machine
ForEach ($Server in $Servers)
{
Write-Host "Processing $Server ..." -ForegroundColor Yellow
Write-progress -activity "Checking RDP Sessions" -status "Querying $Server" -percentcomplete (($CurrentSN / $NumberOfServers) * 100)
try
{
$SessionList += qwinsta /server:$Server |
Select-Object -Skip 1 |
% {
[PSCustomObject] #{
Type = $_.Substring(1, 18).Trim()
User = $_.Substring(19, 20).Trim()
ID = $_.Substring(41, 5).Trim()
State = $_.Substring(48, 6).Trim()
}
} |
? { $_.Type -notin 'console', 'services', 'rdp-tcp' -and $_.User -ne $null -and $_.User -ne 65536 } |
% {
"`n$Server logged in by $($_.User) on $($_.Type), session id $($_.ID) $($_.state)"
}
}
catch
{
$SessionList += "`n Unable to query " + $Server
write-host "Unable to query $Server! `n $($Error[0].Exception)" -foregroundcolor Red
}
$CurrentSN++
}
# Send the output the screen.
$SessionList + "`n`n"
$sendMailArgs = #{
From = "$env:USERNAME#$env:userdnsdomain"
To = 'SOC#domain.com'
SmtpServer = 'SMTP.domain.com'
Priority = 'High'
Body = $SessionList | Select-Object #{ N = 'Server'; E = { $Server } },
#{ N = 'User'; E = { $_.User } },
#{ N = 'LogonType'; E = { $_.Type } },
#{ N = 'ID'; E = { $_.ID } },
#{ N = 'State'; E = { $_.State } }
Subject = "$($SessionList.Count) Logged On users from $($NumberOfServers) online servers as at $($Today)"
}
Send-MailMessage #sendMailArgs
Rendering collected information in different places is way easier if you keep strict separation between data and presentation (or formatting) of said data.
For the $SessionList for example, that means doing less than what you're currently trying to do inside the loop:
$ErrorList = #()
$SessionList = foreach($server in $servers){
try{
qwinsta /server:$Server |Select-Object -Skip 1 |ForEach-Object {
[PSCustomObject] #{
Server = $server
Type = $_.Substring(1, 18).Trim()
User = $_.Substring(19, 20).Trim()
ID = $_.Substring(41, 5).Trim()
State = $_.Substring(48, 6).Trim()
}
} |Where-Object { $_.Type -notin 'console', 'services', 'rdp-tcp' -and $_.User -ne $null -and $_.User -ne 65536 }
}
catch{
$ErrorList += [pscustomobject]#{
Server = $server
ErrorRecord = $_
}
}
}
Notice how I don't construct any strings - I just create the custom objects, filter them - and then leave them as-is.
Now it becomes much easier to format the data as desired for different output media:
# For console output, simply pipe to Format-Table
$SessionList |Format-Table
if($ErrorList.Count -gt 0){
Write-Warning "The following servers had errors, please inspect"
$ErrorList |Format-Table
}
# For email output we can use `ConvertTo-Html`
$Body = $SessionList |ConvertTo-Html -As Table -Fragment
if($ErrorList.Count -gt 0){
$ErrorTable = $ErrorList |ConvertTo-Html -As Table -Fragment
$Body = $Body,$ErrorTable -join '<br />'
}
$sendMailArgs = #{
# ...
Body = ConvertTo-Html -Body $Body -Title "Session list"
BodyAsHtml = $true
# ...
}

Volume Shadow Copy Monitoring Script

I'm new to Powershell and am trying to write a Powershell script that I can use to monitor the status of VSS on our servers using an RMM tool. What I have so far is:
# List all Volumes
$Volumes = gwmi Win32_Volume -Property SystemName,DriveLetter,DeviceID,Capacity,FreeSpace -Filter "DriveType=3" |
Select SystemName,#{n="DriveLetter";e={$_.DriveLetter.ToUpper()}},DeviceID,#{n="CapacityGB";e={([math]::Round([int64]($_.Capacity)/1GB,2))}},#{n="FreeSpaceGB";e={([math]::Round([int64]($_.FreeSpace)/1GB,2))}} | Sort DriveLetter
# get all shadow storage volumes
$ShadowStorage = gwmi Win32_ShadowStorage -Property AllocatedSpace,DiffVolume,MaxSpace,UsedSpace,Volume |
Select #{n="Volume";e={$_.Volume.Replace("\\","\").Replace("Win32_Volume.DeviceID=","").Replace("`"","")}},
#{n="DiffVolume";e={$_.DiffVolume.Replace("\\","\").Replace("Win32_Volume.DeviceID=","").Replace("`"","")}},
#{n="AllocatedSpaceGB";e={([math]::Round([int64]($_.AllocatedSpace)/1GB,2))}},
#{n="MaxSpaceGB";e={([math]::Round([int64]($_.MaxSpace)/1GB,2))}},
#{n="UsedSpaceGB";e={([math]::Round([int64]($_.UsedSpace)/1GB,2))}}
# list all shadow coppies
$ShadowCopies = gwmi Win32_ShadowCopy -Property VolumeName,InstallDate,Count |
Select VolumeName,InstallDate,Count,
#{n="CreationDate";e={$_.ConvertToDateTime($_.InstallDate)}}
# format and output stats
$Shares = gwmi win32_share -Property Name,Path | Select Name,#{n="Path";e={$_.Path.ToUpper()}}
If($Volumes)
{
$Output = #()
ForEach($Volume in $Volumes)
{
$VolumeShares = $VolumeShadowStorage = $DiffVolume = $VolumeShadowCopies = $Null
If($Volume.DriveLetter -ne $Null){[array]$VolumeShares = $Shares | ?{$_.Path.StartsWith($Volume.DriveLetter)}}
$VolumeShadowStorage = $ShadowStorage | ?{$_.Volume -eq $Volume.DeviceID}
If($VolumeShadowStorage){$DiffVolume = $Volumes | ?{$_.DeviceID -eq $VolumeShadowStorage.DiffVolume}}
$VolumeShadowCopies = $ShadowCopies | ?{$_.VolumeName -eq $Volume.DeviceID} | Sort InstallDate
$Object = New-Object psobject
$Object | Add-Member NoteProperty SystemName $Volume.SystemName -PassThru | Add-Member NoteProperty DriveLetter $Volume.DriveLetter -PassThru |
Add-Member NoteProperty CapacityGB $Volume.CapacityGB -PassThru | Add-Member NoteProperty FreeSpaceGB $Volume.FreeSpaceGB -PassThru |
Add-Member NoteProperty ShareCount "" -PassThru | Add-Member NoteProperty Shares "" -PassThru |
Add-Member NoteProperty ShadowAllocatedSpaceGB "" -PassThru | Add-Member NoteProperty ShadowUsedSpaceGB "" -PassThru |
Add-Member NoteProperty ShadowMaxSpaceGB "" -PassThru | Add-Member NoteProperty DiffVolumeDriveLetter "" -PassThru |
Add-Member NoteProperty DiffVolumeCapacityGB "" -PassThru | Add-Member NoteProperty DiffVolumeFreeSpaceGB "" -PassThru |
Add-Member NoteProperty ShadowCopyCount "" -PassThru | Add-Member NoteProperty OldestShadowCopy "" -PassThru |
Add-Member NoteProperty LatestShadowCopy "" -PassThru | Add-Member NoteProperty ShadowAverageSizeGB "" -PassThru | Add-Member NoteProperty ShadowAverageSizeMB ""
If($VolumeShares)
{ $Object.ShareCount = $VolumeShares.Count
If($VolumeShares.Count -eq 1){$Object.Shares = $VolumeShares[0].Name}
Else{$Object.Shares = [string]::join(", ", ($VolumeShares | Select Name)).Replace("#{Name=", "").Replace("}", "")}
}
If($VolumeShadowStorage)
{ $Object.ShadowAllocatedSpaceGB = $VolumeShadowStorage.AllocatedSpaceGB
$Object.ShadowUsedSpaceGB = $VolumeShadowStorage.UsedSpaceGB
$Object.ShadowMaxSpaceGB = $VolumeShadowStorage.MaxSpaceGB
If($DiffVolume)
{ $Object.DiffVolumeDriveLetter = $DiffVolume.DriveLetter
$Object.DiffVolumeCapacityGB = $DiffVolume.CapacityGB
$Object.DiffVolumeFreeSpaceGB = $DiffVolume.FreeSpaceGB
}
}
If($VolumeShadowCopies)
{ $Object.ShadowCopyCount = (($VolumeShadowCopies | Measure-Object -Property Count -Sum).Sum)
$Object.OldestShadowCopy = (($VolumeShadowCopies | Select -First 1).CreationDate)
$Object.LatestShadowCopy = (($VolumeShadowCopies | Select -Last 1).CreationDate)
If($VolumeShadowStorage.UsedSpaceGB -gt 0 -And $Object.ShadowCopyCount -gt 0)
{ $Object.ShadowAverageSizeGB = ([math]::Round($VolumeShadowStorage.UsedSpaceGB/$Object.ShadowCopyCount,2))
$Object.ShadowAverageSizeMB = ([math]::Round(($VolumeShadowStorage.UsedSpaceGB*1KB)/$Object.ShadowCopyCount,2))
}
}
}
If($VolumeShadowStorage -Or $ShowAllVolumes){$Output += $Object}
}
$Output
This will output a block of information like this:
SystemName : server
DriveLetter : F:
CapacityGB : 1850
FreeSpaceGB : 676.75
ShareCount : 17
Shares : List of shares
ShadowAllocatedSpaceGB : 71.41
ShadowUsedSpaceGB : 62.3
ShadowMaxSpaceGB : 292.97
DiffVolumeDriveLetter : F:
DiffVolumeCapacityGB : 1850
DiffVolumeFreeSpaceGB : 676.75
ShadowCopyCount : 961
OldestShadowCopy : 4/25/2020 8:39:29 AM
LatestShadowCopy : 7/8/2020 9:17:56 AM
ShadowAverageSizeGB : 0.06
ShadowAverageSizeMB : 66.38
What I need to do so this will work with our RMM tools is break out each line of this output into their own variables so the RMM tool can read each one. For context this is the script we use now, it works but it really only outputs the basics:
$plaintext=vssadmin list writers
$vsswriters=#()
foreach ($line in $plaintext) {
switch -regex ($line) {
("^Writer name:") {
$vsswriters+=$Obj
Remove-Variable -name Obj -Force -ErrorAction SilentlyContinue
$Obj=New-Object System.Object
$writername=($line -split ": ")[1] -replace "'",""
$Obj | Add-Member -MemberType NoteProperty -Name "WriterName" -value $writername -Force
Remove-Variable -Name writername -force -ErrorAction SilentlyContinue
break;
}
("^ ") {
$attrname=($line -split ": ")[0] -Replace " ",""
$attrval=($line -split ": ")[1] -replace "'",""
$Obj | Add-Member -MemberType NoteProperty -Name $attrname -value $attrval -Force
Remove-Variable -Name attrname,attrval -Force -ErrorAction SilentlyContinue
break;
}
default {
# Skip line as not required.
}
}
}
#$vsswriters |ft
$errorcount =0
$notstablecount = 0
$VSSinerror = "No VSS Writers Reporting Errors"
$VSSnotstable = "No VSS Writers Reporting Unstable Status"
foreach ($vsswriter in $vsswriters)
{
if($vsswriter.'Last error' -ne "No error" -and $vsswriter.WriterName -ne "" -and $vsswriter.WriterName -ne $null)
{
$errorcount = $errorcount +1
if($VSSinerror -eq "No VSS Writers Reporting Errors")
{
$VSSinerror=$vsswriter.WriterName
}
else
{
$VSSinerror=$VSSinerror + ", " + $vsswriter.WriterName
}
}
if($vsswriter.State -ne "[1] Stable" -and $vsswriter.WriterName -ne "" -and $vsswriter.WriterName -ne $null)
{
$notstablecount = $notstablecount +1
if($VSSnotstable -eq "No VSS Writers Reporting Unstable Status")
{
$VSSnotstable=$vsswriter.WriterName
}
else
{
$VSSnotstable=$VSSnotstable + ", " + $vsswriter.WriterName
}
}
}
$outscvsserrors = $errorcount
$outscvssinerrors = $VSSinerror
$outscvssnotstablect = $notstablecount
$outscvssnotstable = $VSSnotstable
$outsctotalvss=$vsswriters.Count
$outscvsserrors
$outscvssinerrors
$outscvssnotstablect
$outscvssnotstable
$outsctotalvss
What I have to do is feed this Powershell into a scripting tool that will allow me to map outputs to be monitored. Here is what that looks like This is then read by our RMM tool like like this Hopefully someone can point me in the right direction to solve this problem I apologize if this is too broad of a question.

Assign local variable within scriptblock

I am trying to assign a local variable from within a scriptblock with no luck. The goal is log a status of each machine for the action taken with the data prior and the data after the change. I am not sure how to assign a local variable from within a script block. Any help is much appreciated.
$csvContents = #() # Create the empty array that will eventually be the CSV file
$Computers = Get-ADComputer -Filter '(OperatingSystem -like "Windows Server*") -and (Name -like "AD*")' | Sort-Object Name
foreach ($Computer in $Computers) {
$row = New-Object PSObject # Create an object to append to the array
$row | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value NotSet
$row | Add-Member -MemberType NoteProperty -Name "PingStatus" -Value NotSet
$row | Add-Member -MemberType NoteProperty -Name "DNSChangeStatus" -Value NotSet
$row | Add-Member -MemberType NoteProperty -Name "BeforeChange" -Value NotSet
$row | Add-Member -MemberType NoteProperty -Name "AfterChange" -Value NotSet
#Write-Host "$($Computer.Name): " -ForegroundColor Yellow
$row.ComputerName = $Computer.Name
$rtn = Test-Connection -CN $Computer.dnshostname -Count 1 -BufferSize 16 -Quiet
if ($rtn -match 'True') {
Write-Host -ForegroundColor Green $Computer.DnsHostname
$row.PingStatus = 'Pingable'
Invoke-Command -ComputerName $Computer.Name -ScriptBlock {
$NewDnsServerSearchOrder = "10.93.108.225","10.93.108.134"
$Adapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DHCPEnabled -ne 'True' -and $_.DNSServerSearchOrder -eq "10.93.108.226"}
if ($Adapters -ne $null) {
# Show DNS servers before update
Write-Host "Before: " -ForegroundColor Green
$row.DNSChangeStatus = 'Change Needed'
$Adapters | ForEach-Object {
$_.DNSServerSearchOrder
$row.BeforeChange = $_.DNSServerSearchOrder
}
# Update DNS servers
$Adapters | ForEach-Object {$_.SetDNSServerSearchOrder($NewDnsServerSearchOrder)} | Out-Null
# Show DNS servers after update
$Adapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DHCPEnabled -ne 'True' -and $_.DNSServerSearchOrder -ne $null}
Write-Host "After: " -ForegroundColor Green
$Adapters | ForEach-Object {
$_.DNSServerSearchOrder
$row.AfterChange = $_.DNSServerSearchOrder
}
} else {
Write-Host "No DNS change needed " -ForegroundColor Yellow
$row.DNSChangeStatus = 'No DNS Change Needed'
}
}
} else {
Write-Host -ForegroundColor Red $Computer.DnsGostname
Write-Host -ForegroundColor Red "Host not pingable"
$row.PingStatus = 'Not Pingable'
}
$csvContents += $row # append the new data to the array
$row = $null
}
$csvContents | Export-Csv -Path C:\DNSChanges.csv
I can't tell exactly what you want to do so I'll guess it's "pass a variable to scriptblock being invoked on a remote machine". If this is the case, you can either add parameters to the scriptblock or use the $using: qualifier as in:
$using:row.DNSChangeStatus = 'Change Needed'
Note that you can't "return" anything that way so you'd have to actually return the modified object from Invoke-Command.
$row = Invoke-Command -ComputerName $Computer.Name -ScriptBlock {
and add
$row
as the last line of the scriptblock.

Powershell Forloop with custom header

Desired output:
Server Data1 Data2
------ ----- -----
Server1 Stuff1 Thing1
Server2 Stuff2 Thing2
Server3 Stuff3 Thing3
Question:
I have 3-4 different variables that I want in a custom table view with a custom header. The problem is that I use a ForEach and I'm not sure how to get it into a table view. Right now the best I can do is Write-Host "$server $stuff $thing" and that puts each one in a row. I tried to some magic with #{Expression={$_.Name};Label="Name" but did not get anywhere.
The main point is that I have 3 vars at the end of the day and I need to get it into a custom table view.
$data = #("Server1", "Server2", "Server3")
while($true) {
cls
"`r"
ForEach ($server in $data) {
if (Test-Connection $server -count 1 -quiet) {
net use r: \\$server\s$\Folder /user:server\administrator password >$null
r:
$inputcount = (dir input -Recurse | Measure-Object).Count
$forwardcount = (dir forward -Recurse | Measure-Object).Count
$forwardcount = ($forwardcount-2)
if($fowardcount -lt 0) {
$forwardcount = 0
}
if($inputcount -gt 0 -or $forwardcount -gt 0) {
if($inputcount -gt 5000 -or $forwardcount -gt 200) {
$sleepless = "1"
}
Write-Host "$server" -ForegroundColor Red -NoNewline
Write-Host ": " -NoNewline
Write-Host "$inputcount" -ForegroundColor Red -NoNewline
Write-Host " - " -NoNewline
Write-Host "$forwardcount" -ForegroundColor Red
$inputcount = ""
c: >$null
net use r: /Delete >$null
} else {
Write-Host "$server" -NoNewline
Write-Host ": " -NoNewline
Write-Host "$inputcount" -NoNewline
Write-Host " - " -NoNewline
Write-Host "$forwardcount"
$inputcount = ""
c: >$null
net use r: /Delete >$null
}
}
}
}
Use a collection of custom objects:
"Server1", "Server2", "Server3" | ForEach-Object {
# NB: Test-Connection "host" -Count 1 sometimes fails on an online host
if(Test-Connection $_ -Quiet -Count 2 -ErrorAction SilentlyContinue) {
$status = "online"
} else {
$status = "offline"
}
# PowerShell v3+
[PSCustomObject]#{
Server = $_
Status = $status
Count = 20
What = "ever"
}
# PowerShell v2
$item = New-Object -TypeName PSObject
$item | Add-Member -MemberType NoteProperty -Name Server -Value $_
$item | Add-Member -MemberType NoteProperty -Name Status -Value $status
$item | Add-Member -MemberType NoteProperty -Name Count -Value 20
$item | Add-Member -MemberType NoteProperty -Name What -Value "ever"
$item
}
Returns:
Server Status Count What
------ ------ ----- ----
Server1 offline 20 ever
Server1 offline 20 ever
Server2 offline 20 ever
Server2 offline 20 ever
Server3 offline 20 ever
Server3 offline 20 ever
This is the general format I would use. The foreach is completely incorrect as I don't know how you're going from one row of data to the next; whatever it is, loop over that.
Bare in mind this is destructive; += deletes the previous array and creates a new one. However it tends to be my goto when dealing with small data sets because of simplicity and readability.
foreach ($line in $lines) {
[array]$tableVariable += New-Object PSObject -Property #{
Server = $server
Data1 = $stuff
Data3 = $thing
}
}

How to use PowerShell to find information for Current User's Network Printers

I'm trying to find the mapped network printers of the signed in user. I found this script here: GetMappedNetworkPrinters, but it is from a few years ago and there isn't a comment section so I'm not sure where to ask questions about the issues...
When I run this script I run into WMI errors on some machines, but the confusing part is for the computers that it does bring information back for I get the notice that there are no mapped printers...
Any idea how to adjust the script so that it might work correctly?
<#
Script Name: GetMappedNetworkPrinters.ps1
Purpose:
This script can be used to collect the mapped network printer information from the users who are logged into the console of the Computer or Computers specified.
Required Modules:
PSRemoteRegistry, and Active Directory
Permission Requirements:
The user account that the script is run with needs to have administrative permissions on the workstations and permission to query Active Directory user accounts.
The computers firewall if enabled needs to allow it to be pinged, connections to WMI and also Remote Registry.
A user will need to be logged into the console so their mapped network printer information can be collected.
How the script functions:
Create a text file that contains a list of Computer names that you want to get the mapped network printers info for.
Execute the script and you will be prompted for the path to the text file that contains the list.
Connectivity will be verified to each of the computers by pinging each of them.
Via WMI it will check to see which user is logged into the computers that responded to the ping.
Next it will query Active Directory for the SID of each of the users that were currently logged into one of the active computers polled.
Using the users SID a Remote Registry query is created to enumerate the list of mapped network printers for the logged on user.
The Log files and CSV file containing the list of mapped printers is located in C:\temp\logs
FileNames:
MappedPrinters-(currentdate).csv -- Contains the list of mapped printers.
NoMappedPrinters-(currentdate).log -- Contains list of users that do not have network printers mapped on their computer.
NoReply-(currentdate).csv -- Contains list of computers that did not respond to ping.
NoUsrLoggedIn-(currentdate).log -- Contains list of computers that responded to ping but did not have a user logged into it.
RemoteRegNotRunning-(currentdate).log -- Contains a list of computers where the Remote Registry service is not running.
WmiError-(currentdate).log -- If there are computers that it is not able to connect to via wmi it will be listed here.
#>
function global:Ping-Host {
BEGIN {
}
PROCESS {
$results = gwmi -Query "SELECT * FROM Win32_PingStatus WHERE Address = '$_'"
$obj2 = New-Object psobject
$obj2 | Add-Member Noteproperty Computer $_
$obj2 | Add-Member Noteproperty IPAddress ($results.protocoladdress)
if ($results.statuscode -eq 0) {
$obj2 | Add-Member NoteProperty Responding $True
} else {
$obj2 | Add-Member NoteProperty Responding $False
}
Write-Output $obj2
}
END {}
}
function VerifyConnectivity {
param (
[parameter(ValueFromPipeline=$true)]
$compList
)
BEGIN {
$modeMSG = "Verifying Connectivity to Desktops"
$HostComputer = #()
$d = Get-Date
$strDate = $d.ToString()
$month = $d.Month
$day = $d.Day
$year = $d.Year
$cDate = "$month-$day-$year"
$logFilePath = "C:\temp\logs\"
$NoReplyLog = $logFilePath + "NoReply-" + $cDate + ".csv"
}
PROCESS {
$i = 1
$numComp = $compList.Count
If ($numComp -ge 1){
Talk $modeMSG
$HostComputer = $HostComputer + $(
foreach ($computer in $compList){
Write-Progress -Activity $modeMSG -Status "Currently Processing: $computer" -CurrentOperation "$i of $numComp" -PercentComplete ($i/$numComp*100)
$computer | Ping-Host
$i = $i + 1
})
}
ElseIf ($numComp -lt 1){
Write-Host "No Computers to Process"
Exit
}
}
END {
$Alive = $HostComputer | Where {$_.Responding -eq "$true"}
$global:Dead = $HostComputer | Where {$_.Responding -ne "$true"}
$global:Dead | select Computer | Export-Csv -Path $NoReplyLog
$Acomp = $Alive | select Computer
$Acomp
}
}
function GetPrinterInfo {
param (
[parameter(ValueFromPipeline=$true)]
$compList
)
BEGIN {
$d = Get-Date
$strDate = $d.ToString()
$month = $d.Month
$day = $d.Day
$year = $d.Year
$cDate = "$month-$day-$year"
$global:logFilePath = "C:\temp\logs\"
$NoPrtMapLog = $logFilePath + "NoMappedPrinters-" + $cDate + ".log"
$WmiErrorLog = $logFilePath + "WmiError-" + $cDate + ".log"
$MappedPrinters = $logFilePath + "MappedPrinters-" + $cDate + ".csv"
$NoUsrLoggedIn = $logFilePath + "NoUsrLoggedIn-" + $cDate + ".log"
$RemoteRegNotRunning = $logFilePath + "RemoteRegNotRunning-" + $cDate + ".log"
$ErrorActionPreference = 'SilentlyContinue'
Import-Module activedirectory
Import-Module psremoteregistry
$global:wmiErrors = #()
$global:NoUserLoggedIn = #()
$CompUserInfo = #()
$arrCompLogonInfo = #()
$arrRemoteRegSvcStopped = #()
$arrNoMappedPrinters = #()
$arrMappedPrinters = #()
$statusMSG = "Getting Logged on User Information"
$statusMSG2 = "Getting User SID from Active Directory"
$statusMSG3 = "Collecting Mapped Printer Information"
}
PROCESS {
$u = 1
$Responded = VerifyConnectivity $compList
if ($Responded.count -gt 0){
Talk $statusMSG
foreach ($client in $Responded){
[string]$c = $client.Computer
$numClient = $Responded.Count
$logonInfo = $null
Write-Progress -Activity $statusMSG -Status "Currently Processing: $c" -CurrentOperation "$u of $numClient" -PercentComplete ($u/$numClient*100)
$logonInfo = Get-WmiObject -ComputerName $c -Query "select * from win32_computersystem" | select Username
if ($?){
if ($logonInfo.Username -ne $null){
[string]$strUserName = $logonInfo.Username
$arrStrUserName = $strUserName.Split("\")
$strUser = $arrStrUserName[1]
$objCUinfo = New-Object psobject
$objCUinfo | Add-Member NoteProperty Workstation $c
$objCUinfo | Add-Member NoteProperty User $strUser
$CompUserInfo = $CompUserInfo + $objCUinfo
}
elseif ($logonInfo.Username -eq $null){
$global:NoUserLoggedIn = $global:NoUserLoggedIn + $c
}
}
else {
$global:wmiErrors = $global:wmiErrors + "Could not Execute WMI Query to collect user logon information on $c"
}
$u = $u + 1
}
if ($CompUserInfo.Count -ge 1){
$u = 1
Talk $statusMSG2
foreach ($logon in $CompUserInfo){
[string]$userLN = $logon.User
$userCount = $CompUserInfo.count
[string]$wrksta = $logon.Workstation
Write-Progress -Activity $statusMSG2 -Status "Currently Processing: $userLN" -CurrentOperation "$u of $userCount" -PercentComplete ($u/$userCount*100)
$getSID = Get-ADUser -Identity $userLN | select SID
if ($?){
[string]$sid = $getSID.sid
$LoggedOnUserInfo = New-Object psobject
$LoggedOnUserInfo | Add-Member Noteproperty Workstation $wrksta
$LoggedOnUserInfo | Add-Member Noteproperty User $userLN
$LoggedOnUserInfo | Add-Member Noteproperty SID $sid
$arrCompLogonInfo = $arrCompLogonInfo + $LoggedOnUserInfo
}
$u = $u + 1
}
}
if ($arrCompLogonInfo.count -ge 1){
$u = 1
Talk $statusMSG3
foreach ($comp in $arrCompLogonInfo){
$numT = $arrCompLogonInfo.Count
$Printers = $null
[string]$cn = $comp.Workstation
[string]$usid = $comp.sid
[string]$uName = $comp.User
Write-Progress -Activity $statusMSG3 -Status "Currently Processing: $cn" -CurrentOperation "$u of $numT" -PercentComplete ($u/$userCount*100)
$regStat = Get-Service -ComputerName $cn -Name "RemoteRegistry"
If ($?){
If ($regStat.Status -eq "Running"){
$Printers = Get-RegKey -ComputerName $cn -Hive "Users" -Key "$usid\Printers\Connections" -Recurse
If ($Printers -ne $null){
foreach ($printer in $Printers){
[string]$printerKey = $printer.key
$arrPrinterKey = $printerKey.Split("\")
$PrinterNamePiece = $arrPrinterKey[3]
$arrPrinterParts = $PrinterNamePiece.Split(",")
$printServer = $arrPrinterParts[2]
$PrinterName = $arrPrinterParts[3]
$PrinterUnc = "\\$printServer\$PrinterName"
$printInfo = New-Object psobject
$printInfo | Add-Member NoteProperty Workstation $cn
$printInfo | Add-Member NoteProperty User $uName
$printInfo | Add-Member NoteProperty PrintServer $printServer
$printInfo | Add-Member NoteProperty PrinterName $PrinterName
$printInfo | Add-Member NoteProperty PrinterUNC $PrinterUnc
$arrMappedPrinters = $arrMappedPrinters + $printInfo
}
}
ElseIf ($Printers -eq $null){
$arrNoMappedPrinters = $arrNoMappedPrinters + "$uName has no mapped printers on $cn"
}
}
ElseIf ($regStat.Status -eq "Stopped"){
$arrRemoteRegSvcStopped = $arrRemoteRegSvcStopped + $cn
}
}
$u = $u + 1
}
}
}
}
END {
$arrMappedPrinters | Export-Csv -Path $MappedPrinters
Add-Content $NoPrtMapLog $arrNoMappedPrinters
Add-Content $WmiErrorLog $wmiErrors
Add-Content $NoUsrLoggedIn $global:NoUserLoggedIn
Add-Content $RemoteRegNotRunning $arrRemoteRegSvcStopped
}
}
function Talk {
param (
[parameter(ValueFromPipeline=$true)]
$talk
)
Add-Type -AssemblyName System.Speech
$synthesizer = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer
$synthesizer.Speak($talk)
}
cls
$getPath = $(Read-Host "Enter path to the text file that contains the list of Computer Names`n")
cls
if ($getPath -like "*.txt"){
$valid = Test-Path -Path $getPath
if ($valid -eq $true){
$compList = get-content -Path $getPath
GetPrinterInfo $compList
Write-Host "The Script Output is located in $logfilepath"
Exit
}
Else {
Write-Host "Path to file is not valid" -ForegroundColor Red
}
}
Elseif ($getPath -notlike "*.txt"){
Write-Host "Path to file is not valid"
Exit
}