Bicep Deployment Solution for GitLab

Bicep Deployment Solution for GitLab

TL;DR: This guide shows how to set up a scalable, modular Bicep deployment solution using GitLab CI/CD and Docker for Azure. It covers prerequisites, pipeline structure, variables, and project setup. The solution ensures consistent, automated, and secure deployments from local development to production, leveraging reusable pipeline definitions and best practices for Infrastructure as Code (IaC).

Overview

This guide describes a centralized and scalable Bicep Deployment Solution designed for efficient infrastructure deployment using Bicep, Azure Deployment Stacks, and GitLab pipelines. The solution is modular and reusable, following best practices for Infrastructure as Code (IaC).

Solution

The following image illustrates the layered architecture of the Bicep Deployment Solution:

Bicep Deployment Solution

  • Docker Layer: The bicep-base-image provides the foundational Docker image with all necessary tools for deployments within the pipeline or in a local dev container.
  • CI/CD Layer (GitLab): The bicep-deployment-solution builds on the base image and provides the centralized pipeline definition bicep.gitlab-ci.yml.
  • Projects (GitLab): Individual deployments of infrastructure (e.g., deployment 1, deployment 2, etc.) that include the pipeline definition from the bicep-deployment-solution and represent specific Azure deployment scenarios.
  • Local Development Layer: The local development environment (VS Code) uses Visual Studio Code and a dev container, leveraging the same image for consistency across environments.

Arrows labeled image indicate which Docker image the pipeline will use, while arrows labeled include show how projects include the centralized and versioned pipeline definition bicep.gitlab-ci.yml. This structure ensures modularity, reusability, and consistency from local development to cloud deployment.

bicep-base-image and the bicep-deployment-solution repositories are versioned and can be updated by a dependency bot like Renovate.

Benefits

  • Declarative: Infrastructure as code for predictable, repeatable deployments.
  • Clear Output: Actionable pipeline outputs for easy troubleshooting.
  • Consistent & Portable: Same tools and process from local dev to production using Docker and dev containers.
  • Automated & Auditable: Fast, transparent, reliable CI/CD with full change tracking in Git.
  • Collaborative & Testable: Team workflows with code reviews and pre-deployment validation.
  • Scalable & Integrated: Manage multiple environments with seamless GitLab, Azure, and Bicep integration.
  • Secure: Built-in secrets and permissions management.

Pipeline Overview

The image illustrates a high-level overview of the CI/CD pipeline stages and their roles in automating Bicep deployments to Azure.

Bicep Deployment Pipeline Overview

When a developer pushes Bicep code changes to GitLab using Visual Studio Code, the action triggers a GitLab CI pipeline defined in the bicep.gitlab-ci.yml file. This pipeline runs within a Docker-based GitLab Runner that uses the bicep-base-image to ensure a consistent environment. The pipeline first lints and builds the Bicep code, generating ARM JSON files as artifacts. These artifacts are then passed to the validation and deployment stages. The pipeline validates deployments for both test and production environments using Azure PowerShell, and upon successful validation, deploys the code as Azure deployment stacks to the respective Azure test and production environments.

Prerequisites

Before you begin, ensure you have the following:

  • Active Azure subscriptions
  • Access to a GitLab instance (self-hosted or gitlab.com)
  • Docker installed on your local machine or CI environment
  • Basic knowledge of Bicep, Azure Resource Manager (ARM), and CI/CD concepts
  • Visual Studio Code

How-To

  1. Create two Azure Subscriptions (Test and Production Environment).
  2. Create an App Registration with a Client Secret. Add a Role Assignment with the Contributor role to your Subscriptions.
  3. Create three new repositories on GitLab:
    1. Repository for the base image with the Dockerfile.
    2. Repository for the deployment solution.
    3. Repository for deploying your actual infrastructure to Azure.

Add the following GitLab CI/CD variables to your bicep-deployment-solution group in GitLab:

  • AZURE_TENANT_ID: Azure Active Directory Tenant ID
  • AZURE_SUBSCRIPTION_TEST_ID: Test Azure Subscription ID
  • AZURE_SUBSCRIPTION_PROD_ID: Production Azure Subscription ID
  • AZURE_APPLICATION_ID: Azure App Registration (Client) ID
  • AZURE_CLIENT_SECRET: Azure App Registration Secret (Masked/Secret)

NOTE:
For large-scale environments and improved security, it is recommended to use your own GitLab Runner infrastructure with Kubernetes. For each purpose, use a different service principal without client secrets, or use managed identities and assign permissions directly via RBAC.

Bicep Deployment

To use the deployment solution, include the bicep.gitlab-ci.yml file in your project, specifying the correct version ref.

1
2
3
4
include: 
  - project: 'YOUR_BICEP_DEPLOYMENT_SOLUTION_PATH'
    file: 'bicep.gitlab-ci.yml'
    ref: '1.0.xx'

Here is the required file structure for your projects.

│   .gitignore
│   .gitlab-ci.yml
│   README.md
├───.devcontainer
│       devcontainer.json
├───config
│       main-dev-westeurope.bicepparam
│       main-prod-westeurope.bicepparam
│       main-test-westeurope.bicepparam
└───src
    │   main.bicep
    └───modules
            tags.bicep
            virtualmachine.bicep

Pipeline Variables

Below are key variables defined in the bicep-deployment-solution pipeline. Adjust them to fit your Azure environment and project requirements.

NOTE:
For example, if you want to deploy to another region, you can set the AZURE_LOCATION variable to switzerlandnorth.
Make sure there is an appropriate .bicepparam file in the config directory of the project.

1
2
3
4
5
6
7
8
9
10
11
variables:
  AZURE_LOCATION: "westeurope"
  AZURE_DEPLOYMENT_NAME: "bicep-${ENVIRONMENT}-${AZURE_LOCATION}-deployment"
  BICEP_SOURCE_DIR: "./src"
  BICEP_MAIN_FILE: "./src/main.bicep"
  BICEP_PARAMETERS_DIR: "./config"
  ARM_OUTPUT_DIR: "./artifacts"
  ARM_TEMPLATE_FILE: "./artifacts/main.json"
  ARM_PARAMETERS_FILE: "./artifacts/main-${ENVIRONMENT}-${AZURE_LOCATION}.parameters.json"
  DENY_SETTINGS_MODE: "None"
  ACTION_ON_UNMANAGE: "DeleteAll"

Example

A complete example project demonstrating the Bicep Deployment Solution, including pipeline configuration and sample infrastructure code, is available in the following repository:

Bicep Deployment Solution on GitLab

Results

This is how my example looks on Azure.

Subscriptions Subscriptions

Resource group Resource Groups

Resources Resources

Azure deployment stacks Azure deployment stacks

Related Links


For more details or questions, feel free to reach out or open an issue in the repository.