PowerShell-How to get values from this JSON file - powershell

This is the partial data after removed the confidential information.
{
"WebhookName":"Azure-CustomAlert-Webhook",
"RequestBody":"{\"schemaId\":\"azureMonitorCommonAlertSchema\",\"data\":{\"essentials\":{\"alertId\":\"/subscriptions/XXXXXXXXX/providers/Microsoft.AlertsManagement/alerts/XXXXXXX\",\"alertRule\":\"Low Memory\",\"severity\":\"Sev3\",\"signalType\":\"Log\",\"monitorCondition\":\"Fired\",\"monitoringService\":\"Log Analytics\",\"alertTargetIDs\":[\"/subscriptions/XXXXX/resourcegroups/XXXX-RG/providers/microsoft.operationalinsights/workspaces/workspacename\"],\"configurationItems\":[\"USE2V5TMP9001\"],\"originAlertId\":\"XXXXX\",\"firedDateTime\":\"2022-03-09T17:49:41.4631455Z\",\"description\":\"Triggers an alert for a low memory condition\",\"essentialsVersion\":\"1.0\",\"alertContextVersion\":\"1.1\"},\"alertContext\":{\"SearchQuery\":\"Perf | where ( CounterName == \\\"% Used Memory\\\" or CounterName == \\\"% Committed Bytes In Use\\\" ) | where Computer contains (\\\"TMP\\\") | summarize AggregatedValue = avg(CounterValue) by Computer, bin(TimeGenerated, 5m)\",\"SearchIntervalStartTimeUtc\":\"2022-03-09T17:42:10Z\",\"SearchIntervalEndtimeUtc\":\"2022-03-09T17:47:10Z\",\"ResultCount\":1,\"SeverityDescription\":\"Informational\",\"WorkspaceId\":\"XXXXX\",\"SearchIntervalDurationMin\":\"5\",\"AffectedConfigurationItems\":[\"USE2V5TMP9001\"],\"AlertType\":\"Metric measurement\",\"IncludeSearchResults\":true,\"Dimensions\":[{\"Name\":\"Computer\",\"Value\":\"USE2V5TMP9001\"}],\"SearchIntervalInMinutes\":\"5\",\"SearchResults\":{\"tables\":[{\"name\":\"PrimaryResult\",\"columns\":[{\"name\":\"Computer\",\"type\":\"string\"},{\"name\":\"TimeGenerated\",\"type\":\"datetime\"},{\"name\":\"AggregatedValue\",\"type\":\"real\"}],\"rows\":[[\"USE2V5TMP9001\",\"2022-03-09T17:42:10Z\",38.267662048339851]]}],\"dataSources\":[{\"resourceId\":\"/subscriptions/XXXXX/resourcegroups/XXXX/providers/microsoft.operationalinsights/workspaces/XXXX\",\"region\":\"eastus2\",\"tables\":[\"Perf\"]}]},\"Threshold\":9,\"Operator\":\"Greater Than\",\"IncludedSearchResults\":\"True\"},\"customProperties\":null}}",
"RequestHeader":{
"Connection":"Keep-Alive",
"Expect":"100-continue",
"Host":"xxxx.webhook.eus2.azure-automation.net",
"User-Agent":"IcMBroadcaster/1.0",
"X-CorrelationContext":"RkkKACgAAAACAAAAEADvqM+sXFG+SYkp7Tcy2IZaAQAQAMflO8/GhoFLrHCgd8ILz2o=",
"x-ms-request-id":"8fdd10d2-4a36-43a5-8e65-4eb20f3b9865"
}
}
The above json i got it from the Azure Log Search alert and trying to customize it.
From the above json can i get the values if i refer the column section keys?.
Ex: If i mention computer then i should be able to get value USE2V5TMP9001 and AggregatedValue is 38.267662048339851.

