XCP-ng
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Register
    • Login

    Terraform with cloud-init

    Scheduled Pinned Locked Moved Xen Orchestra
    25 Posts 7 Posters 8.5k Views 5 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • olivierlambertO Offline
      olivierlambert Vates 🪐 Co-Founder CEO
      last edited by olivierlambert

      Hello!

      Hmm likely a question for @ddelnano 🤔

      1 Reply Last reply Reply Quote 0
      • D Offline
        ddelnano Terraform Team @m4xm0rris
        last edited by ddelnano

        @m4xm0rris Xen Orchestra's web application is doing this templating on the client side (reference). Since terraform already has a nice interface for templating (using its template_file data source), you can do the substitution yourself.

        Here is an example below. Note: this code is not tested but was pulled from an internal project I have.

        # template_file.tpl
        #cloud-config
        
        hostname: ${name}
        
        # any other cloud-config you need
        [...]
        
        # vm.tf
        data "template_file" "cloud_config" {
          template = file("${path.module}/template_file.tpl")
          vars = {
            name = "your_hostname_value"
          }
        }
        
        resource "xenorchestra_cloud_config" "cloud_config" {
          name = "cloud_config"
          # This performs the templating
          template = data.template_file.cloud_config.rendered
        }
        
        resource "xenorchestra_vm" "vm" {
          [ ... ]
          cloud_config = xenorchestra_cloud_config.cloud_config.template
          
        }
        
        

        Please share your terraform code if you would like more specific advice on how to recreate exactly what you are trying to do.

        M 1 Reply Last reply Reply Quote 2
        • M Offline
          m4xm0rris @ddelnano
          last edited by

          Thanks @ddelnano , that worked perfectly!

          Appreciate the help!

          1 Reply Last reply Reply Quote 1
          • olivierlambertO Offline
            olivierlambert Vates 🪐 Co-Founder CEO
            last edited by

            Interesting indeed, we should probably document this 🙂 If you have time @ddelnano obviously 🙂

            1 Reply Last reply Reply Quote 0
            • M Offline
              m4xm0rris
              last edited by

              Just stumbled upon this provider too which may offer a more streamlined approach, not that I am familiar enough with Terraform yet to say this for sure: https://github.com/hashicorp/terraform-provider-cloudinit

              D 1 Reply Last reply Reply Quote 0
              • D Offline
                ddelnano Terraform Team @m4xm0rris
                last edited by

                I created terraform-provider-xenorchestra #181 to track updating the docs.

                Cool, I didn't realize that there was a cloudinit terraform provider :). I'll be giving that a try and may use that in the examples if its better than using template_file directly.

                M D 2 Replies Last reply Reply Quote 1
                • M Offline
                  m4xm0rris @ddelnano
                  last edited by

                  @ddelnano Yeah, I only found it as template_file is deprecated apparently and as it hasn't seen any updates, I couldn't use the code you posted from my M1 Macbook 😞

                  Anyways, thanks for the help!

                  D 1 Reply Last reply Reply Quote 0
                  • D Offline
                    ddelnano Terraform Team @m4xm0rris
                    last edited by

                    @m4xm0rris it looks like there is a builtin function to do templating now (templatefile). Apologies, it's been a while since I've written any new templated terraform code.

                    1 Reply Last reply Reply Quote 1
                    • M Offline
                      m4xm0rris
                      last edited by

                      In case anyone comes here looking for an example (as it took me days of banging head against the proverbial wall to get this working with templatefile function):

                      cloud_config.tftpl

                      #cloud-config
                      hostname: ${hostname}
                      user: ops-user
                      ssh_authorized_keys:
                        - OPS_USER_SSH_KEY
                      manage_etc_hosts: true
                      fqdn: ${hostname}.${domain}
                      package_upgrade: true
                      users:
                       - default
                      

                      cloud_network_config.tftpl

                      #cloud-config
                      version: 1
                      config:
                          - type: physical
                            name: eth0
                            subnets:
                            - type: static
                              address: '${ip}'
                              netmask: '255.255.255.0'
                              gateway: '10.1.50.1'
                      

                      vm.tf

                      resource "xenorchestra_vm" "unc-lab" {
                      ......
                       cloud_config = templatefile("cloud_config.tftpl", {
                        hostname = "unc-lab"
                        domain = "morris.lan"
                      })
                      cloud_network_config = templatefile("cloud_network_config.tftpl", {
                        ip = "10.1.55.34"
                      })
                      .......
                      

                      Thanks @ddelnano for your help!

                      D B D 3 Replies Last reply Reply Quote 5
                      • D Offline
                        ddelnano Terraform Team @ddelnano
                        last edited by

                        I was able to confirm that using the cloudinit data source does work with Xen Orchestra.

                        Here is the following terraform code that I used (essential pieces like SR and network are excluded for the example):

                        data "cloudinit_config" "cloud_config" {
                          gzip = false
                          base64_encode = false
                        
                          part {
                            content_type = "text/cloud-config"
                            content = <<EOF
                        users:
                          - name: ddelnano
                            sudo: ALL=(ALL) NOPASSWD:ALL
                            ssh_import_id:
                              - gh:ddelnano
                        
                        packages:
                        - make
                        - build-essential
                        
                        runcmd:
                        - echo 'this is a test'
                        EOF
                          }
                        }
                        
                        resource "xenorchestra_vm" "vm" {
                          memory_max = 2147467264
                          cpus = 1
                          name_label = "XO terraform tutorial"
                          template = data.xenorchestra_template.vm_template.id
                          cloud_config = data.cloudinit_config.cloud_config.rendered
                        
                          network {
                            network_id = data.xenorchestra_network.network.id
                          }
                        
                          disk {
                            sr_id = data.xenorchestra_sr.sr.id
                            name_label = "VM root volume"
                            size = 4294967296
                          }
                        }
                        
                        

                        This creates a VM and runs the cloudinit I specified, however, it doesn't allow you to do any templating. So you would still need to use the templatfile function built into Terraform.

                        I'm going to update the docs to use the templatefile function since that is the most streamlined approach.

                        1 Reply Last reply Reply Quote 1
                        • D Offline
                          ddelnano Terraform Team @m4xm0rris
                          last edited by

                          @m4xm0rris thanks for posting your solution. Would you mind reviewing my changes to the terraform registry documentation (here)?

                          Since I've been working with terraform long before Xen Orchestra, having your opinion on the documentation would be valuable 🙂

                          M 1 Reply Last reply Reply Quote 0
                          • ppikna97P Offline
                            ppikna97
                            last edited by

                            Hi, I have question to this topic. I figured out to use templatefiles in terraform to provision my VMs. But when I try to provision network alongside with userdata configuration (2 templatefiles) it did not work. It just apply network configuration. My use case was to firstly configure static networking and than install some packages, hostname and so on. I was resolving this few months ago, but did not understand how execution sequence works or if it is even possible. Cloud-init official documentation is not very clear.

                            I would appreciate any help with this.

                            M 1 Reply Last reply Reply Quote 0
                            • M Offline
                              m4xm0rris @ddelnano
                              last edited by

                              @ddelnano Sir, the only thing I could point out from that pull request is the block in your vm.md where you are templating the variables in a separate block.

                              "

                              # Template the cloudinit if needed
                              template = templatefile("cloud_config.tftpl", {
                                hostname = "your-hostname"
                                domain = "your.domain.com"
                              })
                              

                              "
                              I have no where near enough experience with Terraform to tell if makes any practical difference, but I am able to redner the template inline with the rest of my VM resource definition. eg.

                              cloud_network_config = templatefile("cloud_network_config.tftpl", {
                                 ip = "10.1.55.37"
                                 netmask = var.network["netmask"]
                                 gateway = var.network["gateway"]
                               })
                              

                              Just a little tidier IMO, but again have no idea if the way you have done this is better practice etc.

                              Thanks 🙂

                              1 Reply Last reply Reply Quote 0
                              • M Offline
                                m4xm0rris @ppikna97
                                last edited by

                                @ppikna97 Hi! How are you calling the templates in your VM resources? As you can see above from my examples I'm simply pulling both user data and network inline with the rest of my config e.g.

                                cloud_config = templatefile("cloud_config.tftpl", {
                                  hostname = "bw-lab"
                                  domain = var.network["domain"]
                                })
                                cloud_network_config = templatefile("cloud_network_config.tftpl", {
                                  ip = "10.1.55.37"
                                  netmask = var.network["netmask"]
                                  gateway = var.network["gateway"]
                                })
                                
                                ppikna97P 1 Reply Last reply Reply Quote 1
                                • ppikna97P Offline
                                  ppikna97 @m4xm0rris
                                  last edited by

                                  @m4xm0rris thanks. I was using template_file data source with xenorchestra_cloud_config data source like this:

                                  905476d3-fd92-4cf1-8d10-2611cd7c8a31-image.png

                                  But I found answer to both of us... Some video linked me here templatefile vs template_file. Resulting that it is better to use templatefile function to match terraform version and for working with different data types. But in the essence templatefile and template_file do the same thing.

                                  M 1 Reply Last reply Reply Quote 2
                                  • M Offline
                                    m4xm0rris @ppikna97
                                    last edited by

                                    @ppikna97 Np, I was also using template_file originally but as its now deprecated there have been no builds released for M1 based Mac (leading/forcing me down the templatefile function route).

                                    1 Reply Last reply Reply Quote 0
                                    • B Offline
                                      bxen @m4xm0rris
                                      last edited by

                                      @m4xm0rris This was very helpful. Thank you!

                                      1 Reply Last reply Reply Quote 0
                                      • D Offline
                                        dydomby @m4xm0rris
                                        last edited by

                                        @m4xm0rris thanks, it helped much.

                                        1 Reply Last reply Reply Quote 1
                                        • olivierlambertO Offline
                                          olivierlambert Vates 🪐 Co-Founder CEO
                                          last edited by

                                          Hello there!

                                          If you think some extra doc would be important to avoid searching for the solution, please do so 🙂

                                          antoniolfdacruzA 1 Reply Last reply Reply Quote 0
                                          • antoniolfdacruzA Offline
                                            antoniolfdacruz @olivierlambert
                                            last edited by

                                            Hi there!

                                            Please, delete my question if it is considered bad in any way.

                                            I am able to write terraform code that creates a vm in one of our xcp pools.

                                            But can't undertand how I must configure the template in order to be able to pass cloud-init userdata and metadata.

                                            I am using a simple VM install from ubuntu 20.04.5 ISO, turned into a template. It does not seem to load the yaml templates.

                                            code_textalfcruz-adm@local.hcpa.ufrgs.br@volt-ampere:~/terraform-provider-xenorchestra-intro/mkxcpvm2n$ cat main.tf
                                            # Infrastructure parametrization
                                            
                                            variable "xoa_pool_name" {
                                              type        = string
                                              description = "Pool Name at HCPA's XCP-ng infrastructure managed by XenOrchestra"
                                              default     = "UAAPool01"
                                            }
                                            
                                            variable "xoa_template_name" {
                                              type        = string
                                              description = "Template name at the chosen XCP-ng pool (must be unique between all the HCPA's pools"
                                              #default     = "Ubuntu 20.04 LTS Focal Fossa (20211008u)"
                                              default = "template-ubuntu-20045"
                                            }
                                            
                                            variable "xoa_sr_name" {
                                              type        = string
                                              description = "Name of the Storage Resource to place the VM disk(s) at the chosen pool"
                                              default     = "EQL01UHAASR01"
                                            }
                                            
                                            variable "xoa_public_vlanid" {
                                              type        = string
                                              description = "The VLAN id of the public (first) network interface of this VM"
                                              default     = "VLAN702"
                                            }
                                            
                                            variable "xoa_private_vlanid" {
                                              type        = string
                                              description = "The VLAN id of the private (or second) network interface of this VM"
                                              default     = "VLAN902"
                                            }
                                            
                                            # VM parametrization
                                            
                                            variable "vm_memory_max" {
                                              type        = number
                                              description = "VM's maximum memory (in bytes)"
                                              default     = 2147467264
                                            }
                                            
                                            variable "vm_cpus" {
                                              type        = number
                                              description = "VM's number of virtual CPU cores"
                                              default     = 4
                                            }
                                            
                                            variable "vm_name_label" {
                                              type        = string
                                              description = "The VM's name on the XCP-ng infrastructure (XOA and XCP console)"
                                              default     = "Terraformed at HCPA"
                                            }
                                            
                                            variable "vm_disk_name_label" {
                                              type        = string
                                              description = "The name of the first VM's disk"
                                              default     = "Filesystem root"
                                            }
                                            
                                            variable "vm_disk_size" {
                                              type        = number
                                              description = "The size of this VM's first disk (in bytes - default = template thick / size - 30GB)"
                                              default     = 32212254720
                                            }
                                            
                                            variable "vm_public_domain" {
                                              type        = string
                                              description = "Name of the local intranet domain"
                                              default     = "local.hcpa.ufrgs.br"
                                            }
                                            
                                            variable "vm_public_ipv4_address" {
                                              type        = string
                                              description = "The VM's public (first) interface IPv4 address"
                                              default     = "10.70.3.250"
                                            }
                                            
                                            variable "vm_public_ipv4_prefixlen" {
                                              type        = string
                                              description = "The number of network bits to use for the public IPv4 interface netmask"
                                              default     = "23"
                                            }
                                            
                                            variable "vm_public_ipv4_gateway" {
                                              type        = string
                                              description = "The IPv4 default gateway for the public interface"
                                              default     = "10.72.0.1"
                                            }
                                            
                                            # Gattering ambient facts
                                            
                                            data "xenorchestra_pool" "pool" {
                                              name_label = var.xoa_pool_name
                                            }
                                            
                                            data "xenorchestra_template" "vm_template" {
                                              name_label = var.xoa_template_name
                                            }
                                            
                                            data "xenorchestra_sr" "sr" {
                                              name_label = var.xoa_sr_name
                                              pool_id    = data.xenorchestra_pool.pool.id
                                            }
                                            
                                            data "xenorchestra_network" "public_vlanid" {
                                              name_label = var.xoa_public_vlanid
                                              pool_id    = data.xenorchestra_pool.pool.id
                                            }
                                            
                                            data "xenorchestra_network" "private_vlanid" {
                                              name_label = var.xoa_private_vlanid
                                              pool_id    = data.xenorchestra_pool.pool.id
                                            }
                                            
                                            # Create cloudinit templates
                                            
                                            data "template_file" "userdata" {
                                              template  = file("./cloud_userdata_cfg.yaml")
                                              vars      = {
                                                hostname = var.vm_name_label
                                                domain   = var.vm_public_domain
                                              }
                                            }
                                            
                                            data "template_file" "metadata" {
                                              template = file("./cloud_metadata_cfg.yaml")
                                              vars     = {
                                                hostname  = var.vm_name_label
                                                ip        = var.vm_public_ipv4_address
                                                prefixlen = var.vm_public_ipv4_prefixlen
                                                gateway   = var.vm_public_ipv4_gateway
                                              }
                                            }
                                            
                                            resource "xenorchestra_cloud_config" "userdataconfig" {
                                              name = "user data config"
                                              template = data.template_file.userdata.rendered
                                            }
                                            
                                            resource "xenorchestra_cloud_config" "metadataconfig" {
                                              name = "network data config"
                                              template = data.template_file.metadata.rendered
                                            }
                                            
                                            # VM creation
                                            
                                            resource "xenorchestra_vm" "vm" {
                                            #  cloud_config = templatefile("cloud_userdata_cfg.yaml", {
                                            #    hostname = var.vm_name_label
                                            #    domain   = var.vm_public_domain
                                            #  })
                                            #  cloud_network_config = templatefile("cloud_metadata_cfg.yaml", {
                                            #    hostname  = var.vm_name_label
                                            #    ip        = var.vm_public_ipv4_address
                                            #    prefixlen = var.vm_public_ipv4_prefixlen
                                            #    gateway   = var.vm_public_ipv4_gateway
                                            #  })
                                              cloud_network_config = xenorchestra_cloud_config.metadataconfig.template
                                              cloud_config = xenorchestra_cloud_config.userdataconfig.template
                                              memory_max = var.vm_memory_max
                                              cpus       = var.vm_cpus
                                              name_label = var.vm_name_label
                                              template   = data.xenorchestra_template.vm_template.id
                                            
                                              network {
                                                network_id = data.xenorchestra_network.public_vlanid.id
                                              }
                                            
                                            #  network {
                                            #    network_id = data.xenorchestra_network.private_vlanid.id
                                            #  }
                                            
                                              disk {
                                                sr_id      = data.xenorchestra_sr.sr.id
                                                name_label = var.vm_disk_name_label
                                                size       = var.vm_disk_size
                                              }
                                            }
                                            
                                            alfcruz-adm@local.hcpa.ufrgs.br@volt-ampere:~/terraform-provider-xenorchestra-intro/mkxcpvm2n$
                                            alfcruz-adm@local.hcpa.ufrgs.br@volt-ampere:~/terraform-provider-xenorchestra-intro/mkxcpvm2n$ cat cloud_userdata_cfg.yaml
                                            #cloud-config
                                            groups:
                                              - aghuse: [aghu]
                                            
                                            users:
                                              # seiseg and aghu / REDACTED | same public key
                                              - name: aghu
                                                uid: 1003
                                                gecos: AGHU Application Standard User
                                                passwd: <REDACTED>
                                                lock_passwd: true
                                                sudo: ['ALL=(ALL) NOPASSWD:ALL']
                                                groups: [aghuse]
                                                shell: /bin/bash
                                                homedir: /opt/aghu
                                                no_create_home: false
                                            
                                            packages:
                                              - tree
                                            
                                            runcmd:
                                              - sed -i -e "s/PARAMMYHOSTNAME/${hostname}/g" /etc/postfix/main.cf
                                              - sed -i -e "s/PARAMMYHOSTNAME/${hostname}/" /etc/puppetlabs/puppet/puppet.conf
                                              - /opt/ds_agent/dsa_control -r
                                              - /opt/ds_agent/dsa_control -a dsm://goodman.local.hcpa.ufrgs.br:4120/ "policyid:60"
                                              - systemctl disable networker
                                              -
                                            
                                            
                                            alfcruz-adm@local.hcpa.ufrgs.br@volt-ampere:~/terraform-provider-xenorchestra-intro/mkxcpvm2n$ cat cloud_metadata_cfg.yaml
                                            #cloud-config
                                            local-hostname: ${hostname}
                                            instance-id: ${hostname}
                                            network:
                                              version: 2
                                              ethernets:
                                                ens192:
                                                  dhcp4: false
                                                  addresses:
                                                    - ${ip}/${prefixlen}
                                                  gateway4: ${gateway}
                                                  nameservers:
                                                    search: [local.hcpa.ufrgs.br, hcpa]
                                                    addresses: [10.10.31.13, 10.10.31.14, 10.10.31.42]
                                            growpart:
                                              mode: auto
                                              devices: ['/dev/sda2']
                                              ignore_growroot_disabled: true
                                            wait-on-network:
                                              ipv4: true
                                            
                                            alfcruz-adm@local.hcpa.ufrgs.br@volt-ampere:~/terraform-provider-xenorchestra-intro/mkxcpvm2n$
                                            
                                            
                                            
                                            1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post