String array within JSON - arrays

going mad here, have forgot how to add and array to a JSON object in powershell :/, trying to invoke an API from powershell that requires some parameters one of these is a string array, can someone help?
$Children = #("Nanna", "Damian")
$body = #{
'parameters' = #{
'Name' = 'whatever'
'Children' = $Children
}
} | ConvertTo-Json
$body
Invoke-RestMethod -Uri $URI -Body $body -Headers $header -Method Put
but it keeps giving me this error :(
Invoke-RestMethod : {"code":"BadRequest","message":"{\"Message\":\"The request is invalid.\",\"ModelState\":{\"job.properties.
parameters.AllowedResourceTypes\":[\"An error has occurred.\"],\"job.properties.parameters.AllowedResourceTypes[0]\":[\"An err
or has occurred.\"]}}"}
believe I have done this before but cant remember how.

$Children = #{'Child1'='Nanna'
'Child2'='Damian'}
$body = #{
'parameters' = #{
'Name' = 'whatever'
'Children' = $Children
}
} | ConvertTo-Json
$body
proper Json

Related

powershell array sorting posting json vice-versa

I have an issue with putting array elements into JSON.
Here is my script:
function UpdateJSON()
{
param (
[string] $ApplicationSettingUserValue,
[string] $ApplicationSettingPassValue
)
$JSON = Get-Content $File| Out-String | ConvertFrom-JSON
$arrayUsers = #{"username"=$ApplicationSettingUserValue; "password"=$ApplicationSettingPassValue}
$JSON.users | Add-Member -Type NoteProperty -Name 'test_user' -Value $arrayUsers
ConvertTo-Json $JSON | Set-Content "..\Desktop\json\users.json"
}
In Output I receive the following JSON:
{
"users": {
"test user": {
"password": "TestUser1pass",
"username": "username"
}
}
}
The thing is, that when I'm trying to add the third variable into an array the order becomes right in the output: username, password and . Otherwise, the password and username add in the wrong order.
I tried to put elements into array vice-versa however, nothing changes.
Does anyone know what did I miss?
The required output is:
{
"users": {
"test user": {
"username": "username",
"password": "TestUser1pass"
}
}
}
As commented by #JozefZ, the work arround for this issue is to use an ordered dictionary:
$arrayUsers = [ordered]#{"username"=$ApplicationSettingUserValue; "password"=$ApplicationSettingPassValue}
Background
The ConvertFrom-Json cmdlet returns objects ([PSCustomObject]) rather than hashtables by default (unless you use the -AsHashtable parameter):
$Json.users.'test user'.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False PSCustomObject System.Object
Or, if you want to reveal the whole PowerShell structure:
ConvertTo-Expression $Json # https://www.powershellgallery.com/packages/ConvertTo-Expression
[pscustomobject]#{'users' = [pscustomobject]#{'test user' = [pscustomobject]#{
'username' = 'username'
'password' = 'TestUser1pass'
}}}
Note that the properties of a PSCustomObject are also ordered.
Meaning, you might use the [ordered] type to created additional users:
$arrayUsers = [Ordered]#{username = 'user2'; password = 'pwd2'}
$Json.users | Add-Member -Type NoteProperty -Name 'test user2' -Value $arrayUsers
But that will give you different user objects:
$Json.Users | ConvertTo-Expression
[pscustomobject]#{
'test user' = [pscustomobject]#{
'username' = 'username'
'password' = 'TestUser1pass'
}
'test user2' = [ordered]#{
'username' = 'user2'
'password' = 'pwd2'
}
}
But it is actually better to create additional user objects using the [pscustomobject] type:
$arrayUsers = [PSCustomObject]#{username = 'user2'; password = 'pwd2'}
$JSON.users | Add-Member -Type NoteProperty -Name 'test user2' -Value $arrayUsers
$Json.Users | ConvertTo-Expression
[pscustomobject]#{
'test user' = [pscustomobject]#{
'username' = 'username'
'password' = 'TestUser1pass'
}
'test user2' = [pscustomobject]#{
'username' = 'user2'
'password' = 'pwd2'
}
}
It doesn't matter for the end result (if you serialize the whole object using ConvertTo-Json) but it might, if you want manipulate the user list (e.g. sorting) prior serializing it.

Match values from file to array and extract other array column data powershell

