JSON Prettifier Using Azure Function w/ PowerShell and HTTP Trigger - json

Thought this would be pretty simple, but alas, I can't figure it out. It appears that PowerShell will prettify JSON with a single cmdlet.
Goal: Prettify JSON using a PowerShell Azure Function app
Using Microsoft Flow, send an HTTP request (POST) to an Azure Function w/ "ugly", serialized JSON file in body
Azure Function reads file in (then uses ConvertToJson cmdlet to prettify?) and outputs the file back to Flow
Questions:
What do I put in the run.ps1 area of the Azure Function to make this happen?
What do I put in the functions.json area of the Azure Function to make this happen?

I have taken below serialize string
'{ "baz": "quuz", "cow": [ "moo", "cud" ], "foo": "bar" }'
which was mentioned in Prettify json in powershell 3
Here is my function which i used with HttpPost and send the request:
using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."
# Interact with query parameters or the body of the request.
$name = $Request.Query.baz
if (-not $name) {
$name = $Request.Body.baz
}
if ($name) {
$status = [HttpStatusCode]::OK
$body = "Hello $name"
}
else {
$status = [HttpStatusCode]::BadRequest
$body = "Please pass a name on the query string or in the request body."
}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]#{
StatusCode = $status
Body = $body
})
and below you can see , i am able to read it from the string which i posted.
You can use ConvertFrom-Json to convert it but i wondering if you even need it as you can access it by doing below:
$name = $Request.Query.baz
my binding is same as yours. Hope it helps.
Let me know if you still need any help.

Are you looking for something like this?
using namespace System.Net
param($Request, $TriggerMetadata)
Push-OutputBinding -Name Response -Value ([HttpResponseContext]#{
StatusCode = [HttpStatusCode]::OK
Body = $Request.RawBody | ConvertFrom-Json | ConvertTo-Json
})

Related

PowerShell: How can I output a hashtable as json and store it in a variable using a foreach loop?

I'm trying to write a script that interacts with a Rest API to make changes in active directory (I know that doesn't make a lot of sense, but it's how our company does things). The change is to modify the homeDirectory attribute of a list of users belonging to a specific security group. The user's aren't all in the same domain, but are in the same forest and are registered in the global catalog.
So far, this is what I have that works:
function get-groupusers{
$memberlist = get-adgroup -filter "name -eq 'group.users'" -server "server.domain.com" -property member |select -expandproperty member
$GlobalCatalog = Get-ADDomainController -Discover -Service GlobalCatalog
foreach ($member in $memberlist){
get-aduser -identity $member -server "$($GlobalCatalog.name):3268"
}
}
$sAMAccountName = (get-groupusers).samaccountname
This is the part I'm running into problems with:
foreach($an in $SamAccountName){
$json = #{
input = #{
key = "homeDirectory"
value = "\\filepath\$an"
}
}
}
The goal here, is to convert the hashtables into json (the required format for interacting with the API), and save it in the following format (note, the "input" "key" and "value" keys are all what the API actually uses, not just substitutions):
{
"input":{
"key":"homeDirectory",
"value":"\\\\filepath\\$an"
},
{
"key":"homeDirectory",
"value":"\\\\filepath\\$an"
}
}
But right now, the foreach loop just overwrites the $json variable with the last set of hashtables. The loop is iterating over the list correctly, as I can put a convertto-json|write-host cmdlet in it but then the $an variable outputs as "#(samaccountname="$an") instead of just whatever the $an actually is.
If my guess is right and you want a resulting Json as the one shown in your question (with the exception that, as mclayton stated in comments, the input property value should be wrapped in [...] to be an array) then this should do it:
$json = #{
input = #(
foreach($an in $SamAccountName) {
[ordered]#{
key = "homeDirectory"
value = "\\filepath\$an"
}
}
)
}
$json | ConvertTo-Json -Depth 99

ConvertTo-JSON from string - access JSON fields by name in PowerShell

