Logic Apps deployment from Github with Powershell
Sam Vanhoutte
Posted on February 28, 2024
Context
I was working with a startup that is building software to predict time series in a specific industry. One of the big challenges for them is to integrate with systems of their customers.
While they offer an API that can be called by customers to ingest data into their tenant, some of those customers, require that startup to go and fetch the data from one of their systems. And yes, that smells like customer specific integration.
For that, we have set up several Logic Apps, that we isolate from the central part of the runtime. And this post describes how we have structured those Logic Apps, and how we automatically deploy them from our automated Github workflow.
Structure
As written above, the structure of the repo is in such a way that we have put all Logic Apps in a folder, called Integrations
. And at deployment time, we automatically take those workflows from the folder, leveraging the names of the sub folders, to build the right resource name (ensuring our naming conventions). And those Logic Apps get deployed to a consumption Logic App.
Code
All code is available on https://github.com/SamVanhoutte/azure-logic-apps-deployment
Azure authentication
As with any other Github action, it is important to leverage the right credentials to authenticate against your Azure Subscription. For that, the following script has to be executed in the Azure CLI, and the resulting lines should be kept as a secret in your Github environment.
az ad sp create-for-rbac --name "github-deploy-dev" --role contributor --scopes /subscriptions/b73995e3-caad-4882-8644-f2175789c3ff --sdk-auth
The output looks like the following (which should be handled as a secret!).
{
"clientId": "a8fa378b....",
"clientSecret": "cOF8....",
"subscriptionId": "b73995e3-caad-4882-8644-f2175789c3ff",
"tenantId": "37e57300...."
}
Note: you can leave out the urls and just keep the first for relevant parts.
The output has been saved in my Github repo secret (with name AZURE_CREDENTIALS
in the Development environment on my repo.
Github pipeline
To deploy our infrastructure, we default to Github workflows.
The result of our deployment pipeline is shown in the next screenshot:
The definition of the Github workflow can be easily found in the Github repo. But the most relevant sections have been copied here:
name: Deploy the Backend environment
on:
workflow_call:
inputs:
# Removed for brevity
jobs:
Deploy-Azure-Backend-Resources:
runs-on: ubuntu-latest
if: always()
environment: ${{ inputs.environment }}
steps:
# Log in to Azure
- name: Azure Login
uses: azure/login@v1
with:
creds: '${{ secrets.AZURE_CREDENTIALS }}'
enable-AzPSSession: true
# Removed for brevity
Deploy-Integrations:
runs-on: ubuntu-latest
if: always()
needs : [ Deploy-Azure-Backend-Resources ]
environment: ${{ inputs.environment }}
steps:
- name: Azure Login
uses: azure/login@v1
with:
creds: '${{ secrets.AZURE_CREDENTIALS }}'
enable-AzPSSession: true
- name: Download powershell files from pipeline artifact
uses: actions/download-artifact@v3
with:
name: powershell
path: ./artifacts/powershell
- name: Download integration files
uses: actions/download-artifact@v3
with:
name: integrations
path: ./artifacts/integrations
- name: Deploy integrations
uses: azure/powershell@v1
with:
inlineScript: ./artifacts/powershell/deploy-logicapp-folder.ps1 -environmentAcronym ${{ inputs.environmentAcronym }} -location ${{ inputs.region }} -rootFolder './artifacts/integrations/'
azPSVersion: "latest"
Some remarks about the above script:
-
enable-AzPSSession: true
ensures our Powershell script will be running authenticated against our Azure Subscription. - We download the artifacts that we initially uploaded to the runtime storage. We do this for our
powershell
folder & ourintegrations
folder that contains our Logic Apps definitions. - And in the last step, we are calling our Powershell script (that is explained in the next step) that will receive a reference to our
integrations
folder from our repo.
Powershell script
The following script takes the folder reference, walks the tree iteratively and deploys every Logic App definition to the corresponding resource group, applying the naming conventions, based on the folder structure.
param ($rootFolder, $location, $environmentAcronym)
Function Deploy-LogicAppDefinition($logicAppDefinitionFile, $location, $environmentAcronym, $locationAcronym)
{
# This function takes a given workflow definition file
# and deploys it to the resource group
# (based on the folder, location & environment)
# It will create or update the Logic App
### SET UP LOGIC APP NAME
# Taking the file name and remove the folder
$logicAppName = Split-Path $logicAppDefinitionFile -leaf
# and remove the suffix to keep the logical name
$logicAppName = $logicAppName.Replace('.definition.json', '')
# and apply naming conventions
$logicAppName = "$locationAcronym-$environmentAcronym-int-la-$logicAppName"
### SET UP RESOURCE GROUP NAME
# Taking the directory name to which the json file belongs
$logicAppType = Split-Path (Split-Path $logicAppDefinitionFile -Parent) -Leaf
# and construct the resource group name
$resourceGroupName = "$environmentAcronym-$locationAcronym-logapp-rg-int-$logicAppType"
# Check if the resource group exists and create if not
$existingRG = Get-AzResourceGroup -Name $resourceGroupName -ErrorAction SilentlyContinue
if(-Not $existingRG)
{
Write-Host "Creating resource group $resourceGroupName"
New-AzResourceGroup -Name $resourceGroupName -Location 'West Europe' -Force
}
### CRUD OF LOGIC APP DEFINITION
# Check if logic app exists
$existingLogicApp = Get-AzLogicApp -ResourceGroupName $resourceGroupName -Name $logicAppName -ErrorAction SilentlyContinue
if($existingLogicApp)
{
Write-Host "Update logic app $logicAppName to resource group $resourceGroupName"
Set-AzLogicApp -ResourceGroupName $resourceGroupName -Name $logicAppName -DefinitionFilePath $logicAppDefinitionFile -Force
}
else
{
Write-Host "Create logic app $logicAppName to resource group $resourceGroupName"
New-AzLogicApp -ResourceGroupName $resourceGroupName -Name $logicAppName -Location $location -DefinitionFilePath $logicAppDefinitionFile
}
}
# Apply location acronym, based on location
switch ($location)
{
'westeurope' { $locationAcronym = 'weu'}
'northeurope' { $locationAcronym = 'neu'}
'westus' { $locationAcronym = 'wus'}
default { $locationAcronym = 'weu'}
}
# Start of logic
# Search through all folders for files ending with definition.json
Get-ChildItem -Path $rootFolder -Recurse -Filter *.definition.json |
ForEach-Object {
Deploy-LogicAppDefinition $_.FullName $location $locationAcronym $environmentAcronym
}
Conclusion
I believe it's crucial to apply naming conventions in every step of your deployment logic. So, here we have a way to structure our logic apps in our repo and have them deployed, just because the appear in our repo (we don't have to update the pipeline or script for every new workflow).
In another post, I will describe how a custom connector can be deployed and used in another Logic App.
Posted on February 28, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.