loop over parameterized rest api call - powershell

I'm trying to do a history load of data from a ReST api call using powershell. If I were to call the ReST endpoint with no parameters I either run into timeout or out of memory exceptions.
$Header = #{
"authorization" = "Bearer $token"
}
#make REST API call
$Parameters = #{
Method = "GET"
Headers = $Header
ContentType = "application/json"
Body = $BodyJson
}
$startYear = 2014
$endYear = 2022
$Data = {for($year=$startYear; $i -le $endYear; $year=$year+1) {Invoke-RestMethod -Uri "https://api.mysite.com/v1/data/year/Year/" + [string]$year #params -DisableKeepAlive -ErrorAction Stop}} | ConvertTo-Json
tried the below as well.
$Data = foreach($year in $startYear..$endYear) {
$url = "https://api.mysite.com/v1/data/year/Year/" + [string]$year
Invoke-RestMethod -Uri $url #params -DisableKeepAlive -ErrorAction Stop}
$Data | ConvertTo-Json
I don't see all of the data starting from the year 2010 until the year 2022. Also, I would like to append $Data with all the data starting from the year 2010 until the current year.

Related

powershell cannot bind error on if else block

I am working on a requirement where I have to check if the api call needs to be looped over or not. I am using the below code to accomplish this requirement. If I take out the if else block and write for either loop no loop things work as expected.
PoSh:
$Loop = "1" # 0 for no looping 1 for looping
if ($Loop -eq 1) {
$Header = #{
"authorization" = "Bearer $token"
}
#make REST API call
$Parameters = #{
Method = "GET"
Headers = $Header
ContentType = "application/json"
Body = $BodyJson
}
$startYear = 2014
$endYear = 2022
$Data = {for($year=$startYear; $i -le $endYear; $year=$year+1) {Invoke-RestMethod -Uri "https://api.mysite.com/v1/data/year/Year/" + [string]$year #Parameters -DisableKeepAlive -ErrorAction Stop}} | ConvertTo-Json
}
else {Write-Output "No loop"
$Header = #{
"authorization" = "Bearer $token"
}
#make REST API call
$Parameters = #{
Method = "GET"
Headers = $Header
ContentType = "application/json"
Body = $BodyJson
}
$Data = Invoke-RestMethod -Uri "https://api.mysite.com/v1/data" #Parameters -DisableKeepAlive -ErrorAction Stop | ConvertTo-Json
}
Error:
Cannot bind parameter because parameter 'Uri' is specified more than once. To provide multiple values to parameters that can accept multiple values, use the array syntax.
I have of course no idea what your https://api.mysite.com/v1/data would return and if it is actually needed to convert the returned data to Json at all, but continuing from my comments, try
# make this a Boolean value for clarity
$Loop = $true # $false for no looping $true for looping
# splatting Hashtable for REST API call
$Parameters = #{
Method = "GET"
Headers = #{ "Authorization" = "Bearer $token" }
ContentType = "application/json"
Body = $BodyJson
# you can incorporate these parameters as well
DisableKeepAlive = $true
ErrorAction = 'Stop'
}
if ($Loop) {
Write-Host "Start looping.."
$startYear = 2014
$endYear = 2022
# use the $() subexpression to combine the various outputs and convert that to Json
$Data = $(for ($year = $startYear; $year -le $endYear; $year++) {
Invoke-RestMethod -Uri "https://api.mysite.com/v1/data/year/Year/$year" #Parameters
}) | ConvertTo-Json
}
else {
Write-Host "No loop"
$Data = Invoke-RestMethod -Uri "https://api.mysite.com/v1/data" #Parameters | ConvertTo-Json
}
P.S. The error you saw in your code was caused by the wrong variable you used in the for loop with $i -le $endYear instead of $year -le $endYear. That and the fact that you put the whole loop inside a scriptblock made variables $startYear and $endYear invisible..

Trying to use the "SetDeviceName" action with Microsoft Graph

Trying to do a bulk rename of device when they haven't been logged on for a long time.
I have managed to gather the information I need it and pipe it, so it comes out correctly.
But for some reason, I get Bad request and my formating is some how wrong, but can't figure out what. Tried every type of modification to the URI, but no luck.
This should work according to Microsoft's Doc about SetDevicename action
$date = (Get-date (Get-date).adddays(-316) -format "yyy-MM-ddTHH:mm:ssZ")
$devices | where {$_.lastSyncDateTime -le $date} | ForEach-Object {
$newname = "Test-$($_.Devicename)"
$deviceID = "$($_.ID)"
$URI = "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$deviceID/setDeviceName"
$Body = #{ "deviceName" = "$NewName" } | ConvertTo-Json
$Method = "POST"
Invoke-RestMethod -Uri $URI -Method $Method -Headers $appauthToken -body $body -ContentType "application/json"
}
Any Idea what I'm doing wrong?
There is another method of renaming of bulk devices:
Document reference: https://learn.microsoft.com/en-us/mem/intune/remote-actions/device-rename#bulk-rename-devices.

Unable to ingest JSON data into Azure Event Hub

