Home

Published

- 10 min read

Continuous Integration using Self-hosted Agents!

img of Continuous Integration using Self-hosted Agents!

Azure DevOps

In this first blog post of my DevOps experiences, I’d like to share my findings discovering the best-practices using Azure DevOps pipelines. I always like to take a step back and look at the technology and philosophy itself. Unraveling its anatomy to create a basic understanding of this tool, I deem to be fundamental in software development. During my journey I’ve read some books helping out with understanding the philosophy of DevOps and implementation practices. If you’d like to know more, book Recommendations at the end 📚!

Let’s start with the essence of DevOps—a fusion of concepts, processes, and tools aimed at creating value. Basically cherry-picking familiar agile concepts and combining them in a SaaS. Azure DevOps encapsulates this philosophy through its five pivotal processes:

  • Plan,
  • Code,
  • Build,
  • Test,
  • Release/Deploy.
Azure-DevOps.jpg

By today’s standards, this should be a basic workflow within a company. Someone has an idea → idea becomes a plan → turns into code → ??? → profit! Our flow was similar, with the major difference that most deployments still went manually. Which, at times, felt like a business plan proposed by those Underpants Gnomes from South Park. Let’s invest some time, hope for the best and everything should match the wishes of our users!

underpantsGnomes.png

Basically all the clichés you read about when companies make a DevOps transformation. We ticked all the boxes you usually read about before CI/CD was introduced. With just a simple deployment pipeline, we reduced time spent by 80% average! And before you call this fake or whatever, do realize we’re speaking of applications that just lay there ready, only to be picked up by some guy from operations. Operations wasn’t solely to blame; our code quality and feedback loops were lacking. Such challenges aren’t unique, warranting pragmatic solutions, which led to our simple first pipeline.

Azure Pipelines

Azure Pipelines, as the name suggests, is where the magic happens. Defined by configurations housed in YAML files, pipelines orchestrate the compilation and testing of code with precision. Triggers, such as pull requests or code alterations, set pipelines into motion, ensuring swift and automated validation of changes. But before you can even think about using these pipelines, you need something to run them on. Which are our unsung heroes of Azure, DevOps—build and deploy agents! But what are they?

Agents are the workhorses behind the scenes that execute jobs from within the pipeline. Basically a tiny bit of infrastructure handling your jobs instead of you doing it manually yourself. For example, compiling your code and building your project. While Microsoft-hosted agents offer convenience and ease-of-use because they’re already configured, self-hosted agents provide flexibility based on your requirements. Which able you to decide which dependencies you’d like to be available.

Whether it’s leveraging Linux instances or installing specific legacy software. As a developer at the time, it was easier for us to create self-hosted agents than using Microsoft-hosted agents. This was partly due to our skill gap and use of Azure DevOps Server, previously known as Team Foundation Server (TFS). It taught me a lot about networking and configuration while getting this to work, I still benefit from this learning experience every day.

At the time of working, we were still in transition to Azure, meaning we used a Hybrid Cloud model. Because of this, we decided to stick to Self-Hosted Build and Deploy agents. We decided that for our Build-agents we’d use two VMs hosted in Azure, which is kind of funny considering we decided to host the build-agents ourselves.

Self-hosted Build agent for self-hosted DevOps

If you have decided to use self-hosted agents you have decided to take matters into your own hands. They can seem daunting at first but can give you great insight and flexibility. However, nothing is for free with self-hosting things so if you’re like us and self-host Azure Devops (server), than I would recommend upgrading to 2022 first. So basically, a self-hosted build agent is a software component that runs on your own machine or server and performs tasks such as compiling code, running tests, and deploying applications. Self-hosted build agents allow you to have more control over your build environment and can be useful for scenarios such as running specific tests or performing custom build steps.

My recommendation for security would be to have a server which is only accessible by a single account. For a first Build-Agent you could use the basic script Microsoft uses, however I find this one a bit shallow. Apparently Build & Deploy agents are the same software, but configured differently, as I found out in practice. Microsoft helps out with registering a Deploy agent, but barely with build agents. Which makes sense since they don’t need that much config. If you finally have your server prepared, you can register your Build agent with this script. Make as many agents you require!

   <#
.SYNOPSIS
    Configures an Azure DevOps Agent for building. For configuration for a Deployment group you need a different configuration.
