Powershell Forloop with custom header - powershell

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
}
}

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!

Powershell script repeats same server multiple times

I have a script that runs through servers and prints out if they can connect to specific ports or not. The problem seems to be that it keeps on looping through existing servers its already done...hence I see multiple servers results and the CSV file keeps on getting re-written as each iteration restarts the cycle again to add on the next server.
Any ideas on ensuring it just cycles through one server at a time and not repeating the same server each time? The end CSV is fine as it contains the total servers but its taking ages to get there.
Output is below - you can see how it keeps on re-doing exisitng servers and just adding on one server more each time it runs:
Destination Port 3389 Port 445 Port 80
----------- --------- -------- -------
10.104.122.21 Failure Failure Failure
10.104.122.50 Failure Failure Failure
10.104.125.237 Failure Failure Failure
Destination Port 3389 Port 445 Port 80
----------- --------- -------- -------
10.104.122.21 Failure Failure Failure
10.104.122.50 Failure Failure Failure
10.104.125.237 Failure Failure Failure
10.104.125.66 Failure Failure Failure
Destination Port 3389 Port 445 Port 80
----------- --------- -------- -------
10.104.122.21 Failure Failure Failure
10.104.122.50 Failure Failure Failure
10.104.125.237 Failure Failure Failure
10.104.125.66 Failure Failure Failure
10.104.125.95 Failure Failure Failure
The code is below:
Function Test-PortConnections {
[CmdletBinding()]
# Parameters used in this function
Param
(
[Parameter(Position=0, Mandatory = $True, HelpMessage="Provide destination source", ValueFromPipeline = $true)]
$Destination,
[Parameter(Position=1, Mandatory = $False, HelpMessage="Provide port numbers", ValueFromPipeline = $true)]
$Ports = "80"
)
$ErrorActionPreference = "SilentlyContinue"
$Results = #()
ForEach($D in $Destination){
# Create a custom object
$Object = New-Object PSCustomObject
$Object | Add-Member -MemberType NoteProperty -Name "Destination" -Value $D
Write-Verbose "Checking $D"
ForEach ($P in $Ports){
#write-host "Port is $p"
$timeout=100
$requestCallback = $state = $null
$client = New-Object System.Net.Sockets.TcpClient
$beginConnect = $client.BeginConnect($d,$p,$requestCallback,$state)
Start-Sleep -milli $timeOut
if ($client.Connected -eq "True") { $Result = "True" } else { $Result = "False" }
$client.Close()
If($Result -eq "False"){ $status = "Failure" } else { $status = "Success" }
$Object | Add-Member Noteproperty "$("Port " + "$p")" -Value "$($status)"
}
$Results += $Object
If($Results){
$date = $(get-date -f yyyy-MM-dd)
$Results | Format-Table -AutoSize
$Results | Export-Csv -NoTypeInformation -Delimiter "," -Path H:\MyDocuments\Scripts\server_check_$($date).csv
}
}
}
Test-PortConnections -Destination (Get-Content -Path "H:\MyDocuments\Scripts\servers.txt") -Ports 3389,445,80
Every time the outer foreach() loop runs, at the end you:
Add current object to existing results ($Results += $object)
Format and write ALL existing results to the screen ($Results | Format-Table -AutoSize)
Export ALL the existing results to file ($Results | Export-Csv ...)
Export-Csv will simply overwrite the previously written file, so there you won't see a difference in the final result.
To fix this, I'd suggest simply having your function output $object on its own as soon as you reach the end of the loop body, and then either move the Export-Csv step outside the loop:
ForEach ($D in $Destination) {
# Create a custom object
$Object = New-Object PSCustomObject
$Object | Add-Member -MemberType NoteProperty -Name "Destination" -Value $D
Write-Verbose "Checking $D"
ForEach ($P in $Ports) {
#write-host "Port is $p"
$timeout = 100
$requestCallback = $state = $null
$client = New-Object System.Net.Sockets.TcpClient
$beginConnect = $client.BeginConnect($d, $p, $requestCallback, $state)
Start-Sleep -milli $timeOut
if ($client.Connected -eq "True") { $Result = "True" } else { $Result = "False" }
$client.Close()
If ($Result -eq "False") { $status = "Failure" } else { $status = "Success" }
$Object | Add-Member Noteproperty "$("Port " + "$p")" -Value "$($status)"
}
Write-Output $Object
$Results += $object
}
If ($Results) {
$date = $(Get-Date -f yyyy-MM-dd)
$Results | Export-Csv -NoTypeInformation -Delimiter "," -Path H:\MyDocuments\Scripts\server_check_$($date).csv
}
...or use Export-Csv with -Append, at which point you don't need $Results at all:
$date = Get-Date -Format yyyy-MM-dd
$ExportFileName = 'H:\MyDocuments\Scripts\server_check_${date}.csv'
ForEach ($D in $Destination) {
# Create a custom object
$Object = New-Object PSCustomObject
$Object | Add-Member -MemberType NoteProperty -Name "Destination" -Value $D
Write-Verbose "Checking $D"
ForEach ($P in $Ports) {
#write-host "Port is $p"
$timeout = 100
$requestCallback = $state = $null
$client = New-Object System.Net.Sockets.TcpClient
$beginConnect = $client.BeginConnect($d, $p, $requestCallback, $state)
Start-Sleep -milli $timeOut
if ($client.Connected -eq "True") { $Result = "True" } else { $Result = "False" }
$client.Close()
If ($Result -eq "False") { $status = "Failure" } else { $status = "Success" }
$Object | Add-Member Noteproperty "$("Port " + "$p")" -Value "$($status)"
}
Write-Output $object
$object | Export-Csv -Append -NoTypeInformation -Delimiter "," -Path $ExportFileName
}

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.

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
}

