Learning Infrastructure as Code (IaC): Pipelines Part #2

Azure DevOps and GitHub - User Parameter Input.

Introduction

In the previous post on ‘Learning Infrastructure as Code (IaC)’ I covered the the different authentication methods for connecting Azure DevOps and GitHub Actions to Azure. In this post we’re going to look at User Driven deployments with Input Parameters.

User-driven parameter inputs in CI/CD pipelines offer several key benefits, enhancing flexibility, efficiency, and security. They allow users to customize pipeline behavior without modifying the underlying code, making it easier to adapt to different environments, configurations, or versions. This leads to greater reusability, as the same pipeline can be employed across multiple scenarios. By reducing the need for manual intervention, user-driven inputs streamline pipeline execution, improving speed and consistency. They also enhance security by avoiding the hardcoding of sensitive information and provide more control over testing and validation by enabling customized test scenarios. Additionally, these inputs support scalable and adaptable pipelines, allowing projects to evolve without major architectural changes.

Azure DevOps

Azure DevOps parameters are used to pass values into pipelines, allowing you to create reusable and flexible pipeline configurations. Here’s a breakdown of the key aspects of Azure DevOps parameters:

Types of Parameters

Simple Parameters: Basic types such as string, boolean, number, object, and array. These are passed as values into the pipeline. Pipeline Variables: These can be defined in the pipeline and used as input to tasks or other steps. These are defined within variables and can be overridden by pipeline parameters.

Parameter Types Explained

If you want to find out more about the parameter types, you can check the Microsoft Docs.

Data typeNotes
stringstring
numbermay be restricted to values:, otherwise any number-like string is accepted
booleantrue or false
objectany YAML structure
stepa single step
stepListsequence of steps
joba single job
jobListsequence of jobs
deploymenta single deployment job
deploymentListsequence of deployment jobs
stagea single stage
stageListsequence of stages

Defining Parameters

Parameters are defined at the beginning of the YAML pipeline file using the parameters keyword. Example:

1
2
3
4
5
6
7
8
parameters:
  - name: environment
    type: string
    default: 'dev'

  - name: isProduction
    type: boolean
    default: false

Referencing Parameters

You can reference parameters throughout the pipeline using the ${{ parameters.parameterName }} syntax. For example:

1
2
3
4
5
6
jobs:
  - job: deploy
    steps:
      - script: |
          echo "Deploying to ${{ parameters.environment }} environment"          
        displayName: 'Deploy Script'

Conditional Parameters

You can use conditions to change behavior based on parameter values. For example, you can trigger different jobs based on the value of a parameter:

1
2
3
4
5
jobs:
  - job: deployToProd
    condition: eq('${{ parameters.environment }}', 'prod')
    steps:
      - script: echo "Deploying to production"

Parameter Validation

Parameters can include validations to ensure correct values. For example:

Enum (Choice): You can define a set of allowed values for a parameter.

1
2
3
4
5
6
7
8
parameters:
  - name: environment
    type: string
    default: 'dev'
    values:
      - dev
      - test
      - prod

Template Parameters

Parameters can also be used in pipeline templates to make them reusable across multiple pipelines:

1
2
3
4
5
# main.yaml
extends:
  template: build-template.yml
  parameters:
    environment: 'dev'
build-template.yml
1
2
3
4
5
6
7
8
parameters:
  - name: environment
    type: string

jobs:
  - job: build
    steps:
      - script: echo "Building for ${{ parameters.environment }}"
  • Default Values You can assign default values to parameters to ensure they have a value if none is provided when the pipeline is triggered.

  • Runtime Parameter Override You can override parameters during pipeline execution by providing values when manually triggering the pipeline or via API calls.

Example: YAML with Parameters

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
name: 'Azure DevOps - Parameter Examples'
trigger:
  - main

pool:
  vmImage: ubuntu-latest

parameters:
  - name: environment
    type: string
    default: 'dev'

  - name: isProduction
    type: boolean
    default: false

jobs:
  - job: build
    steps:
      - script: echo "Building in ${{ parameters.environment }} environment"
        displayName: 'Verbose Build Step'

      - script: |
          if [ "${{ parameters.isProduction }}" == "True" ]; then
            echo "Production build"
          fi          
        displayName: 'Conditional Production Step'

Example - User Driven Pipeline

Below is an example where you can use user driven parameters to configure bicep deployments.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
name: 'Azure Workload Identity Federation Authentication'
trigger:
  - main

pool:
  vmImage: ubuntu-latest

parameters:
  - name: deploymentType
    displayName: "Deployment Type"
    type: string
    default: "spoke"
    values:
     - hub
     - spoke

  - name: customerName
    displayName: "Customer Name"
    type: string
    default: ""

  - name: subscriptionId
    displayName: "Customer Azure Subscription"
    type: string
    default: ""

  - name: environmentType
    displayName: "Environment Type"
    type: string
    default: "prod"
    values:
      - prod
      - acc
      - dev
      - test

  - name: networkCidr
    displayName: "Network Configuration"
    type: string
    default: ""

  - name: optionalBastionHost
    displayName: Enable Bastion Host
    type: boolean
    default: false

  - name: optionalBastionHostSku
    displayName: Bastion Host Sku
    type: string
    default: none
    values:
      - premium
      - standard
      - basic
      - dev
      - none

jobs:
  - job: build
    steps:
      - script: |
          echo "Deployment Type is: ${{ parameters.deploymentType }}"
          echo "Customer Name is: ${{ parameters.CustomerName }}"
          echo "Customer Subscription is: ${{ parameters.subscriptionId }}"
          echo "Environment Type: ${{ parameters.environmentType}}"
          echo "Network CIDR is: ${{ parameters.networkCidr }}"
          echo "Bastion Deployment: ${{ parameters.optionalBastionHost }}"
          echo "Bastion Host Sku: ${{ parameters.optionalBastionHostSku }}"          
        displayName: 'Verbose Check'

From a pipeline perspective, this would look something like this following:

During the deployment if you needed to pull these values back either during jobs or steps you can use the following

User Driven Outputs

Verbose Output - Bash
1
2
3
4
5
6
7
8
  - script: |
      echo "Deployment Type is: ${{ parameters.deploymentType }}"
      echo "Customer Name is: ${{ parameters.CustomerName }}"
      echo "Customer Subscription is: ${{ parameters.subscriptionId }}"
      echo "Environment Type: ${{ parameters.environmentType}}"
      echo "Network CIDR is: ${{ parameters.networkCidr }}"
      echo "Bastion Deployment: ${{ parameters.optionalBastionHost }}"
      echo "Bastion Host Sku: ${{ parameters.optionalBastionHostSku }}"      

Verbose Output - PowerShell
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  - task: PowerShell@2
    inputs:
      targetType: 'inline'
      script: |
        Write-Output "Deployment Type is: ${{ parameters.deploymentType }}"
        Write-Output "Customer Name is: ${{ parameters.CustomerName }}"
        Write-Output "Customer Subscription is: ${{ parameters.subscriptionId }}"
        Write-Output "Environment Type: ${{ parameters.environmentType}}"
        Write-Output "Network CIDR is: ${{ parameters.networkCidr }}"
        Write-Output "Bastion Deployment: ${{ parameters.optionalBastionHost }}"
        Write-Output "Bastion Host Sku: ${{ parameters.optionalBastionHostSku }}"

Condition Based Parameters

1
2
3
4
5
6
7
        - task: PowerShell@2
          condition:  ${{ eq(parameters.optionalBastionHost, 'TRUE') }}
          displayName: 'Deploy Bastion Host'
          inputs:
            targetType: 'inline'
            script: |
              Write-Output "WIP - Add Bicep Bastion Code Here 😉"              

Summary

Parameters in Azure DevOps are versatile tools for customizing and controlling the behavior of pipelines. They allow for dynamic values, conditional steps, reusable templates, and can be overridden at runtime, making your pipeline definitions more flexible and scalable.

GitHub Actions

In GitHub Actions workflows, inputs are parameters that can be passed to actions, jobs, or workflows. These inputs allow customization and dynamic behavior in the workflow. Below are the main types of inputs you can work with in GitHub Actions:

Parameter Types Explained

If you want to find out more GitHub Docs

Data typeNotes
stringAllows any free-form text input.
choicedefines a dropdown input with a list of valid options.
booleantrue or false

String

A single line of text (the default input type). This is the most common input type.

Example:

1
2
3
4
5
inputs:
  name:
    description: 'Name of the user'
    required: true
    default: 'User'

Choice

A predefined list of options that the user can select from. This type limits the input to the specified choices. Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
inputs:
  logLevel:
    description: 'Log level'
    required: true
    default: 'warning'
    type: choice
    options:
      - info
      - warning
      - debug

Boolean

A true/false value. Boolean inputs are typically used for flags that control the execution of certain steps or jobs. Example:

1
2
3
4
5
inputs:
  print_tags:
    description: 'True to print to STDOUT'
    required: true
    type: boolean

These input types can be used in various contexts within workflows, actions, or jobs, and can also be set with defaults or marked as required.

Example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
name: 'GitHub Actions - User Driven Parameters'

on:
  workflow_dispatch:
    inputs:
      logLevel:
        description: 'Log level'
        required: true
        default: 'warning'
        type: choice
        options:
          - info
          - warning
          - debug

      print_tags:
        description: 'True to print to STDOUT'
        required: true
        type: boolean

      tags:
        description: 'Test scenario tags'
        required: true
        type: string

      environment:
        description: 'Environment to run tests against'
        required: true
        type: string

jobs:
  deploy:
    runs-on: ubuntu-24.04

  print-tag:
    runs-on: ubuntu-latest
    if: ${{ inputs.print_tags }}
    steps:
      - name: Print the input tags to STDOUT
        run: echo "The tags are ${{ inputs.tags }}"

Wrap Up

In the fast-paced world of infrastructure development, automation a cornerstone of efficient CI/CD pipelines, Incorporating user-driven inputs can add essential flexibility and precision. These inputs allow teams to tailor deployments to specific environments, control sensitive rollouts, and focus on targeted debugging or testing without running entire pipelines. They enable on-demand operations, where dynamic workflows adjust based on real-time needs, and foster collaboration by embedding approval steps and contextual feedback directly into the pipeline. Whether it’s specifying deployment regions in Azure DevOps, choosing feature toggles in GitHub Actions, or handling complex release workflows, user-driven inputs empower teams to adapt to real-world scenarios while maintaining control and reducing risks. However, it’s crucial to validate inputs, secure sensitive parameters, and balance manual interventions to avoid bottlenecks. By integrating user-driven inputs thoughtfully, CI/CD pipelines can evolve into more adaptable and intelligent systems, aligning better with the unique demands of each deployment.

Share with your network!

Built with Hugo - Theme Stack designed by Jimmy