Powershell script not parsing correctly - json

So I'm trying to pull the number of hours worked and the date worked from a table in my companies database to make a chart in Power BI through a streaming data set. I'm using powershell to parse a JSON file
Here's a JSON sample:
{"COUNT":"334","DISPLAY_LIST_START":"1","DISPLAY_LIST_STOP":"334","STOP":"334","RECORD":[{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["04/23/2018"]]},{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["04/24/2018"]]},{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["04/26/2018"]]},{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["04/30/2018"]]},{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["05/01/2018"]]},{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["4",["05/02/2018"]]},{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["05/03/2018"]]},{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["05/07/2018"]]},
I know it's not the best in terms of organization, but it's all I have to work with.
Here's the powershell code I have so far:
Invoke-WebRequest -Uri "http://wya.works/rta_develop/xmlServlet?&command=retrieve&sql=select%20%5B%24Hours%5D%2C%20%5B%24Date%20Worked%5D%20from%20%5B%21HOURS%5D%20&attributesOnly=Date%20Worked%2C%20Hours&contentType=JSON&referer=&time=1595443368507&key=696a6768"
$endpoint = "https://api.powerbi.com/beta/d6cdaa23-930e-49c1-9d2a-0fbe648551b2/datasets/91466553-d719-420c-9e3e-73e748379263/rows?noSignUpCheck=1&key=SU5GRBBWuuEIDSjqHW5hdgJzSMCQ3qUQ9mGrBDanjgpExv6woY1Sa1c3PC1Wk3WHHn1N%2FEpIuVgzHHcw0JXwYw%3D%3D"
$json.RECORD | Foreach-Object {
Write-Output "Checking Records"
$hours = 0
$date = ""
$json.FIELD | Foreach-Object{
Write-Output "Checking Field"
if ($_ -match '\d{1,2}/\d{1,2}\/d{4}'){
$date = $_
}
else {
$hours = $_
}
}
$payload = #{
"Hours" = $hours
"Date Worked" =$date
}
}
Invoke-RestMethod -Method Post -Uri "$endpoint" jlk-Body (ConvertTo-Json #($payload))
I need to parse through each record and pull the values of the hours (the numeric value in the JSON) and the Date (the date value).
When I run the code I don't get any errors, but it doesn't seem to be reaching the -match or the else statements. I tried logging the output on both and it returns nothing.
Is there something wrong with my loops?
I'm brand new to powershell and most of this code I got from help from other people, but I understand what its doing for the most part.
Also, anyone who knows about streaming datasets, will pulling this this way even give me what I want?

Store the Invoke-Webrequest values in your $json first. You missed that point; that's why you are getting Null.I don't know it is a typo or a miss.
Secondly, you $json.RECORD is wrong because it doesnt have any Record in the response. What you are looking for is basically the content. $json.content is going to give you the content of numbers.
$json=Invoke-WebRequest -Uri "http://wya.works/rta_develop/xmlServlet?&command=retrieve&sql=select%20%5B%24Hours%5D%2C%20%5B%24Date%20Worked%5D%20from%20%5B%21HOURS%5D%20&attributesOnly=Date%20Worked%2C%20Hours&contentType=JSON&referer=&time=1595443368507&key=696a6768"
Your endpoint and invoke-restmethod has nothing to do with your json parsing. First handle the response in the loop and see what is the outcome you are getting. I have structured it but I have not worked on the JSON sample data as if now:
$json=Invoke-WebRequest -Uri "http://wya.works/rta_develop/xmlServlet?&command=retrieve&sql=select%20%5B%24Hours%5D%2C%20%5B%24Date%20Worked%5D%20from%20%5B%21HOURS%5D%20&attributesOnly=Date%20Worked%2C%20Hours&contentType=JSON&referer=&time=1595443368507&key=696a6768"
$json.content | Foreach-Object {
Write-Output "Checking Records"
$hours = 0
$date = ""
$json.FIELD | Foreach-Object{
Write-Output "Checking Field"
if ($_ -match '\d{1,2}/\d{1,2}\/d{4}'){
$date = $_
}
else {
$hours = $_
}
}
$payload = #{
"Hours" = $hours
"Date Worked" =$date
}
}
$endpoint = "https://api.powerbi.com/beta/d6cdaa23-930e-49c1-9d2a-0fbe648551b2/datasets/91466553-d719-420c-9e3e-73e748379263/rows?noSignUpCheck=1&key=SU5GRBBWuuEIDSjqHW5hdgJzSMCQ3qUQ9mGrBDanjgpExv6woY1Sa1c3PC1Wk3WHHn1N%2FEpIuVgzHHcw0JXwYw%3D%3D"
Invoke-RestMethod -Method Post -Uri "$endpoint" jlk-Body (ConvertTo-Json #($payload))

The root of your problem is this:
$json.RECORD | Foreach-Object {
$json.FIELD | Foreach-Object{
...
}
}
Your outer foreach-object is looping over each item in the RECORD array, but the inner foreach-object is trying to loop over top-level FIELD properties that don't actually exist!
However, I think you'll hit more problems further along your code if you try to troubleshoot what you've already got, and there's an easier way to do what you're trying to do...
First, your sample json isn't quite valid - there's a trailing comma and some unclosed brackets so I'm going to reformat it with some line breaks so it's easier to read, and then fix it.
Note - you'll get the text from your call to Invoke-WebRequest, or as #Lee_Dailey suggested, Invoke-RestMethod.
# reformat the json, fix it, and assign it to a variable using a "Here-String"
# (see https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules?view=powershell-7#here-strings)
$text = #"
{
"COUNT":"334",
"DISPLAY_LIST_START":"1",
"DISPLAY_LIST_STOP":"334",
"STOP":"334",
"RECORD":[
{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["04/23/2018"]]},
{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["04/24/2018"]]},
{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["04/26/2018"]]},
{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["04/30/2018"]]},
{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["05/01/2018"]]},
{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["4",["05/02/2018"]]},
{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["05/03/2018"]]},
{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["05/07/2018"]]}
]
}
"#
Then, we'll parse it - i.e. convert it from a string into an object model with well-defined properties:
# parse the json text
$json = $text | ConvertFrom-Json
And then process each record in turn:
$json.RECORD | ForEach-Object {
# get the number of hours worked. this is the first value in the FIELD array
# (note - the array starts at index 0 because it's zero-indexed)
$hours = $_.FIELD[0] # e.g. "6"
# get the "date worked". we need to get the second value (index 1) in
# the FIELD array, but this is a nested array, so once we've got the
# inner array we need to get the first value (index 0 again) from that
$date = $_.FIELD[1][0] # e.g. "04/23/2018"
# now we can build the payload...
$payload = #{
"Hours" = $hours
"Date Worked" = $date
}
# ...and invoke the api for this record
$endpoint = "https://api.powerbi.com/beta/d6cdaa23-930e-49c1-9d2a-0fbe648551b2/datasets/91466553-d719-420c-9e3e-73e748379263/rows?noSignUpCheck=1&key=SU5GRBBWuuEIDSjqHW5hdgJzSMCQ3qUQ9mGrBDanjgpExv6woY1Sa1c3PC1Wk3WHHn1N%2FEpIuVgzHHcw0JXwYw%3D%3D"
Invoke-RestMethod -Method Post -Uri "$endpoint" -Body (ConvertTo-Json #($payload))
}
Note the api call is inside the foreach loop, otherwise you end up calculating the $payload for each RECORD but only ever actually calling the api for the last one.
(I've also removed a spurious "jlk" from your final line, which is probably a typo).

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

URL Date Pull using Powershell

So I'm trying to generate a streaming dataset in Power BI, so that I can have a tab in teams that constantly updates with data from my companies database. The way I pull the data is through our platform API that generates a JSON and a URL based on the query. As far as I can find, the only way to really do this is through Powershell. I want to pull out the hours and numbers fields from the JSON and then push that data to the dashboard in Power Bi, but I have absolutely no experience with any of this, so I'm completely baffled as to where to start.
Here's the powershell code I have so far, but it throws an error in regard to pulling the URL info.
$request = 'http://wya.works/rta_develop/xmlServlet?&command=retrieve&sql=select%20%5B%24Hours%5D%2C%20%5B%24Date%20Worked%5D%20from%20%5B%21HOURS%5D%20&attributesOnly=Date%20Worked%2C%20Hours&contentType=JSON&referer=&time=1595271424377&key=696a666d'
Invoke-WebRequest $request
$json.RECORD | % {
$hours = 0
$date = ""
$_.Field | % {
if ($_ -match '\d{1,2}/\d{1,2}\/d{4}'){
$date = "$_"
}
else {
$hours = $_
}
}
New-Object -TypeName psobject -Property (#{
Date = $date
Hours = $hours
})
}
$endpoint = "https://api.powerbi.com/beta/d6cdaa23-930e-49c1-9d2a-0fbe648551b2/datasets/34eaea1e-73b6-4759-ac8b-aaae51708654/rows?noSignUpCheck=1&key=Ur9E0GDrhkp4EwJOF4bCbg7EO7aIve54urjB8M%2BHevG1%2F6pDgRJ47Fvkmx4b%2FcMowlhV18ZYyVtF9pfG%2BM1EQA%3D%3D"
$payload = #{
"Hours" =$hours
"Date Worked" =$date
}
Invoke-RestMethod -Method Post -Uri "$endpoint" -Body (ConvertTo-Json #($paylojnjlkhad))
This is a snippet from the JSON file and all of the fields I need (numeric: Hours) (date: Date Worked) are labeled the same, which makes this a lot more difficult.
{"COUNT":"332","DISPLAY_LIST_START":"1","DISPLAY_LIST_STOP":"332","STOP":"332","RECORD":[{"SESSION_ID":"HxNI-Zuc1B2EFAzTS8hx6w7Ek_dbrNCMhYVNI3Ta","FIELD":["6",["04/23/2018"]]},{"SESSION_ID":"HxNI-Zuc1B2EFAzTS8hx6w7Ek_dbrNCMhYVNI3Ta","FIELD":["6",["04/24/2018"]]},{"SESSION_ID":"HxNI-Zuc1B2EFAzTS8hx6w7Ek_dbrNCMhYVNI3Ta","FIELD":["6",["04/26/2018"]]},{"SESSION_ID":"HxNI-Zuc1B2EFAzTS8hx6w7Ek_dbrNCMhYVNI3Ta","FIELD":["6",["04/30/2018"]]},{"SESSION_ID":"HxNI-Zuc1B2EFAzTS8hx6w7Ek_dbrNCMhYVNI3Ta","FIELD":["6",["05/01/2018"]]},{"SESSION_ID":"HxNI-Zuc1B2EFAzTS8hx6w7Ek_dbrNCMhYVNI3Ta","FIELD":["4",["05/02/2018"]]}
I need to use the URL instead of the actual file, because my company's platform runs on blockchain and is constantly updated.
If you want to make an API call per pair of $date and $hours, you can do the following. This will make one call per RECORD value.
$endpoint = "https://api.powerbi.com/beta/d6cdaa23-930e-49c1-9d2a-0fbe648551b2/datasets/34eaea1e-73b6-4759-ac8b-aaae51708654/rows?noSignUpCheck=1&key=Ur9E0GDrhkp4EwJOF4bCbg7EO7aIve54urjB8M%2BHevG1%2F6pDgRJ47Fvkmx4b%2FcMowlhV18ZYyVtF9pfG%2BM1EQA%3D%3D"
$json.RECORD | Foreach-Object {
$hours = 0
$date = ""
$_.Field | Foreach-Object {
if ($_ -match '\d{1,2}/\d{1,2}/\d{4}'){
$date = $_
}
else {
$hours = $_
}
}
$payload = #{
"Hours" = $hours
"Date Worked" = $date
}
Invoke-RestMethod -Method Post -Uri $endpoint -Body (ConvertTo-Json $payload)
}
If you want to make one API call after creating an array of $date and $hours pairs, you can do the following. This will only make one API call.
$endpoint = "https://api.powerbi.com/beta/d6cdaa23-930e-49c1-9d2a-0fbe648551b2/datasets/34eaea1e-73b6-4759-ac8b-aaae51708654/rows?noSignUpCheck=1&key=Ur9E0GDrhkp4EwJOF4bCbg7EO7aIve54urjB8M%2BHevG1%2F6pDgRJ47Fvkmx4b%2FcMowlhV18ZYyVtF9pfG%2BM1EQA%3D%3D"
$payloadArray = $json.RECORD | Foreach-Object {
$hours = 0
$date = ""
$_.Field | Foreach-Object {
if ($_ -match '\d{1,2}/\d{1,2}/\d{4}'){
$date = $_
}
else {
$hours = $_
}
}
$payload = #{
"Hours" = $hours
"Date Worked" = $date
}
$payload
}
Invoke-RestMethod -Method Post -Uri $endpoint -Body (ConvertTo-Json $payloadArray)
Exlanation:
If you want to have a payload per field pair, you will need to make an API call after $date and $hours are both set. Then repeat that process for each pair. If your payload supports an array structure, then you can create a collection of those pairs.
You are only doing the API call after all fields have been processed without creating a collection, which means you only get the last pair of $date and $hours values. As you iterate through your RECORD items, you are overwriting $date and $hours before the API call is ever made.

JSON payload for POST sent as "System.Collections.Hashtable" instead of actual data

I am using Powershell to dynamically create a payload of data to be packaged up and sent on in a REST API Post Request.
My problem is that when it is recived by the API, it is listed as System.Collections.Hashtable. I am clearly doing something wrong here in how the data is being formatted, but nothing seems to work for me.
Here's how it is received by the API
{
"properties": {
"recip_test": [
"System.Collections.Hashtable",
"System.Collections.Hashtable"
],
"offending_shifts": "MAX, OnCall-Default Shift",
"group_name": "Alarmpoint Administrators"
}
}
I've tried ConvertTo-Json as well as += () / .Add() but none of those seem to work.
I am looping through an array of data which represent ID's in that array. For each item in that array (in the loop) I need to make a hash table which looks like this,
$recipient = #{
'id' = $y
'recipientType' = 'PERSON'
}
And then take that hash and shovel it into the payload field for recipients which then needs to be passed in the POST request. Below is the full code.
foreach($x in $collated_group_data) {
if ($x.group_name -ne 'Alarmpoint Administrators') {
next
}
$uuid = "***********/triggers?apiKey=**************"
$webhook_path = "$base/api/integration/1/functions/$uuid"
$payload = #{
'properties' = #{
'group_name' = $x.group_name
'offending_shifts' = $x.offending_shifts.Substring(0, $x.offending_shifts.Length - 2)
'recipients' = #()
}
}
foreach($y in $x.supervisor_ids) {
$payload.properties.recipients += #{'id' = $y; 'recipientType' = 'PERSON'}
}
$payload = $payload | ConvertTo-Json
Invoke-WebRequest -Uri $webhook_path -Method POST -Body $payload -ContentType 'application/json'
}
You must use the -Depth parameter with a value of 3 or greater in the ConvertTo-Json command in this case.
$payload = $payload | ConvertTo-Json -Depth 3
By default, the -Depth parameter is set to 2. The parameter specifies how many levels of contained objects are included in the JSON representation. You have three levels in your example.