Powershell: Calling a script from foreach in another script doesn't get the desired effect

In this script below. When I enter the whole script into the powershell command line then call it with a server name, it works fine. But when i call it from this script:
`sl C:\PowershellScripts
. ./psscript_Get-FreeSpaceFrag.ps1
$svl = gc 'C:\PowershellScripts\ebi_serverlist.txt'
$x = {foreach ($s in $svl) {write-host "Getting Disk Info for Server $s" -
foregroundcolor "Green"; Get-FreeSpaceFrag $s; start-sleep -s 5; }}
$x.invoke() | export-csv "C:\PowershellScripts\DiskInfo.csv" -NoTypeInformation`
It will not work, meaning that the csv file is empty after it processes for a while.
Function Get-FreeSpaceFrag ($s)
{
trap {write-host "Can't connect to WMI on server $s" -ForeGroundColor "Red"
continue
}
$dt = get-date
$Scope = new-object System.Management.ManagementScope "\\$s\root\cimv2"
$query = new-object System.Management.ObjectQuery "SELECT * FROM Win32_Volume"
$searcher = new-object System.Management.ManagementObjectSearcher $scope,$query
$SearchOption = $searcher.get_options()
$timeout = new-timespan -seconds 10
$SearchOption.set_timeout($timeout)
$SearchOption
$searcher.set_options($SearchOption)
$volumes = $searcher.get()
$fr = {foreach ($v in $volumes | where {$_.capacity -gt 0}){
$frag=($v.defraganalysis().defraganalysis).totalPercentFragmentation
$v | Add-Member -Name Frag -MemberType NoteProperty -Value $frag -Force -
PassThru
} }
$fr.invoke() | select #{N="Server";E={$_.Systemname}}, DriveLetter, Label,
Capacity, FreeSpace, #{N="PercentFree";E={"{0,9:N0}" -f
(($_.FreeSpace/1gb)/($_.Capacity/1gb)*100)}}, Frag, #{N="InfoDate";E={$dt}}
}
I think you're making this a bit harder than it should be i.e. I'm not sure why you need part of the code in an anoymous scriptblock? Try this instead:
. ./psscript_Get-FreeSpaceFrag.ps1
Get-Content 'C:\PowershellScripts\ebi_serverlist.txt' |
Foreach {Write-Host "Getting Disk Info for Server $_" -foregroundcolor "Green" `
Get-FreeSpaceFrag $_} |
Export-Csv "C:\PowershellScripts\DiskInfo.csv" -NoTypeInformation
Not sure if it will solve the problem but you can also simplify this part of your function:
$volumes | Where {$_.capacity -gt 0} | Foreach { $_ | Add-Member NoteProperty Frag `
($_.defraganalysis().defraganalysis.totalPercentFragmentation) -PassThru} |
Select #{N="Server";E={$_.Systemname}}, DriveLetter,Label,Capacity,FreeSpace,
#{N="PercFree";E={"{0,9:N0}" -f (($_.FreeSpace/1gb)/($_.Capacity/1gb)*100)}},
Frag, #{N="InfoDate";E={$dt}
BTW rather than invoking a scriptblock like this $fr.invoke() the canonical way in PowerShell is to use the call operator like so &$fr.
Might be your problem in the process block?
$x = {foreach ($s in $svl) {start-sleep -s 5;
write-host "Getting Disk Info for Server $s" -foregroundcolor "Green";
Get-FreeSpaceFrag $s;}}