{"id":3659,"date":"2023-02-11T16:26:10","date_gmt":"2023-02-11T16:26:10","guid":{"rendered":"https:\/\/geekmungus.co.uk\/?p=3659"},"modified":"2023-02-11T16:26:11","modified_gmt":"2023-02-11T16:26:11","slug":"terraform-basics-with-vmware","status":"publish","type":"post","link":"https:\/\/geekmungus.co.uk\/?p=3659","title":{"rendered":"Terraform Basics (with VMware)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Hashicorp&#8217;s Terraform is an Infrastructure as Code (IaC) declarative language that allows you to define the existence and state of objects within your infrastructure and then publish this new state to your infrastructure. Terraform is agnostic of platform, you can use a wide range of &#8220;providers&#8221; to manage a number of different infrastructure components from a single Terraform script.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this simple example, i&#8217;ll show how to create a template and then deploy two virtual machines on VMware using this template using the Terraform tool. The example assumes that you have an Ubuntu 20.04 client machine that you&#8217;ll install Terraform onto, then a small VMware environment which has VMware vCenter and at least one ESXi host.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Install Terraform<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Make sure the system is up-to-date and install these packages which are needed for further steps:<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt-get update &amp;&amp; sudo apt-get install -y gnupg software-properties-common curl<\/code><\/pre>\n\n\n\n<ol class=\"wp-block-list\" start=\"2\">\n<li>Next add the Hashicorp GPG key needed by the repository:<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -fsSL https:\/\/apt.releases.hashicorp.com\/gpg | sudo apt-key add -<\/code><\/pre>\n\n\n\n<ol class=\"wp-block-list\" start=\"3\">\n<li>Then, add the official repository for HashiCorp for Linux:<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt-add-repository \"deb &#91;arch=amd64] https:\/\/apt.releases.hashicorp.com $(lsb_release -cs) main\"<\/code><\/pre>\n\n\n\n<ol class=\"wp-block-list\" start=\"4\">\n<li>Now that we have added the repo to the list, let\u2019s update to add the relevant repository content:<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt update<\/code><\/pre>\n\n\n\n<ol class=\"wp-block-list\" start=\"5\">\n<li>Once update, run this command to install Terraform:<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt install terraform<\/code><\/pre>\n\n\n\n<ol class=\"wp-block-list\" start=\"6\">\n<li>Run terraform to ensure its been installed correctly.<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>terraform<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You should see the help guide, showing the available commands, if that is the case Terraform has been successfully installed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prepare VMware vCenter Permissions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The next step is to create an account within VMware vCenter that you&#8217;ll use to make changes to your VMware environment. If you are testing this on a production environment is recommended to restrict the permissions this account has to change your VMware environment.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Create &#8220;variables.tf&#8221; Terraform File<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The first file we need to create is the &#8220;variables.tf&#8221; file, this defines some variables, in this case a file that defines the username, password and vCenter Server address.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>variable \"username\" {\n  default       = \"terraform-admin@domain.local\"\n}\n\nvariable \"password\" {\n  default       = \"mypassword\"\n}\n\nvariable \"vcenter\" {\n  default       = \"vcenter.domain.com\"\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">These variables are important because we&#8217;ll refer to them from the main.tf file shortly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Create Ubuntu VMware Template<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I&#8217;m assuming you know how to create a VMware VM template, the below gives a great simple guide of how to do this<br>https:\/\/learn.microsoft.com\/en-us\/azure\/cloud-adoption-framework\/manage\/hybrid\/server\/best-practices\/vmware-ubuntu-template<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A very brief procedure is to install Ubuntu (20.04) onto a VM, so you have a bootable Ubuntu machine, with whatever extra packages you require. You then run the following commands to prepare the machine for being turned into a template.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt-get update\r\nsudo apt-get upgrade -y\r\n\r\nsudo sed -i 's\/preserve_hostname: false\/preserve_hostname: true\/g' \/etc\/cloud\/cloud.cfg\r\nsudo truncate -s0 \/etc\/hostname\r\nsudo hostnamectl set-hostname localhost\r\n\r\nsudo rm \/etc\/netplan\/00-installer-config.yaml\r\n\r\ncat \/dev\/null > ~\/.bash_history &amp;&amp; history -c\r\nsudo shutdown now<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Within VMware vCenter now find the VM you&#8217;ve just created, right click and select &#8220;Convert to Template&#8221;, once converted we&#8217;re ready to build our configuration and attempt to deploy some VMs (using the template) by declaring them within Terraform.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Create &#8220;main.tf&#8221; Terraform File<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Create the &#8220;main.tf&#8221; file in the same directory as your variables.tf you created earlier. You notice that this file refers to the variables you declared in the variables.tf file earlier.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You should change the Datacenter name, datastore names etc. to what <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>provider \"vsphere\" {\r\n  user                  = var.username\r\n  password              = var.password\r\n  vsphere_server        = var.vcenter\r\n  allow_unverified_ssl  = true\r\n}\r\n\r\n# A \"data\" resource is a readonly resource, something Terraform knows about but won't be changing.\r\ndata \"vsphere_datacenter\" \"datacenter\" {\r\n  name = \"MyDatacenter\"\r\n}\r\n\r\ndata \"vsphere_datastore\" \"datastore\" {\r\n  name          = \"datastore1\"\r\n  datacenter_id = data.vsphere_datacenter.datacenter.id\r\n}\r\n\r\ndata \"vsphere_compute_cluster\" \"cluster\" {\r\n  name          = \"My Cluster\"\r\n  datacenter_id = data.vsphere_datacenter.datacenter.id\r\n}\r\n\r\ndata \"vsphere_network\" \"network\" {\r\n  name          = \"my-network-pg\"\r\n  datacenter_id = data.vsphere_datacenter.datacenter.id\r\n}\r\n\r\ndata \"vsphere_virtual_machine\" \"template\" {\r\n  name          = \"ubuntu2204\"\r\n  datacenter_id = data.vsphere_datacenter.datacenter.id\r\n}\r\n\r\n\r\nresource \"vsphere_virtual_machine\" \"bill\" {\r\n  name             = \"bill\"\r\n  folder           = \"Production\"\r\n  resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id\r\n  datastore_id     = data.vsphere_datastore.datastore.id\r\n  num_cpus         = 2\r\n  memory           = 2048\r\n  guest_id         = data.vsphere_virtual_machine.template.guest_id\r\n  scsi_type        = data.vsphere_virtual_machine.template.scsi_type\r\n  network_interface {\r\n    network_id = data.vsphere_network.network.id\r\n    adapter_type = data.vsphere_virtual_machine.template.network_interface_types&#91;0]\r\n  }\r\n  disk {\r\n    label            = \"disk0\"\r\n    size             = data.vsphere_virtual_machine.template.disks.0.size\r\n    thin_provisioned = data.vsphere_virtual_machine.template.disks.0.thin_provisioned\r\n  }\r\n  clone {\r\n    template_uuid    = data.vsphere_virtual_machine.template.id\r\n    customize {\r\n        linux_options {\r\n          host_name = \"bill\"\r\n          domain   = \"domain.com\"\r\n        }\r\n        network_interface {\r\n          ipv4_address = \"172.16.0.10\"\r\n          ipv4_netmask = 24\r\n        }\r\n        ipv4_gateway   = \"172.16.0.1\"\r\n        dns_server_list = &#91;\"172.16.200.10\",\"172.16.200.11\"]\r\n        dns_suffix_list = &#91;\"internal.domain.com\",\"domain.com\"]\r\n    }\r\n  }\r\n}\r\n\r\nresource \"vsphere_virtual_machine\" \"ben\" {\r\n  name             = \"ben\"\r\n  folder           = \"Production\"\r\n  resource_pool_id = data.vsphere_compute_cluster.cluster.resource_pool_id\r\n  datastore_id     = data.vsphere_datastore.datastore.id\r\n  num_cpus         = 2\r\n  memory           = 2048\r\n  guest_id         = data.vsphere_virtual_machine.template.guest_id\r\n  scsi_type        = data.vsphere_virtual_machine.template.scsi_type\r\n  network_interface {\r\n    network_id = data.vsphere_network.network.id\r\n    adapter_type = data.vsphere_virtual_machine.template.network_interface_types&#91;0]\r\n  }\r\n  disk {\r\n    label            = \"disk0\"\r\n    size             = data.vsphere_virtual_machine.template.disks.0.size\r\n    thin_provisioned = data.vsphere_virtual_machine.template.disks.0.thin_provisioned\r\n  }\r\n  clone {\r\n    template_uuid    = data.vsphere_virtual_machine.template.id\r\n    customize {\r\n        linux_options {\r\n          host_name = \"ben\"\r\n          domain   = \"internal.sanger.ac.uk\"\r\n        }\r\n        network_interface {\r\n          ipv4_address = \"172.16.0.11\"\r\n          ipv4_netmask = 24\r\n        }\r\n        ipv4_gateway   = \"172.16.0.1\"\n        dns_server_list = &#91;\"172.16.200.10\",\"172.16.200.11\"]\n        dns_suffix_list = &#91;\"internal.domain.com\",\"domain.com\"]\r\n    }\r\n  }\r\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Before running Terraform operations, download plugins using the terraform init command.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>terraform init<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This will then download the &#8220;provider&#8221; packages and intialise the Terraform environment ready for use with in this case vSphere (VMware). You can use various other providers the same way so its there ready for use.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Running Terraform and Observing the Results<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Now we are ready to run Terraform, we&#8217;re going to run a plan first just to see what Terraform thinks it needs to do.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>terraform plan<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Check the output, you should see it says it will create two new VMs. If you are happy run the following to apply the configuration.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>terraform apply<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">After a few seconds you should see the VMs have been deployed, then powered on, they&#8217;ll boot up, which means they run the &#8220;cloud-init&#8221; scripts, this will set the hostname, apply the IP network configurations.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After a few minutes you should have two working VMs deployed, in this example: Bill and Ben.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Destroy it All<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you want to remove what you&#8217;ve deployed, you can run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>terraform destroy<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The thing to remember is that Terraform only manages (changes) what it knows about (in it&#8217;s state). Our script only declared two VMs, so this destroy command will only destroy these two VMs.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Hopefully this gives you a very simple introduction to what you can do with Terraform. Its very extensible, you can add various providers and drive different parts of your infrastructure all from one script.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example, you could have Terraform, create DNS records within your DNS server for the VMs automatically, then deploy the VMs into a ready state, all in one fell swoop.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Additional Information<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/garyflynn.com\/post\/create-your-first-vsphere-terraform-configuration\/\">https:\/\/garyflynn.com\/post\/create-your-first-vsphere-terraform-configuration\/<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/blogs.vmware.com\/cloud\/2019\/11\/19\/infrastructure-code-terraform-vmware-vmware-cloud-aws\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/blogs.vmware.com\/cloud\/2019\/11\/19\/infrastructure-code-terraform-vmware-vmware-cloud-aws<\/a>\/<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/diodonfrost\/terraform-vsphere-examples\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/diodonfrost\/terraform-vsphere-examples<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/thenewstack.io\/install-terraform-and-the-gaia-web-ui-on-ubuntu-server-22-04\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/thenewstack.io\/install-terraform-and-the-gaia-web-ui-on-ubuntu-server-22-04\/<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a rel=\"noreferrer noopener\" href=\"https:\/\/unsplash.com\/@wildmax\" target=\"_blank\">Image Attribution<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hashicorp&#8217;s Terraform is an Infrastructure as Code (IaC) declarative language that allows you to define the existence and state of objects within your infrastructure and then publish this new state to your infrastructure. Terraform is agnostic of platform, you can use a wide range of &#8220;providers&#8221; to manage a number of different infrastructure components from &#8230; <a title=\"Terraform Basics (with VMware)\" class=\"read-more\" href=\"https:\/\/geekmungus.co.uk\/?p=3659\" aria-label=\"Read more about Terraform Basics (with VMware)\">Read more<\/a><\/p>\n","protected":false},"author":4,"featured_media":1747,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[25],"tags":[],"class_list":["post-3659","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-vmware"],"_links":{"self":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/3659","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3659"}],"version-history":[{"count":6,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/3659\/revisions"}],"predecessor-version":[{"id":3665,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/3659\/revisions\/3665"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/media\/1747"}],"wp:attachment":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3659"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3659"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3659"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}