Cleanup PowerShell Command - powershell

I have been trying to re-format this command by making it cleaner but I just can't seem to get around the write-output.
Get-QARSOperation -ParentContainer 'somedomain.com/OU1/OU2' -TargetObjectType 'user' |
Where-Object {$_.Status -eq 'Completed' -and $_.Controls.ID -eq 'OperationReason'} |
ForEach-Object {Get-QARSApprovalTask -Operation $_.ID} |
ForEach-Object {
Write-OutPut ("Target: " + $_.Operation.TargetObjectInfo.DN.Replace("CN=","").Replace("cn=","").Replace("\","").Replace(",","").Replace("OU","").Split('=')[0]);
Write-OutPut ("Operation ID: "+ $_.Operation.ID);
Write-OutPut ("Approver: " + $_.CompletedBy.DN.Replace("CN=","").Replace("\","").Replace(",","").Replace("OU","").Split('=')[0]);
Write-OutPut ("StartedOn: " + $_.Created);
Write-OutPut ("Completed: " + $_.Completed);
Write-OutPut ("Comments: " + $_.CompletionReason);
Write-OutPut ("Operation Type: " + $_.Operation.Type);
Write-OutPut ""
}
Also the format when I export to csv doesn't put the data into columns. What suggestions do you have to make this script look neater?
Thank you!

As suggested in the comments the correct thing to do is use Export-Csv to generate a CSV file. As for creating an object that you want to export and making that easy to read in the code you could do something similar to what you have, and use it to create a custom object that could then be piped to Export-Csv. Also, I think your whole .Replace("CN=","").Replace("cn=","").Replace("\","").Replace(",","").Replace("OU","").Split('=')[0] can be simplified to .Split('=,')[1]. The string's .Split() method accepts multiple characters to split on, and it will split on any of the characters provided. Here's what I would suggest, you will need to update the path at the end, and may have to revert to your longer .Replace bit if mine doesn't work for you.
Get-QARSOperation -ParentContainer 'somedomain.com/OU1/OU2' -TargetObjectType 'user' |
Where-Object {$_.Status -eq 'Completed' -and $_.Controls.ID -eq 'OperationReason'} |
ForEach-Object {Get-QARSApprovalTask -Operation $_.ID} |
ForEach-Object {
[PSCustomObject][Ordered]#{
"Target" = $_.Operation.TargetObjectInfo.DN.Split('=,')[1]
"Operation ID" = $_.Operation.ID
"Approver" = $_.CompletedBy.DN.Split('=,')[1]
"StartedOn" = $_.Created
"Completed" = $_.Completed
"Comments" = $_.CompletionReason
"Operation Type" = $_.Operation.Type
}
} |
Export-Csv C:\Path\To\File.csv -NoTypeInformation
You could use a Select statement, but I think this looks cleaner for you.

Related

Colorize Powershell Format-List output?

I am attempting to highlight a single output field in my Powershell commandlet with some color. Seems simple and perhaps the answer is it cannot be done in a simple fashion. If that is the case, that is fine, just wanted to see if anyone knew of any easy way to accomplish the desired output.
What I am trying to run (Using Hashtable to attempt to add color):
Get-Mailbox -PublicFolder | Sort Name | FL #{Name='Mailbox Name';Expression={write-host -NoNewline $_.Name -ForegroundColor Green}},Proh*,Issue*,MaxReceiveSize,MaxSendSize,UseDatabaseQ*,Database
In the output you will notice the write-host is putting the name of the mailbox before it's label.
Code with Hashtable
Now to contrast, if I avoid attempting to add color (Simplified code without the Hashtable):
Get-Mailbox -PublicFolder | Sort Name | FL Name,Proh*,Issue*,MaxReceiveSize,MaxSendSize,UseDatabaseQ*,Database
Everything comes out perfectly formatted.
Simplified code without Hashtable
Is there a way to get the standard FL formatting but adding color to that one output?
Responses:
To Theo, Thank you that worked! Appreciate you adding the multi color option.
To JS2010, Thank you for your suggestion. I am, unfortuntely, not getting the desired output. As stated below, I am on PS 4.0 currently(plans to upgrade soon).
To Øyvind, similar to the issue seen in JS2010s code. Here is what I am getting from your suggestion. Possibly again a versioning issue.
You could do this by first capturing the Format-List output as string in a variable, split it at the newlines and output each line yourself like this:
$result = Get-Mailbox -PublicFolder | Sort-Object Name |
Format-List Name,Proh*,Issue*,MaxReceiveSize,MaxSendSize,UseDatabaseQ*,Database |
Out-String
If you only want to color the one property:
switch -regex ($result -split '\r?\n') {
'^Name\s*:' {
$label,$value = $_ -split ':',2
Write-Host ($label + ":") -NoNewline
Write-Host $value -ForegroundColor Green
}
default { $_ }
}
Output:
If you want to be able to color more properties, create a hashtable with the property names and the colors to use:
$propertyColors = #{
Name = 'Green'
MaxSendSize = 'Cyan'
}
switch ($result -split '\r?\n') {
default {
if (![string]::IsNullOrWhiteSpace($_)) {
$label,$value = $_ -split ':',2
if ($propertyColors.ContainsKey($label.Trim())) {
Write-Host ($label + ":") -NoNewline
Write-Host $value -ForegroundColor $propertyColors[$label.Trim()]
}
else { $_ }
}
else { $_ }
}
}
Output:
js2010's tip and variable interpolation will do the trick!
$e = [char]0x1b # escape
Get-Mailbox -PublicFolder | Sort Name | FL #{Name='Mailbox Name';Expression={"$e[32m$($_.Name)$e[0m"}},Proh*,Issue*,MaxReceiveSize,MaxSendSize,UseDatabaseQ*,Database
My first attempt. If you want to go through the trouble. There's probably a powershell module that does color (Pansies?). Write-host doesn't write to standard output, and redirection takes away the color.
$e = [char]0x1b # escape
[pscustomobject]#{a = "$e[32mhi$e[0m"} | format-table
Output (pretend the 'hi' is green):
a
-
hi

How to expand two properties from a single select-object and pass it onto Test-Path?

I am having issues finding a way to expand two properties from select-object that I need to pass into Test-Path. The issue is if I do not expand the property and pass into Test-Path I will get #{} in the error - I think powershell treats it as an object?
Does anyone know if there is a way to achieve this?
I have tried few things like below but to no avail...
Invoke-Sqlcmd -ServerInstance $Database -Query $myquery |
Where-Object {$_.Name -like "*JD*"} | Select #{Expression={$_.Name, $_.FileExtension -join "."}} | FT -HideTableHeaders
Ideally my query should return:
FileName.extension
that I can then pass onto Test-Path without powershell treating it as an object, but a string if I am not mistaken?
Edit 1: Not sure if it helps but thought I'd add it here just for more clarity hopefully.
My SQL table has data like this:
Column A | Column B
FileName | FileExtension
Edit 2: Including foreach loop for clarification and pipeline output
$FilePath = "\\Server1\Folder1\Folder2\"
foreach ($path in $myquery) {
$path2 = $FilePath + $path
# check FileExtensions if exist or not
if (!(Test-Path $path2))
{
Write-host " $path | Does not exist"
}
}
Pipeline output looks like this:
\\Server1\Folder1\Folder2\#{LiteralPath=FileName.ExtensionName}
# This is getting treated as does not exist because I have "#{LiteralPath=}" left in there, if I can remove it I should be golden
If I understand the question properly, you want to combine filenames from two columns in the resulting query and next test if that file exists or not.
In that case try
Invoke-Sqlcmd -ServerInstance $Database -Query $myquery |
Where-Object {$_.Name -like "*JD*"} |
ForEach-Object {
# it is unclear what the column names really are..
# your example shows 'Column A' and 'Column A', but your code uses 'Name' or 'FileName'
# and 'FileExtension', so you have to decide which is which..
$file = '{0}.{1}' -f $_.'Column A', $_.'Column B'.TrimStart(".")
# hopefully your query results in a complete path and filename, if not,
# provide the path to the file here:
# $file = Join-Path -Path $FolderPathWhereTheFileShouldBeFound -ChildPath $file
# now you can test if the file exists
if (Test-Path -Path $file -PathType Leaf) {
# do something here
Write-Host "File '$file' exists" -ForegroundColor Green
}
else {
# bummer.. file not found
Write-Host "File '$file' does NOT exist" -ForegroundColor Red
}
}
Please read the inline comments because too much is still unclear to me..

How to send email with Powershell, only when certain variable are not empty?

How to modify the below script, so it does not send the email when certain variables are empty?
Note: These variable ($volumes) -and ($ResultEvents) -and ($services) was part of the bigger script which contains Disk VOlume Info, Event Logs and Services list, if they are a match of certain filter.
$sendMailArgs['Body'] = $null
If ($volumes | Where-Object { ($_.'Free Space (%)'.ToDouble($cultureInfo) -lt $warnPercent) -and ($_.Label -ne 'Recovery') })
{
$sendMailArgs['Body'] += ($volumes | ConvertTo-Html -Head $htmlHead -PreContent "<H3>Server basic information for [$($Machine)</H3><HR><BR>") -join "`r`n"
}
if ($ResultEvents)
{
Write-Host "`t`tFound $($ResultEvents.Count) total ..." -ForegroundColor Green
$sendMailArgs['Body'] += "<H3>Total [$($ResultEvents.Count)]</H3>" + ($ResultEvents | ConvertTo-Html -Head $htmlHead) -join "`r`n"
}
if ($services)
{
$sendMailArgs['Body'] += "<BR><H3>Server Services <br /></H3>" + ($services | ConvertTo-Html -Head $htmlHead) -join "`r`n"
}
if (($volumes) -and ($ResultEvents) -and ($services))
{
# Footer Details
$sendMailArgs['Body'] += "<BR> For more Information, please open the website http://support.domain.com </U></B><BR><HR>"
$sendMailArgs['Subject'] = "$($machine) [$((Resolve-DnsName -Name $Machine -Type A).IPAddress)] Server Status result as of $(Get-Date -Format 'F')"
Send-MailMessage #sendMailArgs
}
}
The issue with the below script is that it always send out email even when there is nothing to display like in the below screenshot:
The thing is that the first If ($volumes | ... is not just testing if $volumes is something, but there it depends if the freespace is lower than a certain warning value.
Later, with if (($volumes) -and ($ResultEvents) -and ($services)) {...} you do not test $volumes the same way, so there could be nothing written in the body there.
I would suggest adding a new (boolean) variable on top
$isThereSomethingToEmail = $false
and set that to $true inside each if() only when the if succeeds and you actually add a line to the body, just before or after $sendMailArgs['Body'] += ...
Then finally simply test for this variable
if ($isThereSomethingToEmail) {
# finish the splat and send the email
}

Show output before Read-Host

I'm having some issues getting some info to write to the console before Read-Host. Let me throw out a simplified example.
Function Add-Build {
[CmdletBinding()]
Param ([Parameter(Mandatory=$True,Position=1)][String]$Build
,[Parameter(Mandatory=$False,Position=2)][System.Nullable``1[[System.Int32]]]$VersionID
,[Parameter(Mandatory=$False,Position=3)][String]$BuildDescription
)
Write-Host -BackgroundColor DarkYellow "Adding SQL Build $($Build)"
IF ($VersionID -eq $null)
{
Get-SqlVersions | Out-String
$VersionID = Read-Host -Prompt "SELECT Version (Enter To Skip)" | % { IF ($_ -eq '') {$null} ELSE {$_}}
}
}
FUNCTION Test-Function {
$BuildID = (Get-BuildID -Build "11.0.3156.0").ToString()
}
If I call Add-Build directly then the Get-SqlVersions | Out-String output before the Read-Host. If I call Test-Function though the Get-SqlVersions no longer outputs to the console at all. Get-SqlVersions makes a SQL proc call and the output is a couple Datarows.
Is there a way to ensure the Get-SqlVersions data shows up when calling Test-Function?
Make it explicitly output to the host.
$GetSQL = Get-SqlVersions | Out-String
Write-Host $GetSQL
Could you please store Get-SqlVersions | Out-String; in a Variable and display that. I think that should work.
$versions = Get-SqlVersions | Out-String;
$versions
I know this is old, but I stumbled on it and can't help but contribute.
The problem is here:
Get-SqlVersions | Out-String
Change it to this:
Get-SqlVersions | Out-Host
I did some quick looking around, and Out-String seems to collect and prepare things for displaying. Out-Host just does it.

Check text file content in PowerShell

The PowerShell command
Get-ADFSRelyingPartyTrust | select Name | out-file C:\listOfNames.txt
generates a file as follows:
Name
----
AustriaRP
BahamasRP
BrazilRP
CanadaRP
[...]
Now, how can I check if BrazilRP has been extracted and C:\listOfNames.txt contains it?
The Get-Content and then Select-String should help. If the string is in the file it will get returned. If not then the command will returned empty value.
Get-Content C:\listOfNames.txt | Select-String "BrazilRP"
If the "BrazilRP" occurs more than once all the occurrences will returned so you know if you got any duplicates. Same holds if the string is a part of a longer expression. For example if you search for "zil" then "BrazilRP" will be returned as well.
Also you can pipe the results out to another file:
Get-Content C:\listOfNames.txt | Select-String "BrazilRP" | Out-File C:\myResults.txt
I found a solution (but thanks to PiotrWolkowski to suggest me the Get-Content function):
$file = Get-Content "C:\listOfNames.txt"
$containsWord = $file | %{$_ -match "BrazilRP"}
if ($containsWord -contains $true) {
Write-Host "There is!"
} else {
Write-Host "There ins't!"
}
If you want to easily see if a file contains your text try this
The [bool] type returns the data as either true or false instead of returning the actual data your searching for
if ([bool]((Get-Content -Path "C:\listOfNames.txt") -like '*BrazilRP*')) {
write-host "found it"
}
else {
write-host "didnt find it"
}