Use the columns array to map the individual row values to the correct type and property name:
$json = #'
<json goes here>
'#
$data = $json |ConvertFrom-Json
$columnDefinitions = $data.columns
$rows = foreach($row in $data.rows){
# prepare dictionary to hold the individual column values
$properties = [ordered]#{}
for($i = 0; $i -lt $row.Length; $i++){
# extract value and column metadata
$value = $row[$i]
$name = $columnDefinitions[$i].name
$type = $columnDefinitions[$i].type
# make sure to translate any type names if necessary
# eg. translate `real` -> `decimal`
if($type -eq 'real'){ $type = 'decimal' }
# convert value to correct type and store in property dictionary
$properties[$name] = $value -as $type
}
# create new object based on the row values
[pscustomobject]$properties
}
$rows will now contain 1 or more objects with the expected values so you can now do:
$rows |ForEach-Object {
# this now resolves `USE2V5TMP9001`
$_.computer
}

Related

Powershell: get column number from CSV file header

I need to get the column number from an imported CSV based on a particular column name ($_."Status"). Once I have the correct column number, I can assign it to a variable and use it in a foreach loop to write text to the corresponding cells. $wsSource.cells.item($tr,49) = "Added by xyz)" Note that the column position often varies from file to file.
I already have the index/row number via $tr = $source.IndexOf($row) ...but struggling with the col number.
Thanks in advance,
Jason
Incomplete code from much larger PS script that writes two different excel files in the one loop:
$source = Import-Csv $csvFile
$i = 2
foreach($row in $source.where{$_.Contacted -like "*Invalid"})
{
$tr = ($source.IndexOf($row)+2)
$wsTemp.cells.item($i,4) = $timeStamp
$wsTemp.cells.item($i,10) = $row."Last Name"
$wsSource.cells.item($tr,49) = "Added by xyz)"
$wsSource.cells.item($tr,49).Interior.ColorIndex =19
$i++
}
}
elseif ...
You need to create a Hashtable to map the Excel column names with their index:
# create a hash with Excel column header names and their indices
$colMax = $wsSource.UsedRange.Columns.Count
$xlHeaders = #{}
for ($col = 1; $col -le $colMax; $col++) {
$name = $wsSource.Cells.Item(1, $col).Value() # assuming the first row has the headers
$xlHeaders[$name] = $col
}
Now you can match the column from the Csv with the column index in Excel like
if (!$xlHeaders.ContainsKey('Status')) {
Write-Warning "Excel sheet does not have a column named 'Status'"
}
else {
$xlColumn = $xlHeaders['Status']
$wsSource.Cells.Item($tr, $xlColumn) = "Added by xyz)"
$wsSource.Cells.Item($tr, $xlColumn).Interior.ColorIndex = 19
}

How to sort data from a csv by a unique Key identifier in powershell?

