Planet PowerShell logo

Contents

Configure On-Premises Servers With Ansible Through Azure Devops

In this post, I will walk through how you can set up an Azure DevOps pipeline agent, with Ansible, and use it for configuring and provisioning resources on your on-premises servers. In the example for this post, I will be setting up a Linux pipeline agent, installing Ansible with Kerberos authentication, and at the end creating a DNS record on a Domain Controller.

/images/configure-on-premises-servers-with-ansible-through-azure-devops/provision-winows-through-azure-devops01.png

Prerequisites

To follow along with this lab you will need:

  • Windows Server (Domain Controller / DNS Role installed)
  • Linux Server
  • Azure DevOps Account

Now for this tutorial, I have not taken into account keeping clear text password out of my repo. If you want to create a similar setup you should keep your secrets/passwords in either a key vault or as environment variables.

Setting up the Azure DevOps pipeline agent

I have created a new Project called: TEST-OnpPremisesIAC, inside my Azure DevOps organization.

To add a new pipeline agent to my new project I need to click on “Project Settings” in the lower-left corner, and then under “Pipelines” I need to click on “Agent pools”.

Inside the “Agent pools” settings menu I will click on “Add Pool”.

For the Pool Type I will select “Self-hosted”, and then give it the name: “LinuxAgents”. I will also set the “Pipeline permissions” to “Grant access permissions to all pipelines”.

/images/configure-on-premises-servers-with-ansible-through-azure-devops/provision-winows-through-azure-devops02.png

Then to add a new agent to my newly created pool, I will click on the Pool “LinuxAgents” and then click on “New agent”.

Here i will chose Linux and x64 for my type of virtual machine. I will then click on the copy button next to the Download button. This will copy the link for downloading the agent binary.

I will then head over to my Linux virtual machine, create a new directory inside my home directory, and name it Downloads

1
2
cd ~
mkdir Downloads && cd Downloads

I will then download the agent binary by running the command:

1
wget https://vstsagentpackage.azureedge.net/agent/2.204.0/vsts-agent-linux-x64-2.204.0.tar.gz && cd ~

You then need to extract the gzip archived file by running the command:

1
2
mkdir myagent && cd myagent
tar zxvf ~/Downloads/vsts-agent-linux-x64-2.204.0.tar.gz

then once the files are extracted you can install all the needed dependencies by running the command:

1
sudo ./bin/installdependencies.sh

Then once the dependencies are installed you are ready to configure the agent. To start the configuration run the command:

1
./config.sh

Configuring the DevOps Agent

First, you will be asked to accept the Team Explorer Everywhere License agreement

1
Enter (Y/N) Accept the Team Explorer Everywhere license agreement now? (press enter for N) > Y

Press ‘Y’ to accept

You will then be prompted for your Server URL. This URL will be: https://dev.azure.com/"name_of_organization"

1
Enter server URL > https://dev.azure.com/scriptingchris

You will then be asked to choose an authentication type. Here I have chosen PAT. To choose PAT just press enter

1
Enter authentication type (press enter for PAT) >

You will now need to create a PAT inside your DevOps organization.

You do this by clicking on the small user icon with a gear, in the upper right corner, and then click on “Personal Access Token”.

/images/configure-on-premises-servers-with-ansible-through-azure-devops/provision-winows-through-azure-devops03.png

Then inside the “Personal Access Token” Settings menu, click on “+ New Token”. Give the Token a name and chose how long time the token should be valid. For this lab environment, I have just chosen 30 days. Then you will need to add a Scope of permissions to which the token has access. Here I have given the Token “Full access”.

You should then receive a Token. Copy it and save it in a secure place. Then paste the token into the Linux Agent.

You will then need to provide the name of the Agent Pool created earlier. Here I will type in LinuxAgents

1
Enter agent pool (press enter for default) > LinuxAgents

You will then need to provide the name of the actual agent. If you want to keep the hostname of the server as the name of the agent, just press “Enter”.

1
Enter agent name (press enter for lab-ans01) >

You then need to provide a work folder. This is the directory from where the agent will run the pipelines. I will just choose the default option, by pressing “Enter”.

1
Enter work folder (press enter for _work) >

If all went well you should see that the Settings have been saved.

Now to run the agent, I want it to run as a systemd service so I don’t have to be logged in and start the agent software manually every time. To create the agent as a service you can run the script svc.sh

