├── group_vars ├── .gitignore └── all.yml.sample ├── ansible.cfg ├── roles ├── initialize-base │ ├── tasks │ │ ├── main.yml │ │ ├── ssh.yml │ │ └── terraform.yml │ ├── templates │ │ └── credentials.auto.tfvars.j2 │ └── files │ │ └── provider.tf ├── gateway-base │ ├── tasks │ │ ├── main.yml │ │ └── gateway.yml │ └── templates │ │ └── plan.tf.j2 └── gateway-cleanup │ └── tasks │ ├── main.yml │ └── cleanup.yml ├── hosts ├── Readme.md ├── deploy-vm.yml ├── plugins └── lookup │ └── randomlist.py ├── init-proxmox.yml └── remove-gateway.yml /group_vars/.gitignore: -------------------------------------------------------------------------------- 1 | *.yml 2 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | lookup_plugins = plugins/lookup 3 | host_key_checking = False -------------------------------------------------------------------------------- /roles/initialize-base/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_tasks: ssh.yml 3 | - include_tasks: terraform.yml -------------------------------------------------------------------------------- /roles/gateway-base/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: web endpoints 3 | include_tasks: "gateway.yml" 4 | loop: "{{ gateways }}" -------------------------------------------------------------------------------- /roles/gateway-cleanup/tasks/main.yml: -------------------------------------------------------------------------------- 1 | ## Loop through stop-gateways.yml 2 | - name: loop through gateways 3 | include_tasks: "cleanup.yml" 4 | loop: "{{ gateways }}" -------------------------------------------------------------------------------- /hosts: -------------------------------------------------------------------------------- 1 | [sysmgr] 2 | 192.168.135.252 3 | 4 | [sysmgr:vars] 5 | ansible_ssh_user=ubuntu 6 | ansible_ssh_pass=bello 7 | ansible_become_pass=bello 8 | ansible_python_interpreter=/usr/bin/python3 9 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # terraform-ansible-demo 2 | 3 | These ansible playbooks and roles are designed for demonstration purposes only. 4 | Do not use in your production environments without proper testing first. 5 | 6 | -------------------------------------------------------------------------------- /roles/initialize-base/templates/credentials.auto.tfvars.j2: -------------------------------------------------------------------------------- 1 | proxmox_api_url = "{{ pve_credentials.api_endpoint }}" 2 | proxmox_api_token_id = "{{ pve_credentials.api_token_id }}" 3 | proxmox_api_token_secret = "{{ pve_credentials.api_secret }}" -------------------------------------------------------------------------------- /deploy-vm.yml: -------------------------------------------------------------------------------- 1 | - name: deploy gateway vms 2 | hosts: 3 | - sysmgr 4 | 5 | gather_facts: false 6 | any_errors_fatal: true 7 | become: true 8 | 9 | pre_tasks: 10 | - name: set gateway 'In Progress' 11 | run_once: true 12 | set_stats: 13 | data: 14 | installer_phase_gateway: 15 | status: "In Progress" 16 | start: "{{ lookup('pipe', 'date +%Y%m%d%H%M%SZ') }}" 17 | 18 | tasks: 19 | - import_role: 20 | name: gateway-base 21 | 22 | post_tasks: 23 | - name: set gateway 'Complete' 24 | run_once: true 25 | set_stats: 26 | data: 27 | installer_phase_gateway: 28 | status: "Complete" 29 | end: "{{ lookup('pipe', 'date +%Y%m%d%H%M%SZ') }}" 30 | -------------------------------------------------------------------------------- /roles/initialize-base/files/provider.tf: -------------------------------------------------------------------------------- 1 | # Proxmox Provider 2 | # --- 3 | # Initial Provider Configuration for Proxmox 4 | 5 | terraform { 6 | 7 | required_version = ">= 0.13.0" 8 | 9 | required_providers { 10 | proxmox = { 11 | source = "telmate/proxmox" 12 | version = "2.9.10" 13 | } 14 | } 15 | } 16 | 17 | variable "proxmox_api_url" { 18 | type = string 19 | } 20 | 21 | variable "proxmox_api_token_id" { 22 | type = string 23 | } 24 | 25 | variable "proxmox_api_token_secret" { 26 | type = string 27 | } 28 | 29 | provider "proxmox" { 30 | 31 | pm_api_url = var.proxmox_api_url 32 | pm_api_token_id = var.proxmox_api_token_id 33 | pm_api_token_secret = var.proxmox_api_token_secret 34 | 35 | # (Optional) Skip TLS Verification 36 | pm_tls_insecure = true 37 | 38 | pm_log_enable = true 39 | pm_log_file = "terraform-plugin-proxmox.log" 40 | pm_debug = true 41 | pm_log_levels = { 42 | _default = "debug" 43 | _capturelog = "" 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /plugins/lookup/randomlist.py: -------------------------------------------------------------------------------- 1 | import ansible.utils as utils 2 | import ansible.errors as errors 3 | from ansible.plugins.lookup import LookupBase 4 | import random 5 | 6 | class LookupModule(LookupBase): 7 | 8 | def __init__(self, basedir=None, **kwargs): 9 | self.basedir = basedir 10 | 11 | def run(self, terms, variables=None, **kwargs): 12 | 13 | items = terms[0] 14 | excluded_items = terms[1] 15 | 16 | if not isinstance(items, list) or len(items) < 1: 17 | raise errors.AnsibleError("randomlist lookup expects a populated list (items)") 18 | 19 | if not isinstance(items, list): 20 | raise errors.AnsibleError("randomlist lookup expects a list (excluded_items)") 21 | 22 | item = None 23 | 24 | if len(items) == 1 or len(items) <= len(excluded_items): 25 | item = random.choice(items) 26 | else: 27 | while item == None: 28 | opt = random.choice(items) 29 | 30 | if opt not in excluded_items: 31 | item = opt 32 | 33 | return [item] -------------------------------------------------------------------------------- /roles/initialize-base/tasks/ssh.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: ssh config 3 | block: 4 | - name: change sshd config - password authentication 5 | lineinfile: 6 | path: /etc/ssh/sshd_config 7 | state: present 8 | regexp: "^PasswordAuthentication" 9 | line: "PasswordAuthentication yes" 10 | 11 | - name: change sshd config - permit root login 12 | lineinfile: 13 | path: /etc/ssh/sshd_config 14 | state: present 15 | regexp: "^PermitRootLogin" 16 | line: "PermitRootLogin yes" 17 | 18 | - name: change sudoers config - sudo with no password 19 | lineinfile: 20 | path: /etc/sudoers 21 | state: present 22 | regexp: '^%sudo' 23 | line: '%sudo ALL=(ALL) NOPASSWD: ALL' 24 | validate: /usr/sbin/visudo -cf %s 25 | 26 | - name: restart sshd 27 | service: 28 | name: sshd 29 | state: restarted 30 | 31 | - name: install ansible authorized key 32 | ansible.posix.authorized_key: 33 | user: "{{ item }}" 34 | state: present 35 | key: "{{ lookup('file', ssh_private_key_path + '.pub') }}" 36 | with_items: 37 | - "root" 38 | - "{{ ansible_ssh_user }}" -------------------------------------------------------------------------------- /init-proxmox.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: 3 | - mains 4 | 5 | gather_facts: false 6 | any_errors_fatal: true 7 | become: true 8 | 9 | vars: 10 | delegate_facts_host: true 11 | 12 | pre_tasks: 13 | - name: set initialize 'In Progress' 14 | run_once: true 15 | set_stats: 16 | data: 17 | installer_phase_initialize: 18 | status: "In Progress" 19 | start: "{{ lookup('pipe', 'date +%Y%m%d%H%M%SZ') }}" 20 | 21 | - name: gather facts 22 | setup: 23 | when: 24 | - not delegate_facts_host | bool 25 | tags: 26 | - always 27 | 28 | - name: gather and delegate facts 29 | setup: 30 | delegate_to: "{{ item }}" 31 | delegate_facts: true 32 | with_items: "{{ groups['all'] }}" 33 | run_once: true 34 | when: 35 | - delegate_facts_host | bool 36 | tags: 37 | - always 38 | tasks: 39 | - import_role: 40 | name: initialize-base 41 | tags: 42 | - always 43 | post_tasks: 44 | - name: set initialize 'Complete' 45 | run_once: true 46 | set_stats: 47 | data: 48 | installer_phase_initialize: 49 | status: "Complete" 50 | end: "{{ lookup('pipe', 'date +%Y%m%d%H%M%SZ') }}" -------------------------------------------------------------------------------- /roles/gateway-base/templates/plan.tf.j2: -------------------------------------------------------------------------------- 1 | {% for node in item.nodes %} 2 | resource "proxmox_vm_qemu" "{{ item.uuid }}-gw-{{ loop.index }}" { 3 | # VM General Settings 4 | target_node = "{{ sgw_pve_nodes[loop.index - 1] }}" 5 | name = "{{ item.uuid }}-{{ loop.index }}" 6 | desc = "{{ item.comments }} - SGW{{ loop.index }}" 7 | 8 | # hagroup = "{{ vm_config.hagroup }}" 9 | 10 | # VM Advanced General Settings 11 | onboot = true 12 | 13 | # VM OS Settings 14 | clone = "ubuntu-cloud" 15 | 16 | full_clone = true 17 | 18 | # VM System Settings 19 | agent = 0 20 | 21 | # VM CPU Settings 22 | cores = {{ vm_config.cores }} 23 | sockets = 1 24 | cpu = "host" 25 | 26 | # VM Memory Settings 27 | memory = {{ vm_config.memory }} 28 | 29 | # VM Network Settings 30 | network { 31 | bridge = "vmbr0" 32 | model = "virtio" 33 | } 34 | 35 | disk { 36 | storage = "{{ vm_config.disk_storage }}" 37 | type = "virtio" 38 | size = "{{ vm_config.disk_size }}" 39 | } 40 | 41 | # VM Cloud-Init Settings 42 | os_type = "cloud-init" 43 | 44 | # (Optional) IP Address and Gateway 45 | ipconfig0 = "ip={{ node.ip_address }}/{{ node.ip_cidr }},gw={{ node.ip_gateway }}" 46 | 47 | nameserver = "{{ item.dns }}" 48 | 49 | # (Optional) Default User 50 | ciuser = "{{ vm_config.username }}" 51 | cipassword = "{{ vm_config.password }}" 52 | 53 | # (Optional) Add your SSH KEY 54 | sshkeys = < 14 | "Exiting remove-gateway playbook, gateway was NOT stopped or removed. 15 | To stop/remove the cluster, either say 'yes' on the prompt or 16 | or use `-e ireallymeanit=yes` on the command line when 17 | invoking the playbook" 18 | when: ireallymeanit != 'yes' 19 | 20 | - name: remove-gateway 21 | hosts: 22 | - sysmgr 23 | 24 | gather_facts: false 25 | any_errors_fatal: true 26 | become: true 27 | 28 | vars: 29 | delegate_facts_host: true 30 | 31 | pre_tasks: 32 | - name: set gateway 'In Progress' 33 | run_once: true 34 | set_stats: 35 | data: 36 | installer_phase_gateway: 37 | status: "In Progress" 38 | start: "{{ lookup('pipe', 'date +%Y%m%d%H%M%SZ') }}" 39 | 40 | - name: gather facts 41 | setup: 42 | when: 43 | - not delegate_facts_host | bool 44 | tags: 45 | - always 46 | 47 | - name: gather and delegate facts 48 | setup: 49 | delegate_to: "{{ item }}" 50 | delegate_facts: true 51 | with_items: "{{ groups['all'] }}" 52 | run_once: true 53 | when: 54 | - delegate_facts_host | bool 55 | tags: 56 | - always 57 | tasks: 58 | - import_role: 59 | name: gateway-cleanup 60 | post_tasks: 61 | - name: set gateway 'Complete' 62 | run_once: true 63 | set_stats: 64 | data: 65 | installer_phase_gateway: 66 | status: "Complete" 67 | end: "{{ lookup('pipe', 'date +%Y%m%d%H%M%SZ') }}" 68 | -------------------------------------------------------------------------------- /roles/gateway-base/tasks/gateway.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: manage gateway 3 | block: 4 | - name: plan exists 5 | stat: 6 | path: "/opt/vm-config/terraform/{{ item.uuid }}.tf" 7 | register: plan_exists 8 | 9 | - name: ssh exists 10 | stat: 11 | path: "/opt/vm-config/ssh/{{ item.uuid }}.pub" 12 | register: ssh_exists 13 | 14 | - name: create ssh environment 15 | file: 16 | path: /opt/vm-config/ssh 17 | state: directory 18 | mode: '0777' 19 | 20 | - set_fact: 21 | sgw_pve_nodes: [] 22 | when: not plan_exists.stat.exists and item.state == "present" 23 | 24 | - set_fact: 25 | sgw_pve_nodes: "{{ sgw_pve_nodes }} + ['{{ lookup('randomlist', pve_nodes, sgw_pve_nodes) }}']" 26 | loop: "{{ item.nodes }}" 27 | loop_control: 28 | loop_var: _item 29 | index_var: idx 30 | when: not plan_exists.stat.exists and item.state == "present" 31 | 32 | - name: generate ssh key 33 | shell: ssh-keygen -t rsa -b 4096 -f /opt/vm-config/ssh/{{ item.uuid }} -P "" 34 | when: not ssh_exists.stat.exists and item.state == "present" 35 | become: false 36 | 37 | - shell: cat /opt/vm-config/ssh/{{ item.uuid }}.pub 38 | register: ssh_public_key_gw_result 39 | when: not ssh_exists.stat.exists and item.state == "present" 40 | 41 | - name: create terraform plan file 42 | template: 43 | src: plan.tf.j2 44 | dest: "/opt/vm-config/terraform/{{ item.uuid }}.tf" 45 | owner: root 46 | group: root 47 | mode: '0644' 48 | when: not plan_exists.stat.exists and item.state == "present" 49 | 50 | - name: terraform apply 51 | shell: terraform apply --auto-approve --target proxmox_vm_qemu.{{ item.uuid }}-gw-{{ idx + 1 }} 52 | args: 53 | chdir: /opt/vm-config/terraform 54 | loop: "{{ item.nodes }}" 55 | loop_control: 56 | loop_var: _item 57 | index_var: idx 58 | when: not plan_exists.stat.exists and item.state == "present" 59 | --------------------------------------------------------------------------------