Déploiement de Logic App Standard via Bicep

Bastien DALLARD
Publié par Bastien DALLARD
Catégorie : Bicep / Logic Apps
09/01/2024

Introduction

Les Logic Apps Standards d’Azure offrent une plateforme performante d’orchestration et d’automatisation de workflows dans le cloud en plus de proposer une grande variété de connecteurs prêts à l’emploi pour intégrer des services et des applications. Dans ce guide, nous allons voir comment les déployer rapidement et efficacement avec l’aide de Bicep, langage de déploiement pour vos solutions d’infrastructure en tant que code dans Azure, qui simplifie considérablement le déploiement et la gestion des ressources Azure.

 

Prérequis

Une connaissance des Logic Apps Standards et du langage Bicep est un plus pour suivre ce tutoriel. Vous pouvez également trouver des informations complémentaires sur ces deux sujets sur notre blog.

Je recommande d’utiliser Visual Studio Code avec l’extension Bicep.

 

Création du conteneur Logic App Standard via Bicep

Pour pouvoir déployer une Logic App Standard d’un environnement vers un autre, il faut tout d’abord déployer le conteneur. En réalité, Logic App Standard étant construit sur la stack Azure Function, les composants à déployer sont très similaires :

  • un compte de stockage Azure (Storage Account)
  • un plan spécial, dédié aux Logic Apps (App Service Plan)

A l’instar des Azure Functions qui peuvent partager un même plan, nous pourrons ensuite déployer plusieurs Logic Apps Standard sur ce plan.

Pour notre exemple, nous allons partir d’une Logic App Standard qui permet de faire de l’insertion de données dans Salesforce. Nous supposons que toutes les ressources Azure dont dépend notre Logic App Standard seront déjà existantes sur l’environnement cible. Pour avoir un aperçu complet, nous ajouterons l’instrumentation via App Insight sur notre service.

Tout d’abord, il nous faut créer un nouveau fichier Bicep que nous allons nommer d’après notre Logic App Standard. Ensuite, nous allons structurer notre Bicep en trois parties :

  • Déclaration des variables
  • Récupération des références des dépendances
  • Création du conteneur Logic App Standard

Voilà un exemple avec les variables nécessaires afin de répondre à notre contexte :

param location string = resourceGroup().location

param targetEnv string

param logicAppName string = 'lap-test-'
param localStorageName string = 'stolaptest'
param appInsightName string = 'ain-shared-'
param plaName string = 'pla-shared-'
param ressourceGroupSharedName string = 'rg-shared-'
param apiSalesForceName string = 'salesforce'
param apiSalesForceResourceGroupName string = 'rg-salesforce-'

 

 

  • location sera récupéré dynamiquement depuis le groupe de ressources sur lequel nous allons déployer
  • targetEnv sera initialisé via notre fichier de paramètre, et il permettra de variabiliser, entre autres, le nom de notre service.

Les autres variables sont des constantes dont la valeur finale va dépendre de l’environnement cible.

Ensuite, nous pouvons créer des références vers nos dépendances et gérer la partie permettant de créer notre conteneur Logic App Standard. Voilà l’aperçu final de notre fichier :

param location string = resourceGroup().location

param targetEnv string

param logicAppName string = 'lap-test-'
param localStorageName string = 'stolaptest'
param appInsightName string = 'ain-shared-'
param plaName string = 'pla-shared-'
param ressourceGroupSharedName string = 'rg-shared-'
param apiSalesForceName string = 'salesforce'
param apiSalesForceResourceGroupName string = 'rg-salesforce-'

resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' existing = {
  name: '${localStorageName}${targetEnv}'
  scope: resourceGroup()
}

resource appInsight 'Microsoft.Insights/components@2020-02-02' existing = {
  name: '${appInsightName}${targetEnv}'
  scope: resourceGroup('${ressourceGroupSharedName}${targetEnv}')
}

resource sharedPla 'Microsoft.Web/serverfarms@2022-09-01' existing = {
  name: '${plaName}${targetEnv}'
  scope: resourceGroup('${ressourceGroupSharedName}${targetEnv}')
}

resource salesForceApiConnection 'Microsoft.Web/connections@2018-07-01-preview' existing = {
  name: apiSalesForceName
  scope: resourceGroup('${apiSalesForceResourceGroupName}${targetEnv}')
} 