1
sudo ./svc.sh install "linux user's username"

Then to start the service, run the command

1
sudo ./svc.sh start

and you should see an output similar to below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15

/etc/systemd/system/vsts.agent.scriptingchris.LinuxAgents.lab\x2dans01.service
● vsts.agent.scriptingchris.LinuxAgents.lab\x2dans01.service - Azure Pipelines Agent (scriptingchris.LinuxAgents.lab-ans01)
     Loaded: loaded (/etc/systemd/system/vsts.agent.scriptingchris.LinuxAgents.lab\x2dans01.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2022-07-01 09:22:59 UTC; 12ms ago
   Main PID: 2048 (runsvc.sh)
      Tasks: 8 (limit: 2196)
     Memory: 3.2M
     CGroup: /system.slice/vsts.agent.scriptingchris.LinuxAgents.lab\x2dans01.service
             ├─2048 /bin/bash /home/vagrant/myagent/runsvc.sh
             └─2064 ./externals/node10/bin/node ./bin/AgentService.js

Jul 01 09:22:59 lab-ans01 systemd[1]: Started Azure Pipelines Agent (scriptingchris.LinuxAgents.lab-ans01).
Jul 01 09:22:59 lab-ans01 runsvc.sh[2048]: .path=/home/vagrant/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/game…s:/snap/bin
Hint: Some lines were ellipsized, use -l to show in full.

You can now also go back to “Azure DevOps”, then to “Project Settings”, then to “Agent pools”, click on “LinuxAgents” and then on “Agents”. You should now see the agent you configured and that it is “online”

/images/configure-on-premises-servers-with-ansible-through-azure-devops/provision-winows-through-azure-devops04.png

Setting the up the DevOps Agent to use Ansible with Kerberos

Before your agent will be able to run any Ansible code you will need to install some prerequisite packages and configure Kerberos authentication. You will need to set up Kerberos Authentication before the agent can connect to any Windows Domain server.

So I will start by installing the packages by running the commands:

1
2
3
4
5
sudo apt update && sudo apt install gcc python-dev libkrb5-dev -y

pip3 install pywinrm --user

sudo apt install krb5-user python3-kerbero -y

during the installation of the packages, you will be asked for some information regarding your “Realm” or “Domain”.

When installing Kerberos the “Configuring Kerberos Authentication” screen will pop up.

Here you should just type in UPPERCASE the domain name. In my case, my domain is called LAB.LOCAL.

You will then be asked two more times for the server settings. Here you can add the IP address or FQDN of your domain controller.

/images/configure-on-premises-servers-with-ansible-through-azure-devops/provision-winows-through-azure-devops05.png

Now once you have Kerberos configured, you will need to add the domain controller to your hosts file. To do this run the command:

1
sudo nano /etc/hosts

then add the line to the bottom of the file:

1
ipaddress_of_dc fqdn_of_dc

here is an example of my hosts file

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
127.0.0.1       localhost
127.0.1.1       ubuntu2004.localdomain

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

127.0.0.1 ubuntu2004.localdomain

127.0.2.1 lab-ans01 lab-ans01
192.168.20.130 lab-dc01.LAB.LOCAL

then reboot your linux server by running the command:

1
sudo reboot now

Once you machine has rebooted you will need to configure Kerberos. To do this open the file /etc/krb5.conf

1
sudo nano /etc/krb5.conf

Inside the file, under your REALM setting, you can remove everything from the file, and just keep the setting you created during the configurations. You should then have a file similar to mine:

 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
[libdefaults]
        default_realm = LAB.LOCAL

# The following krb5.conf variables are only for MIT Kerberos.
        kdc_timesync = 1
        ccache_type = 4
        forwardable = true
        proxiable = true

# The following encryption type specification will be used by MIT Kerberos
# if uncommented.  In general, the defaults in the MIT Kerberos code are
# correct and overriding these specifications only serves to disable new
# encryption types as they are added, creating interoperability problems.
#
# The only time when you might need to uncomment these lines and change
# the enctypes is if you have local software that will break on ticket
# caches containing ticket encryption types it doesn't know about (such as
# old versions of Sun Java).

#       default_tgs_enctypes = des3-hmac-sha1
#       default_tkt_enctypes = des3-hmac-sha1
#       permitted_enctypes = des3-hmac-sha1

# The following libdefaults parameters are only for Heimdal Kerberos.
        fcc-mit-ticketflags = true

[realms]
        LAB.LOCAL = {
                kdc = 192.168.20.130
                admin_server = 192.168.20.130
        }

You should now have Kerberos configured to authenticate against your domain controller. You can test this by running the command:

1
kini -f administrator

you will then be asked to provide a password

1
Password for [email protected]:

If you do not receive any output of the command, it means that the authentication was successful.

Testing If Ansible can authenticate against your Windows Domain Controller

To test that Ansible can authenticate to your Windows domain controller, you can create an Inventory file, named inventory.ini on your Linux agent. Here is an example of my inventory file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[win]
lab-dc01.LAB.LOCAL

[win:vars]
ansible_user="[email protected]"
ansible_password="[email protected]"
ansible_port=5985
ansible_connection=winrm
ansible_winrm_transport=kerberos
ansible_winrm_server_cert_validation=ignore
ansible_become=false

i can now test if my Linux agent can connect to my domain controller by running the command:

1
ansible -i inventory.ini win -m win_ping

and you should recieve an output similar to below:

1
2
3
4
lab-dc01.LAB.LOCAL | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

if the output was successful you are now ready to build the pipeline in Azure DevOps, and test it.

Building an Azure DevOps Pipeline for provisioning with Ansible

The repository

In my repository i have the following two files:

inventory.ini

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[win]
lab-dc01.LAB.LOCAL

[win:vars]
ansible_user="[email protected]"
ansible_password="[email protected]"
ansible_port=5985
ansible_connection=winrm
ansible_winrm_transport=kerberos
ansible_winrm_server_cert_validation=ignore
ansible_become=false

and

dns-playbook.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
---
- name: Deploy DNS Records
  hosts: all
  gather_facts: false
  tasks:
  
  - name: Create a new DNS Record
    win_dns_record:
      name: "ansible"
      type: "A"
      values:
        - 192.168.20.143
      zone: "lab.local"

This playbook will connect to my domain controller, and create a new ‘A’ record inside the DNS zone “lab.local”.

Building the pipeline

To create the pipeline go to “Pipelines”, then click on “Create pipeline”. Then chose where your code is store, in my case it is in “Azure Repos Git”. Then chose you repository and select “Starter pipeline”.

In the pipeline i will define it to be triggered by a push into the main branch

1
2
trigger:
- main

I then define that I want the pipeline to run on my Linux Agent

1
2
pool:
  name: LinuxAgents

and the only step I have is that it should run the Ansible playbook with the provided inventory file

1
2
3
4
5
6
7
8
steps:
- task: [email protected]
  displayName: Run Ansible Playbook
  inputs:
    ansibleInterface: 'agentMachine'
    playbookPathOnAgentMachine: 'dns-playbook.yml'
    inventoriesAgentMachine: 'file'
    inventoryFileOnAgentMachine: 'inventory.ini'

and the complete pipeline should look similar to below:

1
2
3
4
5
6
7
8
steps:
- task: [email protected]
  displayName: Run Ansible Playbook
  inputs:
    ansibleInterface: 'agentMachine'
    playbookPathOnAgentMachine: 'dns-playbook.yml'
    inventoriesAgentMachine: 'file'
    inventoryFileOnAgentMachine: 'inventory.ini'

I can then go ahead and run the pipeline to see if everything worked as supposed. If the Ansible playbook worked, you should see the green checkmark, and inside the log, you should also see that 1 configuration was changed.

/images/configure-on-premises-servers-with-ansible-through-azure-devops/provision-winows-through-azure-devops06.png

and you can run the The PowerShell command on your Domain Controller, just replace the zone name with your own.

1
Get-DnsServerResourceRecord -ZoneName "lab.local"

and you should now see the record created by Ansible

/images/configure-on-premises-servers-with-ansible-through-azure-devops/provision-winows-through-azure-devops07.png

Conclusion

Using agents in your Azure DevOps organization will provide you with the option of running commands or scripts on your on-premises servers. This opens up the possibility of using Azure DevOps in the cloud but provisioning your on-premises servers with IaC.

When setting up a configuration similar to what I showed here, you would need to take into considerations how you want to keep your secrets and passwords for connecting to your servers or domain machines.