I wrote the below Powershell script to get the JSON data from an API endpoint (https://data.melbourne.vic.gov.au/resource/vh2v-4nfs) and then write this data in JSON format to Azure Event hub. I am able to successfully get the data from the endpoint however the data is not getting ingested into Azure Event Hub.
Can anyone please let me know what's wrong with the below code:
$url = "https://data.melbourne.vic.gov.au/resource/vh2v-4nfs"
$apptoken = "k7lQcUCVFoROv7rQh9fSSXMkZ"
# Set header to accept JSON
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Accept","application/json")
$headers.Add("X-App-Token",$apptoken)
$results = Invoke-RestMethod -Uri $url -Method get -Headers $headers
$results
$method = "POST"
$URI = "https://YOURNS.servicebus.windows.net/eh-streetparking/messages"
$signature = "SharedAccessSignature sr=YOURNS.servicebus.windows.net%2feh-streetparking&sig=K6bfL1VjW9FUcL0B5xaI%3d&se=16722&skn=eh-sap-streetparking"
#$authInfo = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$signature"))
# API headers
$headers = #{
"Authorization"=$signature;
# "Content-Type"="application/json;type=entry;charset=utf-8";
"Content-Type"="application/json";
}
# execute the Azure REST API
foreach ( $result in $results)
{
Invoke-RestMethod -Uri $URI -Method $method -Headers $headers -Body $result
}
The value you have presented as the return result from your Invoke-RestMethod is actually a deserialized PowerShell object, not JSON. It appears to be having its quotes removed at some point too.
PSObject ($results) looks like this: $x = #{account_id="12345"; username="12345"; is_locked="False"; employee_id="12345"; first_name="John"; middle_initial="Roger"; last_name="Doe"; full_name="John Roger Doe"}
You can do this to access individual values:
$x.full_name
Finally, follow this syntax to send POST request:
$Cred = Get-Credential
$Url = "https://server.contoso.com:8089/services/search/jobs/export"
$Body = #{
search = "search index=_internal | reverse | table index,host,source,sourcetype,_raw"
output_mode = "csv"
earliest_time = "-2d#d"
latest_time = "-1d#d"
}
Invoke-RestMethod -Method 'Post' -Uri $url -Credential $Cred -Body $body -OutFile output.csv

Get-content not producing an array that Invoke-restmethod can process

As a follow-up to this question, instead of using a long array in the script I wanted to draw from a text file. So I replaced this:
$URLs = 'http://websiteone.com','http://websitetwo.com','http://websitethree.com'
with this
$URLs = Get-Content ./urlfile.txt
or (functionally the same as far I know) this
$URLs = #(Get-Content ./urlfile.txt)
But I end up with Invoke-RestMethod : The remote server returned an error: (400) Bad Request.
Incorporating the great response form my last question, my foreach loop looks like this:
foreach($URL in $URLs) {
$BODY = #([pscustomobject]#{"client" = #{"clientId" = "company"; "clientVersion" = "1.0"}; "threatInfo" = #{"threatTypes" = "MALWARE","SOCIAL_ENGINEERING","THREAT_TYPE_UNSPECIFIED","UNWANTED_SOFTWARE","POTENTIALLY_HARMFUL_APPLICATION"; "platformTypes" = "ANY_PLATFORM"; "threatEntryTypes" = "URL","EXECUTABLE","THREAT_ENTRY_TYPE_UNSPECIFIED"; "threatEntries" = #{"url" = $URL}}})
$JSONBODY = $BODY | ConvertTo-Json
$Result = Invoke-RestMethod -Method 'POST' -Uri $Uri -Body $JSONBODY -Headers $HEADERS
if ( ([string]::IsNullOrEmpty($Result)) ) {} else {write-host $URL "ALERT: Safe browsing match!"}
}
... but this doesn't work if I create the array with the Get-Content cmdlet. If I run the script either way, then type $URLs, I get the exact same data returned. What am I doing wrong with get-content?
The Invoke-RestMethod cmdlet is there to make one Rest request at a time and can't take an array.
You will need to add a forEach loop to step through your $urls one at a time, something like this:
foreach($url in $urls){
$result = Invoke-RestMethod -Uri $url
#do something with $result
}
So to integrate into your sample from the previous question, you should have a urls.txt file which looks like this:
http://google.com
http://small.com
https://fast.com/
And then your code would look like this:
$URLs = get-content .\urls.txt
$HEADERS = #{ 'Content-Type' = "application/json" }
$GOOGLE_API_KEY='[API Key]'
$Uri = 'https://safebrowsing.googleapis.com/v4/threatMatches:find?key='+ $GOOGLE_API_KEY
foreach($URL in $URLs) {
$BODY = #([pscustomobject]#{"client" = #{"clientId" = "company"; "clientVersion" = "1.0"}; "threatInfo" = #{"threatTypes" = "MALWARE","SOCIAL_ENGINEERING","THREAT_TYPE_UNSPECIFIED","UNWANTED_SOFTWARE","POTENTIALLY_HARMFUL_APPLICATION"; "platformTypes" = "ANY_PLATFORM"; "threatEntryTypes" = "URL"; "threatEntries" = #{"url" = $URL}}})
$JSONBODY = $BODY | ConvertTo-Json
$result = Invoke-RestMethod -Method 'POST' -Uri $Uri -Body $JSONBODY -Headers $HEADERS
[pscustomObject]#{SiteName=$url;ThreatInfo=$result.Matches}
}
This would load up the list of $urls from your text file, then run a Rest Request on each, storing the result in $result. Finally, it will make a new PowerShell Object with the site name and show you if there are any matches from the Google SafeBrowsing API.
You'll need to run the command interactively and see which properties from $result are meaningful to you, but you can see all of the expected properties in the Google API Docs.
Edit
Found the bug. It turns out when we use Get-Content the object returned back retains some of the document formatting information from the original file! We can see this by inspecting $JSONBODY. We also see that the conversion to Json from [PSCustomObject is leaving a lot of cruft behind too.
To fix this, we should cast $URL into a string using the ToString() method and also ditch casting to [psCustomObject] too as shown below.
$BODY = #{
"client" = #{
"clientId" = "company"; "clientVersion" = "1.0"
};
"threatInfo" = #{
"threatTypes" = "MALWARE",
"SOCIAL_ENGINEERING",
"THREAT_TYPE_UNSPECIFIED",
"UNWANTED_SOFTWARE",
"POTENTIALLY_HARMFUL_APPLICATION"; "platformTypes" = "ANY_PLATFORM"; "threatEntryTypes" = "URL"; "threatEntries" = #{
"url" = $URL.ToString()
}
}
}
$JSONBODY = $BODY | ConvertTo-Json

