Highlight

Here’s how you can deploy Azure Logic Apps standard in a quick and easy way with Azure DevOps.

Logic Apps Standard might be quite tricky to deploy continously at first. But in this article I’ll share my personal CICD templates which can help speed up this process, with extra powershell scripts to make this more end-2-end in terms of functionality.

DevOps setup

Pre-requisites

  • Set up Azure ARM connection for your project with permissions to deploy code into designated subscription/rg/logic app
  • Permissions to set up new pipelines in Azure DevOps
  • Permissions to set up new variable groups in Azure DevOps

Folder Structure

Azure DevOps repo should have following structure

FOLDER STRUCTURE                         NOTES
<root>                                   
├───🗂️ code                              folder for logic app code
│   ├──📄 ...                            
│   └──📄 ...                            
└───🗂️ pipelines                         folder for devops pipelines
    ├───🗂️ scripts                       extra powershell scripts called from pipeline
    │   ├──📄 ...                        
    │   └──📄 update-la-settings.ps1     update logic app standard settings
    └──📄 la-cicd-pipeline.yml           deployment pipeline

Variable Group

Create a variable group of any name, recommendation is to use one variable group per environment (DEV/TEST/PROD).

  • SubscriptionId - Subscription ID where logic app is residing
  • ResourceGroupName - Resource Group ID where logic app is residing
  • LogicAppName - Name of Logic App standard resource
  • EnableWorkflowList - list of workflow names to enable forcefully after deployment, even if they are disabled on DEV
    • use @() for empty list
    • or “A”, “B”, “C” notation for powershell lists
  • DisableWorkflowList - list of workflow names to disable forcefully after deployment, even if they are enabled on DEV
    • use @() for empty list
    • or “A”, “B”, “C” notation for powershell lists
  • AppSettingOverrideList - list of app service settings to override after code deployment
    • use @() for empty list
    • or “A=123”, “B=432”, “C=ZYX” notation for powershell lists of setting paris in format of “setting_name=setting_value”

Flow

In order to deploy Logic App we will follow two step process

  1. Build stage where we get current code, override variables that change between environments in static files, and zip it

  2. Publish ZIP package to Azure and override app service settings

Build & Deploy pipeline

Here’s the final YAML pipeline script for easy copy and paste

└───🗂️ pipelines        
    └──📄 la-cicd-pipeline.yml 

With following source

name: LogicAppCICD_$(Date:yyyyMMdd)$(Rev:.r)

trigger: none

pool:
  vmImage: ubuntu-latest

variables:
  - group: <variable_group_name>
  - name: connectionName
    value: <azure_arm_connection_name>
  - name: logicAppPackageName
    value: logic-app-pkg.zip

