Create JSON with Keys from Powershell Array - powershell

I have this Powershell Array object with string values in it
[value1,value2,value3,value4,..etc]
I would like to convert it into a JSON object with a key called value that has the values in the array and makes it look like this
[
{ "value" : "value1" },
{ "value" : "value2" },
{ "value" : "value3" },
{ "value" : "value4" },
...
]
Is that possible in powershell? Keep in mind the array could be a length of 50 so it has to loop through the array
Thanks

You can do the following in PowerShell v3+:
# Starting Array $arr that you create
$arr = 'value1','value2','value3'
# Create an array of objects with property named value and value of each array value
# Feed created objects into the JSON converter
$arr | Foreach-Object {
[pscustomobject]#{value = $_}
} | ConvertTo-Json
You can do the following in PowerShell v2:
$json = New-Object -Type 'System.Text.Stringbuilder'
$null = $json.Append("[")
$arr | foreach-Object {
$line = " {{ ""value"" : ""{0}"" }}," -f $_
$null = $json.Append("`r`n$line")
}
$null = $json.Remove($json.Length-1,1)
$null = $json.Append("`r`n]")
$json.ToString()

Related

Create the "skeleton" of a custom PSObject from scratch

I'm trying to create a PSCustomObject that will later on be converted to JSON as the body of an API request.
This body basically has a structure to add / remove different information to a security management tool policy (filehash / filenames / certificates / IPs / file extensions / folders exceptions, on which technology etc.)
Here is an example of a final JSON with all the possibilities.
As a proof of concept, I'm focusing on the following part which is adding a filename & a filehash to the exception policy
{
"add": {
"applications": [
{
"processfile": {
"sha2": "6ddc5c11925ab348eb0d390ec5179c1d655eb4bf70779f7a4e28b7db485d20ea",
"name": "myfilename"
}
}
]
...
My goal is not to import this specific JSON as a custom Object, (I know this can be done through convertfrom-Json). It's to create some empty object, but with the correct structure matching the JSON format. This way, I would just have to populate and access information in my object like this :
PS C:\PSSymantecCloud> $obj.add.applications
processfile
-----------
#{sha2=6ddc5c11925ab348eb0d390ec5179c1d655eb4bf70779f7a4e28b7db485d20ea; name=myfilename}
PS C:\PSSymantecCloud> $obj.add.applications.processfile
sha2 name
---- ----
6ddc5c11925ab348eb0d390ec5179c1d655eb4bf70779f7a4e28b7db485d20ea myfilename
I've been trying to play around with Format-Custom CmdLet but without success.
Any suggestions on how to create such a custom PSObject ?
I'm fond of PowerShell classes so here is one way to do it with them. You can add new processfile with the AddProcessFile method that takes 2 arguments as in your Json:
class addjson {
[object] $add
addjson() {
$this.add = [pscustomobject]#{
applications = [System.Collections.Generic.List[object]]::new()
}
}
[void] AddProcessFile([string] $sha2, [string] $name) {
$this.add.applications.Add([pscustomobject]#{
processfile = [pscustomobject]#{
sha2 = $sha2
name = $name
}
})
}
}
$add = [addjson]::new()
$add.AddProcessFile('6ddc5c11925ab348eb0d390ec5179c1d655eb4bf70779f7a4e28b7db485d20ea', 'myFile')
$add.AddProcessFile('6ddc5c11925ab348eb0d390ec5179c1d655eb4bf70779f7a4e28b7db485d20ea', 'myFile2')
$add | ConvertTo-Json -Depth 4
Resulting Json would be:
{
"add": {
"applications": [
{
"processfile": {
"sha2": "6ddc5c11925ab348eb0d390ec5179c1d655eb4bf70779f7a4e28b7db485d20ea",
"name": "myFile"
}
},
{
"processfile": {
"sha2": "6ddc5c11925ab348eb0d390ec5179c1d655eb4bf70779f7a4e28b7db485d20ea",
"name": "myFile2"
}
}
]
}
}
Create an psObject shell, assign empty strings ““, or $false to the properties
$Object = New-Object PSObject -Property #{
Name = ““
SecondProperty= ““
yetAnotherProoerty= ““
}
Later u can assign your results like this:
$Object.name= $myResult
Or even add new member aka properties to the object like this:
Object | Add-Member -NotePropertyName Status -NotePropertyValue Done
You can also just take your sample text and convert it with convertto-json. e.g.
$HereString = #"
{
"add": {
"applications": [
{
"processfile": {
"sha2": "6ddc5c11925ab348eb0d390ec5179c1d655eb4bf70779f7a4e28b7db485d20ea",
"name": "myfilename"
}
}
]
}
}
"#
$PSObject = $HereString | ConvertFrom-Json
$PSObject | get-member

How to Extract the variable values from terraform variables.tf file using PowerShell commands