I have the following data in a csv:
KEY,VARNAME1,VARNAME2,VARNAME3
**1**,10,20,30
**1**,9,18,25
**1**,8,10,0
**2**,40,80,90
**2**,35,70,80
**2**,5,0,0
I want to read the data from this csv and write a new CSV that looks like this:
**1**,**2**
10,40
9,35
8,5
20,80
18,70
10,0
30,90
25,80
0,0
In other words: The "KEY" for each row is the new header in the new csv. The values for the variables VARNAME1, VARNAME2 etc. are put together in one column for each "KEY".
The data is very very big. It is up to 50 GB (one file). That's why I am not writing a Macro or anything else I am used.
I am struggling a little bit with the sytax of powershell.
I am reading the csv without problems. But then I am not sure how to write the right FOR-Loop.
Or do I need to set up a Hash-Table, also having in mind the huge amount of data?
I almost hesitate to post this because it definitely won't scale to a 50gb file, but it gives you a basic algorithm for looping through your data to get the desired output that you can use as a starting point...
$csv = #"
KEY,VARNAME1,VARNAME2,VARNAME3
1,10,20,30
1,9,18,25
1,8,10,0
2,40,80,90
2,35,70,80
2,5,0,0
"#
$data = $csv | ConvertFrom-Csv;
# get the property names from the data
# i.e. VARNAME1, VARNAME2, VARNAME3
$propertyNames = $data[0].psobject.Properties.Name | where-object { $_ -ne "KEY" }
# group by key so we can pair up the Nth row from each key group together.
# note - this won't scale very well :-(
$groups = $data | group-object "KEY"
# basically, transpose the data so that the VARNAME1 values from the
# first "KEY=1" record and the first "KEY=2" record are paired together,
# then the VARNAME1 values from the second "KEY=1" record and the second
# "KEY=2", etc, until we've processed all the records for VARNAME1, then
# start again with the VARNAME2 property, then VARNAME3. assumes there's
# the same number of "KEY=1" and "KEY=2" records
$rows = #()
foreach( $propertyName in $propertyNames )
{
for( $i = 0; $i -lt $groups[0].Count; $i++ )
{
$row = [ordered] #{};
foreach( $group in $groups )
{
$row.Add($group.Name, $group.Group[$i].$propertyName)
}
# += is exponentially expensive for very big dataset
$rows += new-object PSCustomObject -Property $row
}
}
$csv = $rows | ConvertTo-Csv
# note - strings instead of ints
$csv
# "1","2"
# "10","40"
# "9","35"
# "8","5"
# "20","80"
# "18","70"
# "10","0"
# "30","90"
# "25","80"
# "0","0"

Casting Object to String Array Powershell

I want to create an array of strings instead of a variable object so that I can use the "contains" keyword on each index of the array.
$myArray = Get-ADDomain
The above creates an object, which is not what I want. I also tried
[string[]] $myArray = Get-ADDomain
But after that, $myArray only contains one string and it is the first non-empty property of Get-ADDomain, in my case "ComputersContainer". What should I do to receive an array of strings where each string is a different property, such as
$myArray[0] = "AllowedDNSSuffixes = {}"
PowerShell will always return objects by design of course, and specifying that [string[]], does not really change that.
For what you are trying to use, you have to force the array creation. The below is just one way, but I am sure others will have more elegant ways of doing this as well. Though I am curious why one would want to do this, this way. But, hey, that's just me.
# Create an empty array
$DomainData = #()
# Get all the data points for the utilized cmdlet, split on a common delimiter for the array
[string[]]$DomainData = (Get-ADDomain | Select *) -split ';'
# Display the array count
$DomainData.Count
34
# validate getting a value from the array by using an index number
$Item = $DomainData[17]
NetBIOSName=CONTOSO
[array]::IndexOf($DomainData, $Item)
17
# Use that element number to validate the use of the contains comparison operator
0..($DomainData.Count - 1) | %{ If($DomainData[$_] -contains $item){"Index key is $_ contains a value of $Item"} }
Index key is 17 contains a value of NetBIOSName=CONTOSO
# Use the previous with a partial string for a comparison, -contains cannot be used, like or match has to be used
# From the documentation:
# -Contains
# Description: Containment operator. Tells whether a collection of reference values includes a single test value.
$Item = '*domain*'
0..($DomainData.Count - 1) | %{ If($DomainData[$_] -like $item){"Index key is $_ like a value of $Item"} }
Index key is 1 like a value of *domain*
Index key is 6 like a value of *domain*
Index key is 7 like a value of *domain*
Index key is 8 like a value of *domain*
Index key is 18 like a value of *domain*
Index key is 20 like a value of *domain*
You cannot cast a PSObject directly to a string array like that.
However, this can be accomplished rather easily.
To get an array of string from the object
$myArray = Get-ADDomain
# You can use a standard array #() but these tends to be slower for bigger amount of data
$outArray = New-Object -TypeName System.Collections.Generic.List[String]
#To add just the value
$myArray.psobject.properties | Foreach { $outArray.Add($_.Value) }
# To add Name = {Value} instead
$myArray.psobject.properties | Foreach { $outArray.Add("$($_.Name) = {$($_.Value)}") }
Using an hasthable instead:
$myArray = Get-ADDomain
$hashtable = #{}
$myArray.psobject.properties | Foreach { $hashtable[$_.Name] = $_.Value }
# If you need to do something with the key
Foreach ($key in $hashtable.Keys) {
$Value = $hashtable[$key]
if ($value -like '*prod*') {
Write-Host $key
}
}

