Update PowerShell Script to Check Remote Services - powershell

I am working on a server validation script that runs via PowerShell, and reaches out to collect remote machine information (pulls the list of servers from a text file), such as server uptime and pending reboot. It takes the information, places it in a CSV file and posts it to a HTML (Results) document.
My ask: I'm trying to reach out to these servers (contained on the .txt file) to validate that certain services are in the running state, and if they are, post the status as 'Running' and if not (else), post as 'Not Running'.
Example of getting the list of servers:
$title = 'Important! Please Read'
$message = 'Is this validation going to run against production servers?'
$choice = #(
[System.Management.Automation.Host.ChoiceDescription]::new(
'&Yes', 'This will execute against Production servers' # => This is help message
)
[System.Management.Automation.Host.ChoiceDescription]::new(
'&No', 'This will execute against Non-Production servers' # => This is help message
)
)
$defaultCoice = 0 # => No
$userinput = $host.UI.PromptForChoice($title, $message, $choice, $defaultCoice)
$prodserverlist = if($userinput -eq 0) {
Get-Content '\\networkshare\PostPatchingValidation\ServerListProd.txt'
}
else {
Get-Content '\\networkshare\PostPatchingValidation\ServerListDev.txt'
}
Example of Check Uptime:
Function Uptime($comp){
function WMIDateStringToDate($Bootup) {
[System.Management.ManagementDateTimeconverter]::ToDateTime($Bootup)
}
$NameSpace = "Root\CIMV2"
$wmi = [WMISearcher]""
$wmi.options.timeout = '0:0:10' #set timeout to 10 seconds
$query = 'Select * from Win32_OperatingSystem'
$wmi.scope.path = "\\$comp\$NameSpace"
$wmi.query = $query
Try{
$wmiresult = $wmi.Get()
#$wmiresult
foreach ($wmioutput in $wmiresult){
$Bootup = $wmioutput.LastBootUpTime
$LastBootUpTime = WMIDateStringToDate($Bootup)
$now = Get-Date
$Reporttime = $now - $lastBootUpTime
$d = $Reporttime.Days
$h = $Reporttime.Hours
$m = $Reporttime.Minutes
$a = "Up for: {0} Days, {1} Hours, {2:N0} Minutes" -f $d,$h,$m
return $a
}
}
Example of Posting results to CSV:
foreach($comp in $prodserverlist){
$i++
$ErrorActionPreference = "SilentlyContinue"
Write-Progress -Activity "Server Health Check " -Status ("Checking Server : {0}" -f $comp) -PercentComplete ($i/$prodserverlist.count*100) -Id 0
$ErrorActionPreference = "Continue"
#region Var_Nulling :p
$autoservices= $null
$Reporttimestatus = $null
$service = $null;
$services = $null;
$totalfailednew = $null
#endregion
$Reporttimestatus = uptime -comp $comp
# $services = Get-Service -comp $comp
$pd = PendingReboot $comp
$newobj = $null
$newobj = new-object psobject
$newobj | add-member -membertype noteproperty -name "Server" -value $comp
$newobj | add-member -membertype noteproperty -name "Uptime" -value $Reporttimestatus #-PassThru
$newobj | add-member -membertype noteproperty -name "PendingReboot" -value $pd
$newobj | add-member -membertype noteproperty -name "ServiceCheck" -value $Reporttimestatus
$newobj | ConvertTo-Csv -NoTypeInformation | Out-File "\\networkshare\PostPatchingValidation\result.csv" -Append
$htmlserver = $newobj.Server
$htmluptime = $newobj.Uptime
$htmlpendingreboot = $newobj.PendingReboot
$htmlservicecheck = $newobj.ServiceCheck
$current = "
<tr bgcolor=#CCCCCC>
<td width='14%' align='center'>$htmlserver</td>
<td width='23%' align='center'>$htmluptime</td>
<td width='12%' align='center'>$htmlpendingreboot</td>
<td width='12%' align='center'></td>
</tr>
"
$total += $current
#$newobj | ConvertTo-html -Fragment
#$newobj | ConvertTo-html -Fragment -CssUri \\networkshare\PostPatchingValidation\Style.css | Out-File \\networkshare\PostPatchingValidation\result.html -Append
}
$HTMLEnd = #"
</div>
</body>
</html>
"#
$MainHtml= $html + $total + $HTMLEnd
$MainHtml | Out-File "\\networkshare\PostPatchingValidation\result.html"
Final Screenshot of HTML Document:

Related

Powershell change/edit value of Object in Variable

I create variables in a ForEach loop using data collected from a CSV file like this:
New-Variable -Name $FlexVPN.'IP-adress' -Value (New-Object PSObject -Property #{
IP = $FlexVPN.'IP-adress'
Information = $FlexVPN.'Information'
Priority = $FlexVPN.'Priority'
RegisteredUp = $RegisteredUp
RegisteredDown = $RegisteredDown
ResponseTime = $Result = try{ Test-Connection -ComputerName $FlexVPN.'IP-adress' -Count $FlexVPN.'Priority' -ErrorAction Stop | Select ResponseTime} catch [System.Net.NetworkInformation.PingException] { $_.exception | PingFailed }})
What I'm then trying to do is to change the values of RegisteredUp and RegisteredDown depending of the respond of the ping.
I doesn't understand the New-Member stuff which I have tried but faild using.
Now I tried Set-Variable but I don´t get how to only change a Object within the Variable?
Set-Variable -Name $FlexVPN.'IP-adress' -Value (New-Object PSObject -Property #{RegisteredDown = "TESTING"})
I don´t get any errors neither it´s working.
To explain further.
If no respond on ping set Get-Date in RegisteredDown for that Variable.
If respond on ping ser Get-Date in RegisteredUp for that Variable.
I then use if/else to use the result somehow in the next version ;)
Edit
# Clear variables after loop
Remove-Variable * -force -erroraction silentlycontinue
function PingFailed {
# Add date and time when IP-address first didn't responded
$FlexVPN.RegisteredDown = 'AnotherTest'
# If only error should be printed
if($PrintError -eq 'Yes'){Write-Host -ForegroundColor Red $FlexVPN.'IP-adress' "," $FlexVPN.'Information'}
##########################################################################
####################### NO CHANGES ABOVE THIS LINE #######################
##########################################################################
# Choose between printing output or not for all rows in CSV-file [Yes/No]
$PrintOutput = 'Yes'
# Choose between printing out error or not [Yes/No]
$PrintError = 'No'
##########################################################################
####################### NO CHANGES BELOW THIS LINE #######################
##########################################################################
# Import CSV-file to Powershell to use data in code
$FlexVPNlist = Import-Csv -Path $PSScriptRoot\PingIPEmail.csv -Header 'IP-adress', 'Information', 'Priority' -Delimiter ';' -Encoding UTF7
Foreach($FlexVPN in $FlexVPNlist) {
New-Variable -Name $FlexVPN.'IP-adress' -Value (New-Object PSObject -Property #{
IP = $FlexVPN.'IP-adress'
Information = $FlexVPN.'Information'
Priority = $FlexVPN.'Priority'
RegisteredDown = 'Test'
ResponseTime = $Result = try{ Test-Connection -ComputerName $FlexVPN.'IP-adress' -Count $FlexVPN.'Priority' -ErrorAction Stop | Select ResponseTime} catch [System.Net.NetworkInformation.PingException] { $_.exception | PingFailed }})
if($PrintOutput -eq 'Yes'){
if ($host.name -eq 'Windows PowerShell ISE Host') {if ($Result.ResponseTime -eq $null) { $Host.UI.RawUI.BackgroundColor = ($bckgrnd = 'Red') } else { $psISE.Options.RestoreDefaults() }}
[PSCustomObject]#{
"IP address" = $FlexVPN.'IP-adress'
"Information" = $FlexVPN.'Information'
"Priority" = $FlexVPN.'Priority'
"Response time" = $Result.ResponseTime
"RegisteredDown" = 'Test'
}}
}
}
My Second try above works fine until I catch an exeption during ping and goes to my function PingFailed.
I want to run that function when an IP-address doesn´t respond and add Get-Date to RegisteredDown in those cases.
The error I recieve is:
At C:\Temp\Powershell scripts\PingIPEmail\PingIPEmail.ps1:49 char:13
+ $FlexVPN.RegisteredDown = 'AnotherTest'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting`
Tried the other code
# Importing a csv
$FlexVPNList = Import-Csv -Path 'C:\Temp\Powershell scripts\PingIPEmail\PingIPEmail.csv' -Header 'IP-adress', 'Information', 'Priority' -Delimiter ';' -Encoding UTF7
Foreach($FlexVPN in $FlexVPNlist) {
$FlexVPN.GetType() # Object[]
$FlexVPN[0].GetType() # PSCustomObject
($FlexVPN[0] | gm -MemberType NoteProperty).Count # 3 Noteproperties
$FlexVPN | % {
Add-Member -InputObject $_ -NotePropertyName 'RegisteredUp' -NotePropertyValue 1 -Force
Add-Member -InputObject $_ -NotePropertyName 'RegisteredDown' -NotePropertyValue 1 -Force
}
($FlexVPN[0] | gm -MemberType NoteProperty).Count # 5 Noteproperties
$Result = try{ Test-Connection -ComputerName $FlexVPN.'IP-adress' -Count $FlexVPN.'Priority' -ErrorAction Stop | Select ResponseTime} catch [System.Net.NetworkInformation.PingException] { $_ }
if ($Result.ResponseTime -eq $null){
if ($host.name -eq 'Windows PowerShell ISE Host') { $Host.UI.RawUI.BackgroundColor = ($bckgrnd = 'Red') }
$FlexVPN.RegisteredDown = Get-Date
[PSCustomObject]#{
"IP address" = $FlexVPN.'IP-adress'
"Information" = $FlexVPN.'Information'
"Priority" = $FlexVPN.'Priority'
"Response time" = $Result.ResponseTime
"RegisteredUp" = $FlexVPN.RegisteredUp
"RegisteredDown" = $FlexVPN.RegisteredDown
}
}
if ($Result.ResponseTime -ge '0'){
if ($host.name -eq 'Windows PowerShell ISE Host') { $psISE.Options.RestoreDefaults() }
$FlexVPN.RegisteredUp = Get-Date
[PSCustomObject]#{
"IP address" = $FlexVPN.'IP-adress'
"Information" = $FlexVPN.'Information'
"Priority" = $FlexVPN.'Priority'
"Response time" = $Result.ResponseTime
"RegisteredUp" = $FlexVPN.RegisteredUp
"RegisteredDown" = $FlexVPN.RegisteredDown
}
}
This code if I understand correctly reuse the same variable for each row in my CSV file.
I want to create one variable for each row (name them the IP-address) so that I can reuse the values stored for as long as the script i running.
Looks like you are overcomplicating things. You can create a new variable (object) like this
$FlexVPN = [PSCustomObject] #{
Information='Test'
}
Show the value of Information
$FlexVPN.Information
Change the value of Information
$FlexVPN.Information = 'AnotherTest'
Show the changed value of Information
$FlexVPN.Information
a valid use case for using new-variable would be if you dynamically create/use variables
Edit
your intent is not actual clear to me but following testbed might get you some new ideas to proceed from
# Mimick importing a csv
$FlexVPN = #'
IP-Adress,Information,Priority
1.1.1.1,FlexVPN,1
2.2.2.2,FlexVPN,2
'# | ConvertFrom-Csv
$FlexVPN.GetType() # Object[]
$FlexVPN[0].GetType() # PSCustomObject
($FlexVPN[0] | gm -MemberType NoteProperty).Count # 3 Noteproperties
$FlexVPN | % {
Add-Member -InputObject $_ -NotePropertyName 'RegisteredUp' -NotePropertyValue 1 -Force
Add-Member -InputObject $_ -NotePropertyName 'RegisteredDown' -NotePropertyValue 1 -Force
}
($FlexVPN[0] | gm -MemberType NoteProperty).Count # 5 Noteproperties
Managed what I wanted by doing like this:
# Clear variables after loop
Remove-Variable * -force -erroraction silentlycontinue
# Importing a csv
$FlexVPNList = Import-Csv -Path 'C:\Temp\Powershell scripts\PingIPEmail\PingIPEmail.csv' -Header 'IP', 'Information', 'Priority' -Delimiter ';' -Encoding UTF7
$FlexVPNList | % {
Add-Member -InputObject $_ -NotePropertyName 'RegisteredUp' -NotePropertyValue '' -Force
Add-Member -InputObject $_ -NotePropertyName 'RegisteredDown' -NotePropertyValue '' -Force
Add-Member -InputObject $_ -NotePropertyName 'ResponseTime' -NotePropertyValue '' -Force
}
Foreach($FlexVPN in $FlexVPNlist) {
$Ping = try{ Test-Connection -ComputerName $FlexVPN.IP -Count $FlexVPN.'Priority' -ErrorAction Stop | Select ResponseTime } catch [System.Net.NetworkInformation.PingException] { $_ }
if($Ping.ResponseTime -ge '0'){
$FlexVPN.RegisteredUp = Get-Date
$FlexVPN.ResponseTime = $Ping.ResponseTime
}
if($Ping.ResponseTime -eq $null){ $FlexVPN.RegisteredDown = Get-Date }
New-Variable -Name $FlexVPN.IP -Value (New-Object PSObject -Property #{
IP = $FlexVPN.IP
Information = $FlexVPN.Information
Priority = $FlexVPN.Priority
RegisteredUp = $FlexVPN.RegisteredUp
RegisteredDown = $FlexVPN.RegisteredDown
ResponseTime = $Ping.ResponseTime
})
[PSCustomObject]#{
"IP address" = $FlexVPN.IP
"Information" = $FlexVPN.Information
"Priority" = $FlexVPN.Priority
"Response time" = $FlexVPN.ResponseTime
"RegisteredUp" = $FlexVPN.RegisteredUp
"RegisteredDown" = $FlexVPN.RegisteredDown
}
}
I can now do stuff If computer responded or not!

How do I include on certain values when summing from a powershell array?

I want to get a sum for the total space a SQL server is using for Data and Log files.
From a few other sources on the internet I have the following code: (Yes, I'm a Powershell Noob)
$servers = "SQLSERVER1"
$array = #()
foreach($server in $servers){
$sysinfo = Get-WmiObject Win32_Volume -ComputerName $server
for($i = 0;$i -lt $sysinfo.Count; $i++){
$sname = $sysinfo[$i].SystemName
$servername = $server
$label = $sysinfo[$i].Label
if(($label) -and (!($label.Contains("FILLER")))){
write-host "Processing $label from $server"
$name = $sysinfo[$i].Name
$capacity = [math]::round(($sysinfo[$i].Capacity/1GB),2)
$fspace = [math]::round(($sysinfo[$i].FreeSpace/1GB),2)
$sused = [math]::round((($sysinfo[$i].Capacity - $sysinfo[$i].FreeSpace)/1GB),2)
$fspacepercent = [math]::Round((($sysinfo[$i].FreeSpace*100)/$sysinfo[$i].Capacity),2)
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "ServerName" -Value $server
$obj | Add-Member -MemberType NoteProperty -Name "Label" -Value $label
$obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $name
$obj | Add-Member -MemberType NoteProperty -Name "Used(GB)" -Value $sused
$array += $obj
}
}
$array += write-output " "
$totalSize = ($array | Measure-Object 'Used(GB)' -Sum).Sum
$array += $totalsize
$array += write-output " "
}
$totalsize
This gives me the result of:
Processing Recovery from SQL-Group1-DB
Processing System from SQL-Group1-DB
Processing SQLInstall from SQL-Group1-DB
Processing OCTOPUS from SQL-Group1-DB
Processing SQL_DATA from SQL-Group1-DB
Processing SQL_LOG from SQL-Group1-DB
Processing TEMP_DB from SQL-Group1-DB
Processing SSS_X64FREV_EN-US_DV9 from SQL-Group1-DB
274.92
Of course that has included EVERY drive on the server.
I only want the SQL_DATA and SQL_LOG drives included.
Any ideas on how to achieve this?
(Happy to use entirely different code if it works)
TIA
If you do not want the info for all drives on the server, you could limit the results of the Get-WmiObject cmdlet in the $sysinfo variable by using a Where-Object{} clause like:
$sysinfo = Get-WmiObject Win32_Volume -ComputerName $server |
Where-Object { 'SQL_DATA', 'SQL_LOG' -contains $_.Label }

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
}

Sum Columns Using Powershell

I have written the following PowerShell script for getting disk space information for servers in our environment.
$servers = Get-Content E:\POC.txt
$array = #()
foreach($server in $servers){
$sysinfo = Get-WmiObject Win32_Volume -ComputerName $server
for($i = 0;$i -lt $sysinfo.Count; $i++){
$sname = $sysinfo[$i].SystemName
$servername = $server
$label = $sysinfo[$i].Label
if(($label) -and (!($label.Contains("FILLER")))){
write-host "Processing $label from $server"
$name = $sysinfo[$i].Name
$capacity = [math]::round(($sysinfo[$i].Capacity/1GB),2)
$fspace = [math]::round(($sysinfo[$i].FreeSpace/1GB),2)
$sused = [math]::round((($sysinfo[$i].Capacity - $sysinfo[$i].FreeSpace)/1GB),2)
$fspacepercent = [math]::Round((($sysinfo[$i].FreeSpace*100)/$sysinfo[$i].Capacity),2)
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "SystemName" -Value $sname
$obj | Add-Member -MemberType NoteProperty -Name "ServerName" -Value $server
$obj | Add-Member -MemberType NoteProperty -Name "Label" -Value $label
$obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $name
$obj | Add-Member -MemberType NoteProperty -Name "Capacity(GB)" -Value $capacity
$obj | Add-Member -MemberType NoteProperty -Name "FreeSpace(GB)" -Value $fspace
$obj | Add-Member -MemberType NoteProperty -Name "Used(GB)" -Value $sused
$obj | Add-Member -MemberType NoteProperty -Name "FreeSpace%" -Value $fspacepercent
$array += $obj
}
}
$array += write-output " "
$totalSize = ($array | Measure-Object 'Capacity(GB)' -Sum).Sum
$array += $totalsize
$array += write-output " "
}
$filename = "E:\VolumeReport.csv"
$array | Export-CSV $filename -NoTypeInformation
One additional requirement here is to get the sum of the columns for Capacity, Size and Freespace for each server. I tried using Measure-Object but no success.
No values are getting outputted here. Just blank. Please look into this and kindly assist.
Let try this on for size shall we.
$servers = Get-Content E:\POC.txt
$propertyOrdered = "SystemName","ServerName","Label","Name","Capacity(GB)","FreeSpace(GB)","Used(GB)","FreeSpace%"
$filename = "C:\temp\VolumeReport.csv"
('"{0}"' -f ($propertyOrdered -join '","')) | Set-Content $filename
foreach($server in $servers){
$sysinfo = Get-WmiObject Win32_Volume -ComputerName $server
$serverDetails = #()
for($i = 0;$i -lt $sysinfo.Count; $i++){
$sname = $sysinfo[$i].SystemName
$servername = $server
$label = $sysinfo[$i].Label
if(($label) -and (!($label.Contains("FILLER")))){
write-host "Processing $label from $server"
$name = $sysinfo[$i].Name
$capacity = [math]::round(($sysinfo[$i].Capacity/1GB),2)
$fspace = [math]::round(($sysinfo[$i].FreeSpace/1GB),2)
$sused = [math]::round((($sysinfo[$i].Capacity - $sysinfo[$i].FreeSpace)/1GB),2)
$fspacepercent = [math]::Round((($sysinfo[$i].FreeSpace*100)/$sysinfo[$i].Capacity),2)
$props = #{
"SystemName" = $sname
"ServerName" = $server
"Label" = $label
"Name" = $name
"Capacity(GB)" = $capacity
"FreeSpace(GB)" = $fspace
"Used(GB)" = $sused
"FreeSpace%" = $fspacepercent
}
# Build this server object.
$serverDetails += New-Object PSObject -Property $props
}
}
# Output current details to file.
$serverDetails | Select $propertyOrdered | ConvertTo-Csv -NoTypeInformation | Select-Object -Skip 1 | Add-Content $filename
#Calculate Totals and append to file.
$totals = '"","","","Totals",{0},{1},{2},""' -f ($serverDetails | Measure-Object -Property "Capacity(GB)" -Sum).Sum,
($serverDetails | Measure-Object -Property "FreeSpace(GB)" -Sum).Sum,
($serverDetails | Measure-Object -Property "Used(GB)" -Sum).Sum
$totals | Add-Content $filename
}
Part of the issue here is that you were mixing object output and static string output which most likely would have been holding you back. I tidied up the object generation in a way that should be 2.0 compliant. Not that what you were going was wrong in anyway but this is a little more pleasing to the eye then all the Add-Members
I removed $array since it did not have a place anymore since the logic here is constantly output data to the output file as supposed to storing it temporarily.
For every $server we build an array of disk information in the variable $serverDetails. Once all the disks have been calculated (using your formulas still) we then create a totals line. You were not really clear on how you wanted your output so I guessed. The above code should net output like the following. (It looks a lot nicer in Excel or in a csv aware reader. )
"SystemName","ServerName","Label","Name","Capacity(GB)","FreeSpace(GB)","Used(GB)","FreeSpace%"
"server01","server01","System Reserved","\\?\Volume{24dbe945-3ea6-11e0-afbd-806e6f6e6963}\","0.1","0.07","0.03","71.85"
"","","","Totals",0.1,0.07,0.03,""
"server02","server02","System Reserved","\\?\Volume{24dbe945-3ea6-11e0-afbd-806e6f6e6963}\","0.1","0.07","0.03","69.27"
"server02","server02","images","I:\","1953.12","152.1","1801.02","7.79"
"server02","server02","Data","E:\","79.76","34.59","45.18","43.36"
"","","","Totals",2032.98,186.76,1846.23,""

Powershell script to see currently logged in users (domain and machine) + status (active, idle, away)

I am searching for a simple command to see logged on users on server.
I know this one :
Get-WmiObject -Class win32_computersystem
but this will not provide me the info I need.
It returns :
domain
Manufactureer
Model
Name (Machine name)
PrimaryOwnerName
TotalPhysicalMemory
I run Powershell 3.0 on a Windows 2012 server.
Also
Get-WmiObject Win32_LoggedOnUser -ComputerName $Computer | Select Antecedent -Unique
gives me not the exact answers I need.
I would love to see as well the idle time, or if they are active or away.
In search of this same solution, I found what I needed under a different question in stackoverflow:
Powershell-log-off-remote-session. The below one line will return a list of logged on users.
query user /server:$SERVER
Since we're in the PowerShell area, it's extra useful if we can return a proper PowerShell object ...
I personally like this method of parsing, for the terseness:
((quser) -replace '^>', '') -replace '\s{2,}', ',' | ConvertFrom-Csv
Note: this doesn't account for disconnected ("disc") users, but works well if you just want to get a quick list of users and don't care about the rest of the information. I just wanted a list and didn't care if they were currently disconnected.
If you do care about the rest of the data it's just a little more complex:
(((quser) -replace '^>', '') -replace '\s{2,}', ',').Trim() | ForEach-Object {
if ($_.Split(',').Count -eq 5) {
Write-Output ($_ -replace '(^[^,]+)', '$1,')
} else {
Write-Output $_
}
} | ConvertFrom-Csv
I take it a step farther and give you a very clean object on my blog.
I ended up making this into a module.
There's no "simple command" to do that. You can write a function, or take your choice of several that are available online in various code repositories. I use this:
function get-loggedonuser ($computername){
#mjolinor 3/17/10
$regexa = '.+Domain="(.+)",Name="(.+)"$'
$regexd = '.+LogonId="(\d+)"$'
$logontype = #{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$logon_sessions = #(gwmi win32_logonsession -ComputerName $computername)
$logon_users = #(gwmi win32_loggedonuser -ComputerName $computername)
$session_user = #{}
$logon_users |% {
$_.antecedent -match $regexa > $nul
$username = $matches[1] + "\" + $matches[2]
$_.dependent -match $regexd > $nul
$session = $matches[1]
$session_user[$session] += $username
}
$logon_sessions |%{
$starttime = [management.managementdatetimeconverter]::todatetime($_.starttime)
$loggedonuser = New-Object -TypeName psobject
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid
$loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage
$loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime
$loggedonuser
}
}
Maybe you can do something with
get-process -includeusername
If you want to find interactively logged on users, I found a great tip here :https://p0w3rsh3ll.wordpress.com/2012/02/03/get-logged-on-users/ (Win32_ComputerSystem did not help me)
$explorerprocesses = #(Get-WmiObject -Query "Select * FROM Win32_Process WHERE Name='explorer.exe'" -ErrorAction SilentlyContinue)
If ($explorerprocesses.Count -eq 0)
{
"No explorer process found / Nobody interactively logged on"
}
Else
{
ForEach ($i in $explorerprocesses)
{
$Username = $i.GetOwner().User
$Domain = $i.GetOwner().Domain
Write-Host "$Domain\$Username logged on since: $($i.ConvertToDateTime($i.CreationDate))"
}
}
Here is my Approach based on DarKalimHero's Suggestion by selecting only on Explorer.exe processes
Function Get-RdpSessions
{
param(
[string]$computername
)
$processinfo = Get-WmiObject -Query "select * from win32_process where name='explorer.exe'" -ComputerName $computername
$processinfo | ForEach-Object { $_.GetOwner().User } | Sort-Object -Unique | ForEach-Object { New-Object psobject -Property #{Computer=$computername;LoggedOn=$_} } | Select-Object Computer,LoggedOn
}
Another solution, also based on query user, but can handle variations in culture (as far as I can tell) and produces strongly-typed results (i.e. TimeSpan and DateTime values):
# Invoke "query user", it produces an output similar to this, but might be culture-dependant!
#
# USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
# >jantje rdp-tcp#55 2 Active . 3/29/2021 4:24 PM
# pietje 4 Disc 49+01:01 4/14/2021 9:26 AM
$result = (&query 'user' | Out-String -Stream)
# Take the header text and insert a '|' before the start of every HEADER - although defined as inserting a bar after
# every 2 or more spaces, or after the space at the start.
$fencedHeader = $result[0] -replace '(^\s|\s{2,})', '$1|'
# Now get the positions of all bars.
$fenceIndexes = ($fencedHeader | Select-String '\|' -AllMatches).Matches.Index
$timeSpanFormats = [string[]]#("d\+hh\:mm", "h\:mm", "m")
$entries = foreach($line in $result | Select-Object -Skip 1)
{
# Insert bars on the same positions, and then split the line into separate parts using these bars.
$fenceIndexes | ForEach-Object { $line = $line.Insert($_, "|") }
$parts = $line -split '\|' | ForEach-Object { $_.Trim() }
# Parse each part as a strongly typed value, using the UI Culture if needed.
[PSCustomObject] #{
IsCurrent = ($parts[0] -eq '>');
Username = $parts[1];
SessionName = $parts[2];
Id = [int]($parts[3]);
State = $parts[4];
IdleTime = $(if($parts[5] -ne '.') { [TimeSpan]::ParseExact($parts[5], $timeSpanFormats, [CultureInfo]::CurrentUICulture) } else { [TimeSpan]::Zero });
LogonTime = [DateTime]::ParseExact($parts[6], "g", [CultureInfo]::CurrentUICulture);
}
}
# Yields the following result:
#
# IsCurrent Username SessionName Id State IdleTime LogonTime
# --------- -------- ----------- -- ----- -------- ---------
# True jantje rdp-tcp#32 2 Active 00:00:00 3/29/2021 4:24:00 PM
# False pietje 4 Disc 48.11:06:00 4/14/2021 9:26:00 AM
$entries | Format-Table -AutoSize
Team!
I have pretty nice solution to get local session as [PSObject].
Function Get-LocalSession {
<#
.DESCRIPTION
Get local session. Pasre output of command - 'query session'.
#>
[OutputType([PSObject[]])]
[CmdletBinding()]
Param(
)
try {
#region functions
#endregion
$Result = #()
$Output = . query.exe 'session' | select-object -skip 1
#use regex to parse
$pattern = '^(?<This>.)(?<SessionName>[^\s]*)\s*(?<UserName>[a-z]\w*)?\s*(?<Id>[0-9]*)\s*(?<State>\w*)\s*((?<Type>\w*)\s*)?(?<Device>\w*)?'
foreach ( $line in $output ){
$match = [regex]::Matches( $line, $pattern )
if ( $match ){
$PSO = [PSCustomObject]#{
This = $match[0].groups['This'].Value
SessionName = $match[0].groups['SessionName'].Value
UserName = $match[0].groups['UserName'].Value
Id = $match[0].groups['Id'].Value
State = $match[0].groups['State'].Value
Type = $match[0].groups['Type'].Value
Device = $match[0].groups['Device'].Value
}
$Result += $PSO
}
Else {
write-host "Unable to process line [$line] in function [Get-LocalSession]!"
}
}
}
catch {
#Get-ErrorReporting -Trap $PSItem
write-host $PSItem
}
return $Result
}
#Run it
$SessionObject = Get-LocalSession
$SessionObject | format-table -autosize -property *
I have edited mjolinor script to remove duplicate records, and dummy account names such as system, network services,...etc
If you want to get all users
function get-loggedonuser ($computername){
$regexa = '.+Domain="(.+)",Name="(.+)"$'
$regexd = '.+LogonId="(\d+)"$'
$logontype = #{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$logon_sessions = #(gwmi win32_logonsession -ComputerName $computername)
$logon_users = #(gwmi win32_loggedonuser -ComputerName $computername)
$session_user = #{}
$logon_users |% {
$_.antecedent -match $regexa > $nul
$username = $matches[1] + "\" + $matches[2]
$_.dependent -match $regexd > $nul
$session = $matches[1]
$session_user[$session] += $username
}
$logon_sessions |%{
$starttime = [management.managementdatetimeconverter]::todatetime($_.starttime)
if ($session_user[$_.logonid] -notin $loggedonuser.user -and $session_user[$_.logonid] -notlike "*$*"){
$loggedonuser = New-Object -TypeName psobject
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid
$loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage
$loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime
$loggedonuser
}
}
}
if you want to have only domain users
function get-loggedonuser ($computername){
$HST= hostname
$regexa = '.+Domain="(.+)",Name="(.+)"$'
$regexd = '.+LogonId="(\d+)"$'
$logontype = #{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$logon_sessions = #(Get-WmiObject win32_logonsession -ComputerName $computername)
$logon_users = #(Get-WmiObject win32_loggedonuser -ComputerName $computername)
$session_user = #{}
$logon_users |ForEach-Object {
$_.antecedent -match $regexa > $nul
$username = $matches[1] + "\" + $matches[2]
$_.dependent -match $regexd > $nul
$session = $matches[1]
$session_user[$session] += $username
}
$logon_sessions |ForEach-Object{
if ($session_user[$_.logonid] -notin $loggedonuser.user -and $session_user[$_.logonid] -notlike "*$*" -and $session_user[$_.logonid] -notlike "*$HST*"){
$loggedonuser = New-Object -TypeName psobject
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid
$loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage
$loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime
$loggedonuser
}
}
}
This is what I just figured out and works out great!
Get-Process -IncludeUserName | Select-Object -Unique | Where-Object {$_.UserName -notlike 'NT AUTHORITY\SYSTEM' -and $_.UserName -notlike 'NT AUTHORITY\NETWORK SERVICE' -and $_.UserName -notlike 'NT AUTHORITY\LOCAL SERVICE'} | Format-Table -Wrap -AutoSize