diff --git a/roles/azure_manage_snapshot/README.md b/roles/azure_manage_snapshot/README.md new file mode 100644 index 0000000..ae0bc93 --- /dev/null +++ b/roles/azure_manage_snapshot/README.md @@ -0,0 +1,74 @@ +azure_manage_snapshot +================== + +A role to Create/Delete/Restore an Azure OS Disk Snapshot. + +Requirements +------------ + +* Azure User Account with valid permission + +Role Variables +-------------- + +* **azure_manage_snapshot_operation**: Operation to perform. Valid values are 'create', 'delete', 'restore'. Default is 'create'. +* **azure_manage_snapshot_resource_group**: (Required) Resource group on/from which the snapshot will reside. +* **azure_manage_snapshot_location**: The location to use, if not specified it will use the location of the virtual machine. +* **azure_manage_snapshot_name**: (Required) The name of the snapshot volume. +* **azure_manage_snapshot_vm_name**: The name of the virtual machine where the snapshot came from or will be applied. +* **azure_manage_snapshot_disk_name**: The name of the disk that the snapshot will be restored to. + +Limitations +------------ + +- NA + +Dependencies +------------ + +- NA + +Example Playbook +---------------- + + - hosts: localhost + tasks: + - name: Create a snapshot from a virtual machine + ansible.builtin.include_role: + name: cloud.azure_ops.azure_manage_snapshot + vars: + azure_manage_snapshot_operation: create + azure_manage_snapshot_resource_group: 'resource-group' + azure_manage_snapshot_vm_name: 'example-vm' + azure_manage_snapshot_name: 'example-snapshot-volume' + azure_manage_disk_name: 'example-disk-volume' + + - name: Restore a snapshot to a virtual machine + ansible.builtin.include_role: + name: cloud.azure_ops.azure_manage_snapshot + vars: + azure_manage_snapshot_operation: restore + azure_manage_snapshot_resource_group: 'resource-group' + azure_manage_snapshot_vm_name: 'example-vm' + azure_manage_snapshot_name: 'example-snapshot-volume' + azure_manage_disk_name: 'example-disk-volume' + + - name: Delete a snapshot + ansible.builtin.include_role: + name: cloud.azure_ops.azure_manage_snapshot + vars: + azure_manage_snapshot_operation: delete + azure_manage_snapshot_resource_group: 'resource-group' + azure_manage_snapshot_name: 'example-snapshot-volume' + +License +------- + +GNU General Public License v3.0 or later + +See [LICENCE](https://github.com/redhat-cop/cloud.azure_ops/blob/main/LICENSE) to see the full text. + +Author Information +------------------ + +- Ansible Cloud Content Team diff --git a/roles/azure_manage_snapshot/defaults/main.yml b/roles/azure_manage_snapshot/defaults/main.yml new file mode 100644 index 0000000..0b284d4 --- /dev/null +++ b/roles/azure_manage_snapshot/defaults/main.yml @@ -0,0 +1,3 @@ +--- +azure_manage_snapshot_operation: create +azure_manage_snapshot_disk_name: "{{ azure_manage_snapshot_name }}-disk" diff --git a/roles/azure_manage_snapshot/meta/argument_specs.yml b/roles/azure_manage_snapshot/meta/argument_specs.yml new file mode 100644 index 0000000..9bf9f25 --- /dev/null +++ b/roles/azure_manage_snapshot/meta/argument_specs.yml @@ -0,0 +1,36 @@ +--- +argument_specs: + main: + version_added: 3.1.0 + short_description: A role to manage a snapshot to an Azure Virtual Machine. + description: + - A role to manage a snapshot to an Azure Virtual Machine. + - This role requires an azure user account with valid permission. + options: + azure_manage_snapshot_operation: + description: + - Operation to perform + default: "create" + choices: ["create", "delete", "restore"] + azure_manage_snapshot_resource_group: + description: + - Resource group from which the snapshot resides. + required: true + azure_manage_snapshot_name: + description: + - Name of the snapshot volume. + required: true + azure_manage_snapshot_vm_name: + description: + - Name of the virtual machine to perform the snapshot operation on. + required: false + azure_manage_snapshot_disk_name: + description: + - Optional name of the Managed Disk. + - Default value is I(azure_manage_snapshot_name). + required: false + azure_manage_snapshot_location: + description: + - Optional location to use. + - Default value is the location of the Virtual Machine or Snapshot. + required: false diff --git a/roles/azure_manage_snapshot/tasks/create.yml b/roles/azure_manage_snapshot/tasks/create.yml new file mode 100644 index 0000000..4c579be --- /dev/null +++ b/roles/azure_manage_snapshot/tasks/create.yml @@ -0,0 +1,24 @@ +--- +- name: Get virtualmachine info + azure.azcollection.azure_rm_virtualmachine_info: + resource_group: "{{ azure_manage_snapshot_resource_group }}" + name: "{{ azure_manage_snapshot_vm_name }}" + register: azure_manage_snapshot_vm_info + +- name: Confirm OS is Managed Disk + ansible.builtin.fail: + msg: "{{ azure_manage_snapshot_vm_name }} OS Disk is not of Managed Type" + when: '"managed_disk" not in azure_manage_snapshot_vm_info.vms[0].os_disk' + +- name: Set OS Disk ID + ansible.builtin.set_fact: + azure_manage_snapshot_os_disk_id: "{{ azure_manage_snapshot_vm_info.vms[0].os_disk.managed_disk.id }}" + +- name: Create a snapshot + azure.azcollection.azure_rm_snapshot: + resource_group: "{{ azure_manage_snapshot_resource_group }}" + name: "{{ azure_manage_snapshot_name }}" + location: "{{ azure_manage_snapshot_location | default(azure_manage_snapshot_vm_info.vms[0].location) }}" + creation_data: + create_option: Copy + source_id: "{{ azure_manage_snapshot_os_disk_id }}" diff --git a/roles/azure_manage_snapshot/tasks/delete.yml b/roles/azure_manage_snapshot/tasks/delete.yml new file mode 100644 index 0000000..85f46a2 --- /dev/null +++ b/roles/azure_manage_snapshot/tasks/delete.yml @@ -0,0 +1,6 @@ +--- +- name: Delete snapshot + azure.azcollection.azure_rm_snapshot: + resource_group: "{{ azure_manage_snapshot_resource_group }}" + name: "{{ azure_manage_snapshot_name }}" + state: absent diff --git a/roles/azure_manage_snapshot/tasks/main.yml b/roles/azure_manage_snapshot/tasks/main.yml new file mode 100644 index 0000000..d18fd47 --- /dev/null +++ b/roles/azure_manage_snapshot/tasks/main.yml @@ -0,0 +1,3 @@ +- name: Create, delete or restore Snapshot + ansible.builtin.include_tasks: "{{ azure_manage_snapshot_operation }}.yml" + when: azure_manage_snapshot_operation in ['create', 'delete', 'restore'] diff --git a/roles/azure_manage_snapshot/tasks/restore.yml b/roles/azure_manage_snapshot/tasks/restore.yml new file mode 100644 index 0000000..a8958e8 --- /dev/null +++ b/roles/azure_manage_snapshot/tasks/restore.yml @@ -0,0 +1,25 @@ +--- +- name: Get snapshot info + azure.azcollection.azure_rm_snapshot_info: + resource_group: "{{ azure_manage_snapshot_resource_group }}" + name: "{{ azure_manage_snapshot_name }}" + register: azure_manage_snapshot_info + +- name: Create Managed disk from Snapshot + azure.azcollection.azure_rm_manageddisk: + name: "{{ azure_manage_snapshot_disk_name }}" + location: "{{ azure_manage_snapshot_location | default(azure_manage_snapshot_info.state[0].location) }}" + resource_group: "{{ azure_manage_snapshot_resource_group }}" + create_option: copy + source_uri: "{{ azure_manage_snapshot_info.state[0].id }}" + storage_account_type: "{{ azure_manage_snapshot_info.state[0].sku.name }}" + register: azure_manage_snapshot_disk + +- name: Swap OS Disk + azure.azcollection.azure_rm_virtualmachine: + name: "{{ azure_manage_snapshot_vm_name }}" + resource_group: "{{ azure_manage_snapshot_resource_group }}" + swap_os_disk: + os_disk_id: "{{ azure_manage_snapshot_disk.state.id }}" + os_disk_name: "{{ azure_manage_snapshot_disk_name | default(azure_manage_snapshot_name) }}" + os_disk_resource_group: "{{ azure_manage_snapshot_resource_group }}" diff --git a/tests/integration/requirements.yml b/tests/integration/requirements.yml new file mode 100644 index 0000000..256352b --- /dev/null +++ b/tests/integration/requirements.yml @@ -0,0 +1,4 @@ +--- +collections: + - community.crypto + - azure.azcollection diff --git a/tests/integration/targets/azure_ops_test_azure_manage_snapshot/aliases b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/aliases new file mode 100644 index 0000000..171980f --- /dev/null +++ b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/aliases @@ -0,0 +1,3 @@ +cloud/azure +role/azure_manage_snapshot +time=10m diff --git a/tests/integration/targets/azure_ops_test_azure_manage_snapshot/playbooks/main.yml b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/playbooks/main.yml new file mode 100644 index 0000000..1083190 --- /dev/null +++ b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/playbooks/main.yml @@ -0,0 +1,59 @@ +--- +- name: Setup virtual host + hosts: localhost + connection: local + gather_facts: false + tasks: + - name: Include default variables + ansible.builtin.include_vars: + file: ../vars/vars.yml + - name: Setup + ansible.builtin.include_tasks: ../tasks/setup.yml + +- name: Before snapshot + hosts: azure + gather_facts: false + tasks: + - name: Include default variables + ansible.builtin.include_vars: + file: ../vars/vars.yml + - name: Before snapshot + ansible.builtin.include_tasks: ../tasks/before_snapshot.yml + +- name: Create snapshot + hosts: localhost + connection: local + gather_facts: false + tasks: + - name: Create snapshot + ansible.builtin.include_tasks: ../tasks/create_snapshot.yml + +- name: After snapshot + hosts: azure + gather_facts: false + tasks: + - name: After snapshot + ansible.builtin.include_tasks: ../tasks/after_snapshot.yml + +- name: Restore snapshot + hosts: localhost + connection: local + gather_facts: false + tasks: + - name: Restore snapshot + ansible.builtin.include_tasks: ../tasks/restore_snapshot.yml + +- name: Validate snapshot + hosts: azure + gather_facts: false + tasks: + - name: Validate snapshot + ansible.builtin.include_tasks: ../tasks/validate_snapshot.yml + +- name: Teardown + hosts: localhost + connection: local + gather_facts: false + tasks: + - name: Teardown + ansible.builtin.include_tasks: ../tasks/teardown.yml diff --git a/tests/integration/targets/azure_ops_test_azure_manage_snapshot/runme.sh b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/runme.sh new file mode 100755 index 0000000..42fa9b6 --- /dev/null +++ b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/runme.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -eux + +# Run tests +ansible-playbook playbooks/main.yml "$@" diff --git a/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/after_snapshot.yml b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/after_snapshot.yml new file mode 100644 index 0000000..2527ea6 --- /dev/null +++ b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/after_snapshot.yml @@ -0,0 +1,16 @@ +--- +- name: Wait 300 seconds, but only start checking after 60 seconds + ansible.builtin.wait_for_connection: + delay: 60 + timeout: 300 + +- name: Touch a file + ansible.builtin.file: + path: "{{ after_snapshot_file }}" + state: touch + mode: '0755' + +- name: Sync Filesystems + ansible.builtin.command: sync + register: my_output + changed_when: my_output.rc != 0 diff --git a/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/before_snapshot.yml b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/before_snapshot.yml new file mode 100644 index 0000000..4b08a28 --- /dev/null +++ b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/before_snapshot.yml @@ -0,0 +1,16 @@ +--- +- name: Wait 300 seconds, but only start checking after 60 seconds + ansible.builtin.wait_for_connection: + delay: 60 + timeout: 300 + +- name: Touch a file + ansible.builtin.file: + path: "{{ before_snapshot_file }}" + state: touch + mode: '0755' + +- name: Sync Filesystems + ansible.builtin.command: sync + register: my_output + changed_when: my_output.rc != 0 diff --git a/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/create_snapshot.yml b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/create_snapshot.yml new file mode 100644 index 0000000..e91e269 --- /dev/null +++ b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/create_snapshot.yml @@ -0,0 +1,9 @@ +--- +- name: Create snapshot + ansible.builtin.include_role: + name: cloud.azure_ops.azure_manage_snapshot + vars: + azure_manage_snapshot_operation: create + azure_manage_snapshot_name: "{{ snapshot_name }}" + azure_manage_snapshot_vm_name: "{{ vm_name }}" + azure_manage_snapshot_resource_group: "{{ resource_group_name }}" diff --git a/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/delete_snapshot.yml b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/delete_snapshot.yml new file mode 100644 index 0000000..d1de3c8 --- /dev/null +++ b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/delete_snapshot.yml @@ -0,0 +1,8 @@ +--- +- name: Delete snapshot + ansible.builtin.include_role: + name: cloud.azure_ops.azure_manage_snapshot + vars: + azure_manage_snapshot_operation: delete + azure_manage_snapshot_name: "{{ snapshot_name }}" + azure_manage_snapshot_resource_group: "{{ resource_group_name }}" diff --git a/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/restore_snapshot.yml b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/restore_snapshot.yml new file mode 100644 index 0000000..32392e3 --- /dev/null +++ b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/restore_snapshot.yml @@ -0,0 +1,9 @@ +--- +- name: Restore snapshot + ansible.builtin.include_role: + name: cloud.azure_ops.azure_manage_snapshot + vars: + azure_manage_snapshot_operation: restore + azure_manage_snapshot_name: "{{ snapshot_name }}" + azure_manage_snapshot_vm_name: "{{ vm_name }}" + azure_manage_snapshot_resource_group: "{{ resource_group_name }}" diff --git a/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/setup.yml b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/setup.yml new file mode 100644 index 0000000..818476b --- /dev/null +++ b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/setup.yml @@ -0,0 +1,72 @@ +--- +- name: Gather Resource group info + azure.azcollection.azure_rm_resourcegroup_info: + name: "{{ resource_group }}" + register: rg_info + +- name: Create ~/.ssh directory if it does not exist + ansible.builtin.file: + path: "{{ (ssh_private_key_path | default(ssh_private_key_path_default)) | dirname }}" + state: directory + mode: '0700' + +- name: Generate an OpenSSH keypair + community.crypto.openssh_keypair: + path: "{{ (ssh_private_key_path | default(ssh_private_key_path_default)) }}" + regenerate: never + +- name: Create VM + ansible.builtin.include_role: + name: cloud.azure_ops.azure_virtual_machine_with_public_ip + vars: + azure_virtual_machine_with_public_ip_operation: "create" + azure_virtual_machine_with_public_ip_resource_group: "{{ resource_group_name }}" + azure_virtual_machine_with_public_ip_region: "{{ rg_info.resourcegroups.0.location }}" +# azure_virtual_machine_with_public_ip_availability_set: +# name: "{{ resource_prefix }}-avs" +# platform_fault_domain_count: 2 +# platform_update_domain_count: 5 +# sku: Aligned + azure_virtual_machine_with_public_ip_vm: + name: "{{ vm_name }}" + admin_username: "{{ vm_username }}" + ssh_public_keys: + - path: "/home/{{ vm_username }}/.ssh/authorized_keys" + key_data: "{{ lookup('file', ssh_public_key_path | + default(ssh_public_key_path_default)) }}" + ssh_pw_enabled: False + managed_disk_type: "{{ vm_managed_disk_type }}" + image: "{{ vm_image }}" + size: "{{ vm_size }}" + vnet_address_prefixes_cidr: + - "10.1.0.0/16" + subnet_address_prefixes_cidr: + - "10.1.0.0/24" + +- name: Get VM Info + azure.azcollection.azure_rm_virtualmachine_info: + name: "{{ vm_name }}" + resource_group: "{{ resource_group_name }}" + register: vm_info + +- name: Get IP address + azure.azcollection.azure_rm_publicipaddress_info: + name: "{{ vm_info.vms[0].network_interface_names[0] }}" + resource_group: "{{ resource_group_name }}" + register: public_ip_info + +- name: Set VM ip address + ansible.builtin.set_fact: + ip_address: "{{ public_ip_info.publicipaddresses[0].ip_address }}" + +- name: Add hosts to inventory to manage lvm + ansible.builtin.add_host: + name: "{{ vm_name }}" + hostname: "{{ ip_address }}" + ansible_ssh_host: "{{ ip_address }}" + ansible_ssh_port: "{{ ssh_port | default(ssh_port_default) }}" + ansible_ssh_user: "{{ vm_username }}" + ansible_ssh_private_key_file: "{{ (ssh_private_key_path | default(ssh_private_key_path_default)) }}" + ansible_python_interpreter: /usr/bin/python3 + groups: + - azure diff --git a/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/teardown.yml b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/teardown.yml new file mode 100644 index 0000000..0dfca8e --- /dev/null +++ b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/teardown.yml @@ -0,0 +1,20 @@ +--- +- name: Delete Virtual machine and all attached resources + ansible.builtin.include_role: + name: cloud.azure_ops.azure_virtual_machine_with_public_ip + vars: + azure_virtual_machine_with_public_ip_operation: delete + azure_virtual_machine_with_public_ip_delete_resource_group: false + azure_virtual_machine_with_public_ip_vm: + name: "{{ vm_name }}" + azure_virtual_machine_with_public_ip_resource_group: "{{ resource_group_name }}" + +- name: Delete resource group + azure.azcollection.azure_rm_resourcegroup: + name: "{{ resource_group_name }}" + state: absent + force_delete_nonempty: true + retries: 20 + delay: 5 + register: result + until: result.failed == false diff --git a/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/validate_snapshot.yml b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/validate_snapshot.yml new file mode 100644 index 0000000..b11e70d --- /dev/null +++ b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/tasks/validate_snapshot.yml @@ -0,0 +1,23 @@ +--- +- name: Wait 300 seconds, but only start checking after 60 seconds + ansible.builtin.wait_for_connection: + delay: 60 + timeout: 300 + +- name: "Get stats for {{ before_snapshot_file }}" + ansible.builtin.stat: + path: "{{ before_snapshot_file }}" + register: _ + +- name: "Assert the existence of {{ before_snapshot_file }}" + ansible.builtin.assert: + that: _.stat.exists + +- name: "Get stats for {{ after_snapshot_file }}" + ansible.builtin.stat: + path: "{{ after_snapshot_file }}" + register: _ + +- name: "Assert the non-existence of {{ after_snapshot_file }}" + ansible.builtin.assert: + that: _.stat.exists is false diff --git a/tests/integration/targets/azure_ops_test_azure_manage_snapshot/vars/vars.yml b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/vars/vars.yml new file mode 100644 index 0000000..5c745b0 --- /dev/null +++ b/tests/integration/targets/azure_ops_test_azure_manage_snapshot/vars/vars.yml @@ -0,0 +1,17 @@ +--- +vm_name: "{{ resource_prefix }}-vm" +vm_image: + offer: RHEL + publisher: RedHat + sku: 8-LVM + version: latest +vm_username: 'azureuser' +vm_size: Standard_B1s +vm_managed_disk_type: Premium_LRS +snapshot_name: "{{ resource_prefix }}-snapshot" +before_snapshot_file: "/home/{{ vm_username }}/before_snapshot" +after_snapshot_file: "/home/{{ vm_username }}/after_snapshot" +ssh_public_key_path_default: "~/.ssh/id_rsa.pub" +ssh_private_key_path_default: "~/.ssh/id_rsa" +ssh_port_default: 22 +resource_group_name: "{{ resource_prefix }}-{{ resource_group }}"