Results of multiple foreach loops in one table in PowerShell - html

I have multiple results of a foreach and I need to stick them into a table.
My current result is:
The user Johnny is a trainer
The user Mark is a trainer
The user Bob is a footballer
The user fred is a footballer
The user Moe is a footballer
This comes from the foreach loops:
foreach ($user in $trainer)
{
Write-Host "The user $user.Name is a $user.Type"
#For email $body+= "The user " + $user.Name + " is a " + $user.Type +"<br/>"
}
And it goes on for all the types foreach ($user in $footballers),....
What I'm trying to achieve is to have a nice table sent to me by mail that looks kinda like this:
How can I add results to a table from multiple for each loops in PowerShell?

Your code as written won't work correctly actually, you need to be using the sub-expression operator $() as follows in order to access the object properties:
Write-Host "The user $($user.Name) is a $($user.Type)"
It seems that $trainer is already an object with Name and Type properties so (outside of the foreach loop) it should be a simple case of doing:
$Body = $trainer | Select Name,Type | ConvertTo-HTML
Send-MailMessage -Body $Body -BodyAsHtml # other parameters, e.g to/from
Also if you have multiple objects that have the same properties, you could just combine them first with the addition operator:
$athlete = $trainer + $footballer

Related

how to change column colour base on a condition in powershell html

Firstly i would apologize if i was asking a very noob question, i'm a beginner powershell users and i don't have much exp in scripting, i have some issue to output html report using powershell. I wanted to change the colour of each row according to the utilization % wheter it is above or below 90%. However i only manage to change the colour of the first row.
i have 2 file which i use to output the report.
output from agg.csv
Aggregate_01_Data1,10.01TB,90%,96.93TB,online,86.92TB,
Aggregate_02_Data1,9.03TB,91%,96.93TB,online,87.90TB,
root_Aggregate_01,17.85GB,85%,368.4GB,online,350.6GB,
root_Aggregate_02,17.85GB,95%,368.4GB,online,350.6GB,
output from aggregate.csv
Aggregate,Size,Available,Used,Utilized,State
Aggregate_01_Data1,96.93TB,10.01TB,86.92TB,90,online
Aggregate_02_Data1,96.93TB,9.03TB,87.90TB,91,online
root_Aggregate_01,368.4GB,17.85GB,350.6GB,85,online
root_Aggregate_02,368.4GB,17.85GB,350.6GB,95,online
my code to generate the html report.
$a1=gc $TPATH\agg.csv
$b1 =$a1
for ($i1 = 0;$i1 -lt $a1.count;$i1++)
{
$c1=$b1[$i1].split(",",10)
$c = $c1[2].Length
$c = $c - 1
$d1=$c1[0] + "," + $c1[3] + "," + $c1[1] + "," + $c1[5] + "," + $c1[2].remove($c) + "," + $c1[4]
$d1 | Out-File ("$TPATH\aggregate.csv") -Encoding UTF8 -append
$bodyB = (import-csv $TPATH\aggregate.csv) |Select Aggregate,Size,Available,Used,Utilized,State -OutVariable ag | ConvertTo-Html -Fragment |
foreach { if($ag.Utilized -gt 90) {$_ -replace "<tr>", "<tr bgcolor=#FE0808>"}else{$_ -replace "<tr>", "<tr bgcolor=#E5FCA1>"}} |Out-String
}
convertTo-HTML -Body "<br> $bodyB" |out-file $PATH\Netapp.html
my final output look like this
how can i get the output to be like this, i want to have the colour change to Red if the threshold is above 90% in the Utilized column and Green if the threshold is below 90%.
Thank you in advance for those whom willing to help, really appreciate your help in this.
Thank You.
Replace -OutVariable ag with -PipelineVariable ag, so that $ag in your foreach (ForEach-Object) script block refers only to the single input object at hand.
The purpose of the common -PipelineVariable (-pv) parameter is to store the current object being output in a self-chosen variable, so that it can be accesses in a script block later in the same pipeline.
By contrast, the purpose of the common -OutVariable (-ov) parameter is to collect all output from a command in a self-chosen variable, for later processing in a different, subsequent statement, not in the same pipeline.
If you do access such a variable in a script block in the same pipeline, you'll see the output objects accumulated so far (which explains the behavior you saw - see below).
As for what you tried:
Due to the mistaken use of -OutVariable ag, your script block saw the Import-Csv output objects collected so far in variable $ag rather than the output object at hand.
Thus, starting with the second output object, $ag was in effect an array of objects, and accessing its . Utilized property resulted in member-access enumeration, and therefore returning an array of utilization values.
Since -gt, like all PowerShell comparison operators, acts as a filter when its LHS is an array, $ag.Utilized -gt 90 started to generate a return value (the subarray of matching values) as soon as at least one element in the array of values satisfied this condition.
In a Boolean context such as an if-statement conditional, a non-empty array is interpreted as $true.
Therefore, in effect ($ag.Utilized -gt 90) returned $true as soon as the first value above 90 was part of the return array, and invariably remained true.

