loading...
Cover image for Connecting to Azure with Ansible
CloudSkills.io

Connecting to Azure with Ansible

joshduffney profile image Josh Duffney Updated on ・5 min read

Introduction

Ansible is an open-source software provisioning, configuration management, and application deployment tool that includes a declarative language to describe system configurations. Ansible can configure both Linux and Windows operating systems. It can also deploy, provision, and configure Cloud resources, such as Azure resources. In order to use Ansible with Azure, you must first define the connection settings between the Ansible server and Azure as an infrastructure platform. In this article you'll learn how to connect an Ansible server to Azure using an Azure Service Principal account.

Table Of Contents

Prerequisites

Step 1 - Create an Azure Service Principal

The first step is to create an Azure Service Principal account. You will use this to authenticate to your Azure subscription. After the Service Principal is created you will need to grant it permissions to your subscription. In this tutorial you'll be assigning contributor permissions to the entire subscription. In order to create the service principal with Azure PowerShell you'll need to first create a credentials object which contains the password of the new service principal. Using a technique in PowerShell called splatting you'll pass the user name and password it contains to the cmdlet New-AzAdServicePrincipal. This cmdlet will create a new service principal in Azure.

Providing Credentials to Azure Modules

$credentials = New-Object Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential `
-Property @{ StartDate=Get-Date; EndDate=Get-Date -Year 2024; Password='<PASSWORD>'};

$spSplat = @{
    DisplayName = 'sp-cs-ansible'
    PasswordCredential = $credentials
}

$sp = New-AzAdServicePrincipal @spSplat
Enter fullscreen mode Exit fullscreen mode

Step 2 - Assign a Role to the Service Principal

Once the service principal is created next you'll assign permissions to that service principal in Azure. In this tutorial you'll be assigning the Contributor role to the service principal for the entire subscription. The cmdlet for assigning role permissions is New-AzRoleAssignment. You need to specify three parameters in order to assign the role to the service principal. The ObjectID, which is the Id property of the service principal you just created. When the service principal was created in Step 1 the output was stored in a variable called $sp and the Id property can be accessed by specifying that property on the $sp variable. RoleDefinitionName is set to Contributor and Scope value is set to /subscription/$subId. $subId is a variable containing the subscription id for the Azure subscription you are targeting. That variable is populated by the Get-AzSubscription cmdlet. Update NameofSubscriptionHere with the name of your Azure subscription.

$subId = (Get-AzSubscription -SubscriptionName 'NameOfSubscriptionHere').id

$roleAssignmentSplat = @{
    ObjectId = $sp.id
    RoleDefinitionName = 'Contributor'
    Scope = "/subscriptions/$subId"
}

New-AzRoleAssignment @roleAssignmentSplat

Enter fullscreen mode Exit fullscreen mode

Step 3 - Create Azure credentials

Gather Required Information

For this tutorial you'll need the subscription Id, service principal appId, service principal name, and the tenant Id of the service principal to connect Ansible to Azure. All of the required information can be gather with Azure PowerShell cmdlets. After you have the required information you can connect Ansible to Azure in two different ways. You can either use a credentials file or environment variables.

  • Subscription Id
  • Service Principal AppId
  • Service Principal Password
  • Tenant Id for the Service Principal
$subscriptionId = (Get-AzSubscription -SubscriptionName 'NameOfSubscriptionHere').id

$servicePrincipalAppId = (Get-AzADServicePrincipal -DisplayName 'sp-cs-ansible').ApplicationId

$servicePrincipalPassword = 'Value Specified Previously'

$tenantId = (Get-AzSubscription -SubscriptionName 'NameOfSubscriptionHere').TenantId
Enter fullscreen mode Exit fullscreen mode

Option 1: Use Ansible Credentials File

Ansible looks in specific locations to auto load credentials if certain files exists. The Azure Ansible module uses the path ~/.azure/credentials. Placing a file in this location with the proper values will result in Ansible being able to connect to Azure. Keep in mind that credential files in Ansible are used for development environments. To use this method create a file at ~/.azure/credentials and populated the variables subscription_id, client_id, secret, and tenant.

1. Create a credentials file

mkdir ~/.azure
vi ~/.azure/credentials
Enter fullscreen mode Exit fullscreen mode

2. Populate the required Ansible variables. Replace <Text> with actual values.

[default]
subscription_id=<subscription_id>
client_id=<security-principal-appid>
secret=<security-principal-password>
tenant=<security-principal-tenant>
Enter fullscreen mode Exit fullscreen mode

Option 2: Use Ansible Environment Variables

Instead of using a credentials file you can also populate specific environment variables that Ansible Azure module will use to connect to Azure. Using the bash command export you can define these values. Replace <Text> with actual values.

export AZURE_SUBSCRIPTION_ID=<subscription_id>
export AZURE_CLIENT_ID=<security-principal-appid>
export AZURE_SECRET=<security-principal-password>
export AZURE_TENANT=<security-principal-tenant>
Enter fullscreen mode Exit fullscreen mode

Read more about Providing Credentials to Azure Modules.

Step 4 - Run an Ansible Playbook

After you provided the necessary values for Ansible to connect to Azure through either a credentials file or environment variables you can test the connection by running an Ansible playbook.

Create a playbook file

Ansible playbooks are written in YAML. Create a playbook by creating a new file named playbook.yaml and opening it in vi.

  vi playbook.yaml
Enter fullscreen mode Exit fullscreen mode

Paste playbook contents in

Below is an Ansible playbook that creates an Azure resource group named rg-cs-ansible in the eastus region. It also registers the output to an Ansible variable and outputs it with the debug module. Copy and paste in the contents below to populate the playbook.

---
- hosts: localhost
  connection: local
  tasks:
    - name: Create resource group
      azure_rm_resourcegroup:
        name: rg-cs-ansible
        location: eastus
      register: rg
    - debug:
        var: rg
Enter fullscreen mode Exit fullscreen mode

Run the playbook using ansible-playbook

To execute the playbook use the ansible command ansible-playbook followed by the name of the playbook which is playbook.yaml. Once the playbook finishes running you will have a newly created resource group called rg-cs-ansible in Azure!

Create Resource Group Ansible Playbook Output

ansible-playbook playbook.yaml
Enter fullscreen mode Exit fullscreen mode

Sources

Using Ansible with Azure

Quickstart: Install Ansible on Linux virtual machines in Azure

Create an Azure service principal with Azure PowerShell

New-AzRoleAssignment

Originally posted on CloudSkills.io

Discussion

pic
Editor guide
Collapse
nippytalkin profile image
Sandeep Sharma

Hi Josh,

quick question, can we use ansible in azure cloud shell, and specify custom location for host file using -i /home/user/hosts? something like that?
the idea is not to setup a VM in azure and configure ansible, rather use the native ansible from azure cloud shell. Your thoughts?
Note: I am aware that we can definitely use Dynamic inventory file but i still want to manually enter target severs under host file and use while running playbook.

Collapse
joshduffney profile image
Josh Duffney Author

Hi Sandeep,

That's an excellent question and idea. I actually have a blog post on using Ansible with cloud shell queued up. I love the idea of using cloud shell because it removes the need to stand up additional infrastructure.

As long as the host files and playbooks are stored in the Azure storage account you'll have access to them and can run them. You could also use a git repo and just pull them down when you need them.

Instead of host files you could use a host_list as well. For example ansible playbook.yaml -i web01, the comma at the end will prevent Ansible from attempting to parse the input.

If you start using it more please DM me and let me know how you like it and if there are any limitations.

I hope my reply helps and thank you for the comment! Have a great day!

Collapse
nippytalkin profile image
Sandeep Sharma

Thanks Josh for prompt reply, i tried the same however it does not work. i am sure i am doing something wrong here (Refer the screen shot). this is what i do.

  1. Login to Azure Cloud Shell
  2. change directory to CloudDrive -- this is where my host file lives
  3. run this command ansible -i hosts -m win_ping & also tried ansible -i servername, -m win_ping
Thread Thread
joshduffney profile image
Josh Duffney Author

So you've run into the fun part of using Ansible with Windows. Ansible was developed for Linux first and its default connection will be SSH to Linux targets to override this you need to specify several Ansible variables to modify the connection options.

The settings and values you need to change greatly depend on your configuration, but here are some basic ones for WinRM over HTTP using NTLM authentication.

ansible_user: user
ansible_password: P@ssw0rd
ansible_port: 5985
ansible_connection: winrm
ansible_winrm_transport: ntlm
ansible_winrm_server_cert_validation: ignore

You can place these values anywhere but here are a few examples. One using the Ansible cmd and another using a playbook.

ansible all -i hosts -m win_ping -e "ansible_user=azureuser ansible_password=P@ssw0rd ansible_connection=winrm ansible_winrm_transport=ntlm ansible_winrm_server_cert_validation=ignore ansible_port=5985"

or a playbook

---
- hosts: all

vars:
    ansible_user: azureuser
    ansible_password: P@ssw0rd
    ansible_port: 5985
    ansible_connection: winrm
    ansible_winrm_transport: ntlm
    ansible_winrm_server_cert_validation: ignore

tasks:

- name: Create logging directory
    win_file:
        path: c:\logs
        state: directory

You can also put these vars in the group_vars folder or in the hosts file as vars. Which ever you prefer. I also wrote about remote management with Ansible and Windows see the below post. I hope this helps!

NOTE: If you're using WinRM over HTTP on 5985 you will have to open a firewall port on the vm or disable windows firewall.

dev.to/cloudskills/provisioning-az...

Thread Thread
nippytalkin profile image
Sandeep Sharma

You are right Josh, i have been using group_vars and exactly same method you mentioned on my centOs ansible controller host. However when it comes to Azure Shell it does not work. Sharing my screen output..

➜ clouddrive ansible path -i hosts -m win_ping
[WARNING]: Could not match supplied host pattern, ignoring: path
[WARNING]: No hosts matched, nothing to do
➜ clouddrive ansible patch -i hosts -m win_ping
10.0.0.222 | UNREACHABLE! => {
"changed": false,
"msg": "kerberos: the python kerberos library is not installed",
"unreachable": true
}
➜ clouddrive ls group_vars
patch

➜ clouddrive more group_vars/patch

ansible_user: myUser@Domain.com
ansible_password: Welcome@123
ansible_port: 5986
ansible_connection: winrm
ansible_winrm_server_cert_validation: ignore
ansible_winrm_transport: kerberos
ansible_winrm_kerberos_delegation: true
➜ clouddrive

Thread Thread
joshduffney profile image
Josh Duffney Author

I see you're using Kerberos auth. In that case, you'll have to install the Kerberos python libraries. I'm not sure how those will preserved probably stored in the storage account? Humm, very interesting.

yum install -y krb5-workstation
yum install -y krb5-devel 
Thread Thread
nippytalkin profile image
Sandeep Sharma

Azure Cloud Shell wont allow you to install anything. So there has to be another way or Ansible on Cloud Shell does not serve the purpose.

Thread Thread
joshduffney profile image
Josh Duffney Author

Good point, I'll do some digging. Something tells me there is a way to mount external modules to cloud shell without installing them directly. In the time being NTLM seems to be the best alternative.

Thread Thread
nippytalkin profile image
Sandeep Sharma

Again, NTLM is not an option for most of enterprise customers. either SSP or Kerberos. Will check too about External Modules on azure.

Collapse
putica2010 profile image
Shan Gao

Thank you Josh for such a good series about ansible with Azure!

Followed along, found a small mistake, I believe you meant the below assignment in the 'roleAssignmentSplat' hashtable instead of '$spId'

Scope = "/subscriptions/$subId"

Collapse
joshduffney profile image
Josh Duffney Author

Hey Shan,

You're very welcome and thank you for taking the time to read and walk through the tutorials! You're correct with the mistake you identified. I've updated the code for the roleAssignmentSplat hashtable. I really appreciate you taking the time to point this out. Thank you!

$roleAssignmentSplat = @{
    ObjectId = $sp.id
    RoleDefinitionName = 'Contributor'
    Scope = "/subscriptions/$subId"
}
Collapse
jerppuliz profile image
Jere Vekka

For some reason, the error still exists in the code :|

Thread Thread
joshduffney profile image
Josh Duffney Author

Not sure if it just didn't save or got reverted but it's been fixed! Thanks for the comment.