I have a file containing a list of jobs. I need to run through that file in a foreach loop and for each value I need to look for that in the first column of an array then check the value in the 3rd column and if that is N output the jobname.
So far I have,
$header = 1..4 | ForEach-Object { "h$_" }
$data = Get-Content F:\temp\Connect_Project\connect_jobs.txt
$data2 = "F:\temp\Connect_Project\alljobs.csv" |
Import-Csv -Delimiter ',' -Header $header
$stream = [System.IO.StreamWriter] "F:\temp\Connect_Project\jobs_to_change.txt"
foreach ( $line in $data ) {
if ( $data2.h1 -match $line ) {
if ( $data2.h3 -match 'N' ) {
$stream.WriteLine($line)
}}}
$stream.Close()
I know that's not working (although it is removing jobs that don't exist in the array) but I'm struggling to work out how to fix it.
Can anyone help?
To update based on comments,
I've also tried
$header = 1..4 | ForEach-Object { "h$_" }
$data = Get-Content F:\temp\Connect_Project\connect_jobs.txt
$data2 = "F:\temp\Connect_Project\alljobs.csv" |
Import-Csv -Delimiter ',' -Header $header
$stream = [System.IO.StreamWriter] "F:\temp\Connect_Project\jobs_to_change.txt"
foreach ( $line in $data ) {
$state = ($data2 | Where-Object {$_.h1 -match $file} | Select-Object h3)c
$line
$state
if ( $state = 'N') {
$stream.WriteLine($line)
}}
$stream.Close()
I can see that I'm matching h1 but I'm getting all h3 values not just the one I want.
Well that was obvious once I spotted it. Thanks to EBGreen for the where-object tip. Second version worked once I realised I'd put $file in the match and not $line. Corrected it to $line and it's doing what I wanted.
$header = 1..4 | ForEach-Object { "h$_" }
$data = Get-Content F:\temp\Connect_Project\connect_jobs.txt
$data2 = "F:\temp\Connect_Project\alljobs.csv" |
Import-Csv -Delimiter ',' -Header $header
$stream = [System.IO.StreamWriter] "F:\temp\Connect_Project\jobs_to_change.txt"
foreach ( $line in $data ) {
$state = $data2 | Where-Object {$_.h1 -match $line} | Select-Object h3
if ( $state -match 'N') {
$stream.WriteLine($line)
}}
$stream.Close()

How do I send each result of Get-AdGroupMembership to my array?

