PowerShell - wrong array length/count (1 when empty) [duplicate] - powershell

This question already has answers here:
Eliminate Nulls
(2 answers)
Closed 3 years ago.
I essentially have an array $ServerList which holds a list of array I want to test, while $ExclusionList is a list of servers that have to be exluded.
The code should test the list of server with a Test-Connection and save the unreachable ones in $nonactivearray and remove them from $ServerList.
After that it will remove any entry in $ExclusionList from $ServerList.
If $ServerList becomes and empty array, it will ask the user to type the list of servers.
The code seems to work correctly until a correct server is given.
I found that, although $nonactivearray becomes empty, $nonactivearray.Count or $nonactivearray.Length return 1, hence it will enter in a endless loop since both $ServerList and $nonactivearray have number of elements greater than 0.
# Define function to center the text in the current line (taken from: https://stackoverflow.com/a/48622585/10516690)
function Write-HostCenter {
param($Message)
Write-Host ("{0}{1}" -f (' ' * (([Math]::Max(0, $Host.UI.RawUI.BufferSize.Width / 2) - [Math]::Floor($Message.Length / 2)))), $Message)
}
# Define Test-Connection as a string for Invoke-Expression later
[string]$TestConn = "Test-Connection -Quiet -Count 1 -ComputerName "
# Define excluded servers
$ExclusionList = #("Server1","Server2","Server3")
# Save old progress settings
$OldProgress = $ProgressPreference
$ProgressPreference = 'SilentlyContinue'
# Test Server availability
$nonactivearray = #() # Array of unreachable/excluded servers
$nonactive = "" # String to display the unreachable/excluded doscovered in our list of servers. Probably unnecessary, "$nonactivearray" can be used
$i = 0
foreach ($newserver in $ServerList) {
if (-Not (Invoke-Expression -Command "$TestConn $newserver")) {
$nonactive += $newserver
$nonactivearray += $newserver
if ($i -lt $ServerList.Length) {
$nonactive += " "
}
}
$i++
}
$ProgressPreference = $OldProgress
# List unreachable and excluded servers
$nonactivearray += (Compare-Object -ReferenceObject $ServerList `
-DifferenceObject $ExclusionList `
-ExcludeDifferent -IncludeEqual).InputObject
$nonactive = "$nonactivearray"
$CurPosPing = $host.UI.RawUI.CursorPosition
while ($nonactivearray.Length -ge 1) {
$host.UI.RawUI.CursorPosition = $CurPosPing
Write-Host " These HOSTS are not reachable, hence they will be ignored:"
$TempForegroundColor = $Host.Ui.RawUI.ForegroundColor # <== Temporary solution
$Host.Ui.RawUI.ForegroundColor = "Red" # <== Temporary solution
Write-HostCenter $nonactive # Write-HostCenter defined in a function. It centers the text in the shell
$Host.Ui.RawUI.ForegroundColor = $TempForegroundColor # <== Temporary solution
# Remove unreachable servers with filters
$ServerList = $ServerList | Where-Object {$nonactivearray -notcontains $_}
# Check if the new list is empty
while ($ServerList.Length -lt 1) {
Write-Host " "
$CurPos = $host.UI.RawUI.CursorPosition
Write-Host " The list of server is now empty, please type it now. " -ForegroundColor Yellow
Write-Host " Use the comma `"" -NoNewline; Write-Host "," -NoNewline -ForegroundColor Yellow
Write-Host "`" as a separator (spaces will be ignored)."
Write-Host " "
# Get list from user
# $CurPos = $host.UI.RawUI.CursorPosition
$ServerList = Read-Host -Prompt " Server list"
$ServerList = $ServerList -replace ' ',''
[string[]]$ServerList = $ServerList -split ','
$ServerList = $ServerList | Where-Object {$_.length -gt 0}
while ($ServerList.Length -lt 1) {
$Host.UI.RawUI.CursorPosition = $CurPos
Write-Host " >>> The list is STILL empty, please type it again. <<< " -ForegroundColor Yellow
Write-Host " "
Write-Host " "
# $CurPos = $host.UI.RawUI.CursorPosition
$ServerList = Read-Host -Prompt " Server list"
$ServerList = $ServerList -replace ' ', ''
[string[]]$ServerList = $ServerList -split ','
$ServerList = $ServerList | Where-Object {$_.length -gt 0}
}
}
# Re-check server connectivity
$OldProgress = $ProgressPreference
$ProgressPreference = 'SilentlyContinue'
Write-Host "serverlist"
$ServerList
$nonactivearray = #()
$nonactive = ""
$i = 0
foreach ($newserver in $ServerList) {
if (-Not (Invoke-Expression -Command "$TestConn $newserver")) {
$nonactive += $newserver
$nonactivearray += $newserver
if ($i -lt $ServerList.Length) {
$nonactive += " "
}
}
$i++
}
$ProgressPreference = $OldProgress
$nonactivearray += (Compare-Object -ReferenceObject $ServerList `
-DifferenceObject $ExclusionList `
-ExcludeDifferent -IncludeEqual).InputObject
$nonactive = "$nonactivearray"
}
Thank you and best.
[EDIT]
Sorry, I also forgot the following lines that I have added on the top of the code:
# Define function to center the text in the current line (taken from: https://stackoverflow.com/a/48622585/10516690)
function Write-HostCenter {
param($Message)
Write-Host ("{0}{1}" -f (' ' * (([Math]::Max(0, $Host.UI.RawUI.BufferSize.Width / 2) - [Math]::Floor($Message.Length / 2)))), $Message)
}
# Define Test-Connection as a string for Invoke-Expression later
[string]$TestConn = "Test-Connection -Quiet -Count 1 -ComputerName "
# Define excluded servers
$ExclusionList = #("Server1","Server2","Server3")
I hope I am not forgetting anything else...

