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

Azure DevOps and GitHub - Pipeline Authentication.

Introduction

In the previous post on ‘Learning Infrastructure as Code (IaC)’ I covered the target scope options and deployment methods, This as discussed can lead to some issues. Especially if you might be working with people less adept at using Git, and pulling code updates locally can also be problematic and create issues around version matches and out of date code bases, This dear reader is where and when you want to move your deployments from a local deployment to a pipeline style deployment! In this post we will look at both Azure DevOps and Github Actions

Advantages for CI/CD Pipelines

Using CI/CD pipelines to deploy Infrastructure as Code (IaC) offers several significant advantages. First, it automates the process of provisioning and managing infrastructure, reducing the chances of human error and ensuring consistency across environments. CI/CD pipelines enable faster delivery by allowing continuous integration and continuous deployment, which accelerates the release cycle for infrastructure changes. This automation also ensures that infrastructure updates are tested, validated, and deployed in a controlled and repeatable manner, improving both reliability and scalability. Additionally, by integrating version control with IaC, teams can easily track changes, roll back to previous versions if necessary, and maintain a history of infrastructure modifications. Ultimately, CI/CD pipelines bring efficiency, security, and stability to infrastructure management, making it easier to scale, test, and maintain environments as part of a modern DevOps approach.

Authentication Methods (Overview)

Azure DevOps

Azure DevOps provides several connection methods for interacting with Azure resources. These methods are used to authenticate and authorize actions such as deploying applications, managing infrastructure, or interacting with Azure services. Here are the main connection methods and their use cases:

Service Principal (Service Connection)

Description: A Service Principal is a security identity within Entra ID that can be assigned to Azure resources with specific permissions. In Azure DevOps, a Service Connection (Service Principal) is typically used to authenticate and authorize access to Azure resources.

Use Case:
Recommended for most cases where you need a service account to automate infrastructure tasks (like deploying applications or provisioning resources).

How it Works:
Azure DevOps uses a Service Principal to authenticate with Azure and perform tasks in a secure and automated way. You create a Service Connection in Azure DevOps and grant it the necessary permissions on the Azure subscription or resource group.

Best For:
Automation tasks like continuous deployment or infrastructure management with IaC tools like ARM, Bicep, or Terraform.

Why:
It provides secure, role-based access control (RBAC), ensuring that the right permissions are granted for the task while minimizing risks.

Azure CLI

Description: Azure CLI is a command-line tool that allows you to manage Azure resources. When configured in a pipeline, Azure CLI can authenticate using a Service Principal, managed identity, or Azure user credentials.

Use Case:
Ideal for custom scripts or cases where you want to run commands directly in the pipeline.

How it Works:
Azure CLI in Azure DevOps can authenticate with Azure either through a Service Principal or using Managed Identity (if running in an Azure-hosted agent). The CLI is flexible and can be used for various operations like creating resources, updating configurations, and more.

Best For:
Custom scripting, quick setups, or one-off deployments.

Why:
Azure CLI is versatile and easy to integrate with existing automation scripts, but the security model is less controlled than Service Principals.

Azure Managed Identity

Description: Managed Identity is a feature of Azure Active Directory (AAD) that provides Azure resources (such as VMs, App Services, etc.) with an identity to authenticate to Azure services without needing credentials to be explicitly managed.

Use Case:
Used primarily when Azure DevOps is running on an Azure-hosted agent or when you need to securely authenticate services running in Azure.

How it Works:
When you use Managed Identity with Azure DevOps pipelines, the Azure DevOps service or agent uses the Managed Identity to authenticate to Azure resources, eliminating the need for Service Principal credentials or user credentials.

Best For:
Scenarios where your Azure DevOps agent is running inside Azure and you want a more secure, automated identity management process.

Why:
It offers improved security by eliminating the need to store or manage credentials. It’s ideal when you are working within Azure and want to avoid using service principal secrets.

Azure Personal Access Token

Description: A Personal Access Token (PAT) is a token generated by Azure DevOps that can be used for authentication, often used in conjunction with Git operations or API calls. Use Case:
Typically used for non-interactive authentication when accessing the Azure DevOps REST APIs or performing Git operations.

How it Works:
PAT is a user-specific token with limited permissions. It can be used for authenticating specific actions in Azure DevOps pipelines, but it is generally not used for managing Azure resources directly.

Best For:
User-level operations like API access and Git operations.

Why:
It’s convenient for user authentication, but less secure and less flexible compared to Service Principals for managing Azure resources.

Best Method and Why:

Service Principal (Service Connection) is generally the best method for connecting Azure DevOps to Azure.

Security:
Service Principals allow you to define strict RBAC policies that ensure only authorized actions are performed on Azure resources.

Automation:
They are designed for automated, non-interactive tasks like deployments, making them ideal for CI/CD pipelines.

Scalability:
You can use Service Principals across multiple projects and environments while controlling the scope and permissions of each Service Principal, making them flexible for large-scale deployments.

Granular Control:
You can assign specific roles and permissions to the Service Principal, ensuring the principle of least privilege. For most use cases involving continuous integration and deployment, a Service Principal is secure, manageable, and scalable, making it the best choice for connecting Azure DevOps to Azure.

GitHub Actions

GitHub Actions also provides several methods for connecting to Azure resources, and the choice of connection method depends on the use case, security requirements, and the type of tasks you want to automate. Here are the main connection methods for Azure DevOps, adapted for GitHub Actions:

Service Principal with Azure CLI

Description:
Just like in Azure DevOps, a Service Principal in Entra ID is commonly used for authentication in GitHub Actions workflows. The Service Principal is created in Azure and then used in the workflow to authenticate with Azure using the Azure CLI or other Azure-related actions.

Use Case:
Ideal for automating infrastructure deployment, resource management, and performing tasks like provisioning resources or deploying applications.

How it Works:
A Service Principal is created in Azure with a client ID and client secret (or certificate). In the GitHub Actions workflow, you can configure the Azure CLI or the Azure Action to authenticate using these credentials.

Best For:
Infrastructure as Code (IaC) deployments, automated management of Azure resources, and tasks like Azure Kubernetes Service (AKS) or Azure App Service deployments.

Why:
This method is secure and supports role-based access control (RBAC), ensuring that your workflow can perform only the actions it is authorized for.

Azure Managed Identity (for GitHub-hosted runners on Azure)

Description:
Managed Identity is an identity automatically created by Azure for resources within Azure. GitHub Actions can authenticate to Azure using a Managed Identity if the GitHub-hosted runner is running in an Azure environment that supports Managed Identity.

Use Case:
Ideal for scenarios where the GitHub Actions runner is hosted within Azure, such as deploying to Azure Web Apps or virtual machines.

How it Works:
When the runner is running in an Azure environment, it can automatically authenticate using the Managed Identity assigned to the resource. This removes the need to store or manage credentials.

Best For:
Azure-based CI/CD pipelines where security and simplicity are critical, and there is no need for managing credentials directly.

Why:
Provides secure, automated authentication without managing credentials, ensuring that the workflow runs securely and is easier to maintain.

GitHub Secrets with Service Principal Credentials

Description:
GitHub Secrets are environment variables that are stored securely in your GitHub repository and are used to authenticate your GitHub Actions workflows. You can store the Service Principal credentials (such as client ID, tenant ID, and client secret) as GitHub Secrets and use them in your workflows.

Use Case:
Best for securing and storing authentication credentials for Azure resources without exposing them in the workflow file.

How it Works:
You create GitHub Secrets for your Service Principal credentials (client ID, tenant ID, and client secret). In the GitHub Actions workflow, you reference these secrets when using the Azure CLI, Azure PowerShell, or other Azure-related GitHub Actions to authenticate and perform tasks on Azure.

Best For:
Secure authentication in workflows, where credentials need to be safely stored and accessed.

Why:
GitHub Secrets are encrypted and only available to workflows during runtime, minimizing the risk of accidental exposure.

Azure Login GitHub Action (Using Service Principal)

Description:
The Azure/login GitHub Action is a widely used action that simplifies the process of authenticating to Azure using a Service Principal. It securely stores and uses the credentials from GitHub Secrets to authenticate and perform tasks.

Use Case:
Simplifies authentication for Azure tasks in GitHub Actions, such as provisioning infrastructure or deploying applications to Azure services.

How it Works:
The action requires the following GitHub Secrets: AZURE_CLIENT_ID, AZURE_TENANT_ID, and AZURE_CLIENT_SECRET. It uses these secrets to authenticate the workflow to Azure.

Best For:
CI/CD workflows where you need to authenticate and interact with Azure services.

Why:
This method simplifies and standardizes authentication in GitHub Actions, ensuring secure access while also supporting automatic role-based access control.

Azure Service Principal with Azure PowerShell

Description:
If your GitHub Actions workflow involves PowerShell scripts to interact with Azure, you can authenticate using a Service Principal with Azure PowerShell cmdlets (Connect-AzAccount).

Use Case:
Best for workflows involving PowerShell scripts for managing Azure resources.

How it Works:
Similar to using Azure CLI, you use the Service Principal credentials stored in GitHub Secrets to authenticate with Azure in your PowerShell script.

Best For:
Custom scripts and automation tasks that require Azure PowerShell cmdlets.

Why:
This approach is flexible and can be integrated with custom PowerShell automation tasks while ensuring secure access to Azure.

Best Method and Why:

Service Principal with Azure CLI or Azure/login GitHub Action is the best method for most scenarios in GitHub Actions. Here’s why:

Security:
Using Service Principals with Azure CLI or Azure/login GitHub Action ensures that authentication is done securely using role-based access control (RBAC). Secrets stored in GitHub Secrets are encrypted and safe.

Ease of Use:
The Azure/login GitHub Action simplifies authentication by abstracting the details of Service Principal setup and usage. It’s widely adopted, making it easy to integrate into your workflow.