stages:
  - stage: Build
    displayName: Build Package
    jobs:
    - job: BuildAndPublishPackage
      displayName: Build & Publish Package
      steps:
      - task: PublishPipelineArtifact@1
        inputs:
          targetPath: $(Build.SourcesDirectory)/code
          artifact: LogicAppCode
      - task: ArchiveFiles@2
        inputs:
          rootFolderOrFile: $(Build.SourcesDirectory)/code
          includeRootFolder: false
          archiveType: zip
          archiveFile: $(System.DefaultWorkingDirectory)/$(logicAppPackageName)
      - task: PowerShell@2
        displayName: Connections JSON exists
        inputs:
          targetType: 'inline'
          script: |
            $fileExists = Test-Path -Path "$(Build.SourcesDirectory)/code/connections.json"
            Write-Output "##vso[task.setvariable variable=FileExists]$fileExists"                  
      - task: FileTransform@1
        displayName: Connections JSON transform
        condition: eq(variables['FileExists'], True)
        inputs:
          folderPath: $(System.DefaultWorkingDirectory)/$(logicAppPackageName)
          fileType: json
          targetFiles: '**/connections.json'
      - task: PowerShell@2
        displayName: Parameters JSON exists
        inputs:
          targetType: 'inline'
          script: |
            $fileExists = Test-Path -Path "$(Build.SourcesDirectory)/code/parameters.json"
            Write-Output "##vso[task.setvariable variable=FileExists]$fileExists"               
      - task: FileTransform@1
        displayName: Parameters JSON transform
        condition: eq(variables['FileExists'], True)
        inputs:
          folderPath: $(System.DefaultWorkingDirectory)/$(logicAppPackageName)
          fileType: json
          targetFiles: '**/parameters.json'
      - task: PublishPipelineArtifact@1
        inputs:
          targetPath: $(System.DefaultWorkingDirectory)/$(logicAppPackageName)
          artifact: LogicAppCodePackage
  - stage: Deploy
    displayName: Deploy
    jobs:
    - job: Deploy
      displayName: Deploy
      steps:
      - task: DownloadPipelineArtifact@2
        inputs:
          buildType: 'current'
          targetPath: $(System.DefaultWorkingDirectory)
          artifact: LogicAppCodePackage
      - script: ls $(System.DefaultWorkingDirectory)
        displayName: Working Directory
      - task: AzurePowerShell@5
        displayName: Overriding LA Settings
        inputs:
          azureSubscription: ${{ variables.connectionName }}
          scriptType: filePath
          scriptPath: $(Build.SourcesDirectory)/pipelines/scripts/update-la-settings.ps1
          scriptArguments:
            -SubscriptionId $(SubscriptionId) 
            -ResourceGroupName $(resourceGroupName) `
            -LogicAppName $(logicAppName) `
            -EnableWorkflowList $(enableWorkflowList) `
            -DisableWorkflowList $(disableWorkflowList) `
            -AppSettingOverrideList `
              "MessageFromAppSetting=PROD!"
          azurePowerShellVersion: latestVersion
          pwsh: true
      - task: AzureFunctionApp@2
        displayName: 'Deploy logic app workflows'
        inputs:
          azureSubscription: ${{ variables.connectionName }}
          appType: functionApp
          appName: $(logicAppName)
          package: $(System.DefaultWorkingDirectory)/$(logicAppPackageName)

Updating Azure App Service settings

Why? Azure App Service holds two things that are important from the perspective of Azure Logic Apps.

  1. Connector Connection Strings
  2. Logic App Workflow state

Attached script allows you to do a bulk update of these properties as part of the workflow. This allows you to solve two scenarios

  1. Update connection strings when promoting code between DEV, TEST, PROD environments
  2. Disable workflows which you don’t want to run after deployment, this is especially important when deploying app with triggers

How? - update-la-settings.ps1

For this I’ve created a small PowerShell script which will be invoked from DevOps pipeline

└───🗂️ pipelines
    └───🗂️ scripts               
        └──📄 update-la-settings.ps1

With following code

param(
    [Parameter(Mandatory=$true)]
    [string]
    $SubscriptionId, 

    [Parameter(Mandatory=$true)]
    [string]
    $ResourceGroupName, 

    [Parameter(Mandatory=$false)]
    [string]
    $LogicAppName,

    [Parameter(Mandatory=$false)]
    [string[]]
    $EnableWorkflowList,

    [Parameter(Mandatory=$false)]
    [string[]]
    $DisableWorkflowList,

    [Parameter(Mandatory=$false)]
    [string[]]
    $AppSettingOverrideList
)

$webApp = Get-AzWebApp `
    -Name $LogicAppName `
    -ResourceGroupName $ResourceGroupName
$currentSettings = $webApp.SiteConfig.AppSettings

$NewAppSettings = @{}
foreach ($setting in $currentSettings) {
    $NewAppSettings[$setting.Name] = $setting.Value
}

foreach ($WorkflowName in $EnableWorkflowList) {
    Write-Output "Setting: Workflows.$WorkflowName.FlowState = Enabled"
    $NewAppSettings["Workflows.$WorkflowName.FlowState"] = 'Enabled'
}

foreach ($WorkflowName in $DisableWorkflowList) {
    Write-Output "Setting: Workflows.$WorkflowName.FlowState = Disabled"
    $NewAppSettings["Workflows.$WorkflowName.FlowState"] = 'Disabled'
}

foreach ($Setting in $AppSettingOverrideList) {
    $name = $Setting.split("=")[0]
    $value = $Setting.split("=")[1]
    Write-Output "Setting: $name = $($value.substring(0, [System.Math]::Min(10, $value.Length)))..."
    $NewAppSettings[$name] = $value
}

Set-AzWebApp `
    -AppSettings $NewAppSettings `
    -Name $LogicAppName `
    -ResourceGroupName $ResourceGroupName

How it works? You simply need to provide 6 parameters, all are mandatory

  • SubscriptionId - Subscription ID where logic app is residing
  • ResourceGroupName - Resource Group ID where logic app is residing
  • LogicAppName - Name of Logic App standard resource
  • EnableWorkflowList - list of workflow names to enable forcefully after deployment, even if they are disabled on DEV
    • use @() for empty list
    • or “A”, “B”, “C” notation for powershell lists
  • DisableWorkflowList - list of workflow names to disable forcefully after deployment, even if they are enabled on DEV
    • use @() for empty list
    • or “A”, “B”, “C” notation for powershell lists
  • AppSettingOverrideList - list of app service settings to override after code deployment
    • use @() for empty list
    • or “A=123”, “B=432”, “C=ZYX” notation for powershell lists of setting paris in format of “setting_name=setting_value”

Example usage in devops step

- task: AzurePowerShell@5
  displayName: Overriding LA Settings
  inputs:
    azureSubscription: ${{ variables.connectionName }}
    scriptType: filePath
    scriptPath: $(Build.SourcesDirectory)/pipelines/scripts/update-la-settings.ps1
    scriptArguments:
      -SubscriptionId $(SubscriptionId) 
      -ResourceGroupName $(resourceGroupName) `
      -LogicAppName $(logicAppName) `
      -EnableWorkflowList $(enableWorkflowList) `
      -DisableWorkflowList $(disableWorkflowList) `
      -AppSettingOverrideList `
        "<setting_name>=<setting_value>"
    azurePowerShellVersion: latestVersion
    pwsh: true

And simply run

If you did everything right, you should see deployment succeed.

Adam Marczak

Programmer, architect, trainer, blogger, evangelist are just a few of my titles. What I really am, is a passionate technology enthusiast. I take great pleasure in learning new technologies and finding ways in which this can aid people every day. My latest passion is running an Azure 4 Everyone YouTube channel, where I show that Azure really is for everyone!

Did you enjoy the article?

Share it!

More tagged posts