Powershell variable from HTML table one value

I'm beginner in power shell ( HTML ) and I need some help. I want to get a variable from the McAfee website. In this page there is a table where you can download the latest .dat files.
I only need the version number - now it's 8963 - from the first table ( Download V2 Virus Definition Updates (DATs) ) and this result needs to be saved in a variable because I want to compare it with other variable from another script.
Now I'm able to query all of the tables with all of the data:
$r = Invoke-WebRequest -Uri https://www.mcafee.com/enterprise/en-us/downloads/security-updates.html
$r.parsedhtml.getelementsbytagname("TR") |
% { ( $_.children | ?{ $_.tagName -eq "td"} | % innerText ) }
Write-Host
Unfortunately it's too much information for me, because list all of the data which is in a table.
The retrived data in pic.: data
Thanks for the help.
Well this isn't the cleanest method, but if you expect the first entry to allwayse be the one supplying the Filename of the needed .exe you could use this:
($r.parsedhtml.getelementsbytagname("TR") |% { ( $_.children | ?{ $_.tagName -eq "td"} | % innerText ) } | Select-Object -First 1).Split('xdat')[0]
This basically takes the first String of your query, splits the string on the position of xdat and than takes everything before xdat.
The Output then is 8963.
If you want to do it the more correct way, you should look out for the correct html-attribute to filter for.

Get PowerShell function to write a table to the screen during a run but not append as a return

Here is a short example (my actual code requires me to output many more tables during the function run, and get a single returned output from it):
Function Select-RowFromCSV ($CSV)
{
$CSV
return $CSV[(read-host "select row # from CSV")]
}
Instead of outputting $CSV within the function it gets appended to the return and is getting into the variable that the function inserted to.
PS C:\Windows\system32> $ROW = Select-RowFromCSV -CSV (Import-Csv "C:\scripts\csv.csv")
select row # from CSV: 0
PS C:\Windows\system32> $ROW
Name Phone
Dan 111111
Dave 5555555
Oliver 666666
Dan 111111
PS C:\Windows\system32>
I tried multiple ways to try and print it to the screen, however unlike write-host that do work as expected for strings, none of the other one i tried works for non strings objects (FT, write-output, echo).
If you want to output something to the console without affecting the output of the Function, Write-Host is probably the simplest solution (but is considered harmful). Your code needs to be as follows:
Function Select-RowFromCSV ($CSV)
{
Write-Host ($CSV | Format-Table | Out-String)
Return $CSV[(read-host "select row # from CSV")]
}
$ROW = Select-RowFromCSV -CSV (Import-Csv "raid.csv")
As you observed, because your object isn't a string you need to Format it as you'd like and then convert it to a String (| Format-Table | Out-String).
However, you might want to consider using Write-Verbose which will write output to the Verbose stream instead, only when the -Verbose switch is used. To use this you need to add [cmdletbinding()] and a Param() block to your function, like this:
Function Select-RowFromCSV
{
[cmdletbinding()]
Param($CSV)
Write-Verbose ($CSV | Format-Table | Out-String)
Return $CSV[(read-host "select row # from CSV")-1]
}
Then execute your function with the -Verbose switch to see the extra output:
$ROW = Select-RowFromCSV -CSV (Import-Csv "raid.csv") -Verbose
Further Explanation:
In your original function you were outputting all of $CSV because it appeared on a line by itself, then also returning a row of it. The Return keyword is a little misleading in PowerShell, it doesn't define what is only returned, it just triggers the Function to immediately end and anything that has been output will go in to the output stream.
You should also note that the row number your user enters needs to start from 0, because Array indexes start from 0.
If you wanted the first row to be 1 rather than 0, you might want to do this:
$CSV[(read-host "select row # from CSV")-1]
You could also drop the Return keyword entirely as it's not necessary. If you want to be more explicit, I personally favour Write-Output.
Just use the following method:
$counter = 0
$table = #()
XXX | foreach-object {
do something;
$table +=[pscustomobject]# {
XXXX
}
$table[$counter]
$counter++
}