Convert array output to table using PowerShell

I'm exporting certificates from Windows CA which is an array which has data in below format. I want to convert into table. Any idea?
From:
$a=#(
'a=all
b=call
',
'a=all
b=ll'
)
Current output:
a=all
b=call
a=all
b=ll
Desired output:
a b
all call
all ll
What you have is an array of multiline strings. For the desired output you need an array of objects:
$a = #(
[PSCustomObject]#{
'a' = 'all'
'b' = 'call'
},
[PSCustomObject]#{
'a' = 'all'
'b' = 'll'
}
)
If your input data is actually a list of multiline strings with lines of the form key=value you can transform those into custom objects like this:
$a = #(
'a=all
b=call',
'a=all
b=ll'
)
$a | ForEach-Object {
[PSCustomObject]($_ | ConvertFrom-StringData)
}
ConvertFrom-StringData converts a string with one or more lines of key=value pairs into a hashtable, which can then be cast into a custom object.
You can also Create an empty Array and then just add values using [PsCustomObject]
$Table = #()
$Table += [Pscustomobject]#{a = "all"; b = "call"}
$Table += [Pscustomobject]#{a = "all"; b = "ll"}

Powershell array of arrays [duplicate]

This question already has answers here:
Powershell create array of arrays
(3 answers)
Closed 5 years ago.
This is building $ret into a long 1 dimensional array rather than an array of arrays. I need it to be an array that is populated with $subret objects. Thanks.
$ret = #()
foreach ($item in $items){
$subret = #()
$subRet = $item.Name , $item.Value
$ret += $subret
}
there might be other ways but arraylist normally works for me, in this case I would do:
$ret = New-Object System.Collections.ArrayList
and then
$ret.add($subret)
The suspected preexisting duplicate question is indeed a duplicate:
Given that + with an array as the LHS concatenates arrays, you must nest the RHS with the unary form of , (the array-construction operator) if it is an array that should be added as a single element:
# Sample input
$items = [pscustomobject] #{ Name = 'n1'; Value = 'v1'},
[pscustomobject] #{ Name = 'n2'; Value = 'v2'}
$ret = #() # create an empty *array*
foreach ($item in $items) {
$subret = $item.Name, $item.Value # use of "," implicitly creates an array
$ret += , $subret # unary "," creates a 1-item array
}
# Show result
$ret.Count; '---'; $ret[0]; '---'; $ret[1]
This yields:
2
---
n1
v1
---
n2
v2
The reason the use of [System.Collections.ArrayList] with its .Add() method worked too - a method that is generally preferable when building large arrays - is that .Add() only accepts a single object as the item to add, irrespective of whether that object is a scalar or an array:
# Sample input
$items = [pscustomobject] #{ Name = 'n1'; Value = 'v1'},
[pscustomobject] #{ Name = 'n2'; Value = 'v2'}
$ret = New-Object System.Collections.ArrayList # create an *array list*
foreach ($item in $items) {
$subret = $item.Name, $item.Value
# .Add() appends whatever object you pass it - even an array - as a *single* element.
# Note the need for $null = to suppress output of .Add()'s return value.
$null = $ret.Add($subret)
}
# Produce sample output
$ret.Count; '---'; $ret[0]; '---'; $ret[1]
The output is the same as above.
Edit
It is more convoluted to create an array of tuples than fill an array with PsObjects containing Name Value as the two properties.
Select the properties you want from $item then add them to the array
$item = $item | select Name, Value
$arr = #()
$arr += $item
You can reference the values in this array by doing this
foreach($obj in $arr)
{
$name = $obj.Name
$value = $obj.Value
# Do actions with the values
}