Find the next business day from JSON while loop in PowerShell

I found a nice little JSON API from Kayaposoft which will give a true/false value if the given day is a work day or not (e.g. Sunday the 14th of April; isWorkDay: false). This API is also able to honor our local holidays, like Vappu in Finland, etc.
So, as I try to script this in PowerShell (being the beginner that I am), I quickly realized a problem with the code below. While the code works, it isn't very practical and is dependent on each variable twice (once to set it up and once in the loop).
Is there any way to beautify this code? Can it be made to be more practical and/or to not use each variable twice?
$date = (Get-Date).AddDays(11)
$jsonDate = $date.ToString('dd-MM-yyyy')
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$jsonRequest = Invoke-WebRequest "https://kayaposoft.com/enrico/json/v2.0/?action=isWorkDay&date=$jsonDate&country=fin" | ConvertFrom-Json
while ($jsonRequest.isWorkDay -ne $true) {
$date = $date.AddDays(1)
$jsonDate = $date.ToString('dd-MM-yyyy')
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$jsonRequest = Invoke-WebRequest "https://kayaposoft.com/enrico/json/v2.0/?action=isWorkDay&date=$jsonDate&country=fin" | ConvertFrom-Json
Write-Host $jsonDate
Write-Host $jsonRequest
}
Results:
22-04-2019
#{isWorkDay=False}
23-04-2019
#{isWorkDay=True}
Something like this:
$date = (Get-Date)
# not sure if you really need this ?!?
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# do { ... } while (...) - execute at least once
do {
$jsonDate = $date.ToString('dd-MM-yyyy')
$jsonRequest = Invoke-WebRequest "https://kayaposoft.com/enrico/json/v2.0/?action=isWorkDay&date=$jsonDate&country=fin" | ConvertFrom-Json
$date = $date.AddDays(1)
Write-Host $jsonRequest
Write-Host $jsonDate
}
while ($jsonRequest.isWorkDay -ne $true)