Accessing data retuned by PowerShell Import-CSV

I am trying to write a PowerShell script that uses a CSV file as input that will turn off the clutter feature in Office 365. The CSV file has only 1 column and that has the 2 target email addresses that I am using for testing. When I run this script with a read-host line and enter a valid email address it works with no errors. When I use the CSV file errors follow.
Import-Module MSOnline
$LiveCred = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/PowerShell -Credential $LiveCred -Authentication Basic -AllowRedirection
Import-PSSession -allowclobber $Session
Connect-MsolService -Credential $LiveCred
cd c:\scripts
Write-Host "This tool removes the Clutter feature from O365 "
$Clutter = Import-Csv .\Clutteroff.csv
foreach ($user in $Clutter){
Set-Clutter -Identity $User -Enable $false
}
When I run this I get the following error :
Cannot process argument transformation on parameter 'Identity'. Cannot convert value "#{UserID=xxxxx#myCompany.com}" to type "Microsoft.Exchange.Configuration.Tasks.MailboxIdParameter". Error: "Cannot
convert hashtable to an object of the following type: Microsoft.Exchange.Configuration.Tasks.MailboxIdParameter. Hashtable-to-Object conversion is not supported in restricted language mode or a Data
section."
+ CategoryInfo : InvalidData: (:) [Set-Clutter], ParameterBindin...mationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,Set-Clutter
+ PSComputerName : ps.outlook.com
Any help would be appreciated and explanations will get extra credit :)
CSV file = User, XXXX#MyCompany.com, YYYY#MyCompany.com
Email addresses are valid.
Putting all of the items in one line like that is not going to work well with Import-CSV. Import-CSV is suited to a table structure (columns and rows), whereas you are just using a comma-separated list (one row, with an unknown number of columns). If in fact you do have the items on different lines, then please correct the question and I'll change the answer.
To work with the data from a file formatted like that, I would just split it into an ArrayList, and remove the first item because it is "User" and not not an email address:
[System.Collections.ArrayList]$Clutter = (get-content .\Clutteroff.csv).split(",")
$Clutter.RemoveAt(0)
Then you can iterate through the array:
foreach ($user in $Clutter){
$address = $user.trim()
{Set-Clutter -Identity $address -Enable $false}
}
For the extra credit, $user in your script was returning a row of key/value pairs to represent columns (keys) and the data in the columns (values). Your error message shows #{UserID=xxxxx#myCompany.com}, so to return just the email address you would use $user.UserID to return the value for UserID.
i GOT THIS WORKING TO PULL FROM CSV SO ONLY THOSE USERS ARE MODIFIED!! SORRY FOR THE CAPS BUT I AM A TOTAL NOOB AND I COULDNT BELIEVE I GOT THIS TO WORK!!! I am beyond STOKED!! :)
the csv requires no headers, just the email address of the users you want to modify in one column
$Clutter = (Get-Content "pathofyourcsv.csv")
foreach ($User in $Clutter) {
$address = $User
Get-Mailbox -Identity $User | Set-Clutter -Enable $false}

