Ignoring Null Values - powershell

I have a question regarding Null values.
The script below works fine, but will return errors when nothing is in my Z (optical drive). Obviously if nothing is in the optical drive it is basically null, but I have not found a way around that as of yet. I know it's probably a simple fix and am hoping someone here can help.
Function GetDisk
{
Param(
$ComputerName = $env:COMPUTERNAME,
[Switch]$PassThru
)
# ...
}
Function Get-ColorSplat
{
# Create color Splats
$C1 = #{ForegroundColor="Green"}
$C2 = #{ForegroundColor="Yellow"}
$C3 = #{ForegroundColor="Red"}
# Create color constants in the previous scope.
New-Variable -Name "Good" -Value $C1 -Scope 1
New-Variable -Name "Problem" -Value $C2 -Scope 1
New-Variable -Name "Bad" -Value $C3 -Scope 1
} # End: Get-ColorSplat
Function Write-ColorOutput
{
Param($DiskInfo)
Write-Host""
Write-Host -NoNewLine "OS Version: "
Get-CimInstance Win32_OperatingSystem | Select-Object Caption | ForEach{ $_.Caption }
Write-Host""
# Display the headers.
Write-host "SystemName DeviceID VolumeName Size(GB) FreeSpace(GB) %FreeSpace(GB) Date"
Write-host "---------- -------- ----------- -------- ------------ -------------- ----------------"
# Display the data.
ForEach ($D in $DiskInfo)
{
$PSComputerName = $D.PSComputerName.PadRight(6)
$DeviceID = $D.DeviceID.ToString().PadRight(6).Remove(5)
$VolumeName = $D.VolumeName.ToString().PadRight(20).Remove(19)
$SizeGB = $D.SizeGB.ToString().PadRight(6).Remove(5)
$FSGB = $D.FreeSpaceGB.ToString().PadRight(6).Remove(5)
$PFS = $D.PercentFS.ToString().PadRight(7).Remove(6)
$Date = $D.Date.ToString().PadRight(20).Remove(19)
If ($D.PercentFS -ge 50)
{ Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" #Good }
ElseIf (($D.PercentFS -lt 50) -and ($D.PercentFS -GE 40))
{ Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" #Problem }
Else
{ Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" #Bad }
}
}
# Get the color splats
Get-ColorSplat
$DiskInfo = Get-WMIObject win32_logicalDisk -ComputerName $env:computername | Select-Object PSComputerName, DeviceID, Size, VolumeName,
#{name="SizeGB"; expression={"{0:N2}" -f ($_.Size/1gb)}},
#{Name="FreeSpaceGB"; expression={"{0:N2}" -f ($_.FreeSpace/1gb)}},
#{Name="PercentFS"; expression={"{0:N2}%" -f(($_.FreeSpace/$_.size)*100)}},
#{name="Date"; expression={$(Get-Date -Format "g")}}
#$ErrorActionPreference= 'silentlycontinue'
If (!$PassThru) {
Write-ColorOutput -DiskInfo $DiskInfo
}
Else {
Write-Output $DiskInfo
}
}

You had to problems several values of $D were returning $null but you aren't checking their value (e.g. $D.VolumeName).
The second problem is that you are creating the global variables multiple times (e.g. New-Variable -Name "Good").
I suggest you refactor so that variable creation is in "Get-ColorSplat" if you are going to call it several times.
Edit #1 fixed padding in output table
Function GetDisk
{
Param(
$ComputerName = $env:COMPUTERNAME,
[Switch]$PassThru
)
# ...
}
Function Get-ColorSplat
{
# Create color Splats
$C1 = #{ForegroundColor="Green"}
$C2 = #{ForegroundColor="Yellow"}
$C3 = #{ForegroundColor="Red"}
# Create color constants in the previous scope.
if(Get-Variable Good -Scope Global -ErrorAction SilentlyContinue){}
else{New-Variable -Name "Good" -Value $C1 -Scope 1}
if(Get-Variable Problem -Scope Global -ErrorAction SilentlyContinue){}
else{New-Variable -Name "Problem" -Value $C2 -Scope 1}
if(Get-Variable Bad -Scope Global -ErrorAction SilentlyContinue){}
else{New-Variable -Name "Bad" -Value $C3 -Scope 1}
} # End: Get-ColorSplat
Function Write-ColorOutput
{
Param($DiskInfo)
Write-Host""
Write-Host -NoNewLine "OS Version: "
Get-CimInstance Win32_OperatingSystem | Select-Object Caption | ForEach{ $_.Caption }
Write-Host""
# Display the headers.
Write-host "SystemName DeviceID VolumeName Size(GB) FreeSpace(GB) %FreeSpace(GB) Date"
Write-host "---------- -------- ----------- -------- ------------ -------------- ----------------"
# Display the data.
ForEach ($D in $DiskInfo)
{
$PSComputerName = $D.PSComputerName.PadRight(6)
$DeviceID = $D.DeviceID.ToString().PadRight(6).Remove(5)
$Dname = $D.VolumeName
if($Dname){$VolumeName = $D.VolumeName.ToString().PadRight(20).Remove(19)}
else{$VolumeName = ' '}
$SizeGB = $D.SizeGB.ToString().PadRight(6).Remove(5)
$FSGB = $D.FreeSpaceGB.ToString().PadRight(6).Remove(5)
$PercentFS = $D.PercentFS
if($PercentFS){$PFS = $D.PercentFS.ToString().PadRight(7).Remove(6)}
else{$PFS = ' '}
$Date = $D.Date.ToString().PadRight(20).Remove(19)
If ($D.PercentFS -ge 50)
{ Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" #Good }
ElseIf (($D.PercentFS -lt 50) -and ($D.PercentFS -GE 40))
{ Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" #Problem }
Else
{ Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" #Bad }
}
}
# Get the color splats
Get-ColorSplat
$DiskInfo = Get-WMIObject win32_logicalDisk -ComputerName $env:computername | Select-Object PSComputerName, DeviceID, Size, VolumeName,
#{name="SizeGB"; expression={"{0:N2}" -f ($_.Size/1gb)}},
#{Name="FreeSpaceGB"; expression={"{0:N2}" -f ($_.FreeSpace/1gb)}},
#{Name="PercentFS"; expression={"{0:N2}%" -f(($_.FreeSpace/$_.size)*100)}},
#{name="Date"; expression={$(Get-Date -Format "g")}}
#$ErrorActionPreference= 'silentlycontinue'
If (!$PassThru) {Write-ColorOutput -DiskInfo $DiskInfo}
Else {Write-Output $DiskInfo}

Got it figured out - thanks to all that commented and helped me.
Function GetDisk
{
Param(
$ComputerName = $env:COMPUTERNAME,
[Switch]$PassThru
)
Function Write-ColorOutput
{
Param($DiskInfo)
Write-Host""
Write-Host -NoNewLine "OS Version: "
Get-CimInstance Win32_OperatingSystem | Select-Object Caption | ForEach{ $_.Caption }
Get-CimInstance Win32_OperatingSystem | Select-Object CSName, InstallDate, BuildNumber, OSArchitecture, BootDevice | Format-Table -AutoSize | Out-String | Write-Host -ForegroundColor Green
Get-PhysicalDisk | Select-Object $env:computername, FriendlyName, SerialNumber, MediaType, OperationalStatus, HealthStatus | Format-Table -AutoSize | Out-String | Write-Host -ForegroundColor Green
Write-Host""
# Display the headers.
Write-host "SystemName DeviceID VolumeName Size(GB) FreeSpace(GB) %FreeSpace(GB) Date"
Write-host "---------- -------- ----------- -------- ------------ -------------- ----------------"
# Display the data.
ForEach ($D in $DiskInfo)
{
$PSComputerName = $D.PSComputerName.PadRight(6)
$DeviceID = $D.DeviceID.ToString().PadRight(6).Remove(5)
$Dname = $D.VolumeName
if($Dname){$VolumeName = $D.VolumeName.ToString().PadRight(20).Remove(19)} else{$VolumeName = ' '}
$SizeGB = $D.SizeGB.ToString().PadRight(6).Remove(5)
$FSGB = $D.FreeSpaceGB.ToString().PadRight(6).Remove(5)
$PercentFS = $D.PercentFS
if($PercentFS){$PFS = $D.PercentFS.ToString().PadRight(7).Remove(6)} else{$PFS = ' '}
$Date = $D.Date.ToString().PadRight(20).Remove(19)
If ($D.PercentFS -ge 50)
{Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" -ForegroundColor Green}
ElseIf (($D.PercentFS -lt 50) -and ($D.PercentFS -GE 40))
{Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" -ForegroundColor Yellow}
Else
{Write-Host "$PSComputerName $($DeviceID) $($VolumeName)$($SizeGB) $($FSGB) $($PFS) $($Date)" -ForegroundColor Red}
}
Get-PhysicalDisk | Get-StorageReliabilityCounter | Select-Object $env:computername, PowerOnHours, Temperature, Wear, ReadErrorsTotal, WriteErrorsTotal, FlushLatencyMax, WriteLatencyMax | Format-Table -AutoSize | Out-String | Write-Host -ForegroundColor Green
Disk | Select-Object | Format-Table -AutoSize | Out-String | Write-Host -ForegroundColor Green
}
$DiskInfo = Get-WMIObject win32_logicalDisk -ComputerName $env:computername | Select-Object PSComputerName, DeviceID, Size, VolumeName,
#{name="SizeGB"; expression={"{0:N2}" -f ($_.Size/1gb)}},
#{Name="FreeSpaceGB"; expression={"{0:N2}" -f ($_.FreeSpace/1gb)}},
#{Name="PercentFS"; expression={"{0:N2}%" -f(($_.FreeSpace/$_.size)*100)}},
#{name="Date"; expression={$(Get-Date -Format "g")}}
#$ErrorActionPreference= 'silentlycontinue'
If (!$PassThru) {Write-ColorOutput -DiskInfo $DiskInfo} Else {Write-Output $DiskInfo}
Write-Host""
Write-Host""
}

Related

Why is my if statement not being read properly?

I have a script that backs up a user profile from the local, or remote machine and places it onto a share: $Global:Shared_BackupPath = "\\server\share\". I've been tweaking it a little more and just ended up making some variables into Global variables (not sure if this is the issue - dont see why it would be).
This is the condition:
if(-not (Get-EventSubscriber)){
I tried changing it to -eq $null to see if it would make any difference, but it didn't.
Its just not analyzing the condition properly and goes on to display my message box before all Jobs are done: it's "supposed" to wait till there's no more events and then display the message box:
Register-ObjectEvent -InputObject $job -EventName StateChanged -Action {
#Start-Sleep -Milliseconds 500
$eventSubscriber | Unregister-Event
$eventSubscriber.Action | Remove-Job
if(-not (Get-EventSubscriber)){
$Profile_Sum = Get-ChildItem -Path $Global:BackUp_Path -Recurse |
Measure-Object -Property length -Sum |
Select-Object -ExpandProperty Sum
$Size = try{if($Profile_Sum -lt 1048576){ $TinyByte = " {0:N2}" -f ($Profile_Sum / 1KB) + " KB"; $TinyByte }
elseif($Profile_Sum -gt 1048576 -and $Profile_Sum -lt 1073741824){ $MediumByte = " {0:N2}" -f ($Profile_Sum / 1MB) + " MB"; $MediumByte }
elseif($Profile_Sum -gt 1073741824){ $GiganticByte = " {0:N2}" -f ($Profile_Sum / 1GB) + " GB"; $GiganticByte } } Catch {}
$Begin_Time = Get-Item -Path $Global:BackUp_Path | Select-Object -ExpandProperty LastWriteTime
$End_Time = Get-Date -Format G
Get-Job | Remove-Job
[System.Windows.MessageBox]::Show("Copying Complete!`nStart Time: $Begin_Time `nEnd Time: $End_Time `nProfile Size copied: $Size")
}
} | Out-Null
}
I feel like I may have an idea due to the event itself being registered as a job but, not too sure how to go about it and have it wait until its done with ALL jobs before displaying the messagebox informing me when the copying is complete. Other than that, the script works just fine and anyone reading this can feel free to use it themselves. Heres the full Script:
Function PFL-UserBackUp{
[cmdletbinding()]
Param(
[Parameter(Mandatory=$false,
ValueFromPipeLine=$true,
ValueFromPipeLineByPropertyName=$true)]
[Alias('CN','Computer','server','node')]
[ValidateLength(1, 15)]
[String[]]$ComputerName = $env:COMPUTERNAME )
Begin{
$Global:Shared_BackupPath = "\\server\share\"
}
Process{
Foreach($Computer in $ComputerName){
Try{
$PSSession = New-PSSession -ComputerName $Computer -ErrorAction Stop
[array]$User_List = Invoke-Command -ScriptBlock {
Get-ChildItem -Path "C:\Users" -Exclude Public, Default* |
Sort-Object -Property LastWriteTime -Descending } -Session $PSSession
$userinfo1 = foreach ($user in $User_List.name) {
$userinfo = (net user $user /domain | Select-String "Full Name" -ErrorAction SilentlyContinue) -replace "Full Name ", "" 2>&1 | Out-String -Stream
if ($userinfo.Length -lt 4) { "NO DISPLAY NAME in ADUC" }
elseif($LASTEXITCODE -eq 2) { "ACCOUNT NOT in ADUC" }
elseif($LASTEXITCODE -eq 0) { $userinfo }
else { "Error occured" }
}
$(for($i=0; $i -lt $User_List.Count; $i++){
[pscustomobject]#{
'User Display Name ' = "$($i): $($userinfo1[$i])"
' NAME ' = $User_List.name[$i]
'Last Modified' = "$($User_List.LastWriteTime[$i])"
'Profile Size ' = Try{
$ProfilePath = $User_List.FullName[$i]
$Profile_Sum = Invoke-Command -ScriptBlock {
Get-ChildItem -Path $Using:ProfilePath -Recurse |
Where-Object {$_.PSParentPath -match "Documents|Desktop|Music|Videos|Downloads|Links|Pictures|Favorites|Contacts" -and $_.DirectoryName -notmatch "OneDrive" } |
Measure-Object -Property length -Sum |
Select-Object -ExpandProperty Sum } -Session $PSSession
if($Profile_Sum -lt 1048576){ $TinyByte = " {0:N2}" -f ($Profile_Sum / 1KB) + " KB"; $TinyByte }
elseif($Profile_Sum -gt 1048576 -and $Profile_Sum -lt 1073741824){ $MediumByte = " {0:N2}" -f ($Profile_Sum / 1MB) + " MB"; $MediumByte }
elseif($Profile_Sum -gt 1073741824){ $GiganticByte = " {0:N2}" -f ($Profile_Sum / 1GB) + " GB"; $GiganticByte } #Profile Size
} Catch { "$($Error[0].Exception.Message.Split('.')[2].Trim())!" }
}
} ) | Out-Host
Write-Host "Press 'Q' to quit."
$ii = Read-Host -Prompt "Enter Number of Profile to Back-Up"
$i = $ii.Trim() -split ","
if([String]::IsNullOrEmpty($i) -eq $true) { "Null string"; Break }
elseif($i.ToLower() -like "q*") {"Q was selected. Stopping script."; Break }
<#
" "
" Following Profiles will be Saved:"
" ------------------------------------"
foreach($i in $index) { "$($i.trim()): $($userinfo1[$i])" }
" "
$Confirm = Read-Host -Prompt "Are you sure you want to continue? [Y/N]"
if($Confirm.ToLower().TrimStart() -like "n*" -or $Confirm.ToLower() -like "q*"){Break}
if([String]::IsNullOrEmpty($Confirm.Trim()) -eq $true) { "Null string"; Break }#>
$Profile_Path = "C:\Users\$($User_List.name[$i])"
$Literal_Name = $userinfo1[$i].Replace('/','-')
$Global:BackUp_Path = "$Global:Shared_BackupPath$Literal_Name"
$Test_Path = Test-Path -Path $Global:BackUp_Path
if($Test_Path -eq $false){
New-Item -Path $Global:BackUp_Path -ItemType Directory | Out-Null
Start-Process $Global:BackUp_Path}
elseif($Test_Path -eq $true){
$Find_OldName = Get-ChildItem -Path "$Global:Shared_BackupPath" -Filter "$Literal_Name" -Directory |
Sort-Object -Property LastWriteTime -Descending |
Select-Object -ExpandProperty BaseName -First 1
$New_PathName = $Find_OldName + "1"
New-Item -Path "$Global:Shared_BackupPath" -Name $New_PathName -ItemType Directory -OutVariable Global:BackUp_Path | Out-Null #Global:BackUp_Path variable declared
$Global:BackUp_Path = $Global:BackUp_Path.FullName
Start-Process $Global:BackUp_Path}
$Global:Start_Time = Get-Date -Format G
#Favorites Copy
$FireFox_Favorites = "C:\Users\$($User_List.name[$i])\AppData\Roaming\Mozilla\Firefox\Profiles\*.default\places.sqlite"
$Chrome_Favorites = "C:\Users\$($User_List.name[$i])\AppData\Local\Google\Chrome\User Data\Default\Bookmarks"
$Chrome_Favorites2 = "C:\Users\$($User_List.name[$i])\AppData\Local\Google\Chrome\User Data\Default\Bookmarks.bak"
$Sticky_Notes = "C:\Users\$($User_List.name[$i])\AppData\Local\Packages\Microsoft.MicrosoftStickyNotes_8wekyb3d8bbwe\LocalState\plum.sqlite"
$Favorites_Array = #($FireFox_Favorites,$Chrome_Favorites,$Chrome_Favorites2,$Sticky_Notes)
Foreach($File in $Favorites_Array){
$Test_File = Invoke-Command -ScriptBlock { Test-Path -Path $File }
if($Test_File -eq $true){
Copy-Item -Path $File -Destination $Global:BackUp_Path -Force -Recurse -FromSession $PSSession
}
}
#Folders Copy
$Folders = #('Desktop','Documents','Favorites','Links','Downloads','Music','Videos','Pictures','Contacts')
Foreach($Folder in $Folders){
#Create Arugments for seperate thread
$ArgumentsArray = $null
$ArgumentsArray = #()
$ArgumentsArray += "\\$Computer\c$\Users\$($User_List.name[$i])\$Folder"
$ArgumentsArray += $Global:BackUp_Path
$job = Start-Job -ScriptBlock { Copy-Item -Path $args[0] -Destination $args[1] -Force -Recurse } -Name $Folder -ArgumentList $ArgumentsArray
Register-ObjectEvent -InputObject $job -EventName StateChanged -Action {
#Start-Sleep -Milliseconds 500
$eventSubscriber | Unregister-Event
$eventSubscriber.Action | Remove-Job
if(-not (Get-EventSubscriber)){
$Profile_Sum = Get-ChildItem -Path $Global:BackUp_Path -Recurse |
Measure-Object -Property length -Sum |
Select-Object -ExpandProperty Sum
$Size = try{if($Profile_Sum -lt 1048576){ $TinyByte = " {0:N2}" -f ($Profile_Sum / 1KB) + " KB"; $TinyByte }
elseif($Profile_Sum -gt 1048576 -and $Profile_Sum -lt 1073741824){ $MediumByte = " {0:N2}" -f ($Profile_Sum / 1MB) + " MB"; $MediumByte }
elseif($Profile_Sum -gt 1073741824){ $GiganticByte = " {0:N2}" -f ($Profile_Sum / 1GB) + " GB"; $GiganticByte } } Catch {}
$Begin_Time = Get-Item -Path $Global:BackUp_Path | Select-Object -ExpandProperty LastWriteTime
$End_Time = Get-Date -Format G
Get-Job | Remove-Job
[System.Windows.MessageBox]::Show("Copying Complete!`nStart Time: $Begin_Time `nEnd Time: $End_Time `nProfile Size copied: $Size")
}
} | Out-Null
}
" "
Write-Output -InputObject "Copying will be done in background."
Write-Output -InputObject "You will be notified when copying is done."
} catch [System.Management.Automation.Remoting.PSRemotingTransportException]{
"Unable to connect to PC: $Computer `nError: $($Error[0].Exception.Message.Split('.')[2].Trim())!"
}
}
}
}
I removed some information that could get me in trouble but, it's all cosmetics.(:
EDIT: I must be on crack but, everything is working now. Only changes I made was clearing the global variables ($Global:var = $null) before assigning it a value. Thank you all for the suggestions. Another change i made was change Copy-Item to Robocopy instead.
To just answer your question:
Why is my if statement not being read properly?
Just before you evaluate Get-EventSubscriber your are cancelling your event subscription and thus deleting your event subscriber. Get-EventSubscriber therefore returns $null which evaluates to $true, when negated. In conclusion, the code after your if statement will always be executed immediately.
Commented code:
Register-ObjectEvent -InputObject $job -EventName StateChanged -Action {
$eventSubscriber | Unregister-Event # Here you cancel your event subscription
$eventSubscriber.Action | Remove-Job
if (-not (Get-EventSubscriber)) { # Therefore, Get-EventSubscriber returns $null; not $null evaluates to $true
...
The relevant part in the documentation of Unregister-Event:
The Unregister-Event cmdlet cancels an event subscription that was created by using the Register-EngineEvent, Register-ObjectEvent, or Register-WmiEvent cmdlet.
When an event subscription is canceled, the event subscriber is deleted from the session and the subscribed events are no longer added to the event queue. When you cancel a subscription to an event created by using the New-Event cmdlet, the new event is also deleted from the session.

Export-Csv Exporting List Properties Instead of Content

I'm attempting to export a list to a CSV file. The output should look something like this:
Computer Name
BIOS
Operating System
Version
Disk Number
Size (GB)
Partition Type
DEVICE1
UEFI
Microsoft Windows 10 Pro
10.0.19042.0
0
954
GPT
DEVICE1
UEFI
Microsoft Windows 10 Pro
10.0.19042.0
1
119
GPT
etc...
However, the output in the CSV file looks like this:
Count
Length
LongLength
Rank
SyncRoot
IsReadOnly
IsFixedSize
IsSynchronized
2
2
2
1
System.Object[]
FALSE
TRUE
FALSE
Relevant Code
$Result = New-Object System.Collections.Generic.List[System.Object]
#Irrelevant Code...
foreach($Device in $Devices){
#More irrelevant code...
Write-Host "`tCompiling device information... " -NoNewline
try{
$Cmd = Invoke-Command -Session $Session -ScriptBlock{
Get-Disk | Foreach-Object{
[PSCustomObject]#{
"Computer Name" = $env:COMPUTERNAME
"BIOS" = if (Test-Path HKLM:\System\CurrentControlSet\control\SecureBoot\State) {"UEFI"} else {"Legacy"}
"Operating System" = Get-WmiObject -Class Win32_OperatingSystem | Select-Object -ExpandProperty Caption
"Version" = [Environment]::OSVersion | Select-Object -ExpandProperty Version
"Disk Number" = $_.number
"Size (GB)" = [int]($_.size/1GB)
"Partition Type" = $_.PartitionStyle
}
}
}
$Cmd = $Cmd | Select * -ExcludeProperty PSComputerName, RunspaceId
$Result.Add($Cmd)
Write-Host "Success" -ForegroundColor Green
}catch{
Write-Host "Failed" -ForegroundColor Red
$Result.Add(
[PSCustomObject]#{
"Computer Name" = $Device
"BIOS" = $QueryErrorMsg
"Operating System" = $QueryErrorMsg
"Version" = $QueryErrorMsg
"Disk Number" = $QueryErrorMsg
"Size (GB)" = $QueryErrorMsg
"Partition Type" = $QueryErrorMsg
}
)
Continue
}finally{
Remove-PSSession $Session
Write-Host "`tConnection terminated."
}
$Count++
}
if($DisplayResults){
$Result | ft -a
}
if($ExportToCsv){
$Result | Export-Csv -Path ($OutputDirectory + "\DiskPartitionAudit $((Get-Date).ToString('MM-dd-yyyy hh-mm-ss tt')).csv") -NoTypeInformation
}
Any ideas how I can fix this? Thanks!
The issue is you are adding all your collected records in $cmd as a single object to $result. Instead, use AddRange(). I also changed the select-object as I was getting PSComputerName and PSShowComputerName in my csvs
$Result = New-Object System.Collections.Generic.List[System.Object]
#Irrelevant Code...
foreach($Device in $Devices){
#More irrelevant code...
Write-Host "`tCompiling device information... " -NoNewline
try{
$Cmd = Invoke-Command -Session $Session -ScriptBlock{
Get-Disk | Foreach-Object{
[PSCustomObject]#{
"Computer Name" = $env:COMPUTERNAME
"BIOS" = if (Test-Path HKLM:\System\CurrentControlSet\control\SecureBoot\State) {"UEFI"} else {"Legacy"}
"Operating System" = Get-WmiObject -Class Win32_OperatingSystem | Select-Object -ExpandProperty Caption
"Version" = [Environment]::OSVersion | Select-Object -ExpandProperty Version
"Disk Number" = $_.number
"Size (GB)" = [int]($_.size/1GB)
"Partition Type" = $_.PartitionStyle
}
}
} -HideComputerName
$Result.AddRange($Cmd)
Write-Host "Success" -ForegroundColor Green
}catch{
Write-Host "Failed" -ForegroundColor Red
$Result.Add(
[PSCustomObject]#{
"Computer Name" = $Device
"BIOS" = $QueryErrorMsg
"Operating System" = $QueryErrorMsg
"Version" = $QueryErrorMsg
"Disk Number" = $QueryErrorMsg
"Size (GB)" = $QueryErrorMsg
"Partition Type" = $QueryErrorMsg
}
)
Continue
}finally{
Remove-PSSession $Session
Write-Host "`tConnection terminated."
}
$Count++
}
if($DisplayResults){
$Result | ft -a
}
if($ExportToCsv){
$Result | Select * -ExcludeProperty RunspaceId,PSComputerName,PSShowComputerName |
Export-Csv -Path ($OutputDirectory + "\DiskPartitionAudit $((Get-Date).ToString('MM-dd-yyyy hh-mm-ss tt')).csv") -NoTypeInformation
}
You need to use "Select-Object" with the list of attributes:
$Result | Select-Object -Property "Computer Name", "BIOS", "Operating System", "Version", "Disk Number", "Size (GB)", "Partition Type" | Export-Csv -Path ($OutputDirectory + "\DiskPartitionAudit $((Get-Date).ToString('MM-dd-yyyy hh-mm-ss tt')).csv") -NoTypeInformation

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

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

Nested If Statement first -notlike condition

I am trying to use nested IF statements to check a AD-Computer Attribute and if it is not either VM or VeraCrypt, use Manage-BDE to check the Bitlocker status.
When I use just Get-Adcomputer <name> -Properties extensionAttribute1 | select extensionAttribute1 it returns the expected result VM.
When I do
$Attribute = Get-Adcomputer <name> -Properties extensionAttribute1 | select extensionAttribute1
Write-Host $Attribute
I get #{extensionAttribute1=VM}
I have tried many using -Notlike "*VM* but this did not work.
Here is my code.
# -----------------------
# Define global variables
# -----------------------
$ScanCount = 0
$UnprotectedCount = 0
$Date = Get-Date -Format yyyyMMdd
$StartDate = Get-Date -Format HH:mm:ss
$ReportFile = "C:\BitLocker_Status\BitLocker_Status.csv"
$BackupFile = "C:\BitLocker_Status\BitLocker_Status_Backup$BackupCopy'_$Date.csv"
$OutputArray = #()
$BackupCopy = 0
$SearchBase = "DC=Merallis,DC=net"
# ----------------------------------------
# Checking backing up output file
# ----------------------------------------
if (Test-Path $ReportFile) {
Rename-Item -Path $ReportFile -newname $BackupFile
$BackupCopy = $BackupCopy + 1
}
Else {
$BackupCopy = 0
}
# ----------------------------------------
# Build array from list of computers in AD
# ----------------------------------------
Write-Host -NoNewline "- Gathering a list of Computers from Active Directory..."
Try
{
$Computers = Get-ADComputer -SearchBase $SearchBase -Filter * -Properties Name,Description | Sort-Object
Write-Host -ForegroundColor Green "Success"
}
Catch
{
Write-Host -ForegroundColor Red "Failed ($_)"
}
# -------------------------------------------------
# Use the Manage-BDE command to query each computer
# -------------------------------------------------
Write-Host "- Querying BitLocker status..."
ForEach ($Computer in $Computers)
{
$Name = $Computer.Name
$Description = $Computer.Description
$BDE = Manage-BDE -ComputerName $Computer.Name -Status C:
# -------------------------------------------------
# Use the Get-ADComputer command to query the current attribute for each computer
# -------------------------------------------------
$Attribute = Get-ADComputer $Name -Properties extensionAttribute1 | select extensionAttribute1
Write-Host -nonewline " - $Name ..."
If ($Attribute -notlike "*VM*" -or $Attribute -notlike "*VeraCrypt*") {
If ($BDE -Like "*An error occurred while connecting*") {Write-Host -ForegroundColor Yellow "Unable to connect"; $Status = "Unable to connect"; Set-ADComputer -identity $Name -Replace #{"ExtensionAttribute1"=$Status}}
ElseIf ($BDE -Like "*Protection On*") {Write-Host -ForegroundColor Green "Protected"; $Status = "Protected"; Set-ADComputer -identity $Name -Replace #{"ExtensionAttribute1"=$Status}}
ElseIf ($BDE -Like "*Protection Off*") {Write-Host -ForegroundColor Red $Status; $Status = "Not protected"; $UnprotectedCount = $UnprotectedCount + 1 ; Set-ADComputer -identity $Name -Replace #{"ExtensionAttribute1"=$Status}}
ElseIf ($BDE -Like "*The term 'manage-bde'*") {Write-Host -ForegroundColor Red "error manage-bd!"; $Status = "Not protected"; $UnprotectedCount = $UnprotectedCount + 1}
Else {Set-ADComputer -identity $Name -Replace #{"ExtensionAttribute1"="Unknown"}}
}
$ScanCount = $ScanCount +1
$OutputArray += New-Object PsObject -Property #{
'Computer name' = $Computer.Name
'Description' = $Computer.Description
'BitLocker status' = $Status
}
}
# -----------------
# Generate a report
# -----------------
Write-Host -NoNewline "- Saving report..."
Try
{
$OutputArray | Export-CSV -NoTypeInformation $ReportFile
Write-Host -ForegroundColor Green "Success"
}
Catch
{
Write-Host -ForegroundColor Red "Failed ($_)"
}
# -----------------------------------------
# Display completion message and statistics
# -----------------------------------------
$EndDate = Get-Date -Format HH:mm:ss
$Duration = New-TimeSpan $StartDate $EndDate
Write-Host ""
Write-Host "-------------------------------------------------------------"
Write-Host "Script complete. Start time: $StartDate, End time: $EndDate"
Write-Host "Scanned $ScanCount computers. $UnprotectedCount are unprotected!"
Write-Host "-------------------------------------------------------------"man
Write-Host ""
If you want to filter objects from list based on some property value then you can use Where-Object
i.e. Website list ( without "default" website Name )
$YOURLIST = Get-Website
$YOURLIST | Where-Object { $_.Name -notlike "*Default*" }

Output if/else statement results to formatted table

I have written an environment IP check script in Powershell which works but I can't figure out how to display the output to screen in a formatted table which auto sizes the columns.
$infile = Import-Csv "~\Env_IPlist.csv" -Delimiter ","
$arr1 = #()
$IP_Addresses = $infile |
Select-Object "Device_Type", "Device_Name", "IP_Address",
"IP_Role", "Location"
Write-Host "Started Ping Test ..."
foreach ($IP in $IP_Addresses) {
if (Test-Connection $IP.("IP_Address") -Quiet -Count 1 -ErrorAction SilentlyContinue) {
Write-Host $IP.("Device_Name") ":" $IP.("IP_Address") "Ping successful" -ForegroundColor Green
} else {
Write-Host $IP."Device_Name" ":" $IP.("IP_Address") "Ping failed" -ForegroundColor Red -BackgroundColor white
}
}
Write-Host "Ping Test Completed!"
Add the Test-Connection result via a calculated property, then pipe the output to Format-Table.
$infile |
Select-Object Device_Type, Device_Name, IP_Address, IP_Role, Location,
#{n='Online';e={
[bool](Test-Connection $_.IP_Address -Quiet -Count 1 -EA SilentlyContinue)
}} |
Format-Table -AutoSize
I rewrote the script using PSObject as initially suggested but now I do not know how to add the ForegroundColor in the if..else statement.
$infile = Import-Csv "~\Env_IPlist.csv" -Delimiter ","
$arr1 = #()
$IP_Addresses = $infile |
Select-Object Device_Type, Device_Name, IP_Address, IP_Role,
Location, Status
foreach ($IP in $IP_Addresses) {
if (Test-Connection $IP.IP_Address -Quiet -Count 1 -ErrorAction SilentlyContinue) {
$PSObject = New-Object PSObject -Property #{
Device_Type = $IP.Device_Type
Device_Name = $IP.Device_Name
IP_Address = $IP.IP_Address
IP_Role = $IP.IP_Role
Location = $IP.Location
Status = "Successful"
}
} else {
$PSObject = New-Object PSObject -Property #{
Device_Type = $IP.Device_Type
Device_Name = $IP.Device_Name
IP_Address = $IP.IP_Address
IP_Role = $IP.IP_Role
Location = $IP.Location
Status = "Failed"
}
}
$arr1 += $PSObject
}
$arr1 | Format-Table -AutoSize