301d561420
* Update guide_azure.rst Added a note to the 'Storing in a file' section, that the secret value of the ini file should be UrlEncoded. Otherwise the user might get login errors (depends on the characters used in the secret).
448 lines
17 KiB
ReStructuredText
448 lines
17 KiB
ReStructuredText
Microsoft Azure Guide
|
|
=====================
|
|
|
|
Ansible includes a suite of modules for interacting with Azure Resource Manager, giving you the tools to easily create
|
|
and orchestrate infrastructure on the Microsoft Azure Cloud.
|
|
|
|
Requirements
|
|
------------
|
|
|
|
Using the Azure Resource Manager modules requires having specific Azure SDK modules
|
|
installed on the host running Ansible.
|
|
|
|
.. code-block:: bash
|
|
|
|
$ pip install 'ansible[azure]'
|
|
|
|
If you are running Ansible from source, you can install the dependencies from the
|
|
root directory of the Ansible repo.
|
|
|
|
.. code-block:: bash
|
|
|
|
$ pip install .[azure]
|
|
|
|
You can also directly run Ansible in `Azure Cloud Shell <https://shell.azure.com>`_, where Ansible is pre-installed.
|
|
|
|
Authenticating with Azure
|
|
-------------------------
|
|
|
|
Using the Azure Resource Manager modules requires authenticating with the Azure API. You can choose from two authentication strategies:
|
|
|
|
* Active Directory Username/Password
|
|
* Service Principal Credentials
|
|
|
|
Follow the directions for the strategy you wish to use, then proceed to `Providing Credentials to Azure Modules`_ for
|
|
instructions on how to actually use the modules and authenticate with the Azure API.
|
|
|
|
|
|
Using Service Principal
|
|
.......................
|
|
|
|
There is now a detailed official tutorial describing `how to create a service principal <https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal>`_.
|
|
|
|
After stepping through the tutorial you will have:
|
|
|
|
* Your Client ID, which is found in the "client id" box in the "Configure" page of your application in the Azure portal
|
|
* Your Secret key, generated when you created the application. You cannot show the key after creation.
|
|
If you lost the key, you must create a new one in the "Configure" page of your application.
|
|
* And finally, a tenant ID. It's a UUID (e.g. ABCDEFGH-1234-ABCD-1234-ABCDEFGHIJKL) pointing to the AD containing your
|
|
application. You will find it in the URL from within the Azure portal, or in the "view endpoints" of any given URL.
|
|
|
|
|
|
Using Active Directory Username/Password
|
|
........................................
|
|
|
|
To create an Active Directory username/password:
|
|
|
|
* Connect to the Azure Classic Portal with your admin account
|
|
* Create a user in your default AAD. You must NOT activate Multi-Factor Authentication
|
|
* Go to Settings - Administrators
|
|
* Click on Add and enter the email of the new user.
|
|
* Check the checkbox of the subscription you want to test with this user.
|
|
* Login to Azure Portal with this new user to change the temporary password to a new one. You will not be able to use the
|
|
temporary password for OAuth login.
|
|
|
|
Providing Credentials to Azure Modules
|
|
......................................
|
|
|
|
The modules offer several ways to provide your credentials. For a CI/CD tool such as Ansible Tower or Jenkins, you will
|
|
most likely want to use environment variables. For local development you may wish to store your credentials in a file
|
|
within your home directory. And of course, you can always pass credentials as parameters to a task within a playbook. The
|
|
order of precedence is parameters, then environment variables, and finally a file found in your home directory.
|
|
|
|
Using Environment Variables
|
|
```````````````````````````
|
|
|
|
To pass service principal credentials via the environment, define the following variables:
|
|
|
|
* AZURE_CLIENT_ID
|
|
* AZURE_SECRET
|
|
* AZURE_SUBSCRIPTION_ID
|
|
* AZURE_TENANT
|
|
|
|
To pass Active Directory username/password via the environment, define the following variables:
|
|
|
|
* AZURE_AD_USER
|
|
* AZURE_PASSWORD
|
|
|
|
To pass Active Directory username/password in ADFS via the environment, define the following variables:
|
|
|
|
* AZURE_AD_USER
|
|
* AZURE_PASSWORD
|
|
* AZURE_CLIENT_ID
|
|
* AZURE_TENANT
|
|
* AZURE_ADFS_AUTHORITY_URL
|
|
|
|
"AZURE_ADFS_AUTHORITY_URL" is optional. It's necessary only when you have own ADFS authority like https://yourdomain.com/adfs.
|
|
|
|
Storing in a File
|
|
`````````````````
|
|
|
|
When working in a development environment, it may be desirable to store credentials in a file. The modules will look
|
|
for credentials in ``$HOME/.azure/credentials``. This file is an ini style file. It will look as follows:
|
|
|
|
.. code-block:: ini
|
|
|
|
[default]
|
|
subscription_id=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
client_id=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
secret=xxxxxxxxxxxxxxxxx
|
|
tenant=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
|
|
.. note:: If your secret values contain non-ASCII characters, you must `URL Encode <https://www.w3schools.com/tags/ref_urlencode.asp>`_ them to avoid login errors.
|
|
|
|
It is possible to store multiple sets of credentials within the credentials file by creating multiple sections. Each
|
|
section is considered a profile. The modules look for the [default] profile automatically. Define AZURE_PROFILE in the
|
|
environment or pass a profile parameter to specify a specific profile.
|
|
|
|
Passing as Parameters
|
|
`````````````````````
|
|
|
|
If you wish to pass credentials as parameters to a task, use the following parameters for service principal:
|
|
|
|
* client_id
|
|
* secret
|
|
* subscription_id
|
|
* tenant
|
|
|
|
Or, pass the following parameters for Active Directory username/password:
|
|
|
|
* ad_user
|
|
* password
|
|
|
|
Or, pass the following parameters for ADFS username/pasword:
|
|
|
|
* ad_user
|
|
* password
|
|
* client_id
|
|
* tenant
|
|
* adfs_authority_url
|
|
|
|
"adfs_authority_url" is optional. It's necessary only when you have own ADFS authority like https://yourdomain.com/adfs.
|
|
|
|
|
|
Other Cloud Environments
|
|
------------------------
|
|
|
|
To use an Azure Cloud other than the default public cloud (eg, Azure China Cloud, Azure US Government Cloud, Azure Stack),
|
|
pass the "cloud_environment" argument to modules, configure it in a credential profile, or set the "AZURE_CLOUD_ENVIRONMENT"
|
|
environment variable. The value is either a cloud name as defined by the Azure Python SDK (eg, "AzureChinaCloud",
|
|
"AzureUSGovernment"; defaults to "AzureCloud") or an Azure metadata discovery URL (for Azure Stack).
|
|
|
|
Creating Virtual Machines
|
|
-------------------------
|
|
|
|
There are two ways to create a virtual machine, both involving the azure_rm_virtualmachine module. We can either create
|
|
a storage account, network interface, security group and public IP address and pass the names of these objects to the
|
|
module as parameters, or we can let the module do the work for us and accept the defaults it chooses.
|
|
|
|
Creating Individual Components
|
|
..............................
|
|
|
|
An Azure module is available to help you create a storage account, virtual network, subnet, network interface,
|
|
security group and public IP. Here is a full example of creating each of these and passing the names to the
|
|
azure_rm_virtualmachine module at the end:
|
|
|
|
.. code-block:: yaml
|
|
|
|
- name: Create storage account
|
|
azure_rm_storageaccount:
|
|
resource_group: Testing
|
|
name: testaccount001
|
|
account_type: Standard_LRS
|
|
|
|
- name: Create virtual network
|
|
azure_rm_virtualnetwork:
|
|
resource_group: Testing
|
|
name: testvn001
|
|
address_prefixes: "10.10.0.0/16"
|
|
|
|
- name: Add subnet
|
|
azure_rm_subnet:
|
|
resource_group: Testing
|
|
name: subnet001
|
|
address_prefix: "10.10.0.0/24"
|
|
virtual_network: testvn001
|
|
|
|
- name: Create public ip
|
|
azure_rm_publicipaddress:
|
|
resource_group: Testing
|
|
allocation_method: Static
|
|
name: publicip001
|
|
|
|
- name: Create security group that allows SSH
|
|
azure_rm_securitygroup:
|
|
resource_group: Testing
|
|
name: secgroup001
|
|
rules:
|
|
- name: SSH
|
|
protocol: Tcp
|
|
destination_port_range: 22
|
|
access: Allow
|
|
priority: 101
|
|
direction: Inbound
|
|
|
|
- name: Create NIC
|
|
azure_rm_networkinterface:
|
|
resource_group: Testing
|
|
name: testnic001
|
|
virtual_network: testvn001
|
|
subnet: subnet001
|
|
public_ip_name: publicip001
|
|
security_group: secgroup001
|
|
|
|
- name: Create virtual machine
|
|
azure_rm_virtualmachine:
|
|
resource_group: Testing
|
|
name: testvm001
|
|
vm_size: Standard_D1
|
|
storage_account: testaccount001
|
|
storage_container: testvm001
|
|
storage_blob: testvm001.vhd
|
|
admin_username: admin
|
|
admin_password: Password!
|
|
network_interfaces: testnic001
|
|
image:
|
|
offer: CentOS
|
|
publisher: OpenLogic
|
|
sku: '7.1'
|
|
version: latest
|
|
|
|
Each of the Azure modules offers a variety of parameter options. Not all options are demonstrated in the above example.
|
|
See each individual module for further details and examples.
|
|
|
|
|
|
Creating a Virtual Machine with Default Options
|
|
...............................................
|
|
|
|
If you simply want to create a virtual machine without specifying all the details, you can do that as well. The only
|
|
caveat is that you will need a virtual network with one subnet already in your resource group. Assuming you have a
|
|
virtual network already with an existing subnet, you can run the following to create a VM:
|
|
|
|
.. code-block:: yaml
|
|
|
|
azure_rm_virtualmachine:
|
|
resource_group: Testing
|
|
name: testvm10
|
|
vm_size: Standard_D1
|
|
admin_username: chouseknecht
|
|
ssh_password_enabled: false
|
|
ssh_public_keys: "{{ ssh_keys }}"
|
|
image:
|
|
offer: CentOS
|
|
publisher: OpenLogic
|
|
sku: '7.1'
|
|
version: latest
|
|
|
|
|
|
Dynamic Inventory Script
|
|
------------------------
|
|
|
|
If you are not familiar with Ansible's dynamic inventory scripts, check out :ref:`Intro to Dynamic Inventory <intro_dynamic_inventory>`.
|
|
|
|
The Azure Resource Manager inventory script is called `azure_rm.py <https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/azure_rm.py>`_. It authenticates with the Azure API exactly the same as the
|
|
Azure modules, which means you will either define the same environment variables described above in `Using Environment Variables`_,
|
|
create a ``$HOME/.azure/credentials`` file (also described above in `Storing in a File`_), or pass command line parameters. To see available command
|
|
line options execute the following:
|
|
|
|
.. code-block:: bash
|
|
|
|
$ ./ansible/contrib/inventory/azure_rm.py --help
|
|
|
|
As with all dynamic inventory scripts, the script can be executed directly, passed as a parameter to the ansible command,
|
|
or passed directly to ansible-playbook using the -i option. No matter how it is executed the script produces JSON representing
|
|
all of the hosts found in your Azure subscription. You can narrow this down to just hosts found in a specific set of
|
|
Azure resource groups, or even down to a specific host.
|
|
|
|
For a given host, the inventory script provides the following host variables:
|
|
|
|
.. code-block:: JSON
|
|
|
|
{
|
|
"ansible_host": "XXX.XXX.XXX.XXX",
|
|
"computer_name": "computer_name2",
|
|
"fqdn": null,
|
|
"id": "/subscriptions/subscription-id/resourceGroups/galaxy-production/providers/Microsoft.Compute/virtualMachines/object-name",
|
|
"image": {
|
|
"offer": "CentOS",
|
|
"publisher": "OpenLogic",
|
|
"sku": "7.1",
|
|
"version": "latest"
|
|
},
|
|
"location": "westus",
|
|
"mac_address": "00-00-5E-00-53-FE",
|
|
"name": "object-name",
|
|
"network_interface": "interface-name",
|
|
"network_interface_id": "/subscriptions/subscription-id/resourceGroups/galaxy-production/providers/Microsoft.Network/networkInterfaces/object-name1",
|
|
"network_security_group": null,
|
|
"network_security_group_id": null,
|
|
"os_disk": {
|
|
"name": "object-name",
|
|
"operating_system_type": "Linux"
|
|
},
|
|
"plan": null,
|
|
"powerstate": "running",
|
|
"private_ip": "172.26.3.6",
|
|
"private_ip_alloc_method": "Static",
|
|
"provisioning_state": "Succeeded",
|
|
"public_ip": "XXX.XXX.XXX.XXX",
|
|
"public_ip_alloc_method": "Static",
|
|
"public_ip_id": "/subscriptions/subscription-id/resourceGroups/galaxy-production/providers/Microsoft.Network/publicIPAddresses/object-name",
|
|
"public_ip_name": "object-name",
|
|
"resource_group": "galaxy-production",
|
|
"security_group": "object-name",
|
|
"security_group_id": "/subscriptions/subscription-id/resourceGroups/galaxy-production/providers/Microsoft.Network/networkSecurityGroups/object-name",
|
|
"tags": {
|
|
"db": "mysql"
|
|
},
|
|
"type": "Microsoft.Compute/virtualMachines",
|
|
"virtual_machine_size": "Standard_DS4"
|
|
}
|
|
|
|
Host Groups
|
|
...........
|
|
|
|
By default hosts are grouped by:
|
|
|
|
* azure (all hosts)
|
|
* location name
|
|
* resource group name
|
|
* security group name
|
|
* tag key
|
|
* tag key_value
|
|
* os_disk operating_system_type (Windows/Linux)
|
|
|
|
You can control host groupings and host selection by either defining environment variables or creating an
|
|
azure_rm.ini file in your current working directory.
|
|
|
|
NOTE: An .ini file will take precedence over environment variables.
|
|
|
|
NOTE: The name of the .ini file is the basename of the inventory script (i.e. 'azure_rm') with a '.ini'
|
|
extension. This allows you to copy, rename and customize the inventory script and have matching .ini files all in
|
|
the same directory.
|
|
|
|
Control grouping using the following variables defined in the environment:
|
|
|
|
* AZURE_GROUP_BY_RESOURCE_GROUP=yes
|
|
* AZURE_GROUP_BY_LOCATION=yes
|
|
* AZURE_GROUP_BY_SECURITY_GROUP=yes
|
|
* AZURE_GROUP_BY_TAG=yes
|
|
* AZURE_GROUP_BY_OS_FAMILY=yes
|
|
|
|
Select hosts within specific resource groups by assigning a comma separated list to:
|
|
|
|
* AZURE_RESOURCE_GROUPS=resource_group_a,resource_group_b
|
|
|
|
Select hosts for specific tag key by assigning a comma separated list of tag keys to:
|
|
|
|
* AZURE_TAGS=key1,key2,key3
|
|
|
|
Select hosts for specific locations by assigning a comma separated list of locations to:
|
|
|
|
* AZURE_LOCATIONS=eastus,eastus2,westus
|
|
|
|
Or, select hosts for specific tag key:value pairs by assigning a comma separated list key:value pairs to:
|
|
|
|
* AZURE_TAGS=key1:value1,key2:value2
|
|
|
|
If you don't need the powerstate, you can improve performance by turning off powerstate fetching:
|
|
|
|
* AZURE_INCLUDE_POWERSTATE=no
|
|
|
|
A sample azure_rm.ini file is included along with the inventory script in contrib/inventory. An .ini
|
|
file will contain the following:
|
|
|
|
.. code-block:: ini
|
|
|
|
[azure]
|
|
# Control which resource groups are included. By default all resources groups are included.
|
|
# Set resource_groups to a comma separated list of resource groups names.
|
|
#resource_groups=
|
|
|
|
# Control which tags are included. Set tags to a comma separated list of keys or key:value pairs
|
|
#tags=
|
|
|
|
# Control which locations are included. Set locations to a comma separated list of locations.
|
|
#locations=
|
|
|
|
# Include powerstate. If you don't need powerstate information, turning it off improves runtime performance.
|
|
# Valid values: yes, no, true, false, True, False, 0, 1.
|
|
include_powerstate=yes
|
|
|
|
# Control grouping with the following boolean flags. Valid values: yes, no, true, false, True, False, 0, 1.
|
|
group_by_resource_group=yes
|
|
group_by_location=yes
|
|
group_by_security_group=yes
|
|
group_by_tag=yes
|
|
group_by_os_family=yes
|
|
|
|
Examples
|
|
........
|
|
|
|
Here are some examples using the inventory script:
|
|
|
|
.. code-block:: bash
|
|
|
|
# Execute /bin/uname on all instances in the Testing resource group
|
|
$ ansible -i azure_rm.py Testing -m shell -a "/bin/uname -a"
|
|
|
|
# Execute win_ping on all Windows instances
|
|
$ ansible -i azure_rm.py windows -m win_ping
|
|
|
|
# Execute win_ping on all Windows instances
|
|
$ ansible -i azure_rm.py winux -m ping
|
|
|
|
# Use the inventory script to print instance specific information
|
|
$ ./ansible/contrib/inventory/azure_rm.py --host my_instance_host_name --resource-groups=Testing --pretty
|
|
|
|
# Use the inventory script with ansible-playbook
|
|
$ ansible-playbook -i ./ansible/contrib/inventory/azure_rm.py test_playbook.yml
|
|
|
|
Here is a simple playbook to exercise the Azure inventory script:
|
|
|
|
.. code-block:: yaml
|
|
|
|
- name: Test the inventory script
|
|
hosts: azure
|
|
connection: local
|
|
gather_facts: no
|
|
tasks:
|
|
- debug: msg="{{ inventory_hostname }} has powerstate {{ powerstate }}"
|
|
|
|
You can execute the playbook with something like:
|
|
|
|
.. code-block:: bash
|
|
|
|
$ ansible-playbook -i ./ansible/contrib/inventory/azure_rm.py test_azure_inventory.yml
|
|
|
|
|
|
Disabling certificate validation on Azure endpoints
|
|
...................................................
|
|
|
|
When an HTTPS proxy is present, or when using Azure Stack, it may be necessary to disable certificate validation for
|
|
Azure endpoints in the Azure modules. This is not a recommended security practice, but may be necessary when the system
|
|
CA store cannot be altered to include the necessary CA certificate. Certificate validation can be controlled by setting
|
|
the "cert_validation_mode" value in a credential profile, via the "AZURE_CERT_VALIDATION_MODE" environment variable, or
|
|
by passing the "cert_validation_mode" argument to any Azure module. The default value is "validate"; setting the value
|
|
to "ignore" will prevent all certificate validation. The module argument takes precedence over a credential profile value,
|
|
which takes precedence over the environment value.
|