How can I access a field like $body.uuid?
This is what I have tried:
$body = #"
{ "uuid": "Test07",
"subject": "Template07-Subject",
}
"#
$bodyJSON = ConvertTo-Json $body
Write-Host $bodyJSON
Write-Host "uuid=$($bodyJSON.uuid)"
Write-Host "uuid=$($bodyJSON.$uuid)"
Results:
"{ \"uuid\": \"Test07\",\r\n \"subject\": \"Template07-Subject\",\r\n}"
uuid=
uuid=
Your $body variable contains a JSON string.
Unless your intent is to embed that string in another JSON string, do not call ConvertTo-Json on it - the latter's purpose is to convert objects to JSON strings.
In order to parse the JSON string into an object (graph), pass it to ConvertFrom-Json, which returns [pscustomobject] instance(s).
You can use regular property access, such as .uuid on the resulting object(s).
Note:
As bluuf points out, your original JSON contains an extraneous , after the "subject" property, which makes it technically malformed - this has been corrected below.
Note, however, that ConvertTo-Json in PowerShell (Core) 7+ still accepts such JSON, whereas Windows PowerShell does not.
# Create a string containing JSON
$body = #"
{
"uuid": "Test07",
"subject": "Template07-Subject"
}
"#
# Parse the JSON string into a PowerShell object (graph).
$bodyAsObject = ConvertFrom-Json $body
# Now you can use property access.
$bodyAsObject.uuid # -> 'Test07'

How to send a .json file from Flow to Azure Function and return results?

I want to send a serialized .json file to a PowerShell Azure Function, prettify it, then return the file to Flow for further processing. I cannot figure out how to do this.
In Flow:
Trigger: Button push
Action1: Get file content from OneDrive
Output:
{
"$content-type": "application/octet-stream",
"$content": "eyJUb3BQYXJlbnQiOns...<truncated for this post>"
}
Action2: Send HTTP Request
URI: https://test.azurewebsites.net/api/prettifyJson?code=<api key>
Method: POST
Body: body('Get_file_content') (output from Action1)
In Azure Function:
Default PowerShell run.ps1:
using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."
# Interact with body of the request.
$content = $Request.Query.baz
if (-not $name) {
$name = $Request.Body.baz
}
if ($name) {
$status = [HttpStatusCode]::OK
$body = "Hello $name"
}
else {
$status = [HttpStatusCode]::BadRequest
$body = "Please pass a name on the query string or in the request body."
}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]#{
StatusCode = $status
Body = $content
})
Issues:
A call to the function fails with Please pass a name on the query string or in the request body. I can see why, but do not know what syntax to replace in run.ps1
I have no idea what syntax to use to:
a. Receive the .json file
b. Convert it to pretty json
c. Repackage the .json file
d. Send it back to Flow
Looking for guidance.
About number one, seems you're missing the name in the url.
try to replace from this:
https://test.azurewebsites.net/api/prettifyJson?code=
to this:
https://test.azurewebsites.net/api/prettifyJson?name=test&code=
about your second question, the you'll need to parse the body content as Hashtable. try to combine:
$hash = $Request.Body | ConvertFrom-Json -AsHashtable
then all the variables will be available using $hash[]
e.g: $hash["$content"]

How to check if file has valid JSON syntax in Powershell

