Github Actions and Bicep is fantastic and here is why…
Published: 2023-03-16 12:55:22

From last few years I was mainly working with Azure Pipelines and Ansible as infrastructure as code. From that time I was thinking that Azure Pipelines with Ansible is a very good duo. Especially that project that I worked on invested a tons of money and time to build a robust, innovative, easy to use automation tools. With few easy steps you could import predefined roles, define parameters and deploy Azure building blocks. All of that made me just fall in love with „the framework”. 

Whole automation that was build around it was amazing. Of course „the framework” has pros and cons but for me switching from CLI, ARM templates and json that many companies are still using was a game changer - even thought it took me a while to understand mechanisms hidden behind a nice developer friendly layer of abstraction. 

Few weeks ago I decided to use different tools that I can use when building infrastructure as code. I wanted to go outside my comfort zone and get better understanding what is going on with actual industry trends. After small reaserch I decided that I will go with Github Actions and Microsoft Bicep. 

I simply decided to use Github Actions because I’m keeping all my side projects on Github - probably like most of IT folks. I started going trough documentation and I was amazed how well it is structured and how easy I can find useful stuff. There is also a strong community behind Github Actions that is actively supporting idea behind moving Azure Pipelines to Github. There are many free tools for workflows and actions yaml syntax validation that are extremely useful.

To make Github Actions complementary for Azure I used Bicep language. I liked the idea that Bicep is domain specific language for deploying Azure resources. To simply combine Actions with Bicep you need only one action role azure/arm-deploy. But why Bicep is so cool? Bicep files are often one third of the JSON ARM template, that most of the time is unreadable for humans. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ulyiov7w6b9yr2bx7er.png)

I would say that Bicep templates syntax is very similar to common programming languages like C# or TypeScript - so for me prototyping deployments was very easy to jump in.

Ok but enough of cheap talking… so without further it ado let’s jump straight to the code. 

For sake of simplicity we will use [Python + FastAPI](https://github.com/krzysztofla/Example.Python.Api.git) (To be honest because I'm learning Python lately ^^). Here is my project structure:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pgeaiafrbjm27h6qlx5f.png)
The source of our pipeline is in main.yml file where pipeline has it's starting point. We sliced our workflow in three stages [build, deploy infrastructure, deploy]. 

- Infrastructure step simply is deploying App Service infrastructure to our Azure Subscription.

```
infrastructure_deploy:
    if: github.event.inputs.deploy_infrastructure == 'true' 
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: Example.Python.Api
        
    steps:
      - uses: actions/checkout@v3

      - name: Az Login
        uses: azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}
    
      - name: Bicep deploy infrastructure
        id: infrastructure-deployment
        uses: azure/arm-deploy@v1
        with:
          scope: subscription
          subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }}
          region: westeurope
          template: ./.github/workflows/infrastructure/main.bicep
          parameters: project=python-example-api-aps location=westeurope appName=${{github.event.inputs.appName}}
          deploymentName: aps-python-bicep-demo

```
- Build step build and save our build output as artefact that in the final deployment step we will use to publish our app to cloud.

```
build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Az Login
        uses: azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

      - name: Set up Python 3.11
        uses: actions/setup-python@v3
        with:
          python-version: "3.11"
      
      - name: Create and start virtual environment
        run: |
          python -m venv venv
          source venv/bin/activate
      
      - name: Set up dependency caching for faster installs
        uses: actions/cache@v3
        with:
          path: ~/.cache/pip
          key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
          restore-keys: |
            ${{ runner.os }}-pip-

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Upload artifact for deployment jobs
        uses: actions/upload-artifact@v3
        with:
          name: python-app
          path: |
            .
            !venv/

```

- Azure deployment

```
deploy:
    runs-on: ubuntu-latest
    needs: build

    environment:
      name: 'production'

    steps:
      - name: Az Login
        uses: azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

      - name: Download artifact from build job
        uses: actions/download-artifact@v3
        with:
          name: python-app
          path: .
      
      - name: Retrieve publish profile for deployment
        id: publishProfileRetrieval
        run: |
          publishProfiles=$(az webapp deployment list-publishing-profiles \
            --name app-api-${{github.event.inputs.appName}} \
            --resource-group "ziqq-test-rg2" \
            --subscription "${{ secrets.AZURE_SUBSCRIPTION }}" --xml)
          echo "::add-mask::$publishProfiles"
          echo "::set-output name=publishProfiles::$publishProfiles"
      
      - name: Print pwd
        run: pwd

      - name: 'Deploy to Azure Web App'
        id: deploy-to-webapp
        uses: azure/webapps-deploy@v2
        with:
          images: ''
          app-name: app-api-${{github.event.inputs.appName}}
          publish-profile: ${{ steps.publishProfileRetrieval.outputs.publishProfiles }}

      - name: logout
        run: |
          az logout
```

Now lets take a look at the fantastic Bicep files structure:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jubwiy3qbzxji84nkxr7.png)

- As entry point we have main.bicep file where like before all our infrastructure deployments steps are described.
- params.bicep file describes what type of parameters our pipeline can take and where to pass them.
- Folder modules contains all templates needed to run App Service infrastructure.

**main.bicep**

```
targetScope = 'subscription'

param location string
param project string
param appName string

resource rg 'Microsoft.Resources/resourceGroups@2022-09-01' = {
  name: 'ziqq-test-rg2'
  location: location
}

module resources 'params.bicep' = {
  name: 'deploy-${project}-resources'
  scope: rg

  params: {
    location: location
    project: project
    appName: appName
  }
}

output rgName string = rg.name

```

**params.bicep**

```
param location string
param project string
param appName string

module appServicePlan 'modules/app-service-plan.bicep' = {
  name: 'deploy-plan'

  params: {
    location: location
    project: project
  }
}

module appService 'modules/app-service.bicep' = {
  name: 'deploy-app-service'

  params: {
    appName: appName
    location: location
    planId: appServicePlan.outputs.planId
  }
}

```

**app-service-plan.bicep**


```
param location string
param project string

resource plan 'Microsoft.Web/serverfarms@2022-03-01' = {
  name: 'asp-${project}'
  location: location

  kind: 'app,linux'

  properties: {
    reserved: true
  }

  sku: {
    name: 'S1' 
  }
}

output planId string = plan.id 
```


**app-service.bicep**


```
param location string

param appName string
param planId string

resource app 'Microsoft.Web/sites@2022-03-01' = {
  name: 'app-api-${appName}'
  location: location

  properties: {
    serverFarmId: planId
    reserved: true

    siteConfig: {
      linuxFxVersion: 'PYTHON|3.11'
    }
  }
}

output publishProfile string = ''

```

Azure Bicep team did a great job developing modern and developer friendly way to deploy infrastructure to Azure. I found using Bicep and Github Actions very easy and satisfying. Debugging and maintaining JSON ARM Templates in early stages of infrastructure as code way of dealing with cloud was a nightmare for me so I appreciate even more frameworks/languages like this. I will definitely continue to work with both technologies in my personal projects.