I have a variables.tf file which contains all the project variables and im trying to fetch a variable values using PowerShell.
variables.tf
variable "products" {
default = [
"Product-1",
"Product-2",
"Product-3",
"Product-4"
]
}
variable "product_unified_container" {
default = [
"cont-data",
"cont-data-2"
]
}
variable "location" {
default = "westeurope"
}
Using PowerShell i need to be able to fetch the variable values for any variable I want.
Example : the command should give me a array of all the products variables in variables.tf if it has multiple values.
write-host $product_list
Product-1
Product-2
Product-3
Product-4
if the variable has one value then it should give me that value like "location" variable.
write-host $deployed_location
westeurope
I was going through a similar problem so, I can share a way using which you can extract the values.
The problem is it is easy to extract and manipulate values in a json or other format but in tf files it is not the same. So, I have basically used a workaround where I have to set the given file in a structure that the values are filled in one single line
So, variables.tf will look
variable "products" {
default = ["Product-1", "Product-2", "Product-3", "Product-4"]
}
variable "product_unified_container" {
default = ["cont-data","cont-data-2"]
}
variable "location" {
default = "westeurope"
}
Next comes the PS code to extract the values of variables-
$paramsArray = #()
[system.array]$params = Select-String -Path "variables.tf" -Pattern "default =" -SimpleMatch
if (!([string]::IsNullOrEmpty($Params)))
{
[system.array]$paramsStrings = $params -split {$_ -eq "="}
foreach ($paramString in $paramsStrings)
{
if (($paramString -match "TF-Template") -or ($paramString -match "tf:"))
{
#Write-Output $paramString
}
else
{
If ($paramsArray -notcontains $paramString)
{
$paramsArray+=$paramString
}
}
}
}
write-host $paramsArray
The output generated is-
Since this is an array you can iterate and use it later in the script.
I was able to solve this by below approach hope this will help someone with similar requirement.
Following Command will fetch any variable values present in variables.tf file, in this case variable "products" and assign it to another array variable.
$product_list = (Get-Content "variables.tf" -Raw) | Foreach-Object {
$_ -replace '(?s)^.*products', '' `
-replace '(?s)variable.+' `
-replace '[\[\],"{}]+' `
-replace ("default =","") | Where { $_ -ne "" } | ForEach { $_.Replace(" ","") } | Out-String | ForEach-Object { $_.Trim() }
}
foreach ( $Each_Product in $product_list.Split("`n")) {
write-host "Each Product Name : "$Each_Product
}
Output :
Each Product Name : Product-1
Each Product Name : Product-2
Each Product Name : Product-3
Each Product Name : Product-4

Set Value of Nested Object Property by Name in PowerShell

I want to set value of nested object property using PowerShell. When you are trying to set the value of the first level properties, it's quiet simple:
$propertyName = "someProperty"
$obj.$propertyName = "someValue" # ← It works
For nested properties, it doesn't work:
$propertyName = "someProperty.someNestedProperty"
$obj.$propertyName = "someValue" # ← It doesn't work and raises an error.
How to set value of nested object property by name of property using PowerShell?
MCVE
For those who want to reproduce the problem, here is a simple example:
$Obj= ConvertFrom-Json '{ "A": "x", "B": {"C": "y"} }'
# Or simply create the object:
# $Obj= #{ A = "x"; B = #{C = "y"} }
$Key = "B.C"
$Value = "Some Value"
$Obj.$Key = $Value
Run the command and you will receive an error:
"The property 'B.C' cannot be found on this object. Verify that the
property exists and can be set."
Note: The code supports any level of nesting.
I created SetValue and GetValue functions to let you get and set a nested property of an object (including a json object) dynamically by name and they work perfectly!
They are recursive functions which resolve the complex property and get the nested property step by step by splitting the nested property name.
GetValue and SetValue of Nested properties by Name
# Functions
function GetValue($object, $key)
{
$p1,$p2 = $key.Split(".")
if($p2) { return GetValue -object $object.$p1 -key $p2 }
else { return $object.$p1 }
}
function SetValue($object, $key, $Value)
{
$p1,$p2 = $key.Split(".")
if($p2) { SetValue -object $object.$p1 -key $p2 -Value $Value }
else { $object.$p1 = $Value }
}
Example
In the following example, I set B.C dynamically using SetValue and get its value by name using the GetValue function:
# Example
$Obj = ConvertFrom-Json '{ "A": "x", "B": {"C": "y"} }'
# Or simply create the object:
# $Obj = #{ A = "x"; B = #{C = "y"} }
$Key = "B.C"
$Value = "Changed Dynamically!"
SetValue -object $Obj -key $Key -Value $Value
GetValue -object $Obj -key $Key
May I propose an upgrade to Reza's solution. With this solution, you can have many level of nested properties.
function GetValue($object, [string[]]$keys)
{
$propertyName = $keys[0]
if($keys.count.Equals(1)){
return $object.$propertyName
}
else {
return GetValue -object $object.$propertyName -key ($keys | Select-Object -Skip 1)
}
}
function SetValue($object, [string[]]$keys, $value)
{
$propertyName = $keys[0]
if($keys.count.Equals(1)) {
$object.$propertyName = $value
}
else {
SetValue -object $object.$propertyName -key ($keys | Select-Object -Skip 1) -value $value
}
}
Usage
$Obj = ConvertFrom-Json '{ "A": "x", "B": {"C": {"D" : "y"}} }'
SetValue $Obj -key "B.C.D".Split(".") -value "z"
GetValue $Obj -key "B.C.D".Split(".")
Your own solutions are effective, but do not support indexed access as part of the nested property-access path (e.g., B[1].C)
A simple alternative is to use Invoke-Expression (iex).
While it should generally be avoided, there are exceptional cases where it offers the simplest solution, and this is one of them:
Assuming you fully control or implicitly trust the property-access string:
$obj = ConvertFrom-Json '{ "A": "x", "B": [ {"C": "y"}, { "C": "z"} ] }'
$propPath = 'B[1].C'
# GET
Invoke-Expression "`$obj.$propPath" # -> 'z'
# SET
$value = 'Some Value'
Invoke-Expression "`$obj.$propPath = `$value"
If you don't trust the input, you can avoid unwanted injection of commands as follows:[1]
$safePropPath = $propPath -replace '`|\$', '`$&'
Invoke-Expression "`$obj.$safePropPath"
# ...
For a convenience function / ETS method that safely packages the functionality above, see this answer.
[1] The regex-based -replace operation ensures that any $ characters in the string are escaped as `$, to prevent them Invoke-Expression from treating them as variable references or subexpressions; similarly, preexisting ` instances are escaped by doubling them.

