{"id":34,"date":"2017-10-30T17:56:26","date_gmt":"2017-10-30T17:56:26","guid":{"rendered":"https:\/\/nillsf.wordpress.com\/?p=34"},"modified":"2017-10-30T17:56:26","modified_gmt":"2017-10-30T17:56:26","slug":"terraform-and-azure","status":"publish","type":"post","link":"https:\/\/blog.nillsf.com\/index.php\/2017\/10\/30\/terraform-and-azure\/","title":{"rendered":"Terraform and Azure"},"content":{"rendered":"<p>Have you already played around with Terraform and Azure? Until today, I hadn&#8217;t and decided to try it out. A couple of weeks ago <a href=\"https:\/\/azure.microsoft.com\/en-us\/blog\/more-and-more-fun-with-terraform-on-azure\/\" target=\"_blank\" rel=\"noopener noreferrer\">Corey Sanders announced<\/a> progress with how Azure and Terraform are integrated; and below you can read on how I tried it out.<\/p>\n<h2>Setting up the infrastructure<\/h2>\n<p>Terraform is integrated into our Azure cloud shell, but I decided to set it up on a virtual machine (full installation):<br \/>\nFirst step is to create a VM to run Terraform on:<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-28\" src=\"\/wp-content\/uploads\/2017\/10\/tf1.png\" alt=\"TF1\" width=\"955\" height=\"910\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf1.png 955w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf1-300x286.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf1-768x732.png 768w\" sizes=\"auto, (max-width: 955px) 100vw, 955px\" \/><br \/>\nInstallation is as simple as downloading a zip file, extracting it and adding that path to your $PATH variable. Easy to do:<\/p>\n<blockquote><p>sudo apt-get install unzip -y<br \/>\nwget https:\/\/releases.hashicorp.com\/terraform\/0.10.8\/terraform_0.10.8_linux_amd64.zip<br \/>\nunzip terraform_0.10.8_linux_amd64.zip<br \/>\nrm terraform_0.10.8_linux_amd64.zip<br \/>\nmkdir bin<br \/>\nmv terraform bin\/terraform<br \/>\necho &#8220;PATH=$PATH:$HOME\/bin&#8221; &gt;&gt; .profile<br \/>\nsource .profile<\/p><\/blockquote>\n<p>Next step is to create a service principal that will have access to the azure APIs. We can create this in the portal, but let&#8217;s go ahead and do that via the CLI. First things first: install the CLI.<\/p>\n<blockquote><p>echo &#8220;deb https:\/\/packages.microsoft.com\/repos\/azure-cli\/ wheezy main&#8221; | \\<br \/>\nsudo tee \/etc\/apt\/sources.list.d\/azure-cli.list<br \/>\necho &#8220;deb [arch=amd64] https:\/\/packages.microsoft.com\/repos\/azure-cli\/ wheezy main&#8221; | \\<br \/>\nsudo tee \/etc\/apt\/sources.list.d\/azure-cli.list<br \/>\nsudo apt-key adv &#8211;keyserver packages.microsoft.com &#8211;recv-keys 52E16F86FEE04B979B07E28DB02C46DF417A0893<br \/>\nsudo apt-get install apt-transport-https<br \/>\nsudo apt-get update &amp;&amp; sudo apt-get install azure-cli<\/p><\/blockquote>\n<p>With the CLI installed, let&#8217;s login and create a service principal.<\/p>\n<blockquote><p>az login<br \/>\naz ad sp create-for-rbac &#8211;role=&#8221;Contributor&#8221; &#8211;scopes=&#8221;\/subscriptions\/*SUBCRIPTION ID GOES HERE*&#8221;<\/p><\/blockquote>\n<p>Something weird happened here for me, with a lot of retries happening on the creation of both the SP as on the role assignment. But it worked and returned me the appID and secret. Keep this secret &#8216;secret&#8217;, as it opens the doors to your Azure subscription!<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-29\" src=\"\/wp-content\/uploads\/2017\/10\/tf2.png\" alt=\"TF2\" width=\"825\" height=\"521\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf2.png 825w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf2-300x189.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf2-768x485.png 768w\" sizes=\"auto, (max-width: 825px) 100vw, 825px\" \/><br \/>\nNext step is to create the right environment variables for Terraform to use. I&#8217;ve added them to a neat little script, you could also make them part of your login scripts (.profile or .bash_rc).<\/p>\n<blockquote><p>#!\/bin\/sh<br \/>\necho &#8220;Setting environment variables for Terraform&#8221;<br \/>\nexport ARM_SUBSCRIPTION_ID=your_subscription_id<br \/>\nexport ARM_CLIENT_ID=your_appId<br \/>\nexport ARM_CLIENT_SECRET=your_password<br \/>\nexport ARM_TENANT_ID=your_tenant_id<\/p><\/blockquote>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-30\" src=\"\/wp-content\/uploads\/2017\/10\/tf3.png\" alt=\"TF3\" width=\"825\" height=\"521\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf3.png 825w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf3-300x189.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf3-768x485.png 768w\" sizes=\"auto, (max-width: 825px) 100vw, 825px\" \/><\/p>\n<h2>Creating a resource group<\/h2>\n<p>Next, we&#8217;ll create a resource group via TerraForm. Create an empty directory, with a file in there with the following content:<\/p>\n<blockquote><p>provider &#8220;azurerm&#8221; {<br \/>\n}<br \/>\nresource &#8220;azurerm_resource_group&#8221; &#8220;rg&#8221; {<br \/>\nname = &#8220;TF_created_RG&#8221;<br \/>\nlocation = &#8220;westeurope&#8221;<br \/>\n}<\/p><\/blockquote>\n<p>Next steps are to (1) initialise Terraform (2) test our deployment (3) deploy it.<\/p>\n<blockquote><p>terraform init<br \/>\nterraform plan<br \/>\nterraform apply<\/p><\/blockquote>\n<p>And behold: we have a resource group!<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-31\" src=\"\/wp-content\/uploads\/2017\/10\/tf4.png\" alt=\"TF4\" width=\"1541\" height=\"390\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf4.png 1541w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf4-300x76.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf4-768x194.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf4-1024x259.png 1024w\" sizes=\"auto, (max-width: 1541px) 100vw, 1541px\" \/><\/p>\n<h2>Creating a virtual machine (VM)<\/h2>\n<p>And now, let&#8217;s create a VM. I won&#8217;t go in all the details, but this documentation article explains more about it: <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/virtual-machines\/linux\/terraform-create-complete-vm\">https:\/\/docs.microsoft.com\/en-us\/azure\/virtual-machines\/linux\/terraform-create-complete-vm<\/a><br \/>\nI created the following terraform file and followed the same steps as before:<\/p>\n<blockquote><p>provider &#8220;azurerm&#8221; {<br \/>\n}<br \/>\nresource &#8220;azurerm_resource_group&#8221; &#8220;rg&#8221; {<br \/>\nname = &#8220;TF_created_RG&#8221;<br \/>\nlocation = &#8220;westeurope&#8221;<br \/>\n}<br \/>\nresource &#8220;azurerm_virtual_network&#8221; &#8220;myterraformnetwork&#8221; {<br \/>\nname\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;TF_Vnet&#8221;<br \/>\naddress_space\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = [&#8220;10.0.0.0\/16&#8221;]<br \/>\nlocation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;${azurerm_resource_group.rg.location}&#8221;<br \/>\nresource_group_name = &#8220;${azurerm_resource_group.rg.name}&#8221;<br \/>\ntags {<br \/>\nenvironment = &#8220;Terraform Demo&#8221;<br \/>\n}<br \/>\n}<br \/>\nresource &#8220;azurerm_subnet&#8221; &#8220;myterraformsubnet&#8221; {<br \/>\nname\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;TF_Subnet&#8221;<br \/>\nresource_group_name\u00a0 = &#8220;${azurerm_resource_group.rg.name}&#8221;<br \/>\nvirtual_network_name = &#8220;${azurerm_virtual_network.myterraformnetwork.name}&#8221;<br \/>\naddress_prefix\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;10.0.2.0\/24&#8221;<br \/>\n}<br \/>\nresource &#8220;azurerm_public_ip&#8221; &#8220;myterraformpublicip&#8221; {<br \/>\nname\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;TF_publicIP&#8221;<br \/>\nlocation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;${azurerm_resource_group.rg.location}&#8221;<br \/>\nresource_group_name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;${azurerm_resource_group.rg.name}&#8221;<br \/>\npublic_ip_address_allocation = &#8220;dynamic&#8221;<br \/>\ntags {<br \/>\nenvironment = &#8220;Terraform Demo&#8221;<br \/>\n}<br \/>\n}<br \/>\nresource &#8220;azurerm_network_interface&#8221; &#8220;myterraformnic&#8221; {<br \/>\nname\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;TF_NIC&#8221;<br \/>\nlocation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;${azurerm_resource_group.rg.location}&#8221;<br \/>\nresource_group_name = &#8220;${azurerm_resource_group.rg.name}&#8221;<br \/>\nip_configuration {<br \/>\nname\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;TF_NicConfiguration&#8221;<br \/>\nsubnet_id\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;${azurerm_subnet.myterraformsubnet.id}&#8221;<br \/>\nprivate_ip_address_allocation = &#8220;dynamic&#8221;<br \/>\npublic_ip_address_id\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;${azurerm_public_ip.myterraformpublicip.id}&#8221;<br \/>\n}<br \/>\ntags {<br \/>\nenvironment = &#8220;Terraform Demo&#8221;<br \/>\n}<br \/>\n}<br \/>\nresource &#8220;random_id&#8221; &#8220;randomId&#8221; {<br \/>\nkeepers = {<br \/>\n# Generate a new ID only when a new resource group is defined<br \/>\nresource_group = &#8220;${azurerm_resource_group.rg.name}&#8221;<br \/>\n}<br \/>\nbyte_length = 8<br \/>\n}<br \/>\nresource &#8220;azurerm_storage_account&#8221; &#8220;mystorageaccount&#8221; {<br \/>\nname\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;diag${random_id.randomId.hex}&#8221;<br \/>\nresource_group_name = &#8220;${azurerm_resource_group.rg.name}&#8221;<br \/>\nlocation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;${azurerm_resource_group.rg.location}&#8221;<br \/>\naccount_replication_type = &#8220;LRS&#8221;<br \/>\naccount_tier = &#8220;Standard&#8221;<br \/>\ntags {<br \/>\nenvironment = &#8220;Terraform Demo&#8221;<br \/>\n}<br \/>\n}<br \/>\nresource &#8220;azurerm_virtual_machine&#8221; &#8220;myterraformvm&#8221; {<br \/>\nname\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;myVM&#8221;<br \/>\nlocation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;${azurerm_resource_group.rg.location}&#8221;<br \/>\nresource_group_name\u00a0\u00a0 = &#8220;${azurerm_resource_group.rg.name}&#8221;<br \/>\nnetwork_interface_ids = [&#8220;${azurerm_network_interface.myterraformnic.id}&#8221;]<br \/>\nvm_size\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;Standard_DS1_v2&#8221;<br \/>\nstorage_os_disk {<br \/>\nname\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;myOsDisk&#8221;<br \/>\ncaching\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;ReadWrite&#8221;<br \/>\ncreate_option\u00a0\u00a0\u00a0\u00a0 = &#8220;FromImage&#8221;<br \/>\nmanaged_disk_type = &#8220;Premium_LRS&#8221;<br \/>\n}<br \/>\nstorage_image_reference {<br \/>\npublisher = &#8220;Canonical&#8221;<br \/>\noffer\u00a0\u00a0\u00a0\u00a0 = &#8220;UbuntuServer&#8221;<br \/>\nsku\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = &#8220;16.04.0-LTS&#8221;<br \/>\nversion\u00a0\u00a0 = &#8220;latest&#8221;<br \/>\n}<br \/>\nos_profile {<br \/>\ncomputer_name\u00a0 = &#8220;TFvm&#8221;<br \/>\nadmin_username = &#8220;nilfranadmin&#8221;<br \/>\n}<br \/>\nos_profile_linux_config {<br \/>\ndisable_password_authentication = true<br \/>\nssh_keys {<br \/>\npath\u00a0\u00a0\u00a0\u00a0 = &#8220;\/home\/nilfranadmin\/.ssh\/authorized_keys&#8221;<br \/>\nkey_data = &#8220;ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAslS5LnoCJlj8OE4VncUK2iP6YhVT\/RmeNkvP3VTd\/GbiZd384wrD0rzr3MwEgMm4ZkjUQno54x+bpRhIFDha4Kj89cs7LwuPHZSkXLF+aVydxy2nu464TmflnhVVW71wLE9E3bCUxmh5+IZ3sJ8is2XQMuC1IHiIoEMFc+buMTG+kVc3f+VaJ5ZT+bFPjqs816YBPTSZRmUjzfwRcLIRXvlVxlFsMckhSTa7xCCxunsGKITOnqmlk\/vIWr\/bKfev6RD+qV8DFquM0zxquwcSv5ERXE384m6ESJ\/YJ4IN5P14CDWT3pdZtwM1jOaL\/zPyMHbamk5iTPLfuPao740plQ==&#8221;<br \/>\n}<br \/>\n}<br \/>\nboot_diagnostics {<br \/>\nenabled\u00a0\u00a0\u00a0\u00a0 = &#8220;true&#8221;<br \/>\nstorage_uri = &#8220;${azurerm_storage_account.mystorageaccount.primary_blob_endpoint}&#8221;<br \/>\n}<br \/>\ntags {<br \/>\nenvironment = &#8220;Terraform Demo&#8221;<br \/>\n}<br \/>\n}<\/p><\/blockquote>\n<p>This created my VM (with all the surrounding elements):<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-32\" src=\"\/wp-content\/uploads\/2017\/10\/tf5.png\" alt=\"TF5\" width=\"932\" height=\"315\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf5.png 932w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf5-300x101.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf5-768x260.png 768w\" sizes=\"auto, (max-width: 932px) 100vw, 932px\" \/><\/p>\n<h2>What I learned today:<\/h2>\n<ul>\n<li>How to use Terraform to create Azure resources.<\/li>\n<li>Although Terraform is cloud agnostic, the drivers to each cloud are specific. You can&#8217;t &#8216;simply&#8217; swap a parameter &#8216;AWS&#8217; to &#8216;Azure&#8217; and re-use the same template. #I didn&#8217;t know that before. I was mistakenly thinking that within Terraform you created a &#8216;unbranded&#8217; resources; which got translated to the right provider. I was wrong.<\/li>\n<li>The syntax is surprisingly simple and straightforward, if you understand what needs to happen under the covers (aka if you know what you need to create on Azure).<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Have you already played around with Terraform and Azure? Until today, I hadn&#8217;t and decided to try it out. A couple of weeks ago Corey Sanders announced progress with how Azure and Terraform are integrated; and below you can read on how I tried it out.<\/p>\n","protected":false},"author":1,"featured_media":32,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[2,3,5],"tags":[],"class_list":["post-34","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure","category-devops","category-open-source"],"jetpack_featured_media_url":"https:\/\/nillsfblog.blob.core.windows.net\/media\/2017\/10\/tf5.png","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/34","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/comments?post=34"}],"version-history":[{"count":0,"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/34\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/media\/32"}],"wp:attachment":[{"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/media?parent=34"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/categories?post=34"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/tags?post=34"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}