Scalability:
Service Principals are flexible, and permissions can be adjusted via Azure RBAC, making them suitable for both small and large-scale environments.

Automation:
Service Principals are designed for automation tasks, making them ideal for CI/CD workflows that interact with Azure services. For most use cases, leveraging Service Principals along with the Azure/login GitHub Action or Azure CLI in GitHub Actions workflows provides a balance of security, scalability, and ease of use, making it the best choice for connecting GitHub Actions to Azure.

OpenID Authentication Method (Setup)

OpenID Authentication is an open standard and decentralized authentication protocol that allows users to log in to multiple websites or applications using a single set of credentials managed by an OpenID provider (OP). It simplifies user access across services by eliminating the need to remember separate usernames and passwords for each service.

Azure DevOps

As we’ve seen from the above overview, the recommend method is using a Service Principal, I would then advise to go one step further and look at a federated identity.

An Azure DevOps Federated Identity allows users to authenticate using external identity providers, such as Entra ID or third-party services, without needing separate credentials for Azure DevOps. This approach enables Single Sign-On (SSO), simplifying user management by leveraging existing corporate or third-party credentials for access. Federated identities support centralized security policies like multi-factor authentication and conditional access. The process involves setting up identity federation between Azure DevOps and the external provider, where users authenticate through the provider, and Azure DevOps grants access based on the issued security token, ensuring secure and seamless access to resources.

To configure this kind of Identity, from Azure DevOps (http://dev.azure.com), You want to open your current project and head to Project Settings then look for Service Connections

From here, we want to click Create service connection and then Azure Resource Manager

Lets now build out the Azure Service Connection.

Once the setup is completed, you should see a connection

You’ll need to authorise the new service connections on any pipelines you’ve built!

RBAC (Role Based Access Control) wise, the Service Connection will be added as contributor on the subscription.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
name: 'Azure Workload Identity Federation Authentication'
trigger:
- main

pool:
  vmImage: ubuntu-latest

steps:
- task: AzureCLI@2
  inputs:
    azureSubscription: 'sc-azure-devops-dev'
    scriptType: 'pscore'
    scriptLocation: 'inlineScript'
    inlineScript: |
      az resource list      

GitHub Actions

First we want to create a service principal under Entra ID.

Create Service Principal (Owner)

1
2
3
4
5
$subscriptionId = "<subscription-id>"
$environmentType = "dev"
$spContext = az ad sp create-for-rbac --name "sc-github-openid-$environmentType" --role "Owner" --scopes "/subscriptions/$subscriptionId"
$keyId = az ad app credential list --id ($spContext | ConvertFrom-Json).AppId | ConvertFrom-Json | Select-Object -ExpandProperty keyId
az ad app credential delete --id ($spContext | ConvertFrom-Json).AppId --key-id $keyId

Create Service Principal (Contributor)

1
2
3
4
5
$subscriptionId = "<subscription-id>"
$environmentType = "dev"
$spContext = az ad sp create-for-rbac --name "sc-github-openid-$environmentType""  --role "Contributor" --scopes  "/subscriptions/$subscriptionId"
$keyId = az ad app credential list --id ($spContext | ConvertFrom-Json).AppId | ConvertFrom-Json | Select-Object -ExpandProperty keyId
az ad app credential delete --id ($spContext | ConvertFrom-Json).AppId --key-id $keyId

Once you’ve created the service principal in Azure, You’ll want tp copy the ClientId TenantId SubscriptionId and add to your GitHub repository secrets. From your repository Settings > Secrets and variables > Actions > Repository secrets

next you want to create a Federation Connection.

From the Entra Id Portal, head to App Registrations > sc-github-openid-dev.
From the App Registration head to Certificates & secrets and then Federated credentials

From here click + Add a credential and from the dropdown choose Github Actions deploying Azure resources

Add the configuration for the GitHub Repository.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
name: 'Azure OpenID Authentication'
on:
  push:
    branches: [ "main" ]
  workflow_dispatch:

permissions:
  id-token: write

jobs:
  azure_auth:
    runs-on: ubuntu-latest

    steps:
      - name: Azure CLI Login
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

Wrap Up

In the evolving landscape of DevOps, secure authentication is paramount. This post explored the shift from traditional secret-based authentication to OpenID Connect (OIDC) with federated credentials for CI/CD pipelines in Azure DevOps and GitHub Actions.

Traditional methods, like using service principal secrets or personal access tokens, carry inherent risks. Secrets must be stored, rotated, and secured, creating management overhead and exposing pipelines to potential compromise if those secrets are mishandled or leaked.

OIDC revolutionizes authentication by eliminating the need for static secrets. Through a federated connection, OIDC dynamically generates short-lived tokens, which are tied to specific pipeline runs and have limited scope.

Coming later this week is Part #2 of CI/CD pipelines, where we explore how user-driven inputs can shape pipeline execution. Stay tuned for insights, best practices, and real-world examples to optimize your pipeline workflows.

Share with your network!

Built with Hugo - Theme Stack designed by Jimmy