Deploying Logic App Standard resource using Bicep templates and DevOps pipeline

This post has been republished via RSS; it originally appeared at: New blog articles in Microsoft Community Hub.

Logic Apps Standard simplifies deployment tasks by separating infrastructure and code deployments. If you are interested in setting up the full end-to-end CI/CD pipeline for Logic Apps n Azure DeveOps, you can find detailed description of these steps in the blog Deploying Logic App Standard resource through DevOps pipeline - Microsoft Community Hub

In this blog, we will explore the creation of Logic app infrastructure components using Azure Biceps template.

Bicep is a domain-specific language (DSL) that uses a declarative syntax to deploy Azure resources. In a Bicep file, you define the infrastructure you want to deploy to Azure, and then use that file throughout the development lifecycle to deploy your infrastructure repeatedly.

 

Logic App Bicep template

 

The Bicep VS Code extension helps to decompile ARM JSON template into Bicep template and also provides intellisense for writing the Bicep code.

In this example, I am using an existing ARM template for Logic App standard and decompiling it into Bicep template.

 

Alternatively, you can export the logic app template from the logic app in the Azure portal.

If you are exporting the template from Azure portal, make sure to follow the following steps:

  1. Remove the “siteConfig” section and functionsRuntimeScaleMonitoringEnabled from the template, as these settings aren’t applicable to Logic App.

If it isn’t removed, the deployment would fail with the error "'Runtime Scale Monitoring is not supported for this Functions version"

  1. Add app settings to the template.
  2. Add an output section to output the managed identity object ID and tenant ID.

I am using the ARM template for Logic App Standard deployment from the repository- LogicAppStandard/logicapp-template.json at master · ShreeDivyaMV/LogicAppStandard (github.com)

Install  Bicep VS Code extension on VS Code and open this file. Right-click on the ARM template and select “Decompile into Bicep”

 

Shree_Divya_M_V_0-1678071401197.png

 

It would create a Bicep file in the same folder with the same name (e.g. template.bicep). The resulting Bicep template would look like the one below.

 

 

 

 

 

 

 