I'm trying to recurse an NTFS folder structure, and output a CSV file that only displays each USER account with permissions on only the folders. Everything in the script outputs correctly EXCEPT for the portion that discovers a group and proceeds to enumerate the users in that group using Get-ADGroupMember. While debugging, I can see that each user within the group (even with nested groups) is outputted, but I guess I'm not properly "arraying" each output of the command and sending it onward to my "out" array.
I marked the section I'm having trouble with. Any help folks could provide would be very much appreciated. Thanks!
$Answer = Read-Host 'Do you wish to use an answer file? File must be named answer.csv and must reside in same directory as script. (Default is [N])'
If ($Answer -eq "y") {
$AnsFile = Import-Csv answer.csv | Select src,outdir,domain,user,pwd
$List_Dir = $AnsFile.src
$OutPath = $AnsFile.outdir
$DomainName = $AnsFile.domain
$Admin = $AnsFile.user
$Pwd = $AnsFile.pwd
}
Else {
Do {
$List_Dir = Read-Host 'Enter the directory path to be searched/recursed'
$TestList_Dir = Test-Path $List_Dir
If ($TestList_Dir -eq $True) {Write-Host "List directory checks out..."}
Else {Write-Host "Incorrect source directory. Please try again." -foregroundcolor red -backgroundcolor yellow}
}
While ($TestList_Dir -eq $False)
Do {
$OutPath = Read-Host 'Enter the directory path where the output files will be saved. Do not add a trailing slash.'
$TestOutPath = Test-Path $OutPath
If ($TestOutPath -eq $True) {Write-Host "Output path checks out..."}
Else {Write-Host "Incorrect output path. Please try again." -foregroundcolor red -backgroundcolor yellow}
}
While ($TestOutPath -eq $False)
$DomainName = Read-Host 'Enter the non-distinguished name of the Active Directory domain'
$Admin = Read-Host 'Type in an administrative account with rights to read AD Security Groups'
$Pwd = Read-Host 'Enter the adminstrative account password'
}
$Folder_Array = #()
write-host "List directory = $List_Dir"
write-host "Output path = $OutPath"
write-host "Domain = $DomainName"
write-host "Admin account = $Admin"
write-host "Password = $Pwd"
Import-Module ActiveDirectory
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$CType = [DirectoryServices.AccountManagement.ContextType]::Domain
$IDType = [DirectoryServices.AccountManagement.IdentityType]::SamAccountName
$DomainContext = New-Object DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $CType, $DomainName, $Admin, $Pwd
#$pat = "^[a-zA-Z0-9_:.]+$"
$pat = "^[a-zA-Z0-9_:.\]+$]"
get-childitem $List_Dir -recurse | where-object {$_.psIsContainer -eq $true} | foreach-object {
$a = ($_.FullName)
$d = $a -match $pat
$e = (get-acl $_.FullName).Access
foreach ($e1 in $e) {
$f = $e1.FileSystemRights
$g = $e1.AccessControlType
$SecID = $e1.IdentityReference
foreach ($Sec in $SecID) {
$GroupPrincipal = [DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($DomainContext, $IDType, $Sec)
if ($GroupPrincipal -ne $null) {
$Sec = $Sec.ToString()
$Sec = $Sec.Split("\")[1]
Get-AdGroupMember $Sec -Recursive | ForEach-Object {
$User = ($_.SamAccountName)
foreach ($u in $User) {
$out = new-object psobject
$out | add-member noteproperty Path $a
$out | add-member noteproperty Unix_Safe $d
$out | Add-Member NoteProperty UserAccount $u
$out | add-member noteproperty Permission $f
$out | add-member noteproperty AccessType $g
$Folder_Array += $out
}
}
}
else {
$e2 = $Sec.ToString()
$e2 = $e2.split("\")[1]
$out = new-object psobject
$out | add-member noteproperty Path $a
$out | add-member noteproperty Unix_Safe $d
$out | Add-Member NoteProperty UserAccount $e2
$out | add-member noteproperty Permission $f
$out | add-member noteproperty AccessType $g
$Folder_Array += $out
}
}
}
}
$Folder_Array | Select Path,UserAccount,Permission,AccessType,Unix_Safe | Export-Csv "$OutPath\folderonly.csv" -NoTypeInformation
The problem isn't so much with how you're doing it, it's more of when you're doing things. Let me explain...
Get-AdGroupMember $Sec -Recursive | ForEach-Object {
$User = ($_.SamAccountName)
foreach ($u in $User) {
$e2 = $u
}
}
}
****************************************************
else {
$e2 = $Sec.ToString()
$e2 = $e2.split("\")[1]
}
}
}
$out = new-object psobject
$out | add-member noteproperty Path $a
$out | add-member noteproperty Unix_Safe $d
$out | Add-Member NoteProperty UserAccount $e2
$out | add-member noteproperty Permission $f
$out | add-member noteproperty AccessType $g
$Folder_Array += $out
Given that, if it is a group you are taking all users for the group and setting that array of users to $User, and then going through that array, and assigning each user, one at a time, to $e2. Once you're done with that you create your object, and add that object to the array for output.
Let's say that group has 3 users in it, Tom, Dick, and Harvey (Harry was busy, he sent his brother instead). So now:
$User = #("Tom","Dick","Harvey")
Then you cycle through that assigning each to $e2, which basically comes out to this (some pseudocode here):
If(is a group){
$User = Get-ADGroup |select -expand samaccountname
ForEach($u in $User){
$e2 = "Tom"
<Next item in array>
$e2 = "Dick"
<next item in array>
$e2 = "Harvey"
<No more items in array, end ForEach>
So now when it moves on to create your object $e2 = "Harvey" and Tom and Dick are just out of luck. To resolve that we have options. Either:
A) Move object creation to inside the If/Else portions of the loop, specifically to create an object every time you assign $e2, and add those objects to the output array immediately after making them.
or:
B) Make $e2 an array by changing all references to setting it to read either $e2 += $u or $e2 = ,$Sec.ToString().Split("\")[1]. And then when you create objects do that like:
ForEach($User in $e2){
$Folder_Array += [PSCustomObject][Ordered]#{
'Path' = $a
'Unix_Safe' = $d
'UserAccount' = $User
'Permission' = $f
'AccessType' = $g
}
}

Using += in scriptblock for a switch statement in a foreach loop

Firstly, thank you for taking the time to read this, thank you for your help in advance.
Here is my code:
<#
SCCM Request Alert Script
#>
Import-Module ActiveDirectory
$WMIObjects = Get-WmiObject -Namespace 'ROOT\SMS\Site_EUR' -Class SMS_UserApplicationRequest -ComputerName "EUR-SCCM"
$FileStore = "c:\export\SCCMRequestfile.txt"
foreach ($Obj in $WMIObjects)
{
[String]$RequestValue = $Obj.CurrentState
$Application = $Obj.Application
$User = $Obj.User -replace 'MYDOMAIN\\',""
$ADUser = Get-ADUser -Identity $User
$PendingRequest = #()
$CancelledRequest = #()
$DeniedRequest = #()
$ApprovedRequest = #()
$Unknown = #()
$Args = #{ 'User' = $ADUser.Name; 'Application' = $Obj.Application }
$PR = New-Object -TypeName PSObject -Property $Args
$CR = New-Object -TypeName PSObject -Property $Args
$DR = New-Object -TypeName PSObject -Property $Args
$AR = New-Object -TypeName PSObject -Property $Args
$UR = New-Object -TypeName PSObject -Property $Args
switch ($RequestValue) {
1 { $PendingRequest += $PR }
2 { $CancelledRequest += $CR }
3 { $DeniedRequest += $DR }
4 { $ApprovedRequest += $AR }
default { $Unknown += $UK }
}
}
Write-Host -ForegroundColor 'yellow' "Pending Requests "
$PendingRequest
Write-Host -ForegroundColor 'DarkYellow' "Cancelled Requests "
$CancelledRequest
Write-Host -ForegroundColor 'DarkRed' "Denied Requests "
$DeniedRequest
Write-Host -ForegroundColor 'Green' "Approved Requests "
$ApprovedRequest
Write-Host -ForegroundColor 'White' "Unknown Approval Type "
$Unknown
At them moment it only returns the last object in the foreach loop.
I've tested a foreach loop manually using;
foreach ($Obj in $Objects) {
$array = #()
$array += $Obj
}
$Array
And this places each object in the the array.
So I was wondering if this was an issue with the switch statement or something I haven't done like casting it as an array?
Any help would be appreciate, Thank you.
Nigel Tatschner
You're re-initializing your arrays on each iteration of the loop. Move the following lines so they're before your foreach loop:
$PendingRequest = #();
$CancelledRequest = #();
$DeniedRequest = #();
$ApprovedRequest = #();
$Unknown = #();

Send file using multipart/form-data request in php

I have image resource stored in variable which I need to send to server using its http API and PHP. I have to send request with content type multipart/form-data. So, I need to make similiar request as when form with file input and enctype=multipart/form-data attribute is sent.
I tried this:
<?php
$url = 'here_is_url_for_web_API';
$input = fopen('delfin.jpg','r');
$header = array('Content-Type: multipart/form-data');
$resource = curl_init();
curl_setopt($resource, CURLOPT_URL, $url);
curl_setopt($resource, CURLOPT_USERPWD, "user:password");
curl_setopt($resource, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
curl_setopt($resource, CURLOPT_HTTPHEADER, $header);
curl_setopt($resource, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($resource, CURLOPT_BINARYTRANSFER, true );
curl_setopt($resource, CURLOPT_INFILESIZE, 61631);
curl_setopt($resource, CURLOPT_INFILE, $input);
$result = curl_exec($resource);
curl_close($resource);
var_dump($result);
?>
I don't know how exactly response should look like but this returns:
http status 405
and error report is: The specified HTTP method is not allowed for the requested resource ().
use multipart/form-data and boundaries in POST content with curl.
$filenames = array("/tmp/1.jpg", "/tmp/2.png");
$files = array();
foreach ($filenames as $f){
$files[$f] = file_get_contents($f);
}
// more fields for POST request
$fields = array("f1"=>"value1", "another_field2"=>"anothervalue");
$url = "http://example.com/upload";
$curl = curl_init();
$url_data = http_build_query($data);
$boundary = uniqid();
$delimiter = '-------------' . $boundary;
$post_data = build_data_files($boundary, $fields, $files);
curl_setopt_array($curl, array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
//CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => $post_data,
CURLOPT_HTTPHEADER => array(
//"Authorization: Bearer $TOKEN",
"Content-Type: multipart/form-data; boundary=" . $delimiter,
"Content-Length: " . strlen($post_data)
)
));
//
$response = curl_exec($curl);
$info = curl_getinfo($curl);
//echo "code: ${info['http_code']}";
//print_r($info['request_header']);
var_dump($response);
$err = curl_error($curl);
echo "error";
var_dump($err);
curl_close($curl);
function build_data_files($boundary, $fields, $files){
$data = '';
$eol = "\r\n";
$delimiter = '-------------' . $boundary;
foreach ($fields as $name => $content) {
$data .= "--" . $delimiter . $eol
. 'Content-Disposition: form-data; name="' . $name . "\"".$eol.$eol
. $content . $eol;
}
foreach ($files as $name => $content) {
$data .= "--" . $delimiter . $eol
. 'Content-Disposition: form-data; name="' . $name . '"; filename="' . $name . '"' . $eol
//. 'Content-Type: image/png'.$eol
. 'Content-Transfer-Encoding: binary'.$eol;
$data .= $eol;
$data .= $content . $eol;
}
$data .= "--" . $delimiter . "--".$eol;
return $data;
}
See the full example here: https://gist.github.com/maxivak/18fcac476a2f4ea02e5f80b303811d5f
If you work with CURL, you have to just:
1, set header 'Content-Type' as 'multipart/form-data;'
2, set option 'RETURNTRANSFER' of curl to true (use option method of curl)
3, set option 'POST' of curl to true (use option method of curl)
4, get source of your file (what you get from fopen in PHP):
$tempFile = tempnam(sys_get_temp_dir(), 'File_');
file_put_contents($tempFile, $source);
$post = array(
"uploadedFile" => "#" . $tempFile, //"#".$tempFile.";type=image/jpeg",
);
5, use post method of CURL with parameter in $post variable

Resources