はじめに
Azure DevOps上でのAnsible Role開発をより効率的に行うべく、AzurePipelineとMoleculeを使ってテストの自動化を行います。
Agentの選定
Azure Pipelineから渡された処理を実行するエージェントはMS hosted AgentとSelf hosted Agentの2種類存在します。本環境ではMS hosted Agentを使用しました。
###MS Hosted Agent
Pipelineが起動される度にMicrosoftが予め用意したVMイメージ上で渡された処理を実行する
Self hosted Agent
ユーザー自身が準備/カスタマイズしたVM上でAzure Pipelineから渡された処理を実行する
リソースグループの作成
moleculeが作成するインスタンスのリソースグループを先行して作成しておく必要があります。ここではmolecule-rgという名前で作成しています
azure credentials情報の準備
Ansible公式ドキュメントのAzure操作に記載の通りAnsibleからAzureのリソースを操作するにあたり、下記のようなcredentials情報が必要になります。
[default]
subscription_id=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_id=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
secret=xxxxxxxxxxxxxxxxx
tenant=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
秘匿情報であり、そのまま平文でリポジトリに登録はできないのでAzure pipelineのLibraryにSecret fileとして登録します。
Roleリポジトリの準備
Roleの準備
molecule-azureのインストールされているマシンにてmolecule init role -d azureを実行するとazure用のmoleculeのディレクトリが作成されます。
$ molecule init role pipeline_moleculetest -d azure
$ ls pipeline_moleculetest
README.md files meta tasks tests
defaults handlers molecule templates vars
ここではtasks/main.ymlはwin_pingをするだけのシンプルなtaskを作成します
---
- name: win_ping
win_ping:
またmolecule azure driver標準で作られた状態ではLinuxインスタンスが作成されるのでWindows用にカスタマイズする必要があります。以下はカスタマイズのサンプルです。
---
# yamllint disable rule:line-length
- name: Create
hosts: localhost
connection: local
gather_facts: false
no_log: "{{ molecule_no_log }}"
vars:
resource_group_name: molecule-rg
location: japaneast
user: molecule
password: "Password1!"
remote_connection: winrm
remote_port: 5986
virtual_network_name: molecule-rg-vnet
subnet_name: molecule_subnet
keypair_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key"
tasks:
- name: Create resource group
azure_rm_resourcegroup:
name: "{{ resource_group_name }}"
location: "{{ location }}"
- name: Create security group that allows incoming traffic on SSH/HTTP/HTTPS/RDP/WINRM
azure_rm_securitygroup:
resource_group: "{{ resource_group_name }}"
name: "sg{{ virtual_network_name }}"
rules:
- name: SSH
protocol: Tcp
destination_port_range: 22
access: Allow
priority: 101
direction: Inbound
- name: RDP
protocol: Tcp
destination_port_range: 3389
access: Allow
priority: 102
direction: Inbound
- name: HTTP
protocol: Tcp
destination_port_range: 80
access: Allow
priority: 103
direction: Inbound
- name: HTTPS
protocol: Tcp
destination_port_range: 443
access: Allow
priority: 104
direction: Inbound
- name: WINRM
protocol: Tcp
destination_port_range: 5986
access: Allow
priority: 105
direction: Inbound
- name: Create virtual network
azure_rm_virtualnetwork:
resource_group: "{{ resource_group_name }}"
name: "{{ virtual_network_name }}"
- name: Create molecule instance(s)
azure_rm_virtualmachine:
resource_group: "{{ resource_group_name }}"
name: "{{ item.name }}"
vm_size: "{{ item.vm_size }}"
admin_username: "{{ user }}"
admin_password: "{{ password }}"
os_type: Windows
image:
publisher: "MicrosoftWindowsServer"
offer: "WindowsServer"
sku: "2019-Datacenter"
version: "latest"
open_ports:
- "{{ remote_port }}"
public_ip_allocation_method: Dynamic
data_disks: "{{ data_disks|default(omit) }}"
register: server
with_items: "{{ molecule_yml.platforms }}"
async: 7200
poll: 0
- name: Wait for instance(s) creation to complete
async_status:
jid: "{{ item.ansible_job_id }}"
register: azure_jobs
until: azure_jobs.finished
retries: 300
with_items: "{{ server.results }}"
- name: Create Windows VM Extension
azure_rm_virtualmachineextension:
resource_group: "{{ resource_group_name }}"
name: "{{ item.name }}"
virtual_machine_name: "{{ item.name }}"
publisher: Microsoft.Compute
virtual_machine_extension_type: CustomScriptExtension
type_handler_version: 1.9
settings:
CommandToExecute: "powershell -ExecutionPolicy Unrestricted -File ConfigureRemotingForAnsible.ps1"
FileUris: ["https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1"]
ignore_errors: true
with_items: "{{ molecule_yml.platforms }}"
# Mandatory configuration for Molecule to function.
- name: Populate instance config dict
set_fact:
instance_conf_dict: {
'instance': "{{ item.ansible_facts.azure_vm.name }}",
'address': "{{ item.ansible_facts.azure_vm.properties.networkProfile.networkInterfaces[0].properties.ipConfigurations[0].properties.publicIPAddress.properties.ipAddress }}", # noqa 204
'user': "{{ user }}",
'port': "{{ remote_port }}",
'connection': "{{ remote_connection }}",
'identity_file': "{{ keypair_path }}",
}
with_items: "{{ azure_jobs.results }}"
register: instance_config_dict
when: server is changed
- name: Convert instance config dict to a list
set_fact:
instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}"
when: server is changed
- name: Dump instance config
copy:
content: "{{ instance_conf | to_json | from_json | to_yaml }}"
dest: "{{ molecule_instance_config }}"
mode: 0644
when: server is changed
---
dependency:
name: galaxy
lint: |
set -e
yamllint -c molecule/default/yamllint.cfg .
ansible-lint -x 701 .
driver:
name: azure
platforms:
- name: instance
vm_size: Standard_D2_v3
provisioner:
name: ansible
verifier:
name: ansible
---
- name: Converge
hosts: all
vars:
ansible_user: "molecule"
ansible_password: "Password1!"
ansible_port: 5986
ansible_connection: winrm
ansible_winrm_server_cert_validation: ignore
roles:
- role: pipeline_moleculetest
パイプラインで実行されるazure-pipelineを準備します。
事前に準備したcredentialsも、ここで.azure/配下にコピーします。
moleculeの実行自体はtoxで実施してます。
---
trigger:
branches:
include:
- "*"
parameters:
- name: python_versions
type: object
default:
- 3.6
jobs:
- job: molecule_test
pool:
vmImage: 'ubuntu-20.04'
steps:
- ${{ each python_version in parameters.python_versions }}:
- task: UsePythonVersion@0
inputs:
versionSpec: ${{ python_version }}
displayName: Use Python ${{ python_version }}
- script: |
python -m pip install --upgrade pip
pip install tox
displayName: Install tox
- script: cp -pr . $(Agent.TempDirectory)/$(Build.Repository.Name)
displayName: Prepare a working directory for molecule test
- task: DownloadSecureFile@1
displayName: 'Download azure credential file'
name: azurecredential
inputs:
secureFile: credentials
- script: |
mkdir ~/.azure
cp $(azurecredential.secureFilePath) ~/.azure/credentials
displayName: 'set azure credential'
- script: tox
workingDirectory: $(Agent.TempDirectory)/$(Build.Repository.Name)
displayName: Run tests
toxでpythonパッケージでmoleculeやlint系の他、azureを操作するために必要なlibrary、windows操作のためのpywinrmをインストールします。azure関連のlibraryは、最新にするとazure_rm_系のansibleモジュールがまともに動かないのでバージョン指定してます。
最終行あたりのcommandsでmolecule testを実行しています。
[tox]
envlist = py{36}-ansible{29}
skipsdist = True
[testenv]
passenv = TERM
deps =
ansible-lint == 4.3.7
yamllint == 1.25.0
molecule == 3.2.2
pywinrm==0.3.0
azure-cli-core==2.0.35
azure-cli-nspkg==3.0.2
azure-common==1.1.11
azure-graphrbac==0.40.0
azure-keyvault==1.0.0a1
azure-mgmt-authorization==0.51.1
azure-mgmt-automation==0.1.1
azure-mgmt-batch==5.0.1
azure-mgmt-cdn==3.0.0
azure-mgmt-compute==4.4.0
azure-mgmt-containerinstance==1.4.0
azure-mgmt-containerregistry==2.0.0
azure-mgmt-containerservice==4.4.0
azure-mgmt-cosmosdb==0.5.2
azure-mgmt-devtestlabs==3.0.0
azure-mgmt-dns==2.1.0
azure-mgmt-hdinsight==0.1.0
azure-mgmt-iothub==0.7.0
azure-mgmt-keyvault==1.1.0
azure-mgmt-loganalytics==0.2.0
azure-mgmt-marketplaceordering==0.1.0
azure-mgmt-monitor==0.5.2
azure-mgmt-network==2.3.0
azure-mgmt-nspkg==2.0.0
azure-mgmt-rdbms==1.4.1
azure-mgmt-redis==5.0.0
azure-mgmt-resource==2.1.0
azure-mgmt-servicebus==0.5.3
azure-mgmt-sql==0.10.0
azure-mgmt-storage==3.1.0
azure-mgmt-trafficmanager==0.50.0
azure-mgmt-web==0.41.0
azure-nspkg==2.0.0
azure-storage==0.35.1
molecule-azure==0.5.0
ansible29: ansible>=2.9,<2.10
ansiblelatest: ansible
commands =
python --version
molecule test
#いざ実行
作成したリポジトリをAzure Reposに登録。
Azure pipelineに、作成したazure-pipelines.ymlを登録し、パイプラインを実行
molecule実行中にテスト用instanceが構築されます。
#参考にした文献
#参考にしたリポジトリ
https://github.com/sperreault/molecule-azure-windows-ex