I have a question around processing some data which I am pulling from VMWARE vROPS 6.X.
Basically I wrote a script to pull metric data from the suite API. I almost have the format I want but i need to split the Metric and Timestamp column one step further.
Basically I built a foreach loop and then nested another inside but I was not getting the metrics and timestamps in the correct sequence (so I removed it from the code below).
Current output:
"resourceId","Timestamp","METRIC","value"
"ef951a38-3063-477d-af32-baa6d2744357","1466085599999 1466171999999","cpu:1|costop_summation","4.6296298710836307E-4 0.0 4.5836298710836307E-4 0.0"
"ef951a38-3063-477d-af32-baa6d2744357","1466085599999 1466171999999","mem|usage_average","12.678446789582571 15.390000343322754"
Desired output:
"resourceId","Timestamp","METRIC","value"
"ef951a38-3063-477d-af32-baa6d2744357","1466085599999","cpu:1|costop_summation","4.6296298710836307E-4 0.0"
"ef951a38-3063-477d-af32-baa6d2744357","1466171999999","cpu:1|costop_summation","4.5836298710836307E-4 0.0"
"ef951a38-3063-477d-af32-baa6d2744357","1466085599999","mem|usage_average","12.678446789582571"
"ef951a38-3063-477d-af32-baa6d2744357","1466171999999","mem|usage_average","15.390000343322754"
My Code:
#Call vROPS SUITE-API with Invoke-Rest
#Take all certs.
Add-Type #"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"#
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
#date
[DateTime]$StartDate = (Get-date).adddays(-5)
[DateTime]$EndDate = (Get-date)
$StartDateEpoc = Get-Date -Date $StartDate -UFormat %s
$EndDateEpoc = Get-Date -Date $EndDate -UFormat %s
#Variables
$username = "admin"
$password = "password"
$secPw = ConvertTo-SecureString $password -AsPlainText -Force
$cred = New-Object PSCredential -ArgumentList $username,$secPw
$ContentType = "application/xml;charset=utf-8"
$header = New-Object "System.Collections.Generic.Dictionary[[String,[String]]"
$header.Add("Accept", 'application/xml')
#intervalType=
$SECONDS = 'SECONDS'
$MINUTES = 'MINUTES'
$HOURS = 'HOURS'
$DAYS = 'DAYS'
#rollUpType=
$AVG = 'AVG'
$MAX = 'MAX'
$SUM = 'SUM'
$MIN = 'MIN'
$COUNT = 'COUNT'
Invoke-RestMethod -Method GET -uri "https://192.168.0.125/suite-api/api/resources/stats?resourceId=ef951a38-3063-477d-af32-baa6d2744357&resourceId=1ef459e5-789e-446b-9852-3dc92c43e74a&statKey=cpu|usage_average&rollUpType=$AVG&intervalType=$DAYS" -Credential $cred -ContentType $ContentType -Headers $header -OutFile d:\quickcheck.xml
[xml]$Data = Get-Content 'D:\quickcheck.XML'
$report = #()
$resources = $Data.'stats-of-resources'
$UUIDS = $Resource.'resourceId'
foreach ($Resource in $Resources.'stats-of-resource') {
foreach ($node in $Resource.'stat-list'.stat)
{
#Collection Date, not run time
$MetricName = $node.statKey.Key
$Values = #($node.data -replace '( \d\.\d) ',"`$1`n" -split "`n")
$Timestamps = #($node.timestamps -split ' ')
for ($i=0; $i -lt $Values.Count -and $i -lt $Timestamps.Count; $i++) {
$report += New-Object PSObject -Property #{
METRIC = $MetricName
resourceId = $Resource.'resourceId'
Timestamp = $Timestamps[$i]
value = $Values[$i]
}
}
}
$report | Export-Csv D:\reprop.csv -NoTypeInformation
Split $Timestamps at spaces and $Values at every second space, and create an object for each resulting pair of fragments, e.g. like this:
$Values = #($Values -split ' ')
$Timestamps = #($Timestamps -split ' ')
for ($i=0; $i -lt $Values.Count -and $i -lt $Timestamps.Count; $i++) {
$report += New-Object PSObject -Property #{
METRIC = $MetricName
resourceId = $Resource.'resourceId'
Timestamp = $Timestamps[$i]
value = $Values[$i]
}
}
Related
I am trying to read the data from CSV file which has 2200000 records using PowerShell and storing each record in JSON file as NDJSON format.
Total document in JSON file is 2200000 (200 MB)
Sample JSON Data
{"index":{}}
{"ip-address":"1.5.0.1","is-vpn":"true","#timestamp":"2022-12-01T18:59:48.8325021+05:30"}
{"index":{}}
{"ip-address":"243.11.0.1","is-vpn":"true","#timestamp":"2022-12-01T18:59:48.8853225+05:30"}
{"index":{}}
{"ip-address":"253.11.0.1","is-vpn":"true","#timestamp":"2022-12-01T18:59:48.8853225+05:30"}
{"index":{}}
{"ip-address":"39.24.0.1","is-vpn":"true","#timestamp":"2022-12-01T18:59:48.8853225+05:30"}
{"index":{}}
{"ip-address":"163.24.0.1","is-vpn":"true","#timestamp":"2022-12-01T18:59:48.8853225+05:30"}
Code
function Get-IPDataPath
{
$dbFilePath = Get-ChildItem -Path $rootDir -Filter "IP2*.CSV" | ForEach-Object FullName | Select-Object -First 1
Write-Host "file path - $dbFilePath"
$dbFilePath # implicit output
}
function Convert-NumberToIP
{
param(
[Parameter(Mandatory=$true)][string]$number
)
[Int64] $numberInt = 0
if( [Int64]::TryParse( $number, [ref] $numberInt ) ) {
if( ($numberInt -ge 0) -and ($numberInt -le 0xFFFFFFFFl) ) {
# Convert to IP address like '192.168.23.42'
([IPAddress] $numberInt).ToString()
}
}
# In case TryParse() returns $false or the number is out of range for an IPv4 address,
# the output of this function will be empty, which converts to $false in a boolean context.
}
function Insert-Document
{
param(
[Parameter(Mandatory=$true)][string]$indexName,
[Parameter(Mandatory=$true)][object]$filePath
)
$url = "https://esdev2:9200/naveen/_doc/_bulk"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($cred))
$headers = #{
"Authorization" = "Basic $encodedCreds"
"Transfer-Encoding" = "chunked"
"Content-Type" = "application/x-ndjson"
}
$content = Get-Content -Path $filepath
$response = Invoke-WebRequest -Uri $url -Method POST -Body $content -Headers $Headers -ContentType 'application/x-ndjson'
if($response.StatusCode -eq 201)
{
Write-Host "Documents added successfully"
}
else
{
Write-Host $response
Write-Host "$($response.content)"
throw "Trouble adding document: $_"
}
}
$dbFilePath = Get-IPDataPath
$outputFile = Join-Path -Path $rootDir -ChildPath "output.json"
Write-Host "Converting CSV file $dbFilePath to $outputFile"
$object = [PSCustomObject]#{
'ip-address' = ''
'is-vpn' = 'true'
'#timestamp' = ''
}
# Enclose foreach loop in a script block to be able to pipe its output to Set-Content
$count = 0
& {
foreach( $item in [Linq.Enumerable]::Skip( [IO.File]::ReadLines( $dbFilePath ), 1 ) )
{
$row = $item -split ','
$ipNumber = $row[0].trim('"')
if( $ip = Convert-NumberToIP -number $ipNumber )
{
$index = [PSCustomObject]#{"index" = [PSCustomObject]#{}} | ConvertTo-Json -Depth 10 -Compress
$object.'ip-address' = $ip
$object.'#timestamp' = (Get-Date).ToString('o')
#$index | ConvertTo-Json -Depth 100 -Compress
$document = $object | ConvertTo-Json -Depth 100 -Compress
if($count -lt 25)
{
Add-Content -Path $outputFile $index
Add-Content -Path $outputFile $document
$count++
}
else
{
$count = 0
BulkInsert-Document -indexName $indexName -filePath $outputFile
Clear-Content -Path $outputFile
}
}
}
} | Set-Content -Path $outputFile
Write-Host "Inserting document in elastic"
Insert-Document -indexName $indexName -filePath $outputFile
But I am getting Error: {"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"The bulk
request must be terminated by a newline
[\n]"}],"type":"illegal_argument_exception","reason":"The bulk request must be
terminated by a newline [\n]"},"status":400}
Though I have new line at the end of JSON file, Could you please help how to insert all the document.
I have a JSON-File where I want to replace several values with a placeholder.
So i made a .csv with the parameters to replace. line[0] (if existing) is the path in the file, line[1] the element, line[2] the placeholder.
journeys.legs.origin.properties.downloads;url;placeholder;
;url;placeholder;
download;url;placeholder;
;psFileName;placeholder;
;serverTime;placeholder;
;calcTime;placeholder;
now I defined the following functions, to get the file, read the csv and replace the stuff.
$storage = "D:\Service\test.json"
$parameters2replace = "D:\Service\parameters2replace.csv"
function Get-JSONProperty([object] $InputObject, [string] $Property) {
$path = $Property -split '\.'
$obj = $InputObject
$path | %{ $obj = $obj.$_ }
$obj
}
function setParameter(){
foreach ($parameter in Get-Content $parameters2replace){
$line=$parameter.split(";")
$path = $line[0]
$elementName = $line[1]
$newValue = $line[2]
replaceElement $path $elementName $newValue
}
}
function replaceElement($path, $elementName, $newValue){
ForEach($JSONPath in Get-JSONProperty $JSON $path){
if (!$line[0]){
if($JSON.$elementName){
$JSON.$elementName = $newValue
}
}
else{
if($JSON.$path.$elementName){
echo $JSON.$path.$elementName
$JSON.$path.$elementName = $newValue
}
}
}
$JSON | ConvertTo-Json -depth 32| set-content $storage
}
$JSON = Get-Content $storage -raw | ConvertFrom-Json
setParameter
My problem now is, that the following if-argument won't work with the $path variable. If i put it in hardcoded it works just fine.
if($JSON.$path.$elementName)
I hope i could make everything clear, this was my first post.
$JSON.$path makes PowerShell look for a property with the literal name 'journeys.legs.origin.properties.downloads'. You need to split the string into it's individual parts and iterate (or recurse) through the path:
function Get-JSONPropertyValue {
param(
$JSON,
[string]$Path,
[string]$Name
)
$object = $JSON
foreach($propName in $Path.Split('.')){
$object = $object.$propName
}
return $object.$Name
}
function Set-JSONPropertyValue
{
param(
$JSON,
[string]$Path,
[string]$Name,
$Value
)
$object = $JSON
foreach($propName in $Path.Split('.')){
$object = $object.$propName
}
$object.$Name = $Value
}
Now you can do:
if($value = Get-JSONPropertyValue $JSON -Path $path -Name $elementName){
echo "Old value: $value"
Set-JSONPropertyValue $JSON -Path $path -Name $elementName -Value $newValue
}
I have a dictionary like this:
function HashHandlerSHA256
{
param($Path, $Checksum)
$csp = new-object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider
$ComputedHash = $csp.ComputeHash([System.IO.File]::ReadAllBytes($Path))
$ComputedHash = [System.BitConverter]::ToString($ComputedHash).Replace("-", "").ToLower()
$result = $ComputedHash.CompareTo($Checksum)
return $result -eq 0
}
$HashHandler = #{"SHA256" = HashHandlerSHA256}
containing validation algorithms and the functions to be called for validation. The functions should all have the same parameter and return type.
Now when I have:
$Checksums = #{"SHA256" = "..."}
I'd like to call the correct function depending on which algorithms and values I have available. In this case I'd have a valid sha256 hash.
Now I want to do:
function Validate
{
param($Path, $Checksums)
foreach($Hash in $Checksums) {
$Type = $Hash.Name
$Value = $Hash.Value
if ($HashHandler.ContainsKey($Type)) {
$Handler = $HashHandler.Get_Item($Type)
if (-Not ($Handler -Path $Path -Checksum $Value)) {
return $FALSE
}
}
}
return $TRUE
}
The errormessage I get is:
At C:\Users\username\Desktop\hashtest.ps1:27 char:23
+ if (-Not ($Handler -Path $Path -Checksum $Value)) {
+ ~~~~~
Unexpected token '-Path' in expression or statement.
I am relatively new to PowerShell. I know how to call functions with parameters, but when stored in a variable, I didn't manage to solve this and when searching online for this, I didn't get the answers I needed.
Thanks for help.
if i understand you want Something like this
function HashHandlerSHA256
{
param($Path, $Checksum)
$csp = new-object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider
$ComputedHash = $csp.ComputeHash([System.IO.File]::ReadAllBytes($Path))
$ComputedHash = [System.BitConverter]::ToString($ComputedHash).Replace("-", "").ToLower()
$result = $ComputedHash.CompareTo($Checksum)
return $result -eq 0
}
function Validate
{
param($Path, $Checksums)
foreach($Hashkey in $Checksums.Keys)
{
$Value = $Checksums[$Hashkey]
if ($script:HashHandler.ContainsKey($Hashkey))
{
if (-Not (&$script:HashHandler[$Hashkey] -Path $Path -Checksum $Value)) { return $false}
}
}
return $TRUE
}
#add here your couples of algo/function
$script:HashHandler = #{"SHA256" = 'HashHandlerSHA256'}
#checksum to test
$Checksums=#{}
$Checksums["SHA256"]= 'd6a0a09fb1a7971b497674675d5b5621d865d6020e384137548de9c4ac6d4994'
$Checksums["MD5"]= 'xxxx'
#test list checksum and algo
Validate -Path "c:\temp\hello.csv" -Checksums $Checksums
an other solution
$file="C:\temp\exludevalue.txt"
$Checksums=#{}
$Checksums["SHA256"]= 'd6a0a09fb1a7971b497674675d5b5621d865d6020e384137548de9c4ac6d4994'
$Checksums["MD5k"]= '11A8D99F80F9B29FCF6A995D2F17B2E3'
$Checksums.Keys |
%{
if ($(gcm Get-FileHash).Parameters.Algorithm.Attributes.ValidValues -contains $_)
{
$algocalc=(Get-FileHash -path $file -Algorithm $_).Hash;
}
else
{
$algocalc='ALGO NOT FOUNDED'
}
new-object psobject -Property #{
Algo=$_
OldValue=$Checksums[$_]
CalculedValue=$algocalc
ResultComparison= $algocalc -eq $Checksums[$_]
}
}
GOAL: Create a GUI form populated with CSV data, allow the user to edit the data, then save the data in an array for further manipulation.
NOTE: Using PowerShell Studio to generate a form with data from the CSV
CURRENT CODE:
- Calling code ($path is passed from the calling form):
$rows = Import-Csv -Path $path
$table = ConvertTo-DataTable -InputObject $rows
Load-DataGridView -DataGridView $datagridviewResults -Item $table
ConvertTo-DataTable function:
function ConvertTo-DataTable {
[OutputType([System.Data.DataTable])]
param(
[ValidateNotNull()]
$InputObject,
[ValidateNotNull()]
[System.Data.DataTable]$Table,
[switch]$RetainColumns,
[switch]$FilterWMIProperties
)
if($Table -eq $null) {
$Table = New-Object System.Data.DataTable
}
if($InputObject-is [System.Data.DataTable]) {
$Table = $InputObject
} else {
if(-not $RetainColumns -or $Table.Columns.Count -eq 0) {
#Clear out the Table Contents
$Table.Clear()
if($InputObject -eq $null){ return } #Empty Data
$object = $null
#find the first non null value
foreach($item in $InputObject) {
if($item -ne $null) {
$object = $item
break
}
}
if($object -eq $null) { return } #All null then empty
#Get all the properties in order to create the columns
foreach ($prop in $object.PSObject.Get_Properties()) {
if(-not $FilterWMIProperties -or -not $prop.Name.StartsWith('__')) { #filter out WMI properties
#Get the type from the Definition string
$type = $null
if($prop.Value -ne $null) {
try{ $type = $prop.Value.GetType() } catch {}
}
if($type -ne $null) { # -and [System.Type]::GetTypeCode($type) -ne 'Object')
[void]$table.Columns.Add($prop.Name, $type)
} else { #Type info not found
[void]$table.Columns.Add($prop.Name)
}
}
}
if($object -is [System.Data.DataRow]) {
foreach($item in $InputObject) {
$Table.Rows.Add($item)
}
return #(,$Table)
}
} else {
$Table.Rows.Clear()
}
foreach($item in $InputObject) {
$row = $table.NewRow()
if($item) {
foreach ($prop in $item.PSObject.Get_Properties()) {
if($table.Columns.Contains($prop.Name)) {
$row.Item($prop.Name) = $prop.Value
}
}
}
[void]$table.Rows.Add($row)
}
}
return #(,$Table)
}
Load-DataGridView function:
function Load-DataGridView {
Param (
[ValidateNotNull()]
[Parameter(Mandatory=$true)]
[System.Windows.Forms.DataGridView]$DataGridView,
[ValidateNotNull()]
[Parameter(Mandatory=$true)]
$Item,
[Parameter(Mandatory=$false)]
[string]$DataMember
)
$DataGridView.SuspendLayout()
$DataGridView.DataMember = $DataMember
$DataGridView.EditMode = 'EditOnEnter'
if ($Item -is [System.ComponentModel.IListSource]`
-or $Item -is [System.ComponentModel.IBindingList]`
-or $Item -is [System.ComponentModel.IBindingListView]) {
$DataGridView.DataSource = $Item
} else {
$array = New-Object System.Collections.ArrayList
if ($Item -is [System.Collections.IList]) {
$array.AddRange($Item)
} else {
$array.Add($Item)
}
$DataGridView.DataSource = $array
}
$DataGridView.ResumeLayout()
}
ADDITIONAL INFORMATION: Code is working in that it generates the Grid View and populates it with CSV data. However, I cannot edit it and need help coding the ability to capture changes once it is edited.
Thanks in advance.
12/5 EDIT: Added "$DataGridView.EditMode = 'EditOnEnter'" to the function "Load-DataGridView" above. Nothing changed. Tried to invoke the "BeginEdit" Event in a new RowCellClick event, but that didn't work either. Still struggling with this one.
For anyone else you has struggled with this ....
set-strictmode -Version 2.0
function EditCSV($title, $Instructions, $csvPath, $x = 100, $y=100, $Width=600, $Height=400, $SaveChangesToFile=$true, $ReturnStatusOrArray='Status') {
#Windows Assemblies
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
#LoadCSV
#Variables MUST have script scope to allow form to see them
$script:Updated = $false
$script:CsvData = New-Object System.Collections.ArrayList
$script:CsvData.AddRange((import-csv $csvPath))
#Helper Functions
function paint($form, $ctrl, $TablIndex, $name, $Text, $x, $y, $Width, $Height){
try{$form.Controls.Add($ctrl) }catch{}
try{$ctrl.TabIndex = $TablIndex }catch{}
try{$ctrl.Text = $Text }catch{}
try{$ctrl.name = $name }catch{}
try{$ctrl.Location = System_Drawing_Point $x $y }catch{}
try{$ctrl.size = System_Drawing_Size $Width $Height }catch{}
try{$ctrl.DataBindings.DefaultDataSourceUpdateMode = 0 }catch{}
$ctrl
}
function System_Drawing_Point($x, $Y) {$_ = New-Object System.Drawing.Point; $_.x = $X; $_.Y = $Y; $_}
function System_Drawing_Size( $Width, $Height){$_ = New-Object System.Drawing.Size; $_.Width = $Width; $_.Height = $Height; $_}
#Paint Form
$form1 = paint $null (New-Object System.Windows.Forms.Form) $null 'form1' $Title $x $y $Width $Height
$form1.add_Load({
$dataGrid1.DataSource = $script:CsvData;
$form1.refresh()
})
$label1 = paint $form1 (New-Object System.Windows.Forms.Label) $null "label1" "$Instructions" 12 13 ($width-100) 23
$label1.Font = New-Object System.Drawing.Font("Microsoft Sans Serif",9.75,2,3,0)
$label1.ForeColor = [System.Drawing.Color]::FromArgb(255,0,102,204)
$buttonSave = paint $form1 (New-Object System.Windows.Forms.Button) 1 "button1" "Save" ($width-200) ($Height-75) 75 23
$buttonSave.UseVisualStyleBackColor = $True
$buttonSave.add_Click({
$script:Updated = $true
$Form1.Close()
})
$buttonClose = paint $form1 (New-Object System.Windows.Forms.Button) 2 'button2' 'Close' ($width-105) ($Height-75) 75 23
$buttonClose.UseVisualStyleBackColor = $True
$buttonClose.add_Click({
$Form1.Close()
})
$dataGrid1 = paint $form1 (New-Object System.Windows.Forms.DataGrid) 0 "dataGrid0" $Null 12 40 ($width-40) ($Height-125)
$dataGrid1.HeaderForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0)
#Show and Wait till complete
$form1.ShowDialog()| Out-Null
#Save CSV
if( $SaveChangesToFile -eq $true -and $script:Updated ){
$script:CsvData| export-csv -NoTypeInformation -path $csvPath
}
if( $ReturnStatusOrArray -eq 'Status'){
return $script:Updated
}else{
return $script:CsvData
}
}
## Unit Test
cls
function script:Indent-ConsoleOutput($output, $indent = ' '){
if(!($output -eq $null)){
if(!( $indent -is [string])){
$indent = ''.PadRight($indent)
}
$width = (Get-Host).UI.RawUI.BufferSize.Width - $indent.length
($output| out-string).trim().replace( "`r", "").split("`n").trimend()| %{
for($i=0; $i -le $_.length; $i+=$width){
if(($i+$width) -le $_.length){
"$indent"+$_.substring($i, $width)
}else{
"$indent"+$_.substring($i, $_.length - $i)
}
}
}
}
}
Write-Host '## Before '.PadRight(120, '#')
$filePath = 'C:\temp\Text.csv'
$d = (dir c: |select-object -property Directory, Mode, LastWriteTime, Length, Name)[0..5]
$d |export-csv -path $filePath -NoTypeInformation
Indent-ConsoleOutput (import-csv $filePath |format-table) 4
Write-Host '## Edit - Save to File '.PadRight(120, '#')
Indent-ConsoleOutput (EditCSV 'Example of PS Editable Grid' '[SAVE] - To Save Changes' $filePath ) 4
Write-Host '## After '.PadRight(120, '#')
Indent-ConsoleOutput (import-csv $filePath |format-table) 4
Write-Host '## Edit - Return Array '.PadRight(120, '#')
Indent-ConsoleOutput (EditCSV 'Example of PS Editable Grid' '[SAVE] - To Save Changes' $filePath -SaveChangesToFile $false -ReturnStatusOrArray 'Array'|format-table) 4
Indent-ConsoleOutput (import-csv $filePath |format-table) 4
Enjoy
I use ConvertTo-HTML to convert a list of objects into a table. The only problem is that I cannot define the order of the columns in that table. I want a specific property (hostname) for all of the objects to be the first column in the table. Is there any way to do this?
Example Code:
function Create-MyObject {
param(
$name
)
$object = New-Object -TypeName PSObject -Property #{
"Name" = $name
"Prop1" = Get-Property1 $name
"Prop2" = Get-Property2 $name
"Prop3" = Get-Property3 $name
}
return $object
}
$myarray = #()
foreach($value in $list)
{
$myarray += Create-MyObject -name $value
}
Add-Content -Value $(ConvertTo-HTML $myarray | Out-String) -Path "C:\Temp\output.html"
Here is one solution that modifies the function to create the object from an ordered hash table so that the order is maintained. This requires V3 or better.
function Create-MyObject {
param(
$name
)
$object = New-Object -TypeName PSObject -Property [ordered]#{
"Name" = $name
"Prop1" = Get-Property1 $name
"Prop2" = Get-Property2 $name
"Prop3" = Get-Property3 $name
}
return $object
}
$myarray = #()
foreach($value in $list)
{
$myarray += Create-MyObject -name $value
}
Add-Content -Value $(ConvertTo-HTML $myarray | Out-String) -Path "C:\Temp\output.html"
Heres's another solution using Select-Object in the main script to reorder the properties before it's converted. This will work on V2 or better:
$myarray = #()
foreach($value in $list)
{
$myarray += Create-MyObject -name $value
}
Add-Content -Value $(ConvertTo-HTML $myarray | Out-String) -Path "C:\Temp\output.html"
function Create-MyObject {
param(
$name
)
$object = New-Object -TypeName PSObject -Property #{
"Name" = $name
"Prop1" = Get-Property1 $name
"Prop2" = Get-Property2 $name
"Prop3" = Get-Property3 $name
}
return $object
}
$myarray = #()
foreach($value in $list)
{
$myarray += Create-MyObject -name $value
}
$myarray = $myarray | select Name,Prop1,Prop2,Prop3
Add-Content -Value $(ConvertTo-HTML $myarray | Out-String) -Path "C:\Temp\output.html"