Get ServiceNow Records Powershell - More than 250

I am trying to retrieve more than 250 records from ServiceNow using Powershell cmdlet Invoke-RestMethod.
Is there a powershell script that I can use ?
Old topic but perhaps this answer can still be helpful.
I found that if I requested more than a certain number of results, seemingly nothing would be returned. Here's what works for me (change 300 to whatever number you want and remove any of the conditions after the ampersand):
$restapiuri = "https://yourserver.service-now.com/api/now/table/incident?sysparm_limit=300&sysparm_query=active=true^ORDERBYDESCnumber"
Use whatever method you prefer for credentials:
$SNowUser = “account username”
$SNowPass = ConvertTo-SecureString –String “Password” –AsPlainText -Force
$SNowCreds = New-Object –TypeName System.Management.Automation.PSCredential –ArgumentList $SNowUser, $SNowPass
Next should be fairly familiar. We're building the request, invoking it and assigning the results to a variable ($completeticket). Without adding " | out-string" you may see no results.
I'm also splitting the results into individual incidents by finding unique text in the first line of the results and assigning that to a variable ($separatetickets) and then iterating through each of them ($separateticket).
$i = 0
$headers = Get-HttpBasicHeader $Credentials
Invoke-RestMethod -uri $restapiuri -Headers $headers -Method GET -ContentType "application/json" |
% {
$completeticket = $_.result | Out-String
$separatetickets = $completeticket -split "whatever the first line of your record is"
foreach ($separateticket in $separatetickets) {
$i++
Write-Host
Write-Host "$i" -ForegroundColor White
Write-Host "$separateticket" -ForegroundColor Magenta
}
}