ARM nested templates vs linked templates

Let’s say you wanna deploy an ARM template for an API Management service without using a storage account for deployment. In that case you can add all of your ARM template code to the orchestrator file. We can refer to that scenario as nested templates. The alternative is to create separate ARM template files for your version set, named values, policies, products and the API itself and use them as linked templates in your orchestrator file. In other words. You don’t assemble all template files in your orchestrator, you just refer to the linked template files via a so called templateLink.

Let’s look at an example. The first resource you will have to create, is a version set. An orchestrator with a linked template would look like this:

{
      "name": "versionSetTemplate",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2018-01-01",
      "dependsOn": [],
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "uri": "[concat(parameters('LinkedTemplatesBaseUrl'), '/alfen-is-tst-apim-apiVersionSets.template.json', parameters('LinkedTemplatesUrlQueryString'))]",
          "contentVersion": "1.0.0.0"
        }
      }
    }

Whereas an orchestrator with a nested template would look like this:

{
      "name": "TransformerSubstationVersionSet",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2019-05-01",
      "dependsOn": [],
      "properties": {
        "mode": "Incremental",
        "template": {
          "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
          "contentVersion": "1.0.0.0",
          "resources": [
            {
              "name": "[concat(parameters('apiManagementName'), '/', parameters('versionSet'))]",
              "type": "Microsoft.ApiManagement/service/apiversionsets",
              "apiVersion": "2019-01-01",
              "properties": {
                "displayName": "[parameters('apiName')]",
                "versioningScheme": "Segment"
              }
            }
          ]
        }
      }
    }

ARM resourceId Function

The syntax of the resourceid function is as follows:
resourceId(<subscriptionID>,<ResourceGroupName>,<Resource-Type>, <ResourceName1>,<ResourceName2>)

In its basic form, we need to provide just two pieces of information to the resourceId function: Resource type and resource name. We must add add subscription id and resource group name in situations where the resource is in a different resource group.

Example: KeyVaultAccessPolicy that references API Management instance:

resourceId(subscription().subscriptionId, resourceGroup().name, ‘Microsoft.ApiManagement/service’, parameters(‘apiManagementName’))

Note that in this case subscriptionId and resourceGroupName could have been left out, because KeyVault and API Management are in the same resourcegroup. I left the example as is, because it shows the correct syntax. The short notation would be:
resourceId(‘Microsoft.ApiManagement/service’, parameters(‘apiManagementName’))

You can use the Resource Explorer in the Azure Portal to find the ID of a resource.

See: Techgenix.com

ARM DependsOn

Today I have been struggling with the ARM template syntax related to dependency relations. I tried to create a servicebus subscription with subscription rules to a servicebus topic. The correct (abbreviated) syntax can be found below. I’m using function resourceId with separate parameters for servicebus, topic and subscription name, not one parameter with a nested reference.

