Deploying Logic App Standard resource using Bicep templates and DevOps pipeline
Published Mar 06 2023 02:27 AM 13.1K Views
Microsoft

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

 

 

 

2 Comments
Co-Authors
Version history
Last update:
‎Mar 06 2023 06:17 AM
Updated by: