LoginSignup
1
1

More than 3 years have passed since last update.

AzurepipelinesとmoleculeでWindowsRoleのテスト自動化

Last updated at Posted at 2021-02-10

はじめに

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という名前で作成しています
image.png

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として登録します。
image.png

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を作成します

tasks/main.yml
---
- name: win_ping
  win_ping:

またmolecule azure driver標準で作られた状態ではLinuxインスタンスが作成されるのでWindows用にカスタマイズする必要があります。以下はカスタマイズのサンプルです。

molecule/default/create.yml
---
# 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

molecule/default/molecule.yml
---
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
molecule/default/converge.yml
---
- 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で実施してます。

azure-pipelines.yml
---
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.ini
[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を登録し、パイプラインを実行
image.png

molecule実行中にテスト用instanceが構築されます。
image.png

参考にした文献

Azure Pipelines で Ansible を使う

参考にしたリポジトリ

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1