Thank you for your help.
I could not fix the issue with the solutions proposed, but you gave me the insight to find a solution.
I searched for "how to remove $null values from an array" and I found this answer from mklement0 in another post.
I tried the following:
PS N:\> $list = #("1","","3","$null","7")
PS N:\> $list
1
3
7
PS N:\> $list -notlike ''
1
3
7
and then added the following line in my code:
$nonactivearray = $nonactivearray -notlike ''
That solved the issue. :)
Thank you again.

Related

List SPN's Script - Write results to file issue

Good morning everyone.
I found this script on the InterWeb's which works phenominal. HOWEVER... No matter what I try, and where I put it, I can't seem to get it to do the results to an out-file.
What the hell am I doing wrong, and where does the variable need to go?
# Source / credit:
# https://social.technet.microsoft.com/wiki/contents/articles/18996.active-directory-powershell-script-to-list-all-spns-used.aspx
cls
$search = New-Object DirectoryServices.DirectorySearcher([ADSI]"")
$search.filter = "(servicePrincipalName=*)"
## You can use this to filter for OU's:
## $results = $search.Findall() | ?{ $_.path -like '*OU=whatever,DC=whatever,DC=whatever*' }
$results = $search.Findall()
foreach( $result in $results ) {
$userEntry = $result.GetDirectoryEntry()
Write-host "Object Name = " $userEntry.name -backgroundcolor "yellow" -foregroundcolor "black"
Write-host "DN = " $userEntry.distinguishedName
Write-host "Object Cat. = " $userEntry.objectCategory
Write-host "servicePrincipalNames"
$i=1
foreach( $SPN in $userEntry.servicePrincipalName ) {
Write-host "SPN ${i} =$SPN"
$i+=1
}
Write-host ""
}

powershell & sqlplus script in for loop

So I am quite new to powershell and I have a Oracle sql scipt I want to run inside a powershell loop
Function to read a text file to read a list of servers.
$oracleServers = "C:\Temp\oracle-servers.txt"
function Database-Servers
{
$servers = Get-Content $oracleServers
Return($servers.Trim())
}
Next I read from that list what Oracle Services are running
function Installed-Databases
$svr = $args[0]
$runningDBs = " "
$runningDBs = Get-Service -Name "OracleService*" -ComputerName $svr |
Select-Object -ExpandProperty name | Out-String | % {$_.replace("OracleService","")}
Return($runningDBs)
function ConnectString
{
$CString = " " #$args[0]
foreach ($server in Database-Servers)
{
$runningDBs = Installed-Databases $server
$rdblist = $runningDBs -split '[\n]'
foreach ( $rdb in $rdblist)
{
if(!$rdb -eq " ")
{
$CString = ' system/$rdb#$server$tnsalias$rdb'
&sqlplus $CString $scriptsql | out-file $outputfile
Write-Host $CString
Return($CString)
}
}
}
}

powershell highlight the blank space found