"resources": [
        {
            "name": "[concat(parameters('serviceBusName'), '/', 'shared~werknemers')]",
            "type": "Microsoft.ServiceBus/namespaces/topics",
            "resources": [
                {
                    "name": "[concat(parameters('serviceBusName'), '/shared~werknemers/', Parameters('SBTopDeskPnbSubscriptionNaam'))]",
                    "type": "Microsoft.ServiceBus/namespaces/topics/subscriptions",
                    "dependsOn": [
	                    "[resourceId('Microsoft.ServiceBus/namespaces/topics', parameters('serviceBusName'), 'shared~werknemers')]"
                    ],
                    "resources": [
                        {
                        	"name": "[concat(parameters('serviceBusName'), '/shared~werknemers/', Parameters('SBTopDeskPnbSubscriptionNaam'), '/', parameters('SBTopDeskPnbRuleNaam'))]",
                        	"type": "Microsoft.ServiceBus/namespaces/topics/subscriptions/rules",
                        	"dependsOn": [
                            		"[resourceId('Microsoft.ServiceBus/namespaces/topics/subscriptions', parameters('serviceBusName'), 'shared~werknemers', Parameters('SBTopDeskPnbSubscriptionNaam'))]"
				]

For learning purposes, you will find my failed attempts and error messages below:

“dependsOn”: [
“[concat(‘Microsoft.ServiceBus/namespaces/topics/’, parameters(‘serviceBusName’), ‘/’, ‘shared~werknemers’)]”
]

The resource ‘/subscriptions/f7d88689-03f4-4be2-bf6d-6165ef232ad6/resourceGroups/rg-int-shared-cwd-dev/
providers/Microsoft.ServiceBus/namespaces/sb-pubsub-cwd-dev/topics/shared~werknemers/subscriptions/la-topdesk-pnb-medewerker-uit-cwd-sub’
at line ’43’ and column ’17’ doesn’t depend on parent resource
‘/subscriptions/f7d88689-03f4-4be2-bf6d-6165ef232ad6/resourceGroups/rg-int-shared-cwd-dev/
providers/Microsoft.ServiceBus/namespaces/sb-pubsub-cwd-dev/topics/shared~werknemers’.

“dependsOn”: [
“[concat(‘Microsoft.ServiceBus/namespaces/topics/’, ‘shared~werknemers’)]”
]

The template resource ‘sb-pubsub-cwd-dev/shared~werknemers/la-topdesk-pnb-medewerker-uit-cwd-sub’
at line ’43’ and column ’17’ is not valid:
The resource identificator ‘Microsoft.ServiceBus/namespaces/topics/shared~werknemers’ is malformed.

“dependsOn”: [
“[concat(parameters(‘serviceBusName’), ‘/’, ‘shared~werknemers’)]”
]

The template resource ‘sb-pubsub-cwd-dev/shared~werknemers/la-topdesk-pnb-medewerker-uit-cwd-sub’
at line ’43’ and column ’17’ is not valid:
The resource identificator ‘sb-pubsub-cwd-dev/shared~werknemers’ is malformed.

“dependsOn”: [
“[resourceId(‘Microsoft.ServiceBus/namespaces/topics’, ‘shared~werknemers’)]”
]

The template resource ‘sb-pubsub-cwd-dev/shared~werknemers/la-topdesk-pnb-medewerker-uit-cwd-sub’
at line ’43’ and column ’17’ is not valid:
Unable to evaluate template language function ‘resourceId’:
the type ‘Microsoft.ServiceBus/namespaces/topics’ requires ‘2’ resource name argument(s).

Using Parameters and Variables in Logic Apps

Using parameters and variables in Workflow Definition Language can be tempting. I already discussed the topic in the following post. The examples are chosen quite randomly, but hopefully they will help you when parameterizing your logic apps.

Using parameters in Logic Apps (basis):

“accountName”: “@parameters(‘customerName’)”
“customerName”: “First name: @{parameters(‘firstName’)} Last name: @{parameters(‘lastName’)}”
By using curly braces, you always return a string, also when the input parameter is of type integer or boolean.

Connection Parameters (no ARM templates):

@parameters(‘$connections’)[‘azureeventgrid’][‘connectionId’]
Refers to the $connections in the parameters section within your workflow definition, not in the parameters section at the top. This is an array. [‘azureeventgrid’] is the name of your connection, used as an array index. Each connection has a property connectionId.

For example:
“parameters”: {
“$connections”: {
“value”: {
“azureeventgrid”: {
“id”: “[concat(‘/subscriptions/’,subscription().subscriptionId,’/providers/Microsoft.Web/locations/westeurope/managedApis/azureeventgrid’)]”,
“connectionId”: “[resourceId(‘Microsoft.Web/connections’, parameters(‘EventGridConnectionName’))]”,
“connectionName”: “[parameters(‘EventGridConnectionName’)]”
},

Using parameters in Logic App expressions:

@concat(‘My age is ‘, string(parameters(‘myAge’)))

Parameters(‘TopicNaam’): o/[project]/[system]/[entity]/[in/out]
Parameters(‘SubscriptionName’): logicapp-[o/t/a/p]-sub with logicapp=[system]-[entity]-[protocol]-[in/out]
[concat(‘/@{encodeURIComponent(encodeURIComponent(”’, parameters(‘TopicNaam’),”’))}/subscriptions/@{encodeURIComponent(”’, parameters(‘SubscriptionNaam’), ”’)}/messages/complete’)]

Double encodeURIComponent statements are needed when the parameter used has url characters like forward slash. I’m not sure whether the ”’ notation is actually needed. Why not use string(parameters(‘TopicNaam’) or not use curly braces for the enclosed encodeURIComponent statement. So, @encodeURIComponent instead of @{encodeURIComponent}.

Using variables without concatenation:

Set Path within an Azure Storage Table Get statement.
/Tables/@{encodeURIComponent(‘Relaties’)}/entities(PartitionKey=’@{encodeURIComponent(variables(‘Source’))}’,RowKey=’@{encodeURIComponent(variables(‘RelatieNr’))}’)

Using ResourceId:

[resourceId(parameters(‘ResourceGroup’), ‘Microsoft.EventGrid/topics’,parameters(‘Topic’))]
[resourceId(subscription().subscriptionId, resourceGroup().name, ‘Microsoft.Logic/workflows’, parameters(‘logicAppNaam’))]
[resourceId(‘Microsoft.Web/connections’, parameters(‘EGTopicConnectionNaam’))]
Function ResourceId returns the unique identifier of a resource. You use this function when the resource name is ambiguous or not provisioned within the same template. Can be used at the
scope of the subscription, resource group or resource(type).

Deriving variables from parameters:

“MessageCreatorFunction”: “[concat(‘[function_name]’, parameters(‘OTAPEnvironment’),’-afu’)]”
Derive the function name from the environment, instead of using a parameter that defines the environment specific function name.

SAS key for EventGrid topic

If you want to create an API Connection for an EventGrid topic, you can retrieve the access key from the Azure Portal and store/retrieve that key via Azure Key Vault. But what if security restrictions hide the access key in the Azure Portal? What if you want to deploy your EventGrid topic via ARM templates in dev.azure.com?

Below you will find an ARM template excerpt that first creates an EventGrid topic and then creates an API Connection for that EventGrid topic by retrieving the access key:

Eventgrid Topic

{
“$schema”: “https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#&#8221;,
“contentVersion”: “1.0.0.0”,
“parameters”: {
“TopicNaam”: {
“type”: “String”
}
},
“variables”: {},
“resources”: [
{
“type”: “Microsoft.EventGrid/topics”,
“apiVersion”: “2019-06-01”,
“name”: “[parameters(‘TopicNaam’)]”,
“location”: “westeurope”
}
]
}

Eventgrid Topic Connection voor Publish

{
“$schema”: “https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#&#8221;,
“contentVersion”: “1.0.0.0”,
“parameters”: {
“Naam” : {
“type”: “string”
},
“ResourceGroup”: {
“type”: “string”,
“metadata”: {
“description”: “Resourcegroup van de Event Grid”
}
},
“Topic”: {
“type”: “string”,
“metadata”: {
“description”: “Naam van de Topic”
}
}
},
“variables”: {},
“resources”: [
{
“type”: “Microsoft.Web/connections”,
“apiVersion”: “2016-06-01”,
“location”: “westeurope”,
“name”: “[parameters(‘Naam’)]”,
“properties”: {
“api”: {
“id”: “[concat(‘/subscriptions/’, subscription().subscriptionId, ‘/providers/Microsoft.Web/locations/westeurope/managedApis/azureeventgridpublish’)]”
},
“displayName”: “[parameters(‘Naam’)]”,
“parameterValues”: {
“endpoint”: “[reference(resourceId(parameters(‘ResourceGroup’), ‘Microsoft.EventGrid/topics’, parameters(‘Topic’)), ‘2018-01-01’).endpoint]”,
“api_key”: “[listKeys(resourceId(parameters(‘ResourceGroup’), ‘Microsoft.EventGrid/topics’,parameters(‘Topic’)),’2018-01-01′).key1]”
}
}
}
]
}

Deploy API Connection using ARM

Azure API Connections have a name and a display name. When you create a new API Connection via the Azure Portal, you are not able to specify the name of the API Connection. This name is actually used in Logic Apps however. Example:
@parameters(‘$connections’)[‘sftp_1’][‘connectionId’]

In this case sftp_1 is the name, not the display name. If you deploy this logic app from DEV to ACC, you might run into problems when connection name sftp_1 refers to a different FTP server. The only way around this, is to create the API Connection via Powershell using an ARM template. The ARM template will create a resource of type Microsoft.Web/connections.

Example ARM Template:

{
“$schema”: “https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#&#8221;,
“contentVersion”: “1.0.0.0”,
“parameters”: {
“logicAppLocation”: {
“type”: “string”,
“defaultValue”: “[resourceGroup().location]”,
“allowedValues”: [
“[resourceGroup().location]”,
“eastasia”,
“southeastasia”,
“centralus”,
“eastus”,
“eastus2”,
“westus”,
“northcentralus”,
“southcentralus”,
“northeurope”,
“westeurope”,
“japanwest”,
“japaneast”,
“brazilsouth”,
“australiaeast”,
“australiasoutheast”,
“westcentralus”,
“westus2”
],
“metadata”: {
“description”: “Location of the Logic App.”
}
},
“sftp_name”: {
“type”: “string”,
“defaultValue”: “sftp”
},
“sftp_displayName”: {
“type”: “string”,
“defaultValue”: “”
},
“sftp_hostName”: {
“type”: “string”,
“defaultValue”: “”,
“metadata”: {
“description”: “Host Server Address”
}
},
“sftp_userName”: {
“type”: “string”,
“defaultValue”: “”,
“metadata”: {
“description”: “User Name”
}
},
“sftp_password”: {
“type”: “securestring”,
“defaultValue”: null,
“metadata”: {
“description”: “Password”
}
},
“sftp_portNumber”: {
“type”: “int”,
“defaultValue”: 22,
“metadata”: {
“description”: “SFTP Port Number (example: 22)”
}
},
“sftp_giveUpSecurityAndAcceptAnySshHostKey”: {
“type”: “bool”,
“defaultValue”: true,
“metadata”: {
“description”: “Disable SSH Host Key Validation? (True/False)”
}
},
“sftp_sshHostKeyFingerprint”: {
“type”: “string”,
“defaultValue”: “”,
“metadata”: {
“description”: “SSH Host Key Finger-print”
}
},
“sftp_disableUploadFilesResumeCapability”: {
“type”: “bool”,
“defaultValue”: false,
“metadata”: {
“description”: “Disable Resume Capability? (True/False)”
}
}
},
“variables”: {},
“resources”: [
{
“type”: “Microsoft.Web/connections”,
“apiVersion”: “2016-06-01”,
“location”: “[parameters(‘logicAppLocation’)]”,
“name”: “[parameters(‘sftp_name’)]”,
“properties”: {
“api”: {
“id”: “[concat(‘/subscriptions/’,subscription().subscriptionId,’/providers/Microsoft.Web/locations/’,parameters(‘logicAppLocation’),’/managedApis/sftp’)]”
},
“displayName”: “[parameters(‘sftp_displayName’)]”,
“parameterValues”: {
“hostName”: “[parameters(‘sftp_hostName’)]”,
“userName”: “[parameters(‘sftp_userName’)]”,
“password”: “[parameters(‘sftp_password’)]”,
“portNumber”: “[parameters(‘sftp_portNumber’)]”,
“giveUpSecurityAndAcceptAnySshHostKey”: “[parameters(‘sftp_giveUpSecurityAndAcceptAnySshHostKey’)]”,
“sshHostKeyFingerprint”: “[parameters(‘sftp_sshHostKeyFingerprint’)]”,
“disableUploadFilesResumeCapability”: “[parameters(‘sftp_disableUploadFilesResumeCapability’)]”
}
}
}
],
“outputs”: {}
}

It’s a bit too much, to share the entire Powershell function, but I will explain its workings. The Powershell function reads settings like the subscriptionId and the resource group from a settings.xml file. Next, it’s checked whether the user is already logged on. If not, Login-AzureRmAccount is called without parameters. This opens a window where you can specify username en password.

Finally the following statement is run:
New-AzureRmResourceGroupDeployment -TemplateFile $templateFilePath -ResourceGroupName $resourceGroupName -TemplateParameterFile $parametersFilePath

This command uses the above template file and a parameters file for configuration.

Deploy Service on API Management

My colleague Eldert Grootenboer has written an excellent series of blog posts on deploying Azure API Management via Visual Studio Team Services (VSTS). This is a short summary.

API Management can be deployed in three steps.

  • Create API Management instance via ARM deployment.
  • Create users, groups and products via ARM deployment.
  • Create virtual service that you want to expose via API Management.

For now I have performed the two first steps manually, because they are one time operations. I’m not using a specific Product, but I’m just using the Starter product. After performing the first two steps, let’s turn our attention to the third step. Creating or updating API’s can be done directly in the ARM template or it can be done in the Azure Portal. I used the Azure Portal, because that seems an easier approach to me.

Next steps:

  • You can download a tool named the ARM Template Creator at this download link.
  • Compile the ARM Template Creator solution.
  • Create a PowerShell script that uses the ARM Template Creator to generate an ARM template for the API you want to expose via API Management.

The PowerShell script looks like this:

[box type=”info”]
#if you have problem with execution policy execute this in a administrator runned powershell window.
Set-ExecutionPolicy -ExecutionPolicy Unrestricted

Import-Module “.\APIManagementTemplate.dll”

#Set the name of the API Mangement instance
$apimanagementname = ‘tulipexpress’

#Set the resource group
$resourcegroupname = ‘tlx-apimgmt’
#Set the subscription id
$subscriptionid = ‘guid’
#Set the tenant to use when login ing, make sure it has the right tennant
$tenant = ‘tulipexpress.onmicrosoft.com’

#optional set filter for a specific api (using standard REST filter, with path we can select api based on the API path)
#$filter = “path eq ‘api/v1/currencyconverter'”
$filter = “path eq ‘test'”

#setting the output filename
$filenname = ‘D:\’ + $apimanagementname + ‘.json’

Get-APIManagementTemplate -APIFilters $filter -APIManagement $apimanagementname -ResourceGroup $resourcegroupname -SubscriptionId $subscriptionid -TenantName $tenant -ExportPIManagementInstance $false | Out-File $filenname

[/box]

After running the PowerShell script via the PowerShell ISE (run as administrator), the ARM template is created. The generated ARM template (json) contains a few important parameters that define the name of the API Management instance, the name of the virtual service and the name of the backend service. Note that the template also contains a few hard-coded values (like the display name of the virtual service). Replace those with parameterized values. Obviously you will also have to create a parameter file for each environment.

When using the ARM template for the first time, I could perfectly deploy the ARM template via an ARM Resource Group deployment step (don’t mind the somewhat confusing name) in VSTS. In this particular case, a policy was used to set the backend URL. That didn’t work, so I replaced it with the normal solution that sets the backend URL via the Settings tab.

A fragment of the ARM template where the service is created is shown below:

[box type=”info”]

“resources”: [
{
“comments”: “Generated for resource /subscriptions/3c855a42-60cd-42c5-b343-f46528d6a24b/resourceGroups/FEYE-TST-API/providers/Microsoft.ApiManagement/service/feye-api-mgt-tst/apis/feye-api-external-tst”,
“type”: “Microsoft.ApiManagement/service/apis”,
“name”: “[concat(parameters(‘api-mgt_name’), ‘/’ ,parameters(‘api__name’))]”,
“apiVersion”: “2017-03-01”,
“properties”: {
“displayName”: “[parameters(‘api_name’)]”<- parameterized, not hard-coded
“apiRevision”: “[parameters(‘api_apiRevision’)]”,
“description”: “”,
“serviceUrl”: “[parameters(‘api_serviceUrl’)]”, <- url to the backend-service (no policy)
“path”: “test”,
“protocols”: [
“https”
],
“authenticationSettings”: {
“oAuth2”: null,
“openid”: null
},
“subscriptionKeyParameterNames”: {
“header”: “Ocp-Apim-Subscription-Key”,
“query”: “subscription-key”
},
“isCurrent”: “[parameters(‘feye-api-external-tst_isCurrent’)]”
}

[/box]

Logic App error using workflow parameters

I received the following error when deploying a Logic App via Powershell/ARM:
InvalidTemplate. Unable to process template language expressions in action ‘HTTP’ inputs at line ‘1’ and column ‘1420’: ‘The workflow parameter ‘ahakStorageConnectorName’ is not found.’.

At the top of the Logic App Json file, I defined the parameters. The parameter values were contained in a separate parameters.json file. I thought it would work when using a deploy statement in Powershell referencing both the json template file and the parameter file, like this:
$logicAppTemplate = $baseDir + ‘\ProcessAGP.json’
$logicAppParameter = $baseDir + ‘\ProcessAGP.parameters.json’
New-AzureRmResourceGroupDeployment -Name ‘DeployAGPTst’ -ResourceGroupName $resourcegroup -TemplateFile $logicAppTemplate -TemplateParameterFile $logicAppParameter

As I received an error I took a different approach. If you scroll down the json template file, you’ll notice there are an additional two parameter sections.
In the first parameter section I copied the parameter definitions from the top of the json template file. In the second parameter section I copied the parameter values from the parameters.json file.
This is apparantly the way, you can make parameters available to a Logic App.

Example:
{
“$schema”: “https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#&#8221;,
“contentVersion”: “1.0.0.0”,

“parameters”: {
“ahakStorageConnectorName”: {
“type”: “string”,
“metadata”: {
“description”: “Name of the AHakStorageConnector”
}
},
“dspConnectorName”: {
“type”: “string”,
“metadata”: {
“description”: “Name of the DSPConnector”
}
},
“logicAppUploadToSharepoint”: {
“type”: “string”,
“metadata”: {
“description”: “LogicApp UploadToSharepoint”
}
},
“rg”: {
“type”: “string”,
“metadata”: {
“description”: “Resourcegroup”
}
},
“rgFunctions”: {
“type”: “string”,
“metadata”: {
“description”: “Resourcegroup Functions”
}
},
“functionContainer”: {
“type”: “string”,
“metadata”: {
“description”: “Function Container”
}
}
},
“triggers”: {
“manual”: {
“type”: “Request”,
“kind”: “Http”,
“inputs”: {
“schema”: {
“$schema”: “http://json-schema.org/draft-04/schema#&#8221;,
“properties”: {
“Event”: {
“type”: “string”
},
“Id”: {
“type”: “string”
}
},
“required”: [
“Event”,
“Id”
],
“type”: “object”
}
}
}
},
“contentVersion”: “1.0.0.0”,
“outputs”: {}
},
“parameters”: {
“ahakStorageConnectorName”: {
“value”: “tstahakstorageconnector”
},
“dspConnectorName”: {
“value”: “tstdspconnector”
},
“logicAppUploadToSharepoint”: {
“value”: “TstUploadToSharePoint”
},
“rg”: {
“value”: “ahak-appservices-tst”
},
“rgFunctions”: {
“value”: “ahak-appfunctions-tst”
},
“functionContainer”: {
“value”: “ahak-functions-tst”
}
}
}
}
],
“outputs”: {}
}

Deploy Logic App using Powershell and ARM

To deploy a logic app via Powershell, I used the following approach;
Create a directory structure with two folders, i.e.:
C:\Sources\PBaars\Deployment\Release1.5\AAA-DeployResources
C:\Sources\PBaars\Deployment\Release1.5\ProcessPlanning

Folder AAA-DeployResources contains reusable artefacts like a parameter file and a Powershell module with reusable code. In this case the reusable code uploads files for a Web App or API App to a FTP folder. I will come back to that later.The parameters file is named ParametersTst.ps1. That’s because I want to deploy a Logic App to a test environment. The parameters file can also be used for AppSettings. AppSettings are relevant for Web Apps and API Apps. I will come back to that later.
The folder ProcessPlanning contains artefacts related to Logic App ProcessPlanning. The artefacts are:DeployProcessPlanning.cmdDeployProcessPlanning.ps1ProcessPlanning.jsonProcessPlanningTst.parameters.json
DeployProcessPlanning.cmd has just one purpose: call DeployProcessPlanning.ps1 passing a parameter for the environment specific parameter file. Note that we can pass a different parameter file for different environments like Acc and Prod.


@Echo Off

Echo Changing PowerShell Execution policy…

powershell Get-Execution

Policypowershell Set-ExecutionPolicy Unrestricted
powershell %~dpn0.ps1 -paramFileName “ParametersTst.ps1”
pause


DeployProcessPlanning.ps1 is the Powershell script that kicks off the Logic App deployment using both ProcessPlanning.json and ProcessPlanningTst.parameters.json. As we will see later ProcessPlanningTst.parameters.json – as the name suggests – is used for parameterization of the json file. In this case we renamed the file to ProessPlanningTst.parameters.json (note the Tst prefix), because this file performs paramterization of the Test version of the Logic App.
To use DeployProcessPlanning.ps1, we first have to add a parameter for the name of the Logic App to ParametersTst.ps1 in folder AAA-DeployResources.
# ProcessPlanning
$nameProcessPlanning = ‘TstProcessPlanning’

Now we can edit the contents of DeployProcessPlanning.ps1:


param (
[parameter(Mandatory = $true)][string] $paramFileName
)

Import-Module AzureRM.Resources

Write-Host “Login to Azure” –fore gray;
Login-AzureRmAccount

# $PSScriptRoot is null when you run from Powershell ISE
# Run from cmd file
$baseDir = $PSScriptRoot
$pos = $basedir.LastIndexOf(‘\’)
$baseDirParam = $baseDir.Substring(0,$pos)
$paramFile = $baseDirParam + “\AAA-DeployResources\” + $paramFileName

#region Load Parameter file
. $paramFile
#endregion

$logicAppTemplate = $baseDir + ‘\ProcessPlanning’ + $environment + ‘.json’
$logicAppParameter = $baseDir + ‘\ProcessPlanning’ + $environment + ‘.parameters.json’

Write-Host “BaseDir: ” $baseDir –fore gray;
Write-Host “LogicAppTemplate: ” $logicAppTemplate –fore gray;
Write-Host “LogicAppParameter: ” $logicAppParameter –fore gray;
Write-Host “ParameterFile: ” $paramFile -fore gray;

# New-AzureRmResourceGroup -Name ahak-appservices-tst -Location “westeurope”
Write-Host $(Get-Date).ToString(“yyyyMMdd_HHmss”) ” Add logic app ” $nameProcessPlanning ” to Azure” –fore gray;
New-AzureRmResourceGroupDeployment -Name ‘DeployPlanningTst’ -ResourceGroupName $resourcegroup -TemplateFile $logicAppTemplate -TemplateParameterFile $logicAppParameter

# Check status of last command, i.e. deployment to Azure
if ($?)
{
Write-Host $(Get-Date).ToString(“yyyyMMdd_HHmss”) ” Add logic app ” $nameProcessPlanning ” to Azure succeeded” –fore green
}
else
{
Write-Host $(Get-Date).ToString(“yyyyMMdd_HHmss”) ” Add logic app ” $nameProcessPlanning ” to Azure failed” –fore red
}

# List Swagger
#Invoke-AzureRmResourceAction -ResourceGroupName ahak-appservices-tst -ResourceType Microsoft.Logic/workflows -ResourceName TstProcessAGPBeoordeling -Action listSwagger -ApiVersion 2016-06-01 -Force
# GET DevProcessAGPBeoordeling
Get-AzureRmResource -ResourceGroupName $resourcegroup -ResourceType Microsoft.Logic/workflows -ResourceName $nameProcessPlanning -ApiVersion 2016-06-01


There are a few things to note:

  • First you see how the parameter file can be passed to the ps1 script.
  • Next you see the LogicAppTemplate and the LogicAppParameter file is specified. Both the template file and the parameter file have a variable $environment which makes the parameter file specific per environment. If we pass parameter ParametersPrd.ps1 to theps1 file. We can add a file ParamterPrd.ps1 to folder AAA-DeployResources and files ProcessPlanningPrd.json and ProcessPlanningPrd.parameters.json to folder ProcessPlanning.
  • Next you see, I added parameter $nameProcessPlanning at multiple places.
  • Finally I renamed the deployment to DeployProcessPlanning in command New-AzureRmResourceGroupDeployment.

Now, let’s turn our attention to ProcessPlanning.json and ProcessPlanningTst.parameters.json. Both files are copied from the original ResourceGroup project containing the Logic App.In the json file we see there are some environment-specific settings:
Custom Api App: “https://devdspconnector.azurewebsites.net/api/Planning&#8221;
Custom Api App: “https://devahakstorageconnector.azurewebsites.net/api/Storage/OpdrachtGegevens/@{triggerBody()[‘Id’]}”
Reusable Workflow: “/subscriptions/1ea9735d-00df-4375-86d5-d0d35362dd7f/resourceGroups/ahak-appservices-dev/providers/Microsoft.Logic/workflows/DevUploadToSharePoint”

It’s obvious that you want one version of the Logic App.json file, but – depending on the environment – this Logic App has to call different Api Apps (Dev/Tst/Prd) and different child workflows. To make this possible we will have to definea few parameters at the top of the json file in section parameters:


“ahakStorageConnectorName”: {
“type”: “string”,
“metadata”: {        “description”: “Name of  the AHakStorageConnector”      }
},
“dspConnectorName”: {
“type”: “string”,
“metadata”: {        “description”: “Name of  the DSPConnector”      }
},
“logicAppUploadToSharepoint”: {
“type”: “string”,
“metadata”: {        “description”: “LogicApp UploadToSharepoint”      }
},
“rg”: {
“type”: “string”,
“metadata”: {        “description”: “Resourcegroup”      }
}


Note that we have to enter the definitions of the custom parameters twice. Look for the Parameter section between the sections Resources and Triggers. If we don’t enter the parameter definitions here, we will receive an error like: ‘The only declared parameters for this definition are ‘$connections’ (i.e. there are no custom parameters).

Also note that we added a variable rg to the variables section. I can’t tell why exactly this is necessary, but otherwise it won’t work.


“variables”:
{ “rg”: “[parameters(‘rg’)]”
}


The values for these parameters we have to add the environment specific parameters file, in this case: ProcessPlanningTst.parameters.json


{
“$schema”: “https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#&#8221;,
“contentVersion”: “1.0.0.0”,
“parameters”:
{
“logicAppName”: {      “value”: “TstProcessPlanning”    },
“logicAppLocation”: {      “value”: “westeurope”    },
“ahakStorageConnectorName”: { “value”: “tstahakstorageconnector”      },
“dspConnectorName”: { “value”: “tstdspconnector”      },
“rg”: { “value”: “ahak-appservices-tst”
},
“logicAppUploadToSharepoint”: { “value”: “TstUploadToSharePoint”
}  }}


The final step, is to use the parameters in the json file:
Custom Api App: “https://@{encodeURIComponent(parameters(‘dspConnectorName’))}.azurewebsites.net/api/Planning”
Custom Api App: “https://@{encodeURIComponent(parameters(‘ahakStorageConnectorName’))}.azurewebsites.net/api/Storage/OpdrachtGegevens/@{triggerBody()[‘Id’]}”
Reusable Workflow: “[concat(‘/subscriptions/1ea9735d-00df-4375-86d5-d0d35362dd7f/resourceGroups/’, variables(‘rg’), ‘/providers/Microsoft.Logic/workflows/’, parameters(‘logicAppUploadToSharepoint’))]”

We are almost done now. Go to directory ProcessPlanning, right-click DeployProcessPlanning.cmd and Run As Administrator. When things go well, the Logic App will be deployed.

Useful link Microsoft Azure site: ARM template for Logic App

Deploy Logic App from Visual Studio

If you deploy a logic app from Visual Studio, you may run into problems. In this specific case I entered a WDL expression as the body of a Http Action:  @{decodeBase64(actionBody(‘GetDocument’)._buffer)}

After deploying the Logic App from Visual Studio, I looked at the Logic App in the Azure Portal. The code view looked OK, but in Design view the expression seemed to be replaced by actionBody(‘GetDocument’)._buffer. Strange.

If I look at the run details of the Logic App run, the input document is different:

Correct:
{
“uri”: “https://dspconnector.azurewebsites.net/api/AGA&#8221;,
“method”: “post”,
“body”: “…”
}

Incorrect:
{
“uri”: “https://dspconnector.azurewebsites.net/api/AGA&#8221;,
“method”: “query”,
“queries”: {
“document”: “…”

}

The solution of the problem is easy. Open the Logic App in the Azure Portal. In design view paste the code expression in the body of the http action, and voila, it works again. To keep the code in the Azure Portal and in Visual Studio consistent, you can – as a workaround – copy the code behind from the Azure Portal to Visual Studio. I couldn’t really tell the difference, but after doing that, Visual Studio Design View suddenly looked the same as the Azure Portal Design View. I could also successfully redeploy again. No idea what’s happening here.