I am trying to write a powershell script that reads a file and prints "true" if it is a valid JSON file. I am using Powershell v3.0 and this is what I have right now :
$text = Get-Content .\filename.txt -Raw
$powershellRepresentation = $text | ConvertFrom-Json
How do I check the return code? I mean I want something like this :
if(file not a JSON file){
Write-Host "not JSON"
}
else{
Write-Host "True"
}
UPDATE 2021: PowerShell 6 and newer versions
PowerShell 6 brings a brand new Test-Json cmdlet. Here is the reference.
You can simply pass the raw file content directly to the Test-Json cmdlet.
$text = Get-Content .\filename.txt -Raw
if ($text | Test-Json) {
$powershellRepresentation = ConvertFrom-Json $text -ErrorAction Stop;
Write-Host "Provided text has been correctly parsed to JSON";
} else {
Write-Host "Provided text is not a valid JSON string";
}
PowerShell 5 and earlier versions
There is no Test-Json cmdlet in these versions, so the best way is to put your ConvertFrom-Json cmdlet inside a try ... catch block
try {
$powershellRepresentation = ConvertFrom-Json $text -ErrorAction Stop;
$validJson = $true;
} catch {
$validJson = $false;
}
if ($validJson) {
Write-Host "Provided text has been correctly parsed to JSON";
} else {
Write-Host "Provided text is not a valid JSON string";
}
If you encounter this question and can use PowerShell 6 or later, there is now a Test-Json cmdlet. It can also not just validate that it's valid JSON, but that it conforms to a particular JSON schema using the -Schema param.
Example
$isValid = Get-Content .\filename.txt -Raw | Test-Json
if($isValid){
Write-Host "not JSON"
}
else{
Write-Host "True"
}
ARM Template Warning
A note for users looking to validate an ARM template via -Schema (I can't imagine a more perfect use case). At the time of writing, there are one or more bugs in the underlying library Test-Json uses for validation, NJsonSchema, and it is not possible to validate an ARM template.
GitHub Issues
PowerShell Issue #9560
NJsonSchema Issue #588
I don't think that it exists an other solution than catching the exception using ConvertFrom-Json.
ConvertFrom-JSON would work but only for a JSON object < 2MB in size.
For higher you can use JavaScriptSerializer class
try
{
$jsser = New-Object System.Web.Script.Serialization.JavaScriptSerializer
$jsser.MaxJsonLength = $jsser.MaxJsonLength * 10
$jsser.RecursionLimit = 99
$outObject = $jsser.DeserializeObject($json)
}
catch
{
Write-Host "Error converting $text to JSON"
}
ConvertFrom-Json shd be the appropriate way to go.
Test-Json unfortunately has alot of known unsupported JSON Types.
I.E. it cannot parse Json-Arrays or Primitives properly leading to falsely assuming it has wrong JSON-Syntax.

Open IE and send JSON POST data

I'm trying using PowerShell to open a web page while sending JSON as my POST data.
Here is what I have
$ie = New-Object -comObject InternetExplorer.Application
$ie.visible = $true
$postBytes = [System.Text.Encoding]::Default.GetBytes('"xCoord":"11.11","yCoord":"22.22","scale":"8,000,016.00","coordSysId":"123"');
$ie.navigate("http://localhost/ProcessSearch", "_blank", $postBytes, "Content-Type: application/json; charset=utf-8")
The web page does open however the JSON isn't getting posted according to Fiddler.
How can I send the POST data?
I would use the cURL utility http://curl.haxx.se/dlwiz/?type=bin&os=Win64 and run it from PowerShell. No need to use IE or any browser for that matter.
I've been looking at something similar and I've posted this http://amonkeysden.tumblr.com/post/5842982257/posting-to-a-restful-api-with-powershell - there's a link to the source there as well but more specifically this is the module:
https://bitbucket.org/thompsonson/powershellmodulerepository/src/5e0afe9d0e26/PsUrl/PsUrl.psm1
and you probably want the New-RestItem function though the Write-URL might do it for you as well.
You should pass the JSON as a string - the example below isn't tested... :).
eg.
New-RestItem http://www.tumblr.com/api/write -Data #{"JSON" = '{"something": "value", "more": {"morekey1":"value", "morekey2": "value"} }'
function New-RestItem {
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, Position=0)]
[String]$Url,
[HashTable]$Data,
[TimeSpan]$Timeout = [System.TimeSpan]::FromMinutes(1)
)
Add-Type -AssemblyName System.Web
$formData = [System.Web.HttpUtility]::ParseQueryString("")
foreach($key in $Data.Keys){
$formData.Add($key, $Data[$key])
}
Write-Verbose $formData.ToString()
$reqBody = [System.Text.Encoding]::Default.GetBytes($formData.ToString())
try{
$req = [System.Net.WebRequest]::Create($Url)
$req.Method = "POST"
$req.ContentType = "application/x-www-form-urlencoded"
$req.Timeout = $Timeout.TotalMilliseconds
$reqStream = $req.GetRequestStream()
$reqStream.Write($reqBody, 0, $reqBody.Length)
$reqStream.Close()
$resp = $req.GetResponse()
Write-Verbose $resp
Write-Output $resp.StatusCode
}
catch [System.Net.WebException]{
if ($_.Exception -ne $null -and $_.Exception.Response -ne $null) {
$errorResult = $_.Exception.Response.GetResponseStream()
$errorText = (New-Object System.IO.StreamReader($errorResult)).ReadToEnd()
Write-Warning "The remote server response: $errorText"
Write-Output $_.Exception.Response.StatusCode
} else {
throw $_
}
}
<#
.Synopsis
POSTs the data to the given URL
.Description
POSTs the data to the given URL and returns the status code
.Parameter Url
URL to POST
.Parameter Data
Hashtable of the data to post.
.Parameter Timeout
Optional timeout value, by default timeout is 1 minute.
.Input
URL and a hash of the data to create
.Output
The HTTP Status code (Created (201), Forbidden (403) or Bad Request (400))
.Example
PS:> New-RestItem http://www.tumblr.com/api/write -Data #{"Foo" = "Bar" }
Description
-----------
POST's data to the tumblr write API as application/x-www-form-urlencoded
#>
}