I have the following script which finds a blank space in a csv file by going line by line
$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Retain blank space."
$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Delete blank space."
$n = #()
$f = Get-Content C:\MyPath\*.csv
foreach($item in $f) {
if($item -like "* *"){
$res = $host.ui.PromptForChoice("Title", "want to keep the blank on this line? `n $item", [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no), 0)
switch ($res)
{
0 {$n+=$item}
1 {$n+=$item -replace ' '}
}
} else {
$n+=$item -replace ' '
}
}
$n | Set-Content C:\MyPath\*.csv
The question is: When there is a space found, how can I highlight where it is on a line or put a color there, anything that would ease the process of finding it?
EDIT: don't want to alter the file or the text, this should be done only shown in the console for PowerShell or in the window popup for ISE.
A basic code sample for the method described in the comments using Read-Host for user input and changing the background color with write-host would look like this:
$str= "test abc1 abc2 test3"
$index = $str.IndexOf(" ")
while ($index -gt -1) {
write-host $str.Substring(0,$index) -NoNewline
write-host "_" -foreground "magenta" -NoNewline
$str = $str.Substring( $index + 1, $str.length - $index - 1);
$index = $str.IndexOf(" ")
}
write-host $str.Substring( $index + 1, $str.length - $index - 1);
$confirmation = Read-Host "Do you want to keep the blank on this line?"
if ($confirmation -eq 'y') {
#do action
}
Edit: Included code for multiple white spaces
Code for initial Post:
$n = #()
$f = Get-Content C:\MyPath\*.csv
foreach($item in $f) {
if($item -like "* *"){
#$res = $host.ui.PromptForChoice("Title", "want to keep the blank on this line? `n $item", [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no), 0)
$str = $item
$index = $str.IndexOf(" ")
while ($index -gt -1) {
write-host $str.Substring(0,$index) -NoNewline
write-host "_" -foreground "magenta" -NoNewline
$str = $str.Substring( $index + 1, $str.length - $index - 1);
$index = $str.IndexOf(" ")
}
write-host $str.Substring( $index + 1, $str.length - $index - 1);
$confirmation = Read-Host "Do you want to keep the blank on this line?"
if (($confirmation -eq 'y') -or ($confirmation -eq 'Y')) {
$n+=$item
}
else {
$n+=$item -replace ' '
}
} else {
$n+=$item -replace ' '
}
}
$n | Set-Content C:\MyPath\*.csv

sql failed jobs export to csv with powershell

I am battling with a PowerShell script that captures all SQL Failed jobs for the past day and exports it to .CSV
Please view code below.
param (
#[string]$serverInstance = '03RNB-VSQLPRD4\SQLPRD04
)
begin {
[void][reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
}
process {
try {
Write-Verbose "List failed SQL Server jobs using SMO..."
$serverInstance = Get-Content "C:\MountSpaceCollector\SQLJobFailures\servers2.txt";
$server = new-object Microsoft.SqlServer.Management.Smo.server $serverInstance
$results = #()
$reasons = #()
$jobs = $server.jobserver.jobs | where-object {$_.isenabled}
# Process all SQL Agent Jobs looking for failed jobs based on the last run outcome
foreach ($job in $jobs) {
[int]$outcome = 0
[string]$reason = ""
# Did the job fail completely?
if ($job.LastRunOutcome -eq "Failed") {
$outcome++
$reasons += "Job failed: " + $job.name + " Result: " + $job.LastRunOutcome
# Did any of the steps fail?
foreach ($jobStep in $job.jobsteps) {
if ($jobStep.LastRunOutcome -ne "Succeeded") {
$outcome++
$reasons += "Step failed: " + $jobStep.name + " Result: " + $jobStep.LastRunOutcome
}
}
}
if ($outcome -gt 0) {
$jobFailure = New-Object -TypeName PSObject -Property #{
Name = $job.name
LastRunDate = $job.lastrundate
LastRunOutcome = $reasons
}
$results += $jobFailure
}
}
Write-Output $results | Export-CSV -Path 'C:\MountSpaceCollector\SQLJobFailures\SQLJobFailures.csv' -Delimiter '|'
}
catch [Exception] {
Write-Error $Error[0]
$err = $_.Exception
while ( $err.InnerException ) {
$err = $err.InnerException
Write-Output $err.Message
Write-Output $results
}
}
}
But It Exports all except the last field (LastRunOutcome). It only displays "System.Object[]"?
Can anyone please assist with this as I do not know what I am doing wrong?
It's because $Reasons is an object, more specifically an array. You need to format the reasons differently, as a string for instance, to be able to have it appear in the CSV normally.
Perhaps you meant to use the string $Reason that you declare, but don't use?

Write-Host vs Write-Output - Newlines