param sites_shmvlastandarddemo1_name string = 'shmvlastandarddemo1' param serverfarms_LAStandardDeploymentASPdemo1_externalid string = '/subscriptions/ea3e783f-6b4a-4e74-b379-9fa512da2b7f/resourceGroups/TriageDemo/providers/Microsoft.Web/serverfarms/LAStandardDeploymentASPdemo1' param virtualNetworks_LAV2VNET_externalid string = '/subscriptions/<subscriptionID>/resourceGroups/PrivateEndpointVeeraTemplate/providers/Microsoft.Network/virtualNetworks/LAV2VNET' resource sites_shmvlastandarddemo1_name_resource 'Microsoft.Web/sites@2022-03-01' = { name: sites_shmvlastandarddemo1_name location: 'East US' tags: { Environment: 'dev' Project: 'logicappsample' } kind: 'functionapp,workflowapp' identity: { type: 'SystemAssigned' } properties: { enabled: true hostNameSslStates: [ { name: '${sites_shmvlastandarddemo1_name}.azurewebsites.net' sslState: 'Disabled' hostType: 'Standard' } { name: '${sites_shmvlastandarddemo1_name}.scm.azurewebsites.net' sslState: 'Disabled' hostType: 'Repository' } ] serverFarmId: serverfarms_LAStandardDeploymentASPdemo1_externalid reserved: false isXenon: false hyperV: false vnetRouteAllEnabled: true vnetImagePullEnabled: false vnetContentShareEnabled: false scmSiteAlsoStopped: false clientAffinityEnabled: false clientCertEnabled: false clientCertMode: 'Required' hostNamesDisabled: false customDomainVerificationId: '75E6CA55AA209D7CA6DAD86D9D0FC9A27D1EA10B12483E62DF91E9605058419C' containerSize: 1536 dailyMemoryTimeQuota: 0 httpsOnly: true redundancyMode: 'None' storageAccountRequired: false virtualNetworkSubnetId: '${virtualNetworks_LAV2VNET_externalid}/subnets/Subnet1' keyVaultReferenceIdentity: 'SystemAssigned' } } resource sites_shmvlastandarddemo1_name_ftp 'Microsoft.Web/sites/basicPublishingCredentialsPolicies@2022-03-01' = { parent: sites_shmvlastandarddemo1_name_resource name: 'ftp' location: 'East US' tags: { Environment: 'dev' Project: 'logicappsample' } properties: { allow: true } } resource sites_shmvlastandarddemo1_name_scm 'Microsoft.Web/sites/basicPublishingCredentialsPolicies@2022-03-01' = { parent: sites_shmvlastandarddemo1_name_resource name: 'scm' location: 'East US' tags: { Environment: 'dev' Project: 'logicappsample' } properties: { allow: true } } resource sites_shmvlastandarddemo1_name_web 'Microsoft.Web/sites/config@2022-03-01' = { parent: sites_shmvlastandarddemo1_name_resource name: 'web' location: 'East US' tags: { Environment: 'dev' Project: 'logicappsample' } properties: { numberOfWorkers: 1 defaultDocuments: [ 'Default.htm' 'Default.html' 'Default.asp' 'index.htm' 'index.html' 'iisstart.htm' 'default.aspx' 'index.php' ] netFrameworkVersion: 'v4.0' requestTracingEnabled: false remoteDebuggingEnabled: false remoteDebuggingVersion: 'VS2019' httpLoggingEnabled: false acrUseManagedIdentityCreds: false logsDirectorySizeLimit: 35 detailedErrorLoggingEnabled: false publishingUsername: '$shmvlastandarddemo1' scmType: 'VSTSRM' use32BitWorkerProcess: true webSocketsEnabled: false alwaysOn: false managedPipelineMode: 'Integrated' virtualApplications: [ { virtualPath: '/' physicalPath: 'site\\wwwroot' preloadEnabled: false } ] loadBalancing: 'LeastRequests' experiments: { rampUpRules: [] } autoHealEnabled: false vnetName: 'aaf431f7-0911-45ac-8b14-c4b0020cfbd2_Subnet1' vnetRouteAllEnabled: true vnetPrivatePortsCount: 0 localMySqlEnabled: false managedServiceIdentityId: 31238 ipSecurityRestrictions: [ { ipAddress: 'Any' action: 'Allow' priority: 2147483647 name: 'Allow all' description: 'Allow all access' } ] scmIpSecurityRestrictions: [ { ipAddress: 'Any' action: 'Allow' priority: 2147483647 name: 'Allow all' description: 'Allow all access' } ] scmIpSecurityRestrictionsUseMain: false http20Enabled: false minTlsVersion: '1.2' scmMinTlsVersion: '1.2' ftpsState: 'AllAllowed' preWarmedInstanceCount: 1 functionAppScaleLimit: 0 minimumElasticInstanceCount: 1 azureStorageAccounts: { } } } resource sites_shmvlastandarddemo1_name_sites_shmvlastandarddemo1_name_azurewebsites_net 'Microsoft.Web/sites/hostNameBindings@2022-03-01' = { parent: sites_shmvlastandarddemo1_name_resource name: '${sites_shmvlastandarddemo1_name}.azurewebsites.net' location: 'East US' properties: { siteName: 'shmvlastandarddemo1' hostNameType: 'Verified' } } resource sites_shmvlastandarddemo1_name_aaf431f7_0911_45ac_8b14_c4b0020cfbd2_Subnet1 'Microsoft.Web/sites/virtualNetworkConnections@2022-03-01' = { parent: sites_shmvlastandarddemo1_name_resource name: 'aaf431f7-0911-45ac-8b14-c4b0020cfbd2_Subnet1' location: 'East US' properties: { vnetResourceId: '${virtualNetworks_LAV2VNET_externalid}/subnets/Subnet1' isSwift: true } } output logicAppSystemAssignedIdentityTenantId string = subscription().tenantId output logicAppSystemAssignedIdentityObjectId string = reference(sites_shmvlastandarddemo1_name_resource.id, '2019-08-01', 'full').identity.principalId output LAname string = sites_shmvlastandarddemo1_name

 

 

 

 

 

 

 

 

Right click on the Bicep file and click on “Deploy Bicep file”. It would ask for few details like resource group name and parameter values. Once you provide the parameter values, you can choose to save them as a parameter file too.

 

Shree_Divya_M_V_1-1678071401219.png

 

Managed API Connections template:

As part of the infrastructure pipeline, we would deploy the API connections, add access policy to the logic app’s managed identity, and export the connection runtime URL as an app setting.

