Hi,
That’s an interesting question! I’ve had a look at this and come up with a solution. It isn’t a straight forward JSON variable substitution due to the requirement to “add an item if it doesn’t exist”.
The steps we’re trying to achieve:
- For some APIs, do nothing with the JSON, keep the data as it is
- For a number of Named APIs where we want to replace the key/server values
- take the API name
- see if the JSON object array has an item for that API already
- if there is an item for that API then replace the
ApiKey
value and Server
- if there is NO item for that API then create an entry and set the
ApiKey
and Server
To get this to work I took two steps
- Get all the items into the object array in the JSON file for each of the APIs
- Perform variable substitution to set the values
Pre-deployment Script
Within the deployment process step choose Configure Features
and then select Custom Deployment Scripts
Documentation can be found here: https://octopus.com/docs/deployment-examples/custom-scripts
I used a Pre-deployment script.
The PowerShell script is as follows:
$extractPath = $OctopusParameters['Octopus.Action.Package.InstallationDirectoryPath']
$json = Get-Content "$extractPath\appsettings.json" | ConvertFrom-Json
$apiKeyNames = $OctopusParameters["OctoPetShop.ApiKeyItems"].split(',')
$jsonItemTemplate = "{'ApiKey': '###APIKEY###','Server': '###APIKEYSERVER###','Name': '###APIKEYNAME###'}"
foreach($apiKeyName in $apiKeyNames){
$foundItem = $json.ArrayOfObjects | Where-Object { $_.Name -eq $apiKeyName }
if ($foundItem) {
$foundItem.ApiKey = "#{OctoPetShop.APIKey_$($apiKeyName)}"
$foundItem.Server = "#{OctoPetShop.APIKeyServer_$($apiKeyName)}"
} else {
Write-Verbose "$($apiKeyName) NOT FOUND IN CONFIG, ADDING."
$newJsonItem = $jsonItemTemplate.Replace("###APIKEY###","#{OctoPetShop.APIKey_$($apiKeyName)}").Replace("###APIKEYSERVER###","#{OctoPetShop.APIKeyServer_$($apiKeyName)}").Replace("###APIKEYNAME###","$($apiKeyName)")
$json.ArrayOfObjects += ($newJsonItem | ConvertFrom-Json)
}
}
$jsonString = ($json | ConvertTo-Json)
Set-Content -path "$extractPath\appsettings.json" -value $jsonString
Where the JSON I had in my file was
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"AppSettings": {
"AppVersion": "0.0.0",
"EnvironmentName": "Dev"
},
"ConnectionStrings": {
"OPSConnectionString": "Server=(local)\\SqlExpress; Database=ops; Trusted_connection=true"
},
"ArrayOfObjects": [
{
"ApiKey": "API KEY",
"Server": "localhost",
"Name": "apiKey1"
},
{
"ApiKey": "SomeOtherApiKey",
"Server": "Otherserver",
"Name": "Should stay untouched"
}
]
}
To know which items I want in ArrayOfObjects
I set a variable OctoPetShop.ApiKeyItems
to be used by the script of OctoPetShop.ApiKeyItems
and gave it a value of apiKey1,apiKey2,apiKey3,apiKey4
The script will take:
{
"ApiKey": "API KEY",
"Server": "localhost",
"Name": "apiKey1"
},
and will output
{
"ApiKey": "#{OctoPetShop.APIKey_apiKey1}",
"Server": "#{OctoPetShop.APIKeyServer_apiKey1}",
"Name": "apiKey1"
},
This gives us a file with a number of items that can have variable substitution applied to them.
Variable Substitution
Now we have all of the items required in the array we can look at putting the correct values against them.
Return to the Configure Features
options on the step editor and then select Substitute variables in files
in addition to Custom Deployment Scripts
.
This now gives you a section on the step editor where you must supply the name of the file to perform variable substitution against, in this case appsettings.json
:
Set the variables
The variables required to be set in order to get the replacements done are shown here:
Documentation on variables can be found here: https://octopus.com/docs/projects/variables
And on variable substitutions here: https://octopus.com/docs/projects/variables/variable-substitutions
Result
This gave the result shown here:
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"AppSettings": {
"AppVersion": "0.0.0",
"EnvironmentName": "TheKeyForApi2"
},
"ConnectionStrings": {
"OPSConnectionString": "Server=(local)\\SqlExpress; Database=ops; Trusted_connection=true"
},
"ArrayOfObjects": [
{
"ApiKey": "TheKeyForApi2",
"Server": "https://server1/api",
"Name": "apiKey1"
},
{
"ApiKey": "SomeOtherApiKey",
"Server": "Otherserver",
"Name": "Should stay untouched"
},
{
"ApiKey": "TheKeyForApi1",
"Server": "https://server2/api",
"Name": "ApiKey2"
},
{
"ApiKey": "TheKeyForApi3",
"Server": "https://server3/api",
"Name": "ApiKey3"
},
{
"ApiKey": "TheKeyForApi4",
"Server": "https://server2/api",
"Name": "ApiKey4"
}
]
}