I am having difficulty outputting a variable on the same line as a string of text. It works when I use Write-Host, but not Write-Output. I want to use Write-Output because it is seemingly the best practice (keeping things in the pipeline), but Write-Output always outputs the variable on a newline.
The line of code that I am having trouble with is:
Write-Host $VM "- VM name not valid: check the input file for spelling errors."
Also, I am fairly new to PowerShell scripting so I welcome any other input on this script.
Function Get-VMSwapping {
#requires –version 3.0
<#
.SYNOPSIS
Outputs the following: Cluster, VMHost, VM, SwappedMemory, BalloonedMemory.
.DESCRIPTION
Outputs the following: Cluster, VMHost, VM, SwappedMemory, BalloonedMemory.
Make sure to be connected to the vCenter server that has all of the VMs to enumerate.
.NOTES
Author: Nick Sousa
Date: Jan 28, 2014
Version: 1.0
#>
[CmdletBinding(
HelpURI='https://www.vmware.com/support/developer/PowerCLI/',
SupportsShouldProcess=$true,
ConfirmImpact="High"
)]
Param(
[ValidateScript({
If(Test-Path -Path $_) {$true}
Else {Throw "Input filename is not valid."}
})]
[Parameter(Mandatory=$false, ParameterSetName="InputFile",Position = 0)][string]$InputFile
)
Begin {
$ErrorActionPreference = "Stop"
} # End "Begin"
Process {
Try {
If($PSCmdlet.ShouldProcess("VMs that are ballooning & swapping")) {
If($PSBoundParameters.ContainsKey('InputFile')) {
$VMReport = #()
# Loop through each item in the file specified in -InputFile.
# If the VM cannot be validated with Get-VM, write an error to the screen.
# If the VM is found within vCenter, output its Swap/Balloon amounts.
ForEach($VM in Get-Content -Path $InputFile) {
If(Get-VM -Name $VM -ErrorAction SilentlyContinue) {
$VMFull = Get-VM -Name $VM
$VMName = $VMFull.Name
$VMView = Get-View -ViewType VirtualMachine -Filter #{"Name" = "^$VMName$"}
$obj = "" | Select-Object Cluster, VMHost, VM, SwappedMemory, BalloonedMemory
$obj.Cluster = $VMFull.Host.Parent.Name
$obj.VMHost = $VMFull.Host.Name
$obj.VM = $VMView.Name
$obj.SwappedMemory = $VMView.Summary.QuickStats.SwappedMemory
$obj.BalloonedMemory = $VMView.Summary.QuickStats.BalloonedMemory
$VMReport += $obj
} # End "If(Get-VM -Name $VM)"
Else {
Write-Host $VM "- VM name not valid: check the input file for spelling errors."
} # End Else for "If(Get-VM -Name $VM)"
} # End "ForEach($VM in $VMs)"
$VMReport | Format-Table -AutoSize
} # End "If($PSBoundParameters.ContainsKey('InputFile'))"
Else {
# Pull a list of all VMs that have Balloon Memory greater than 0.
# Loop through all VMs found and prepare a collection object to be outputted.
$VMReport = #()
$VMs = Get-View -ViewType VirtualMachine | Where-Object {$_.Summary.QuickStats.BalloonedMemory -ne "0"}
ForEach($VM in $VMs) {
$VMFull = Get-VM -Name $VM.Name
$obj = "" | Select-Object Cluster, VMHost, VM, SwappedMemory, BalloonedMemory
$obj.Cluster = $VMFull.Host.Parent.Name
$obj.VMHost = $VMFull.Host.Name
$obj.VM = $VM.Name
$obj.SwappedMemory = $vm.Summary.QuickStats.SwappedMemory
$obj.BalloonedMemory = $vm.Summary.QuickStats.BalloonedMemory
$VMReport += $obj
} # End "ForEach($VM in $VMs)"
$VMReport | Format-Table -AutoSize
} # End Else for "If($PSBoundParameters.ContainsKey('InputFile'))"
} # End "If($PSCmdlet.ShouldProcess("VMs that are ballooning & swapping"))"
} # End "Try"
Catch {
Write-Output "Caught an exception:"
Write-Output "Exception Type: $($_.Exception.GetType().FullName)"
Write-Output "Exception Message: $($_.Exception.Message)"
} # End "Catch"
Finally {
$ErrorActionPreference = "Continue"
} # End "Finally"
} # End "Process"
} # End "Function Get-VMSwapping"
Concatenate the string
Write-Output ($VM + "- VM name not valid: check the input file for spelling errors.")