How to output multiple hash tables in Powershell

I have a hashtable of hashtables of key/value pairs (from a .ini file). It looks like this:
Name Value
---- -----
global {}
Variables {event_log, software_repo}
Copy Files {files.mssql.source, files.utils.source, files.utils.destination, fil...
How can I output all of the key/value pairs in one hash table, instead of doing this?
$ini.global; $ini.variables; $ini."Copy Files"
Given that $hh is you hashtable of hashtables you can use a loop :
foreach ($key in $hh.keys)
{
Write-Host $hh[$key]
}
You can easily write a function to recurse through an hash:
$hashOfHash = #{
"outerhash" = #{
"inner" = "value1"
"innerHash" = #{
"innermost" = "value2"
}
}
"outer" = "value3"
}
function Recurse-Hash($hash){
$hash.keys | %{
if($hash[$_] -is [HashTable]){ Recurse-Hash $hash[$_] }
else{
write-host "$_ : $($hash[$_])"
}
}
}
Recurse-Hash $hashOfHash
The above is just write-hosting the key values, but you get the idea.

How do I loop through a dataset in PowerShell?

I am trying to output values of each rows from a DataSet:
for ($i=0;$i -le $ds.Tables[1].Rows.Count;$i++)
{
Write-Host 'value is : ' + $i + ' ' + $ds.Tables[1].Rows[$i][0]
}
gives the output ...
value is : +0+ +System.Data.DataSet.Tables[1].Rows[0][0]
value is : +1+ +System.Data.DataSet.Tables[1].Rows[1][0]
value is : +2+ +System.Data.DataSet.Tables[1].Rows[2][0]
value is : +3+ +System.Data.DataSet.Tables[1].Rows[3][0]
value is : +4+ +System.Data.DataSet.Tables[1].Rows[4][0]
value is : +5+ +System.Data.DataSet.Tables[1].Rows[5][0]
value is : +6+ +System.Data.DataSet.Tables[1].Rows[6][0]
How do I get the actual value from the column?
The PowerShell string evaluation is calling ToString() on the DataSet. In order to evaluate any properties (or method calls), you have to force evaluation by enclosing the expression in $()
for($i=0;$i -lt $ds.Tables[1].Rows.Count;$i++)
{
write-host "value is : $i $($ds.Tables[1].Rows[$i][0])"
}
Additionally foreach allows you to iterate through a collection or array without needing to figure out the length.
Rewritten (and edited for compile) -
foreach ($Row in $ds.Tables[1].Rows)
{
write-host "value is : $($Row[0])"
}
Here's a practical example (build a dataset from your current location):
$ds = new-object System.Data.DataSet
$ds.Tables.Add("tblTest")
[void]$ds.Tables["tblTest"].Columns.Add("Name",[string])
[void]$ds.Tables["tblTest"].Columns.Add("Path",[string])
dir | foreach {
$dr = $ds.Tables["tblTest"].NewRow()
$dr["Name"] = $_.name
$dr["Path"] = $_.fullname
$ds.Tables["tblTest"].Rows.Add($dr)
}
$ds.Tables["tblTest"]
$ds.Tables["tblTest"] is an object that you can manipulate just like any other Powershell object:
$ds.Tables["tblTest"] | foreach {
write-host 'Name value is : $_.name
write-host 'Path value is : $_.path
}
The parser is having trouble concatenating your string. Try this:
write-host 'value is : '$i' '$($ds.Tables[1].Rows[$i][0])
Edit: Using double quotes might also be clearer since you can include the expressions within the quoted string:
write-host "value is : $i $($ds.Tables[1].Rows[$i][0])"