I'm currently working on a project developing PowerShell commands to administer a device my company makes. The device responds to all sorts of different APIs for all its functions.
I'm currently having an issue converting a group of strings into the proper format so that when I ConvertTo-JSON it comes out right.
I have gotten it to work, but I had to do some string manipulation at the end. I'm sure there's a way to do it right, but I can't find the solution. It's not the prettiest solution, but it currently works. I was hoping someone had another idea, or simply I missed something.
The problem is with the Subject section of the CSR.
My API wants:
"names": [
{
"C": string,
"L": string,
"O": string,
"OU": string,
"ST": string
}
And I can't seem to get it to function "cleanly"
Here is the schema my device is looking for for the API call.
{
"cn": string,
"algorithm": string,
"dnsNames": [
string
],
"emailAddresses": [
string
],
"encryptionAlgo": string,
"ipAddresses": [
string
],
"name": string,
"names": [
{
"C": string,
"L": string,
"O": string,
"OU": string,
"ST": string
}
],
"password": string,
"privateKeyBytes": string,
"size": integer
}
Here's my function:
function New-ConnectionCSR {
param (
[Parameter(Mandatory=$true)] [string] $name,
[Parameter()] [string] $cn=$name,
[Parameter()] [string] $algorithm="RSA",
[Parameter()] [int] $size=2048,
[Parameter()] [string[]] $dnsNames,
[Parameter()] [string[]] $ipAddresses,
[Parameter()] [string[]] $emailAddresses,
[Parameter()] [Alias('organization','org')] [string] $o="",
[Parameter()] [Alias('oganizationalunit')] [string] $ou="",
[Parameter()] [Alias('location','locality')] [string] $l="",
[Parameter()] [Alias('state')] [string] $st="",
[Parameter()] [Alias('country')] [string] $c=""
)
Write-Debug "Start: $($MyInvocation.MyCommand.Name)"
# Mandatory Parameters
$body=@{
"name" = $name
"cn" = $cn
"algorithm" = $algorithm
"names" = @()
}
#Optional Parameters
if($size){$body.Add('size',$size)}
if($dnsNames){
$dnsNames = $dnsNames.Split(",")
$body.Add('dnsNames',$dnsNames)
}
if($ipAddresses){
$ipAddresses = $ipAddresses.Split(",")
$body.Add('ipAddresses',$ipAddresses)
}
if($emailAddresses){
$emailAddresses = $emailAddresses.Split(",")
$body.Add('emailAddresses',$emailAddresses)
}
if($ou -or $o -or $l -or $st -or $c){
$names = @()
if($ou){ $names += ('"ou":"' + $ou + '"') }
if($o){ $names += ('"o":"' + $o + '"') }
if($l){ $names += ('"l":"' + $l + '"') }
if($st){ $names += ('"st":"' + $st + '"') }
if($c){ $names += ('"c":"' + $c + '"') }
$body.names += "{ $(($names -join ",")) }"
}
$jsonBody = ($body | ConvertTo-Json).Replace('\"','"')
$jsonBody = $jsonBody.Replace('"{','{')
$jsonBody = $jsonBody.Replace('}"','}')
Write-Debug "JSON Body:`n$($jsonBody)"
}
**Note the string manipulation at the end. That's what I don't want. **
After that last line, I do all the API calls, but the code should work as is as a function and spit out the JSON Body.
Lastly, here's the command I use to invoke:
New-ConnectionCSR -name "MyConnectionCert" -cn "mydevice.local" -size 2048 -dnsNames "mydevice.contoso.com,mydevice.contoso.local" -ipAddresses "10.0.0.1" -emailAddresses "support@contoso.com" -ou "MyOU" -o "MyOrg" -l "MyCity" -st "FDL" -c "USA"
If you change the middle block of your function to this it should work...
...
if($ou -or $o -or $l -or $st -or $c){
$dn = [ordered] @{}
if($c) { $dn.C = $c }
if($l) { $dn.L = $l }
if($o) { $dn.O = $o }
if($ou) { $dn.OU = $ou }
if($st) { $dn.ST = $st }
$body.names = @( $dn )
}
$jsonBody = $body | ConvertTo-Json
...
Instead of building an array of strings you build an (ordered) dictionary of key-value pairs - PowerShell will happily convert this cleanly to json without any further work required...
...
"names": [
{
"C": "USA"
"L": "MyCity",
"O": "MyOrg",
"OU": "MyOU",
"ST": "FDL"
}
],
...
(I used $dn
for the temporary variable as it looks like you're building a Distinguished Name for an X509 certificate...)
Btw, if you want to control the order of the root object properties you can do this to make $body
an ordered dictionary as well, instead of a hashtable - that way your json properties will be emitted in a predictable order:
$body = [ordered] @{
... etc ...
}