resource logicAppTest 'Microsoft.Web/sites@2021-02-01' = {
  name: '${logicAppName}${targetEnv}'
  location: location
  kind: 'functionapp,workflowapp'
  identity: {
      type: 'SystemAssigned'
  }
  tags: {
      Env:targetEnv
      application: 'Test'
  }
  properties: {
      httpsOnly: true
      siteConfig: {
          appSettings: [
              { name: 'APP_KIND', value: 'workflowApp' }
              { name: 'APPINSIGHTS_INSTRUMENTATIONKEY', value: appInsight.properties.InstrumentationKey }
              { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: appInsight.properties.ConnectionString }
              { name: 'AzureFunctionsJobHost__extensionBundle__id', value: 'Microsoft.Azure.Functions.ExtensionBundle.Workflows' }
              { name: 'AzureFunctionsJobHost__extensionBundle__version', value: '[1.*, 2.0.0)' }
              { name: 'AzureWebJobsStorage', value: '@Microsoft.KeyVault(SecretUri=https://kv-shared-${targetEnv}.vault.azure.net/secrets/StorageAccount--ConnectionString/)' }
              { name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4' }
              { name: 'FUNCTIONS_WORKER_RUNTIME', value: 'node' }
              { name: 'salesforce-apiId', value: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Web/locations/${location}/managedApis/salesforce' }
              { name: 'salesforce-connectionId', value:'/subscriptions/${subscription().subscriptionId}/resourceGroups/rg-salesforce-${targetEnv}/providers/Microsoft.Web/connections/salesforce' }
              { name: 'salesforce-connectionRuntimeUrl', value: salesForceApiConnection.properties.connectionRuntimeUrl }  
              { name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING', value: '@Microsoft.KeyVault(SecretUri=https://kv-shared-${targetEnv}.vault.azure.net/secrets/StorageAccount--ConnectionString/)' }
              { name: 'WEBSITE_CONTENTSHARE', value: toLower('${logicAppName}${targetEnv}') }
              { name: 'WEBSITE_NODE_DEFAULT_VERSION', value: '~16' }
              { name: 'Workflows.my-workflow.FlowState', value: 'Enabled' }
          ]
          use32BitWorkerProcess: true
      }
      serverFarmId: sharedPla.id
      clientAffinityEnabled: false
  }
}

Dans cette partie, nous pouvons :

  • variabiliser le nom de notre Logic App Standard
  • le kind va indiquer le type de notre conteneur
  • dans le identity, il est possible de spécifier le mode « system assigned identity » ou d’affecter des « users assigned identity »
  • dans les properties nous allons retrouver toute notre config nécessaire au bon fonctionnement de notre Logic App Standard. Notez que nous aurions pu rajouter les properties dans un autre template en utilisant le mode « Web/sites/config@2022-03-01»

Déploiement du conteneur Logic App Standard

Maintenant, il faut via GitHub créer un nouveau pipeline yaml permettant d’exécuter notre fichier de déploiement. Voilà un exemple de fichier yaml qui permet de déployer notre Logic App Standard :

name: Deploy laptest
on:
  push:
    branches:
      - main 
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout_Repo
        uses: actions/checkout@v3
      - name: Log in with Azure
        uses: azure/login@v1
        with:
          creds: ${{ secrets.az-deploy-credentials }}
      - name: Set up Azure CLI
        uses: azure/CLI@v1
        with:
          azcliversion: 2.0.72 # Choisissez la version appropriée de l'Azure CLI
      - name: Deploy laptest to staging/rg-test
        run: |
          az deployment group create --subscription ${{ secrets.az-subscription-id }} --resource-group rg-test --template-file LogicAppWorkspace/laptest.bicep --parameters LogicAppWorkspace/laptest.parameters.staging.json

Il s’agit ici d’une approche assez simple et triviale. Dans l’idéal, il faudrait créer un fichier yaml qui servirait uniquement à déployer des fichiers Bicep et d’avoir d’autres yaml spécifiques à chaque ressource qui l’appellerait en lui donnant les informations nécessaires à son traitement.

La partie nous permettant  de déployer le conteneur est terminée. Il nous reste à nous occuper des workflows.

Déploiement des workflows

Pour déployer les workflows, nous devons créer un projet Logic App Standard. Le moyen le plus simple consiste à accéder à la section « Aperçu » de notre Logic App Standard et à télécharger celui déjà existant :

Téléchargement contenu Logic Apps

Une fois le téléchargement terminé, nous allons retrouver une archive dans laquelle se trouve :

  • un fichier « csproj» qui correspond à notre projet Logic App Standard
  • un fichier « json» qui stock toutes les connections de notre Logic App Standard
  • un dossier (ou plusieurs dossiers) avec pour nom celui de notre workflow et contenant un fichier « json»

Folder téléchargement Logic App Standard

 

Pour la suite, je vous recommande de passer par Visual Studio et de compiler votre projet afin de vérifier qu’il n’y ait pas de problèmes :

Pour ajouter de nouveaux workflows, vous devez les déclarer ici afin qu’ils soient correctement pris en compte lors du déploiement.

Maintenant que nous avons pu extraire les workflows de notre Logic App Standard, il nous reste à mettre en place le processus qui permettra de les déployer.

Pour cela, il faut à nouveau créer un pipeline dans GitHub :

name: Deploy lap-test workflows
on:
  push:
    branches:
     - main 
        
 # CONCURRENCY SETTINGS
concurrency: 
  group: ${{ github.ref }}
  cancel-in-progress: true
  
jobs:
  build-Azure-Function:
    name: Build Azure Function
    runs-on: ubuntu-latest
    steps:
      - name: Download Sources
        uses: actions/checkout@v3
      #Setup .net
      - name: Setup .Net
        uses: actions/setup-dotnet@v2
        with:
          dotnet-version: 6.0.x
      - name: Setup NuGet
        uses: NuGet/setup-nuget@v1.0.6
      #create package
      - name: Create package artifact folder
        shell: pwsh
        run: |
          Write-Host "Create Function folder"
          New-Item -Path ".\artifact-function" -ItemType Directory

      # BUILD PROJECT
      - name: Build & Scan Project
        shell: pwsh
        run: |
          Write-Host "Build Solution..."
          dotnet publish ".\LogicApps\lap-test\lap-test.csproj" --configuration "release" --self-contained --output "artifact-function"
       #UPLOAD ARTIFACT
      - name: Upload Artifact - Function
        uses: actions/upload-artifact@v3
        with:
          name: function
          path: artifact-function/
          if-no-files-found: error
          
  deploy:
    runs-on: ubuntu-latest
    needs: [build-Azure-Function]
    steps:
      - name: Checkout
        uses: actions/checkout@master
      - name: Login to azure
        uses: azure/login@v1
        with:
          creds: ${{ secrets.az-deploy-credentials }}
      #Deploy function    
      - name: Download Artifacts
        uses: actions/download-artifact@v3
        with:
          name: function
          path: function
      - name: Deploy Function
        uses: azure/functions-action@v1
        with:
          app-name: lap-test-staging
          package: './function'

Une fois votre pipeline lancée sans erreur, vous devriez retrouver vos workflows dans votre Logic App Standard sur votre environnement cible.

Cependant, veuillez noter que dans cet exemple, nous avons actuellement désactivé le workflow déployé (statut « désactivé »), car nous avons spécifié dans le fichier Bicep, en utilisant le paramètre « Workflows.my-workflow.FlowState », de le mettre en mode inactif.

De plus, notre ressource Azure sera en mode « read only », il sera alors impossible d’effectuer des modifications sur d’autres environnement que celui de développement.

Conclusion

Au cours de cet article, nous avons montré comment déployer une Logic App Standard en utilisant le système de déploiement Bicep et GitHub pour nos pipelines. En fin de compte, nous pouvons conclure que vous devez effectuer le déploiement de cette ressource Azure de manière relativement similaire aux Azure Functions Apps et aux Azure Functions, en le divisant en deux étapes :

  • déploiement du conteneur
  • déploiement de la logique (workflow)

Selon moi, Bicep offre un système de déploiement qui est beaucoup plus lisible et compréhensible que le standard ARM. Ainsi, la gestion des connexions API et des dépendances est grandement simplifiée.

Bien que leur déploiement soit plus compliqué que celles à la consommation, les Logic App Standard permettent une plus grande souplesse, une meilleure gestion et anticipation des coûts.