Import only select data from json to powershell

I want to Import selected data from Json url and so I can convert it to XML.
I am using following code to import.
(Invoke-RestMethod -URI "http://www.broadbandmap.gov/broadbandmap/broadband/dec2013/wireline?latitude=29.488412&longitude=-98.550208&format=json").Results.wirelineServices.providerName | Select-Object | Format-Table –AutoSize
so I am using .Results.wirelineServices.providerName to pull selected columns from one branch/table.
how can I pull data from .Results.broadbandSource.stateFips also at same time?
Thanks bunch.
Json code screenshot.
follow up question
I think that you should separate out your steps a bit:
$r = Invoke-RestMethod -URI "http://www.broadbandmap.gov/broadbandmap/broadband/dec2013/wireline?latitude=29.488412&longitude=-98.550208&format=json"
$providers = $r.Results.wirelineServices.providerName
$stateFips = $r.Results.broadbandSource.stateFips
Note that in your example, your call to Select-Object is redundant (you're not selecting anything, so it's not changing the input object).
Also, a very important point about Format-Table (and any Format- cmdlet) is that those are for display only so they should always be the last thing you do, if in fact they're needed at all.
The code I've given gives you the information in objects, which you can then work with, filter or, display as needed. I'm not sure how you wanted to use/display it, but since there are multiple providers and only one stateFips value, I might assume that you would apply the Fips value to each provider. Here's an example of that which uses the $stateFips variable we created:
$r.Results.wirelineServices | Select-Object providerName,#{Name='stateFips' ; Expression={ $stateFips }}
And here's an example that uses only the original result $r:
$r.Results.wirelineServices | Select-Object providerName,#{Name='stateFips' ; Expression={ $r.Results.broadbandSource.stateFips }}
The Select-Object computed column syntax
Note that the second column definition looks a bit wonky. It's actually a hashtable that allows you to specify the name of the column, and an expression (a complete code block) whose return value will be the value of the column. It could be spread over multiple lines like this:
$r.Results.wirelineServices | Select-Object providerName,#{
Name = 'stateFips'
Expression = {
$r.Results.broadbandSource.stateFips
}
}
Or you could even create the hashtable as a variable first:
$computed = #{
Name = 'stateFips'
Expression = {
$r.Results.broadbandSource.stateFips
}
}
$r.Results.wirelineServices | Select-Object providerName,$computed
XML?
#Stephen Connolly's answer reminded me that you wanted to make XML out of this. Let's take the above code and assign it to a variable:
$computed = #{
Name = 'stateFips'
Expression = {
$r.Results.broadbandSource.stateFips
}
}
$data = $r.Results.wirelineServices | Select-Object providerName,$computed
Because $data is still an object and wasn't sent through a Format- command, we can still use it!
$xml = $data | ConvertTo-Xml -NoTypeInformation
As his comment also suggested though, we don't know how you wanted the resultant XML to be formatted.
So here's another approach:
Forget the JSON
$r = Invoke-RestMethod -URI "http://www.broadbandmap.gov/broadbandmap/broadband/dec2013/wireline?latitude=29.488412&longitude=-98.550208&format=xml"
Now $r contains XML already. You can filter it out and modify it using XPATH. I won't get into that at the moment unless you think that way would work better for you.
Hope this helps, let me know if I've misunderstood what you're trying to do here.
If you want a composite object try something like
$results = (Invoke-RestMethod -URI "http://www.broadbandmap.gov/broadbandmap/broadband/dec2013/wireline?latitude=29.488412&longitude=-98.550208&format=json")
$obj = $results.Results.wirelineServices
$obj | add-member -type noteproperty -Name StateFips -Value $($results.Results.broadbandSource.stateFips) -PassThru
$obj | convertto-xml -as string