Can not pull right data using Azure Aduit Logs using Graph API thru Powershell

I have been trying to pull out Azure Audit logs using Microsoft Graph. I am using Powershell to do this nut I can't get filtering to work.
For example, this URL returns lots of older data than then date specified in the filter.
https://graph.microsoft.com/v1.0/auditLogs/directoryAudits?\$filter=activityDateTime ge 2020-02-24T18:39:36Z
Here is my code
$ClientID = "yyyyyy"
$ClientSecret = "xxxxxxxxx"
$loginURL = "https://login.microsoftonline.com"
$tenantdomain = "xxx.onmicrosoft.com"
$resource = "https://graph.microsoft.com"
$Date = Get-Date
$UTC = $Date.ToUniversalTime();
$UTC
$Time = "{0:s}" -f $UTC.AddMinutes(-5) + "Z"
$Time
$body =
#{grant_type="client_credentials";resource=$resource;client_id=$ClientID;client_secret=$ClientSecret}
$oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token?api-
version=1.0 -Body $body
$headerParams = #{'Authorization'="$($oauth.token_type) $($oauth.access_token)"}
$url = 'https://graph.microsoft.com/v1.0/auditLogs/directoryAudits?\`$filter=activityDateTime ge ' +
$Time
$Data = Invoke-WebRequest -UseBasicParsing -Headers $headerParams -Uri $url
$DataFromJson = $Data.Content | ConvertFrom-Json
$ValueFromJason = $DataFromJson.value
$AllAuditData += $ValueFromJason
# Output
$Time
2020-02-24T18:39:36Z
$short = $AllAuditData | Sort-Object activityDateTime
$short[1].activityDateTime
2020-02-24T17:52:09.9673372Z # Old data as comparison to $Time
$short[-1].activityDateTime
2020-02-24T18:44:15.1283452Z
According to my test, we can use the following script
$ClientID = "yyyyyy"
$ClientSecret = "xxxxxxxxx"
$loginURL = "https://login.microsoftonline.com"
$tenantdomain = "xxx.onmicrosoft.com"
$resource = "https://graph.microsoft.com"
$body =
#{grant_type="client_credentials";resource=$resource;client_id=$ClientID;client_secret=$ClientSecret}
$oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token?api-
version=1.0 -Body $body
$Date = Get-Date
$UTC = $Date.ToUniversalTime();
$Time = "{0:s}" -f $UTC.AddMinutes(-5) + "Z"
$Time
$url= "https://graph.microsoft.com/beta/auditLogs/directoryAudits?`$filter=activityDateTime gt " + $Time
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "$($oauth.token_type) $($oauth.access_token)")
$AllAuditData=#()
$response = Invoke-RestMethod $url -Method 'GET' -Headers $headers
$AllAuditData += $response.value
$short = $AllAuditData | Sort-Object activityDateTime
$short[1].activityDateTime
$short[-1].activityDateTime
Update
According to my research, we can use the Azure AD PowerShell command Get-AzureADAuditDirectoryLogs to filter audit logs. For more details, please refer to https://learn.microsoft.com/en-us/azure/active-directory/reports-monitoring/reference-powershell-reporting.
For example
Install-module AzureADPreview
Connect-AzureAD
$result=Get-AzureADAuditDirectoryLogs -Filter "activityDisplayName eq 'Update user'"
$result | Select-Object ActivityDisplayName