For this example, I have taken the connections ARM template from this repository - LogicAppStandard/connectors-template.json at master · ShreeDivyaMV/LogicAppStandard (github.com)

 

Right-click on the connections ARM template and select “Decompile into Bicep”. The resulting bicep would look like the below snippet.

VS Code IntelliSense might show some warnings on the access policy section, But it doesn’t throw any errors during deployment.

 

 

 

 

 

 

@description('The datacenter to use for the deployment.') param location string param logicAppSystemAssignedIdentityTenantId string param logicAppSystemAssignedIdentityObjectId string param sa_name string = 'sa' param connections_azureblob_name string = 'azureblob' var sa_var = concat(toLower(sa_name), uniqueString(resourceGroup().id)) resource sa 'Microsoft.Storage/storageAccounts@2020-08-01-preview' = { name: sa_var location: location sku: { name: 'Standard_LRS' tier: 'Standard' } kind: 'Storage' properties: { networkAcls: { bypass: 'AzureServices' virtualNetworkRules: [] ipRules: [] defaultAction: 'Allow' } supportsHttpsTrafficOnly: true encryption: { services: { file: { keyType: 'Account' enabled: true } blob: { keyType: 'Account' enabled: true } } keySource: 'Microsoft.Storage' } } } resource sa_default_blobs 'Microsoft.Storage/storageAccounts/blobServices/containers@2018-02-01' = { name: '${sa_var}/default/blobs' properties: { defaultEncryptionScope: '$account-encryption-key' denyEncryptionScopeOverride: false publicAccess: 'Container' } dependsOn: [ sa ] } resource connections_azureblob_name_resource 'Microsoft.Web/connections@2016-06-01' = { name: connections_azureblob_name location: location kind: 'V2' properties: { displayName: 'privatestorage' parameterValues: { accountName: sa_var accessKey: concat(listKeys('${resourceGroup().id}/providers/Microsoft.Storage/storageAccounts/${sa_var}', '2019-06-01').keys[0].value) } api: { id: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Web/locations/${location}/managedApis/azureblob' } } dependsOn: [ sa ] } resource connections_azureblob_name_logicAppSystemAssignedIdentityObjectId 'Microsoft.Web/connections/accessPolicies@2016-06-01' = { parent: connections_azureblob_name_resource name: '${logicAppSystemAssignedIdentityObjectId}' location: location properties: { principal: { type: 'ActiveDirectory' identity: { tenantId: logicAppSystemAssignedIdentityTenantId objectId: logicAppSystemAssignedIdentityObjectId } } } } output blobendpointurl string = reference(connections_azureblob_name_resource.id, '2016-06-01', 'full').properties.connectionRuntimeUrl

 

 

 

 

 

 

 

This template can also be deployed from VSCode- Right-click on the Bicep file and click on “Deploy Bicep file”

 

Steps to export the API connection from portal as a Bicep template:

Instead of the above sample, you can directly export the managed API connection as an ARM template from Azure portal using the “Export template” option and then decompile it into a Bicep template.

Once decompiled, add the below details:

1. Add Logic App’s managed identity as an input parameter.

 

 

 

 

 

 

param logicAppSystemAssignedIdentityTenantId string param logicAppSystemAssignedIdentityObjectId string

 

 

 

 

 

 

2. Add the Access policy resource:

 

 

 

 

 

 

resource connections_azureblob_name_logicAppSystemAssignedIdentityObjectId 'Microsoft.Web/connections/accessPolicies@2016-06-01' = { parent: connections_azureblob_name_resource name: '${logicAppSystemAssignedIdentityObjectId}' location: location properties: { principal: { type: 'ActiveDirectory' identity: { tenantId: logicAppSystemAssignedIdentityTenantId objectId: logicAppSystemAssignedIdentityObjectId } } } }

 

 

 

 

 

 

3. Output the connection runtime URL

 

 

 

 

 

 

output blobendpointurl string = reference(connections_azureblob_name_resource.id, '2016-06-01', 'full').properties.connectionRuntimeUrl

 

 

 

 

 

 

 

Anatomy of a managed API connection bicep template:

 

 

 

 

 

 

resource <connections_resource_name> 'Microsoft.Web/connections@2016-06-01' = { name: <name> location: <location> kind: 'V2' properties: { "displayName": "<name>", "parameterValues": { }, "api": { "id": <connection_api> } } dependsOn: {} } resource <resource_name> 'Microsoft.Web/connections/accessPolicies@2016-06-01' = { parent: connections_resource_name> name: <name> location: <location> properties: { principal: { type: 'ActiveDirectory' identity: { tenantId: <logicAppSystemAssignedIdentityTenantId> objectId: <logicAppSystemAssignedIdentityObjectId> } } } }

 

 

 

 

 

 

Creating DevOps Pipeline:

 

For more details on DevOps project creation and adding the service connection, please refer Deploying Logic App Standard resource through DevOps pipeline - Microsoft Community Hub

In the pipeline creation, I choose GitHub as the file source. You can use any other source control option as well.

In this example, I show the pipeline creation through DevOps UI, you can alternatively use yaml file as well.

Shree_Divya_M_V_1-1678074113693.png

 

Task 1: ARM Template deployment: Resource Group scope

This task deploys the Logic App Bicep template. I have provided template file path and parameter file path. We can override the parameters if required.

Shree_Divya_M_V_2-1678074163806.png

Task 2: ARM outputs:

This task is used to output the managed identity object ID and tenant ID parameters from the previous step.

Shree_Divya_M_V_3-1678074180661.png

Task 3: ARM Template deployment: Resource Group scope

This task deploys the API connections Bicep template. I provide a template file path and parameter file path. In the override section, I pass the output from ARM output task.

 

Shree_Divya_M_V_4-1678074190620.png

Task 4: ARM outputs:

This task is used to output the Connection runtime URL generated from the previous step.

 

Shree_Divya_M_V_5-1678074199339.png

Task 5: Azure CLI

This task is used to export the connection runtime URL as an app setting:

 

Shree_Divya_M_V_6-1678074209601.png

 

The resulting YAML file looks like below:

 

 

 

 

 

 

 

pool: name: Azure Pipelines variables: resourceGroupName: 'TriageDemo' steps: - task: AzureResourceManagerTemplateDeployment@3 displayName: 'ARM Template deployment: Resource Group scope' inputs: azureResourceManagerConnection: '<azureSubscription>' subscriptionId: '<subscriptionID>' resourceGroupName: TriageDemo location: 'North Central US' csmFile: template.bicep csmParametersFile: template.parameters.json overrideParameters: '-sites_shmvlastandarddemo1_name shmvlastandarddemo-Bicep' deploymentName: test - task: keesschollaart.arm-outputs.arm-outputs.ARM Outputs@6 displayName: 'ARM Outputs' inputs: ConnectedServiceNameARM: '<azureSubscription>' resourceGroupName: TriageDemo - task: AzureResourceManagerTemplateDeployment@3 displayName: 'ARM Template deployment: Resource Group scope' inputs: azureResourceManagerConnection: '<azureSubscription>' subscriptionId: '<subscriptionID>' resourceGroupName: TriageDemo location: 'North Central US' csmFile: Connector.bicep csmParametersFile: Connector.parameters.json overrideParameters: '-location "North Central US" -logicAppSystemAssignedIdentityTenantId $(logicAppSystemAssignedIdentityTenantId) -logicAppSystemAssignedIdentityObjectId $(logi-cAppSystemAssignedIdentityObjectId) -sa_name "sabicep" -connections_azureblob_name "az-ureblobdemo"' - task: keesschollaart.arm-outputs.arm-outputs.ARM Outputs@6 displayName: 'ARM Outputs' inputs: ConnectedServiceNameARM: '<azureSubscription>' resourceGroupName: TriageDemo - task: AzureCLI@2 displayName: 'Azure CLI ' inputs: azureSubscription: '<azureSubscription>' scriptType: ps scriptLocation: inlineScript inlineScript: | az functionapp config appsettings set --name $(LAname) --resource-group $(resourceGroup-Name) --settings "BLOB_CONNECTION_RUNTIMEURL=$(blobendpointurl)" az functionapp config appsettings set --name $(LAname) --resource-group $(resourceGroup-Name) --settings "WORKFLOWS_RESOURCE_GROUP_NAME=$(resourceGroupName)"

 

 

 

 

 

 

References:

Deploying Logic App Standard resource through DevOps pipeline - Microsoft Community Hub

ShreeDivyaMV/ExportedTemplate-TriageDemo (github.com)- Bicep samples used in the demo

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.