I'm still having issues with getting json working with the curl command in powershell.
even a simple request to POST something into elastic fails miserably with the error
Unexpected character ('D' (code 68)): was expecting double-quote to start field name
I've stripped the script down to the basics just to try and test curl and json and still get failures
$curlExe = "h:\powershell\esb\elastic\curl\curl.exe"
$elasticdata = #{
timereceived = "test"
timesent = "testing"
name = "anon"
status = 0
}
$curldata = $elasticdata | convertto-json -Compress
$elasticoutput = "h:\powershell\esb\elastic\elastic.txt"
$elastichost = "http://localhost:9200/newtest20/filecopy/?pretty"
$elasticheader = "content-type: application/json"
$elamethod = "POST"
$curlargs = $elastichost,
'-X',$elamethod,
'-d',$curldata,
'-H',$elasticheader
& $curlexe #curlargs
If your server is running Powershell 2.0 you will not have Invoke-webRequestbut ConvertTo-Json will also be missing.
I also encountered this issue in the past and I made these functions to workaround that issue
function Invoke-WebRequest([string] $Url, [string] $Method, $BodyObject)
{
$request = [System.Net.WebRequest]::Create($Url)
$request.Method = $Method
$request.ContentType = "application/json"
if ($Method -eq "POST")
{
try
{
$body = ConvertTo-Json20 -InputObject $BodyObject
$requestStream = $request.GetRequestStream()
$streamWriter = New-Object System.IO.StreamWriter($requestStream)
$streamWriter.Write($body)
}
finally
{
if ($null -ne $streamWriter) { $streamWriter.Dispose() }
if ($null -ne $requestStream) { $requestStream.Dispose() }
}
}
$response = $request.GetResponse()
if ($response.StatusCode -ne [System.Net.HttpStatusCode]::OK)
{
throw "ERROR Could not $Method url [$Url]"
}
return $response
}
function ConvertTo-Json20($InputObject){
Add-Type -Assembly System.Web.Extensions
$jsonSerializer = New-Object System.Web.Script.Serialization.JavascriptSerializer
return $jsonSerializer.Serialize($InputObject)
}
Related
I am writing a PowerShell function to build the JSON that will be passed to an API. I am using PowerShell 5.
The API expects a certain format and an example is below.
{
“task”: {
“status”: {
“name”: “Resolved”
},
“owner”: {
“name”: “User”
},
“comment”: “Test”
}
}
The structure of the JSON is stored in nested hash tables then piped to ConvertTo-Json and I am passing the data values through parameters.
$baseTask = #{
task = #{
comment = $Comment;
status = #{
name = $Status;
owner = #{
name = $Owner;
}
}
}
I want the function to be as dynamic as possible so I have the identifier as mandatory and then all other parameters as optional.
Param(
[Parameter(Mandatory=$true)]
$TaskID,
$Comment,
$Status,
$Owner
)
If the parameter is NULL I want to remove that element from the definition so that it doesn’t pass NULL to the API. I have the logic below.
If([String]::IsNullOrEmpty($Comment)) {
$baseTask.task.Remove(‘comment’)
}
If([String]::IsNullOrEmpty($Status)) {
$baseTask.task.Remove(‘status’)
}
If([String]::IsNullOrEmpty($Owner)) {
$baseTask.task.Remove(‘owner’)
}
This works; however if I have 50 parameters then I am going to have a lot of repetition with the null checks so I feel there must be a more dynamic way to do this ideally with some sort of loop.
I thought about a hash table with mapping between variable name and command to run, but I can’t find a way to get the name of the variable during execution.
Any help would be much appreciated.
You may check to see which parameters were not used by comparing the bound parameters against all available parameters for your command and then use this to remove the different elements from your hash table.
function New-DynamicJson {
Param(
[Parameter(Mandatory = $true)]
$TaskID,
$Comment,
$Status,
$Owner
)
$baseTask = #{
task = [ordered]#{
Id = $TaskID;
comment = $Comment;
status = #{
name = $Status;
}
owner = #{
name = $Owner;
}
}
}
$commonParams = 'Verbose', 'Debug', 'ErrorAction', 'WarningAction', 'InformationAction', 'ErrorVariable',
'WarningVariable', 'InformationVariable', 'OutVariable', 'OutBuffer', 'PipelineVariable'
$MyInvocation.MyCommand.Parameters.Keys.Where({ $_ -notin $commonParams }) |
Where-Object { $_ -notin $PSBoundParameters.Keys } |
ForEach-Object { $baseTask.task.Remove($_) }
$baseTask | ConvertTo-Json
}
Output
PS> New-DynamicJson -TaskID 103 -Comment 'some comment'
{
"task": {
"Id": 103,
"comment": "some comment"
}
}
(Thank you mklement0 for the suggestion to use $MyInvocation instead of $PSCmdlet.MyInvocation which also makes the solution work in non-advanced functions)
You can take something like that with care.
The idea is to convert names ('status') to paths where it should be ('status.name') and then get a hashtable like
'id' = 22
'status.name' = 'new'
'comment' = 'text'
'owner.name' = 'user'
and convert it to multi-layered hashtable based on "dot" separator then:
$baseTask = #{
task = #{
comment = $Comment;
status = #{
name = $Status;
owner = #{
name = $Owner;
}
}
}
And then to JSON
The example below is an example and should be used with caution as it HAS some problems.
function test {
[CMDLetBinding()]
Param(
[Parameter(Mandatory=$true)]
$TaskID,
$Comment,
$Status,
$Owner
)
$paramList = #{}
$params = $PSCmdlet.MyInvocation.BoundParameters
foreach ($paramName in $params.Keys) {
switch -Exact ($paramName) {
'TaskID' { $paramList['id'] = $params[$paramName] }
'Comment' { $paramList['comment'] = $params[$paramName] }
'Status' { $paramList['status.name'] = $params[$paramName] }
'Owner' { $paramList['owner.name'] = $params[$paramName] }
}
}
$replaced = 0
do {
$replaced = 0
$keys = #($paramList.Keys)
foreach ($key in $keys) {
$dotPos = $key.LastIndexOf('.')
if ($dotPos -gt 0) {
$value = $paramList[$key]
if (-not $value.GetType().IsValueType) {
if ($value.Clone -ne $null) {
$value = $value.Clone()
}
}
$subKey = $key.Substring($dotPos + 1)
$newKey = $key.Substring(0, $dotPos)
$paramList.Remove($key) | Out-Null
$newVal = #{$subKey=$value}
$paramList.Add($newKey, $newVal)
$replaced++
}
}
} while ($replaced -gt 0)
return $paramList
}
test -TaskID 123 -Status 'New' -Comment 'Test' -Owner 'user' | ConvertTo-Json -Depth 10 -Compress:$false
I'm using the following code to change a Pre-shared key for MEraki, however, at the moment I'm only able to do it statically.
I'd like to automate the code so that the PSK is an actual variable.
I've tried using the " or ' however in either scenario i'm unable to recall the variable into the array, and my end goal is to automate this script so that I can just run it and poof, off it goes.
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 -bor [System.Net.SecurityProtocolType]::Tls11
function Get-RandomCharacters($length, $characters) {
$random = 1..$length | ForEach-Object { Get-Random -Maximum $characters.length }
$private:ofs=""
return [String]$characters[$random]
}
function Scramble-String([string]$inputString){
$characterArray = $inputString.ToCharArray()
$scrambledStringArray = $characterArray | Get-Random -Count $characterArray.Length
$outputString = -join $scrambledStringArray
return $outputString
}
$password = Get-RandomCharacters -length 5 -characters 'abcdefghiklmnoprstuvwxyz'
$password += Get-RandomCharacters -length 1 -characters 'ABCDEFGHKLMNOPRSTUVWXYZ'
$password += Get-RandomCharacters -length 1 -characters '1234567890'
$password = Scramble-String $password
$newDailyPassword = $password
$newDailyPassword
#Meraki API KEY
$api_key = "API_KEY"
#Meraki Network URL
$network_id = "NETWORK_ID"
#Base API URL
$api = #{
"endpoint" = 'https://api.meraki.com/api/v0'
}
#API URL for SSID PSK Change XXX
$api_put = #{
"endpoint" = 'https://nXXX.meraki.com/api/v0'
}
$header_org = #{
"X-Cisco-Meraki-API-Key" = $api_key
"Content-Type" = 'application/json'
}
# PSK = New password
$data = #{
"psk" = $newDailyPassword **#----> I'd like this to be the new daily password variable. Is there a way to do this?**
}
#Convert data to Json format
$jbody = ConvertTo-Json -InputObject $data
#URL Network_ID and SSID number
$api.ssid = "/networks/$network_id/ssids/2"
#Combine base api_put URL and $api.ssid
$Merakiuri = $api_put.endpoint + $api.ssid
Invoke-RestMethod -Method Put -Uri $Merakiuri -Headers $header_org -Body $jbody -Verbose
EDIT1: I've managed to resolve this, by applying the concatination of the variable string, as I was parsing unexpected query via JSON (single string).
I've done the below.
$JsonJoiner = "'"
$passwordx = -join($JsonJoiner,$password, $JSonJoiner)
$newDailyPassword = $passwordx
now, whenever I parse the
$newDailyPassword
the correct value is parsed, and i'm able to generate the PW.
i need to write a powershell script to connect to a REST API and to output into a text file stored locally. How do i write it such that if the REST API has issues and timeout without returning a response, the powershell script will trigger an email to send out when the saved text file is empty?
Also, how do i append the date to the saved text file for eg. c:\scripts\response_DDMMYYYY.txt
Below are my current powershell script. Newbie at powershell scripting. Hope someone can enlighten me.
Thanks alot!
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12
add-type #"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"#
[System.Net.ServicePointManager]::CertificatePolicy = New-Object
TrustAllCertsPolicy
$url = "https://someapi.com/get_info"
$token = '12345'
$header = #{Authorization = "Bearer $token" }
$body = #{code = "ABC"}
$result = Invoke-RestMethod -Method POST -Uri $url -ContentType
'application/json' -Body (ConvertTo-Json $body) -Header $header
$result | ConvertTo-Json -depth 100 | Set-Content "c:\scripts\response.txt"
Edited based on kumar's advice:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12
add-type #"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"#
[System.Net.ServicePointManager]::CertificatePolicy = New-Object
TrustAllCertsPolicy
$url = "https://someapi.com/get_info"
$token = '12345'
$header = #{Authorization = "Bearer $token" }
$body = #{code = "ABC"}
try {
$result = Invoke-RestMethod -Method POST -Uri $url -ContentType
'application/json' -Body (ConvertTo-Json $body) -Header $header
$time = (Get-Date).ToString("DD-MM-YYYY")
$result | ConvertTo-Json -depth 100 | Set-Content "c:\scripts\response-
"+$time+".txt"
} catch {
Write-Host "StatusCode: " $_.Exception.Response.StatusCode.value__
Write-Host "StatusDescription:"
$_.Exception.Response.Status.StatusDescription
Send-MailMessage -To "ABC<abc#gmail.com>" -From "server <admin#abc.com>"
-Subject "Error with sync to server"
}
You have a few options to get the response status code of the REST call like so..
try {
Invoke-RestMethod ... your parameters here ...
} catch {
# Dig into the exception to get the Response details.
# Note that value__ is not a typo.
Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
}
& Use the Send-MailMessage
Send-MailMessage -To "User01 <user01#example.com>" -From "User02 <user02#example.com>" -Subject "Test mail"
Also, how do i append the date to the saved text file for eg. c:\scripts\response_DDMMYYYY.txt
A simple and nice way is:
$time = (Get-Date).ToString("DD-MM-YYYY")
and simply
("C:\scripts\response-"+$time+".txt")
I have following script it works fine in ISE. I can execute and debug this script by ISE.
$file = "backup_settings.json";
class JsonParser
{
$json = $null;
$setting = $null;
$validJson = $false;
$baseAddress = $null;
JsonParser()
{
}
ParseJson()
{
try
{
$this.json = Get-Content -Raw $global:file;
$this.setting = ConvertFrom-Json $this.json -ErrorAction Stop;
$this.validJson = $true;
}
catch
{
$this.validJson = $false;
}
if (-Not $this.validJson)
{
Write-Host "Provided text is not a valid JSON string" -foregroundcolor "Red";
return;
}
if($this.setting.baseAddress)
{
$this.baseAddress = $this.setting.baseAddress;
Write-Host $this.baseAddress;
}
else
{
Write-Host "No valid baseAddress provided setting file" -foregroundcolor "Red";
return;
}
}
}
$json = [JsonParser]::new();
$json.ParseJson();
But in PowerShell version 5 the result is:-
Provided text is not a valid JSON string
Note that I have backup_settings.json in the same directory. What could be the reason? How can I run this in power shell with correct result?
Updated code and result:-
I tried the following:-
$file = (Join-Path $PSScriptRoot "backup_settings.json")
Write-Host $file;
if([String]::IsNullOrEmpty($file))
{
Write-Host "Empty path"
}
else
{
Write-Host "file path: "$file;
}
if(Test-Path $file)
{
Write-Host "File exist" $file.ToString();
}
else
{
Write-Host "file not found".
}
$json = [JsonParser]::new();
$json.ParseJson();
Result:-
C:\tools\backup_settings.json
file path: C:\tools\backup_settings.json
File exist C:\tools\backup_settings.json
Provided text is not a valid JSON string
Sounds like the path to your JSON file is not properly resolved when you're running it in powershell. Add the following to see if that's the case:
Test-Path $global:file
If it prints out False, that would mean that your script is not able to find the json file. In that case, you should probably change the first line to something like this:
$file = (Join-Path $PSScriptRoot "backup_settings.json")
I have a dictionary like this:
function HashHandlerSHA256
{
param($Path, $Checksum)
$csp = new-object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider
$ComputedHash = $csp.ComputeHash([System.IO.File]::ReadAllBytes($Path))
$ComputedHash = [System.BitConverter]::ToString($ComputedHash).Replace("-", "").ToLower()
$result = $ComputedHash.CompareTo($Checksum)
return $result -eq 0
}
$HashHandler = #{"SHA256" = HashHandlerSHA256}
containing validation algorithms and the functions to be called for validation. The functions should all have the same parameter and return type.
Now when I have:
$Checksums = #{"SHA256" = "..."}
I'd like to call the correct function depending on which algorithms and values I have available. In this case I'd have a valid sha256 hash.
Now I want to do:
function Validate
{
param($Path, $Checksums)
foreach($Hash in $Checksums) {
$Type = $Hash.Name
$Value = $Hash.Value
if ($HashHandler.ContainsKey($Type)) {
$Handler = $HashHandler.Get_Item($Type)
if (-Not ($Handler -Path $Path -Checksum $Value)) {
return $FALSE
}
}
}
return $TRUE
}
The errormessage I get is:
At C:\Users\username\Desktop\hashtest.ps1:27 char:23
+ if (-Not ($Handler -Path $Path -Checksum $Value)) {
+ ~~~~~
Unexpected token '-Path' in expression or statement.
I am relatively new to PowerShell. I know how to call functions with parameters, but when stored in a variable, I didn't manage to solve this and when searching online for this, I didn't get the answers I needed.
Thanks for help.
if i understand you want Something like this
function HashHandlerSHA256
{
param($Path, $Checksum)
$csp = new-object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider
$ComputedHash = $csp.ComputeHash([System.IO.File]::ReadAllBytes($Path))
$ComputedHash = [System.BitConverter]::ToString($ComputedHash).Replace("-", "").ToLower()
$result = $ComputedHash.CompareTo($Checksum)
return $result -eq 0
}
function Validate
{
param($Path, $Checksums)
foreach($Hashkey in $Checksums.Keys)
{
$Value = $Checksums[$Hashkey]
if ($script:HashHandler.ContainsKey($Hashkey))
{
if (-Not (&$script:HashHandler[$Hashkey] -Path $Path -Checksum $Value)) { return $false}
}
}
return $TRUE
}
#add here your couples of algo/function
$script:HashHandler = #{"SHA256" = 'HashHandlerSHA256'}
#checksum to test
$Checksums=#{}
$Checksums["SHA256"]= 'd6a0a09fb1a7971b497674675d5b5621d865d6020e384137548de9c4ac6d4994'
$Checksums["MD5"]= 'xxxx'
#test list checksum and algo
Validate -Path "c:\temp\hello.csv" -Checksums $Checksums
an other solution
$file="C:\temp\exludevalue.txt"
$Checksums=#{}
$Checksums["SHA256"]= 'd6a0a09fb1a7971b497674675d5b5621d865d6020e384137548de9c4ac6d4994'
$Checksums["MD5k"]= '11A8D99F80F9B29FCF6A995D2F17B2E3'
$Checksums.Keys |
%{
if ($(gcm Get-FileHash).Parameters.Algorithm.Attributes.ValidValues -contains $_)
{
$algocalc=(Get-FileHash -path $file -Algorithm $_).Hash;
}
else
{
$algocalc='ALGO NOT FOUNDED'
}
new-object psobject -Property #{
Algo=$_
OldValue=$Checksums[$_]
CalculedValue=$algocalc
ResultComparison= $algocalc -eq $Checksums[$_]
}
}