.DESCRIPTION
    This script will download the specified Azure DevOps Agent (VSTS), configure it and make it run as a service.
    It will also set the service account and password.
.PARAMETER agentName
    The name of the Agent that will be used to register with Azure DevOps.
.EXAMPLE
    .\Configure-Agent.ps1 -agentName "AZ1-$($env:COMPUTERNAME)"
#>
function Configure-Agent([string]$agentName) {
    # You can get these from your Azure DevOps organization settings
    $pat = ""
    $serviceAccount = ""
    $serviceAccountPassword = ""
    $poolName = ""

    # Checks if you are running as Admin
    $ErrorActionPreference = "Stop";
    If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
        throw "Run command in an administrator PowerShell prompt"
    }
    If ($PSVersionTable.PSVersion -lt (New-Object System.Version("3.0"))) {
        throw "The minimum version of Windows PowerShell that is required by the script (3.0) does not match the currently running version of Windows PowerShell."
    }

    # Checks if there is an azagent directory else makes it for you
    If (-NOT (Test-Path 'D:\BuildAgents')) {
        mkdir 'D:\BuildAgents'
    };
    cd 'D:\BuildAgents';
    if (-NOT (Test-Path ($agentName))) {
        mkdir $agentName;
        cd $agentName;
    }
    # Downloading and upacking the agent.zip software
    $agentZip = "$PWD\agent.zip";
    $DefaultProxy = [System.Net.WebRequest]::DefaultWebProxy;
    $securityProtocol = @(); $securityProtocol += [Net.ServicePointManager]::SecurityProtocol;
    $securityProtocol += [Net.SecurityProtocolType]::Tls12; [Net.ServicePointManager]::SecurityProtocol = $securityProtocol;
    $WebClient = New-Object Net.WebClient;
    #### Get the latest agent from  https://github.com/microsoft/azure-pipelines-agent/releases
    $Uri = 'https://vstsagentpackage.azureedge.net/agent/3.225.2/vsts-agent-win-x64-3.225.2.zip';
    if ($DefaultProxy -and (-not $DefaultProxy.IsBypassed($Uri))) {
        $WebClient.Proxy = New-Object Net.WebProxy($DefaultProxy.GetProxy($Uri).OriginalString, $True);
    };
    $WebClient.DownloadFile($Uri, $agentZip);
    Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory( $agentZip, "$PWD");
    # The config needed if you want a regular agent (aimed at using YAML pipelines)
    .\config.cmd --unattended `
        --replace `
        --agent $agentName `
        --pool $poolName `
        --runasservice `
        --url 'https://YOUR_SELF_HOSTED_DEVOPS_URL' `
        --collectionname 'COLLECTION_NAME' `
        --auth $pat `
        --runAsService `
        --windowsLogonAccount $serviceAccount --windowsLogonPassword $serviceAccountPassword;

    # Removing the zip file we don't need anymore
    Remove-Item $agentZip;
}

If all is well, you should be able to select your pool name inside your Azure DevOps project and start building from your own pipeline! This Build agent can be used for both classic and Yaml pipelines. If you want to enforce the selection of a certain build-pool you could add this to your YAML file. I would recommend launching a pool of at least 2-4 depending on the server capacity you’re running with. This ensures that not too many build jobs get queued.

   stages:
  - stage: build
    pool:
      name: your-build-pool

Congratulations! You should now be able to build your project using your own self-hosted Build agent on your self-hosted Azure DevOps Environment!

Self-hosted Build-Agents in combination with DevOps Services

But what if you’re like us, and you’re on your way moving to the cloud entirely? I got you covered. Even though the method is partly the same, there is a minor difference in configuration. If you’re working for a security-minded firm, then chances are your entire network is working with Vnets/subnets and the like. The easiest way for this is working with a separate Azure Account which serves as a managed identity and using a PAT

   <#
This function configures an agent for use with Azure DevOps. It performs the following tasks:
- Checks if the script is running as an administrator
- Checks the version of Windows PowerShell
- Checks if the 'C:\BuildAgents' directory exists, and creates it if it doesn't
- Creates a directory for the specified agent if it does not exist
- Downloads and unpacks the agent software from a specified URI
- Runs the configuration command for the agent with specified parameters
- Removes the downloaded zip file

Example Usage:
Configure-Agent -agentName "AGENT-1-$($env:COMPUTERNAME)"
#>
function Configure-Agent([string]$agentName) {
    $pat = "PAT"
    $serviceAccount = "account email"
    $serviceAccountPassword = "password"
    $poolName = "pool-name"

    # Checks if you are running as Admin
    $ErrorActionPreference = "Stop";
    If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
        throw "Run command in an administrator PowerShell prompt"
    }
    If ($PSVersionTable.PSVersion -lt (New-Object System.Version("3.0"))) {
        throw "The minimum version of Windows PowerShell that is required by the script (3.0) does not match the currently running version of Windows PowerShell."
    }

    # Checks if there is an azagent directory else makes it for you
    If (-NOT (Test-Path 'C:\BuildAgents')) {
        mkdir 'C:\BuildAgents'
    };
    cd 'C:\BuildAgents';
    if (-NOT (Test-Path ($agentName))) {
        mkdir $agentName;
        cd $agentName;
    }
    # Downloading and upacking the agent.zip software
    $agentZip = "$PWD\agent.zip";
    $DefaultProxy = [System.Net.WebRequest]::DefaultWebProxy;
    $securityProtocol = @(); $securityProtocol += [Net.ServicePointManager]::SecurityProtocol;
    $securityProtocol += [Net.SecurityProtocolType]::Tls12; [Net.ServicePointManager]::SecurityProtocol = $securityProtocol;
    $WebClient = New-Object Net.WebClient;
    $Uri = 'https://vstsagentpackage.azureedge.net/agent/3.225.0/vsts-agent-win-x64-3.225.0.zip';
    if ($DefaultProxy -and (-not $DefaultProxy.IsBypassed($Uri))) {
        $WebClient.Proxy = New-Object Net.WebProxy($DefaultProxy.GetProxy($Uri).OriginalString, $True);
    };
    $WebClient.DownloadFile($Uri, $agentZip);
    Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory( $agentZip, "$PWD");
    # Running the actual config
    .\config.cmd --unattended `
        --replace `
        --agent $agentName `
        --pool $poolName `
        --runasservice `
        --url 'https://dev.azure.com/COMPANYNAME' `
        --auth PAT `
        --token $pat `
        --runAsService `
        --windowsLogonAccount $serviceAccount --windowsLogonPassword $serviceAccountPassword;

    # Removing the zip file we don't need anymore
    Remove-Item $agentZip;
}

