Skip to content

Commit

Permalink
Rebase
Browse files Browse the repository at this point in the history
Signed-off-by: Alina Buzachis <[email protected]>
  • Loading branch information
alinabuzachis committed Sep 19, 2023
1 parent ba9c4b5 commit 7fa9b60
Show file tree
Hide file tree
Showing 19 changed files with 568 additions and 258 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@ Name | Description
[cloud.aws_ops.awsconfig_apigateway_with_lambda_integration](https://github.com/ansible-collections/cloud.aws_ops/blob/main/roles/awsconfig_apigateway_with_lambda_integration/README.md)|A role to create/delete an API gateway with lambda function integration.
[cloud.aws_ops.manage_transit_gateway](https://github.com/ansible-collections/cloud.aws_ops/blob/main/roles/manage_transit_gateway/README.md)|A role to create/delete transit_gateway with vpc and vpn attachments.
[cloud.aws_ops.deploy_flask_app](https://github.com/ansible-collections/cloud.aws_ops/blob/main/roles/deploy_flask_app/README.md)|A role to deploy a flask web application on AWS.
[cloud.aws_ops.clone_on_prem_vm](https://github.com/ansible-collections/cloud.aws_ops/blob/main/roles/clone_on_prem_vm/README.md)|A role to clone an existing on prem VM using the KVM hypervisor.
[cloud.aws_ops.import_image_and_run_aws_instance](https://github.com/ansible-collections/cloud.aws_ops/blob/main/roles/import_image_and_run_aws_instance/README.md)|A role that imports a local .raw image into an Amazon Machine Image (AMI) and run an AWS EC2 instance.

### Playbooks
Name | Description
--- | ---
[cloud.aws_ops.eda](https://github.com/ansible-collections/cloud.aws_ops/blob/main/playbooks/README.md)|A set of playbooks to restore AWS Cloudtrail configurations, created for use with the [cloud.aws_manage_cloudtrail_encryption rulebook](https://github.com/ansible-collections/cloud.aws_ops/blob/main/extensions/eda/rulebooks/AWS_MANAGE_CLOUDTRAIL_ENCRYPTION.md).
[cloud.aws_ops.webapp](https://github.com/ansible-collections/cloud.aws_ops/blob/main/playbooks/webapp/README.md)|A set of playbooks to create, delete, or migrate a webapp on AWS.
[cloud.aws_ops.upload_file_to_s3](https://github.com/ansible-collections/cloud.aws_ops/blob/main/playbooks/UPLOAD_FILE_TO_S3.md)|A playbook to upload a local file to S3.
[cloud.aws_ops.move_vm_from_on_prem_to_aws](https://github.com/ansible-collections/cloud.aws_ops/blob/main/playbooks/move_vm_from_on_prem_to_aws/README.md)|A playbook to migrate an existing on prem VM running on KVM hypervisor to AWS.

### Rulebooks
Name | Description
Expand Down
78 changes: 78 additions & 0 deletions playbooks/move_vm_from_on_prem_to_aws/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# cloud.aws_ops.move_vm_from_on_prem_to_aws playbooks

A playbook to migrate an existing on prem VM running on KVM hypervisor to AWS.

## Requirements

VM Import requires a role to perform certain operations on your behalf. You must create a service role named vmimport with a trust relationship policy document that allows VM Import to assume the role, and you must attach an IAM policy to the role.

AWS User Account with the following permissions:
* s3:GetBucketLocation
* s3:GetObject
* s3:ListBucket
* s3:GetBucketLocation
* s3:GetObject
* s3:ListBucket
* s3:PutObject
* s3:GetBucketAcl
* ec2:ModifySnapshotAttribute
* ec2:CopySnapshot
* ec2:RegisterImage
* ec2:Describe*
* ec2:RunInstances

(Optional) To import resources encrypted using an AWS KMS key from AWS Key Management Service, add the following permissions:
* kms:CreateGrant
* kms:Decrypt
* kms:DescribeKey
* kms:Encrypt
* kms:GenerateDataKey*
* kms:ReEncrypt*

## Playbook Variables

### Needed for the cloud.aws_ops.clone_on_prem_vm role

* **on_prem_source_vm_name** (str): (Required) The name of the on-prem VM you want to clone.
* **on_prem_vm_clone_name** (str): (Optional) The name you want to call the cloned image. If not set, the the **on_prem_vm_clone_name** will be used with a _-clone_ suffix.
* **uri** (str): (Optional) Libvirt connection uri. Default: "qemu:///system".
* **overwrite_clone** (bool): (Optional) Weather to overwrite or not an already existing on prem VM clone. Default: true.

### Needed for the cloud.aws_ops.clone_on_prem_vm role

* **aws_access_key** (str): (Required) AWS access key ID for user account with the above permissions
* **aws_secret_key** (str): (Required) AWS secret access key for user account with the above permissions
* **aws_region** (str): (Required) AWS region in which to run the EC2 instance
* **security_token** (str): (Optional) Security token for AWS session authentication
* **s3_bucket_name** (str): (Required) The name of the S3 bucket name where you want to upload the .raw image. It must exist in the region the instance is created.
* **import_task_name** (str): (Required) The name you want to assign to the AWS EC2 import image task.
* **image_path** (str): (Required) The path where the .raw image is stored.
* **instance_name** (str): (Required) The name of the EC2 instance you want to create using the imported AMI.
* **instance_type** (str): (Optional) The EC2 instance type you want to use. Default: "t2.micro".
* **keypair_name** (str): (Optional) The name of the SSH access key to assign to the EC2 instance. It must exist in the region the instance is created. If not set, your default AWS account keypair will be used.
* **security_groups** (list): (Optional) A list of security group IDs or names to assiciate to the EC2 instance.
* **vpc_subnet_id** (str): (Optional) The subnet ID in which to launch the EC2 instance instance (VPC). If none is provided, M(amazon.aws.ec2_instance) will chose the default zone of the default VPC.
* **instance_volumes** (dict): (Optional) A dictionary of a block device mappings, by default this will always use the AMI root device so the **instance_volumes** option is primarily for adding more storage. A mapping contains the (optional) keys _device_name_, _ebs.volume_type_, _ebs.volume_size_, _ebs.kms_key_id_, _ebs.iops_, and _ebs.delete_on_termination_.

* **kvm_host** (str): Information about the host running the KVM hypervisr that are dynamically added to the inventory.
* **name**: This is a user-defined name for the host you are adding to the inventory.
* **ansible_host**: This variable specifies the hostname or IP address of the host you are adding to the inventory.
* **ansible_user**: This variable specifies the SSH username that Ansible should use when connecting to the host.
* **ansible_ssh_private_key_file** This variable specifies the path to the SSH private key file that Ansible should use for authentication when connecting to the host.
* **groups** This variable enabled you to assign the newly added host to one or more groups in the inventory.

## Example Usage

Create a `credentials.yaml` file with the folling contents:

```yaml
aws_access_key: "xxxxxxxxxxxxxxxxxxxx"
aws_secret_key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
aws_region: "us-east-1"
```
To migrate an existing on prem VM running on KVM hypervisor to AWS, run:
```bash
ansible-playbook move_vm_from_on_prem_to_aws.yml -e "@credentials.yaml"
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
- name: A playbook to migrate an existing on prem VM running on KVM hypervisor to AWS
hosts: localhost
gather_facts: false

vars_files:
- vars/main.yml

module_defaults:
group/aws:
aws_access_key: "{{ aws_access_key | default(omit) }}"
aws_secret_key: "{{ aws_secret_key | default(omit) }}"
security_token: "{{ security_token | default(omit) }}"
region: "{{ aws_region | default('us-east-1') }}"

tasks:
- name: Add host to inventory
ansible.builtin.add_host:
name: "{{ kvm_host.name }}"
ansible_host: "{{ kvm_host.ip }}"
ansible_user: "{{ kvm_host.ansible_user }}"
ansible_ssh_common_args: -o "UserKnownHostsFile=/dev/null" -o StrictHostKeyChecking=no -i {{ kvm_host.ansible_ssh_private_key_file }}
groups: "{{ kvm_host.groups }}"

- name: Import 'cloud.aws_ops.clone_on_prem_vm' role
ansible.builtin.import_role:
name: cloud.aws_ops.clone_on_prem_vm
vars:
clone_on_prem_vm_source_vm_name: "{{ on_prem_source_vm_name }}"
clone_on_prem_vm_dest_vm_name: "{{ on_prem_vm_clone_name }}"
clone_on_prem_vm_uri: "{{ uri }}"
clone_on_prem_vm_local_image_path: "{{ local_image_path }}"
delegate_to: kvm

- name: Import 'cloud.aws_ops.import_image_and_run_aws_instance' role
ansible.builtin.import_role:
name: cloud.aws_ops.import_image_and_run_aws_instance
vars:
import_image_and_run_aws_instance_bucket_name: "{{ s3_bucket_name }}"
import_image_and_run_aws_instance_image_path: "{{ clone_on_prem_vm_local_image_path }}"
import_image_and_run_aws_instance_instance_name: "{{ instance_name }}"
import_image_and_run_aws_instance_instance_type: "{{ instance_type }}"
import_image_and_run_aws_instance_import_image_task_name: "{{ import_task_name }}"
14 changes: 14 additions & 0 deletions playbooks/move_vm_from_on_prem_to_aws/vars/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
on_prem_source_vm_name: "ubuntu-guest"
on_prem_vm_clone_name: "ubuntu-guest-clone"
s3_bucket_name: "clone-vm-s3-bucket"
instance_name: "ubuntu-vm-clone"
local_image_path: "~/images"
import_task_name: "import-clone"
instance_type: "t2.micro"
uri: "qemu:///system"
kvm_host:
name: kvm
ip: 192.168.1.117
ansible_user: vagrant
ansible_ssh_private_key_file: ~/.ssh/id_rsa.pub
groups: "libvirt"
69 changes: 69 additions & 0 deletions roles/clone_on_prem_vm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
Role Name
=========

A role to clone an existing on prem VM using the KVM hypervisor. The role sets the **clone_on_prem_vm_local_image_path** variable containing the path where the image was saved on localhost.

Requirements
------------

**qemu** and **qemu-img** packages installed.

Role Variables
--------------

* **clone_on_prem_vm_source_vm_name**: (Required) The name of the on-prem VM you want to clone.
* **clone_on_prem_vm_image_name**: The name you want to call the cloned image. If not set, the the **clone_on_prem_vm_source_vm_name** will be used with a _-clone_ suffix.
* **clone_on_prem_vm_overwrite**: Weather to overwrite or not an already existing on prem VM clone. Default: true.
* **clone_on_prem_vm_local_image_path**: The path where you would like to save the image. If the path does not exists on localhost, the role will create it. If this parameter is not set, the role will save the image in a _~/tmp_ folder.
* **clone_on_prem_vm_uri**: Libvirt connection uri. Default: "qemu:///system".

Dependencies
------------

N/A

Example Playbook
----------------

- hosts: localhost
gather_facts: false

vars:
on_prem_source_vm_name: "ubuntu-guest"
on_prem_vm_image_name: "ubuntu-guest-image"
local_image_path: "~/images/"
kvm_host:
name: kvm
ip: 192.168.1.117
ansible_user: vagrant
ansible_ssh_private_key_file: ~/.ssh/id_rsa.pub

tasks:
- name: Add host to inventory
ansible.builtin.add_host:
name: "{{ kvm_host.name }}"
ansible_host: "{{ kvm_host.ip }}"
ansible_user: "{{ kvm_host.ansible_user }}"
ansible_ssh_common_args: -o "UserKnownHostsFile=/dev/null" -o StrictHostKeyChecking=no -i {{ kvm_host.ansible_ssh_private_key_file }}
groups: "libvirt"

- name: Import 'cloud.aws_ops.clone_on_prem_vm' role
ansible.builtin.import_role:
name: cloud.aws_ops.clone_on_prem_vm
vars:
clone_on_prem_vm_source_vm_name: "{{ on_prem_source_vm_name }}"
clone_on_prem_vm_dest_image_name: "{{ on_prem_vm_image_name }}"
clone_on_prem_vm_local_image_path: "{{ local_image_path }}"
delegate_to: kvm

License
-------

GNU General Public License v3.0 or later

See [LICENCE](https://github.com/ansible-collections/cloud.azure_roles/blob/main/LICENSE) to see the full text.

Author Information
------------------

- Ansible Cloud Content Team
3 changes: 3 additions & 0 deletions roles/clone_on_prem_vm/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
clone_on_prem_vm_uri: "qemu:///system"
clone_on_prem_vm_overwrite: true
5 changes: 5 additions & 0 deletions roles/clone_on_prem_vm/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
- name: Delete temporary directory
ansible.builtin.file:
state: absent
path: "{{ clone_on_prem_vm__tmpdir.path }}"
141 changes: 141 additions & 0 deletions roles/clone_on_prem_vm/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
---
- name: Gather package facts
ansible.builtin.package_facts:
manager: auto
register: package_facts

- name: qemu is not installed
debug:
msg: "qemu is not installed"
when: "'qemu' not in package_facts.ansible_facts.packages"

- name: qemu-img is not installed
debug:
msg: "qemu-img is not installed"
when: "'qemu-img' not in package_facts.ansible_facts.packages"

- name: Create temporary directory to create the clone in
ansible.builtin.tempfile:
state: directory
suffix: .storage
register: clone_on_prem_vm__tmpdir
notify:
- "Delete temporary directory"

- name: Get information about the on prem VM

Check failure on line 25 in roles/clone_on_prem_vm/tasks/main.yml

View workflow job for this annotation

GitHub Actions / ansible-lint

syntax-check[specific]

couldn't resolve module/action 'community.libvirt.virt'. This often indicates a misspelling, missing collection, or incorrect module path.
community.libvirt.virt:
command: info
name: "{{ clone_on_prem_vm_source_vm_name }}"
uri: "{{ clone_on_prem_vm_uri }}"
register: clone_on_prem_vm__vm_info

- name: Fail when on prem VM does not exist
ansible.builtin.fail:
msg: "The on prem VM {{ clone_on_prem_vm_source_vm_name }} does not exist."
when: clone_on_prem_vm_source_vm_name not in clone_on_prem_vm__vm_info

- name: Fail when on prem VM's state is destroyed
ansible.builtin.fail:
msg: "The VM {{ clone_on_prem_vm_source_vm_name }} has been destroyed."
when: clone_on_prem_vm__vm_info[clone_on_prem_vm_source_vm_name].state == "destroyed"

- name: Set 'clone_on_prem_vm_image_name' varible
ansible.builtin.set_fact:
clone_on_prem_vm_image_name: "{{ clone_on_prem_vm_source_vm_name }}-clone"
when: clone_on_prem_vm_image_name is undefined

- name: Check if domain exists
community.libvirt.virt:
name: "{{ clone_on_prem_vm_image_name }}"
command: info
uri: "{{ clone_on_prem_vm_uri }}"
register: clone_on_prem_vm__domain_info

- name: Fail when a domain already exists
ansible.builtin.fail:
msg: "A domain {{ clone_on_prem_vm_image_name }} already exists. Please undefine it first or set clone_on_prem_vm_overwrite: true."
when: clone_on_prem_vm_image_name in clone_on_prem_vm__domain_info and clone_on_prem_vm_overwrite is false

- name: Undefine domain
community.libvirt.virt:
name: "{{ clone_on_prem_vm_image_name }}"
command: undefine
when: clone_on_prem_vm_image_name in clone_on_prem_vm__domain_info and clone_on_prem_vm_overwrite is true

- name: Ensure on prem VM is paused
community.libvirt.virt:
state: paused
name: "{{ clone_on_prem_vm_source_vm_name }}"
uri: "{{ clone_on_prem_vm_uri }}"
when: clone_on_prem_vm__vm_info[clone_on_prem_vm_source_vm_name].state == "running"

- name: Set 'clone_on_prem_vm__clone_path' and 'clone_on_prem_vm__raw_image_path'
ansible.builtin.set_fact:
clone_on_prem_vm__clone_path: "{{ clone_on_prem_vm__tmpdir.path }}/{{ clone_on_prem_vm_dest_vm_name }}.qcow2"
clone_on_prem_vm__raw_image_path: "{{ clone_on_prem_vm__tmpdir.path }}/{{ clone_on_prem_vm_dest_vm_name }}.raw"

- name: Cloning {{ clone_on_prem_vm_source_vm_name }} on prem VM
ansible.builtin.command: |
virt-clone --original {{ clone_on_prem_vm_source_vm_name }} \
--name {{ clone_on_prem_vm_image_name }} \
--file {{ clone_on_prem_vm__clone_path }}
environment:
LIBVIRT_DEFAULT_URI: "{{ clone_on_prem_vm_uri }}"

- name: Get information about the clone
ansible.builtin.stat:
path: "{{ clone_on_prem_vm__clone_path }}"
register: clone_on_prem_vm__clone_info

# Priviledge escalation is needed because the .qcow2 file is owned by root
# when default hypervisor is used
- name: Convert qcow2 to raw using qemu-img with priviledge escalation
ansible.builtin.command: |
qemu-img convert -f qcow2 -O raw \
{{ clone_on_prem_vm__clone_path }} \
{{ clone_on_prem_vm__raw_image_path }}
become: true
become_method: sudo
environment:
LIBVIRT_DEFAULT_URI: "{{ clone_on_prem_vm_uri }}"
when: clone_on_prem_vm__clone_info.stat.exists and clone_on_prem_vm__clone_info.stat.pw_name == "root"

- name: Convert qcow2 to raw using qemu-img
ansible.builtin.command: |
qemu-img convert -f qcow2 -O raw \
{{ clone_on_prem_vm__clone_path }} \
{{ clone_on_prem_vm__raw_image_path }}
environment:
LIBVIRT_DEFAULT_URI: "{{ clone_on_prem_vm_uri }}"
when: clone_on_prem_vm__clone_info.stat.exists and clone_on_prem_vm__clone_info.stat.pw_name != "root"

- name: Create temporary directory to localcolhost when clone_on_prem_vm_local_image_path is not set
ansible.builtin.tempfile:
state: directory
suffix: .storage
register: clone_on_prem_vm__dir_localhost
when: clone_on_prem_vm_local_image_path is undefined
delegate_to: localhost

- name: Create directory if it does not exist
ansible.builtin.file:
path: "{{ clone_on_prem_vm_local_image_path }}"
state: directory
mode: 0775
recurse: yes
register: clone_on_prem_vm__dir_localhost
when: clone_on_prem_vm_local_image_path is defined
delegate_to: localhost

- name: Fetch the converted RAW image to localhost
ansible.builtin.fetch:
src: "{{ clone_on_prem_vm__raw_image_path }}"
dest: "{{ clone_on_prem_vm__dir_localhost.path }}"
flat: yes
fail_on_missing: yes
validate_checksum: true
register: clone_on_prem_vm_fetch_to_localhost

- name: Set 'clone_on_prem_vm_local_image_path'
ansible.builtin.set_fact:
clone_on_prem_vm_local_image_path: "{{ clone_on_prem_vm_fetch_to_localhost.dest }}"
Loading

0 comments on commit 7fa9b60

Please sign in to comment.