# Example Usage:
Configure-Agent -agentName "AGENT-1-$($env:COMPUTERNAME)"

Additional sources

  1. Microsoft Docs: Pipeline Agents
  2. Microsoft Docs: Windows Agents
  3. Microsoft Docs: Unattended Config

Start Your DevOps challenges right with These Must-Read Books

If you’re ready to embark on a transformative journey in the world of DevOps, these handpicked recommendations will be your guiding light:

  1. The Phoenix Project by Gene Kim, Kevin Behr, and George Spafford

    Dive into the captivating narrative of Parts Unlimited and join Bill Palmer as he navigates the challenges of management and organizational change. This book not only serves as an inspiring introduction to DevOps but also equips you with valuable insights to overcome obstacles along the way.

  2. The Unicorn Project by Gene Kim

    Building upon the success of The Phoenix Project, The Unicorn Project offers a unique perspective from the world of developers. Explore the crucial concepts of automation and pipeline development without getting bogged down in technical details. With a forward-looking approach, this sequel ensures your understanding remains future-proof.

  3. The DevOps Handbook by Gene Kim, Patrick Debois, John Willis, and Jez Humble

    Ready to take your DevOps knowledge to the next level? Look no further than The DevOps Handbook. Tailored for readers with foundational understanding, this comprehensive guide empowers you to expand your horizons and drive meaningful change within your organization. Packed with real-life case studies and actionable strategies, it’s your roadmap to unlocking new possibilities.

  4. Accelerate by Nicole Forsgren, Jez Humble, and Gene Kim

    Short on time but craving impactful insights? Accelerate delivers the benefits of DevOps backed by scientific research and compelling statistics. Whether you’re aiming to persuade management or inspire your team, this concise yet powerful resource makes a compelling case for embracing practices like CI/CD.

By clicking on the titles above, you’ll not only gain valuable insights but also support me in producing more content like this! Feeling extra generous? Consider buying me a coffee! as a token of appreciation for the knowledge shared!