{"id":73511,"date":"2018-05-22T11:00:50","date_gmt":"2018-05-22T18:00:50","guid":{"rendered":""},"modified":"2025-06-30T03:16:06","modified_gmt":"2025-06-30T10:16:06","slug":"cicd-azure-terraform-ansible-vsts-java-springboot-app","status":"publish","type":"post","link":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/","title":{"rendered":"Tutorial: CI\/CD for Azure using Terraform, Ansible and VSTS"},"content":{"rendered":"\n<p>This is part 1 of a 2-part series demonstrating how to continuously build and deploy Azure infrastructure for the applications running on Azure. The first article will show how open source tools, such as Terraform and Ansible, can be leveraged to implement Infrastructure as Code. The second article in the series will enhance the infrastructure deployment to build immutable infrastructure for the applications and adding Packer into the set of tools.<\/p>\n\n\n\n<p>In part 1, we will walk though how to continually build and deploy a Java Spring Boot application and its required infrastructure and middleware using <a href=\"https:\/\/www.visualstudio.com\/team-services\/\">Visual Studio Team Services<\/a>. We will apply software development practices to infrastructure build and configuration. To demonstrate Infrastructure as a Code principle we will use Terraform to codify and provision infrastructure, and Ansible to automate configuration and middleware.<\/p>\n\n\n\n<p>Here is a picture of the flow:<\/p>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow-1024x364.webp\" alt=\"Picture of the flow\" class=\"wp-image-73515 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow.webp 1428w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow-300x107.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow-1024x364.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow-768x273.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow-330x117.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow-800x284.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow-400x142.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow.png 1428w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow-300x107.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow-1024x364.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow-768x273.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow-330x117.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow-800x284.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow-400x142.png 400w\"><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Code or configuration change is committed to Git<\/li>\n\n\n\n<li>VSTS Build builds and packages Spring Boot application using Gradle<\/li>\n\n\n\n<li>VSTS Release provisions Infrastructure using Terraform<\/li>\n\n\n\n<li>VSTS Release configures JDK, Tomcat and application on the provisioned servers<\/li>\n<\/ul>\n\n\n\n<p>Prerequisites:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Configure custom VSTS agent with required tools, as described in this blog post: <a href=\"https:\/\/open.microsoft.com\/2018\/05\/22\/how-to-create-vsts-agent-azure-aci-terraform\/\">How to create a custom VSTS agent on Azure ACI with Terraform<\/a>.<\/li>\n\n\n\n<li>Service Principal with Contributor access to the subscription. Refer to <a href=\"https:\/\/docs.microsoft.com\/en-us\/cli\/azure\/create-an-azure-service-principal-azure-cli?view=azure-cli-latest\">this documentation<\/a> for instructions.<\/li>\n\n\n\n<li>Storage account and container to save Terraform state in (update \u201cbackend.tfvars\u201d with the names). Terraform must store state about your managed infrastructure and configuration. This state is used by Terraform to map real world resources to your configuration, keep track of metadata, and to improve performance for large infrastructures.<\/li>\n\n\n\n<li>Ansible task extension installed from <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=ms-vscs-rm.vss-services-ansible\">VSTS marketplace<\/a>.<\/li>\n\n\n\n<li>SSH Key for Ansible connections to VMs.<\/li>\n\n\n\n<li>All code and pipelines for this article could be found on <a href=\"https:\/\/github.com\/lenisha\/vsts-terraform-ansible\">GitHub<\/a>.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"spring-boot-application-build\">Spring Boot Application Build<\/h2>\n\n\n\n<p>In this example, we first build and package a Spring Boot application using Gradle. You can import the full build definition from <a href=\"https:\/\/github.com\/lenisha\/vsts-terraform-ansible\">GitHub<\/a> repository or create a Java Gradle project from scratch by following steps provided in documentation &#8220;<a href=\"https:\/\/docs.microsoft.com\/en-us\/vsts\/build-release\/apps\/java\/build-gradle?view=tfs-2017&amp;tabs=vsts\">Build your Java app with Gradle<\/a>.&#8221;<br>Here is outline of the steps and commands customizations:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Clone GitHub repo from this example or import to VSTS<\/li>\n\n\n\n<li>Create a build definition (Build &amp; Release tab &gt; Builds)<\/li>\n\n\n\n<li>Search and use \u201cGradle\u201d definition.<br>In the repository tab of build definition make sure the repository selected is the one where you pushed (Git).<br>It will create the template of the steps required.<\/li>\n\n\n\n<li>In \u201dCopy Files\u201d &#8211; customize the step to copy built war file and \u201ciac\u201d directory with configuration<\/li>\n<\/ol>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-1024x433.webp\" alt=\"Screenshot 1 - Spring Boot Application Build\" class=\"wp-image-73517 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2.webp 2099w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-300x127.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-1024x433.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-768x325.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-1536x650.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-2048x866.webp 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-330x140.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-800x338.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-400x169.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2.png 2099w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-300x127.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-1024x433.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-768x325.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-1536x650.png 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-2048x866.png 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-330x140.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-800x338.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-2-400x169.png 400w\"><\/figure>\n\n\n\n<p>On the Triggers tab, enable continuous integration (CI). This tells the system to queue a build whenever new code is committed. Save and Queue the build.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"infrastructure-provisioning\">Infrastructure Provisioning<\/h2>\n\n\n\n<p>Release is built on the VSTS Agent that has the required Terraform and Ansible installed.<\/p>\n\n\n\n<p>Terraform versions, plans and build infrastructure. Ansible automation provides agentless way of managing servers. All it requires is SSH connection and python installed. We will configure VSTS agent with the ssh key that is provided by Terraform during provisioning VMs on Azure and by Ansible to communicate to provisioned servers.<\/p>\n\n\n\n<p>For Ansible to be able to communicate to VMs it has to know server IPs, provided to it in the form of inventory file. Once Terraform completes provisioning, we will output servers IPs into a file which is used by Ansible.<\/p>\n\n\n\n<p>Here is the Release pipeline definition (it could be imported from GitHub as well):<\/p>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-1024x367.webp\" alt=\"Screenshot 2 - Infrastructure Provisioning\" class=\"wp-image-73519 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4.webp 2236w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-300x108.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-1024x367.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-768x275.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-1536x551.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-2048x735.webp 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-330x118.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-800x287.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-400x143.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4.png 2236w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-300x108.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-1024x367.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-768x275.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-1536x551.png 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-2048x735.png 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-330x118.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-800x287.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-4-400x143.png 400w\"><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Start by defining Empty Release Definition, and link the build prepared above as an artifact.<\/li>\n\n\n\n<li>Use custom VSTS Agent from \u201cACI-Pool\u201d<\/li>\n\n\n\n<li>Define Variable Group with environment variables (<a href=\"https:\/\/docs.microsoft.com\/en-us\/vsts\/pipelines\/library\/variable-groups?view=vsts\">see documentation<\/a>) that provide connectivity to subscription:\n<ul class=\"wp-block-list\">\n<li>ARM_SUBSCRIPTION_ID, ARM_TENANT_ID : Subscription (run ` az account list` in Azure CLI to find out tenantId and subscriptionId )<\/li>\n\n\n\n<li>ARM_CLIENT_ID, ARM_CLIENT_SECRET: Service Principal is and password obtained when creating SP<\/li>\n\n\n\n<li>ARM_ACCESS_KEY: Storage account access key<\/li>\n\n\n\n<li>SSH_PUB_KEY: Public SSH Key (keypair generated `ssh-keygen -t rsa -b 4096 -C your_email@example.com` )<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5-1024x479.webp\" alt=\"Screenshot 3 - Infrastructure Provisioning\" class=\"wp-image-73520 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5.webp 1836w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5-300x140.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5-1024x479.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5-768x359.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5-1536x719.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5-330x154.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5-800x374.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5-400x187.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5.png 1836w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5-300x140.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5-1024x479.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5-768x359.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5-1536x719.png 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5-330x154.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5-800x374.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-5-400x187.png 400w\"><\/figure>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Add the following steps in the Tasks pipeline:<\/li>\n<\/ol>\n\n\n\n<p>a. Install SSH Key \u2013 Installs SSH key on the agent. Add public (SSH_PUB_KEY env variable) and private part of the previously generated keypair. And set Known hosts entry to \u201cdefault.\u201d<\/p>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-SSH-key-image-1024x564.webp\" alt=\"Screenshot 4 - Infrastructure Provisioning\" class=\"wp-image-73584 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-SSH-key-image.webp 1292w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-SSH-key-image-300x165.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-SSH-key-image-1024x564.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-SSH-key-image-768x423.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-SSH-key-image-330x182.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-SSH-key-image-800x441.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-SSH-key-image-400x220.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-SSH-key-image.jpg\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-SSH-key-image.jpg 1292w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-SSH-key-image-300x165.jpg 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-SSH-key-image-1024x564.jpg 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-SSH-key-image-768x423.jpg 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-SSH-key-image-330x182.jpg 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-SSH-key-image-800x441.jpg 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-SSH-key-image-400x220.jpg 400w\"><\/figure>\n\n\n\n<p>b. Shell Script \u2013 Terraform Init \u2013 point to Terraform init.sh script and pass environment variables $(ARM_CLIENT_ID) $(ARM_CLIENT_SECRET) $(ARM_SUBSCRIPTION_ID) $(ARM_TENANT_ID) $(ARM_ACCESS_KEY)<\/p>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-1024x465.webp\" alt=\"Screenshot 5 - Infrastructure Provisioning\" class=\"wp-image-73521 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6.webp 2299w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-300x136.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-1024x465.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-768x348.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-1536x697.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-2048x929.webp 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-330x150.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-800x363.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-400x181.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6.png 2299w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-300x136.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-1024x465.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-768x348.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-1536x697.png 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-2048x929.png 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-330x150.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-800x363.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-6-400x181.png 400w\"><\/figure>\n\n\n\n<p>Terraform must initialize Azure Resource provider and configured backend for keeping the state (Azure storage in this example) before the use. Here is the snippet doing it from our Terraform template:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; auto-links: false; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\nterraform {\n required_version = \">= 0.11\"\nbackend \"azurerm\" {}\n }\n# Configure the Microsoft Azure Provider\n provider \"azurerm\" {}\n<\/pre><\/div>\n\n\n<p>Terraform initialization can be done by simply running \u201cterraform init\u201d command.<\/p>\n\n\n\n<p>To avoid hard coding backend storage in terraform template, we are using <a href=\"https:\/\/www.terraform.io\/docs\/backends\/config.html#partial-configuration\">partial configuration<\/a> and providing the required backend configuration in variables file &#8211; \u201cbackend.tfvars\u201d. Here is a configuration example that uses Storage account we created as part of prerequisites:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; auto-links: false; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\nstorage_account_name = \"<unique storage accountname>\"\ncontainer_name = \"terraform-state\"\nkey = \"demo-java.terraform.tfstate\"\n<\/pre><\/div>\n\n\n<p class=\"lang:js decode:true\">To initialize Terraform shell script will run init command with provided backend configuration:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; auto-links: false; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\n#!\/bin\/bash\nterraform init -backend-config=backend.tfvars\n<\/pre><\/div>\n\n\n<p>Upon successful run it will have following output indication terraform has been initialized.<\/p>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-1024x630.webp\" alt=\"Screenshot 6 - Infrastructure Provisioning\" class=\"wp-image-73522 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets.webp 2261w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-300x184.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-1024x630.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-768x472.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-1536x944.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-2048x1259.webp 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-330x203.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-800x492.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-400x246.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets.png 2261w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-300x184.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-1024x630.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-768x472.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-1536x944.png 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-2048x1259.png 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-330x203.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-800x492.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-7_after-the-3-snipets-400x246.png 400w\"><\/figure>\n\n\n\n<p>c. Shell Script \u2013 Terraform apply<br>Terraform apply will apply the changes required to reach the desired state of the configuration as defined by \u201cmain.tf\u201d Add &nbsp;$(SSH_PUB_KEY) for Terraform to provision VM\u2019s with it.<\/p>\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-shell-script-image-1024x290.webp\" alt=\"Screenshot 7 - Infrastructure Provisioning\" class=\"wp-image-73585 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-shell-script-image-1024x290.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-shell-script-image-300x85.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-shell-script-image-768x217.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-shell-script-image-330x93.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-shell-script-image-800x226.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-shell-script-image-400x113.webp 400w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-shell-script-image.webp 1364w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_new-shell-script-image-1024x290.webp\"><\/figure>\n\n\n\n<p>Terraform generates an execution plan describing what it will do to reach the desired state, and then executes it to build the described infrastructure. As the configuration changes, Terraform is able to determine what changed and create incremental execution plans that can be applied.<\/p>\n\n\n\n<p>In the example below, Terraform detected that some changes are required in the infrastructure:<\/p>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9-1024x598.webp\" alt=\"Screenshot 8 - Infrastructure Provisioning\" class=\"wp-image-73524 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9.webp 1873w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9-300x175.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9-1024x598.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9-768x449.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9-1536x897.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9-330x193.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9-800x467.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9-400x234.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9.png 1873w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9-300x175.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9-1024x598.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9-768x449.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9-1536x897.png 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9-330x193.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9-800x467.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-9-400x234.png 400w\"><\/figure>\n\n\n\n<p>The shell file executes terraform build and generates inventory file with the details of the provisioned VMs. Ansible will use it to configure the application.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; auto-links: false; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\nterraform apply -auto-approve\nexport vmss_ip=$(terraform output vm_ip)\n echo \"host1 ansible_ssh_port=50000 ansible_ssh_host=$vmss_ip\" > inventory\n echo \"host2 ansible_port=50001 ansible_ssh_host=$vmss_ip\" >> inventory\n<\/pre><\/div>\n\n\n<p>The full Terraform template can be found on <a href=\"https:\/\/github.com\/lenisha\/vsts-terraform-ansible\/tree\/master\/iac\/terraform\/azure\">GitHub here<\/a>.<\/p>\n\n\n\n<p>It provisions resource group, virtual network, subnet, public IP, load balancer and NAT rules and VM availability set.<\/p>\n\n\n\n<p>For example, here is the VM resource template:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; auto-links: false; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\nresource \"azurerm_virtual_machine\" \"vm\" {\n name = \"vm${count.index}\"\n location = \"${azurerm_resource_group.rg.location}\"\n resource_group_name = \"${azurerm_resource_group.rg.name}\"\n availability_set_id = \"${azurerm_availability_set.avset.id}\"\n network_interface_ids = [\"${element(azurerm_network_interface.nic.*.id, count.index)}\"]\n count = 2\n vm_size = \"Standard_D1\"\nstorage_os_disk {\n name = \"osdisk${count.index}\"\n create_option = \"FromImage\"\n }\nstorage_image_reference {\n publisher = \"RedHat\"\n offer = \"RHEL\"\n sku = \"7.3\"\n version = \"latest\"\n }\nos_profile {\n computer_name = \"myvm\"\n admin_username = \"azureuser\"\n admin_password = \"xxxx\"\n }\nos_profile_linux_config {\n disable_password_authentication = true\nssh_keys {\n path = \"\/home\/azureuser\/.ssh\/authorized_keys\"\n key_data = \"xxxx\"\n }\n }\ntags {\n environment = \"Terraform Demo\"\n }\n }\n<\/pre><\/div>\n\n\n<p>Template defines the following output variables that are used by pipeline files:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; auto-links: false; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\noutput \"vm_ip\" {\n value = \"${azurerm_public_ip.demo_public_ip.fqdn}\"\n }\noutput \"vm_dns\" {\n value = \"http:\/\/${azurerm_public_ip.demo_public_ip.fqdn}\"\n }\n<\/pre><\/div>\n\n\n<p>d. Ansible playbook run \u2013 Ansible is a tool that greatly simplifies configuration management tasks. Playbook is the desired state configuration expressed in YAML.<\/p>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-1024x601.webp\" alt=\"Screenshot 9 - Infrastructure Provisioning\" class=\"wp-image-73525 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10.webp 2106w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-300x176.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-1024x601.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-768x451.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-1536x902.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-2048x1203.webp 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-330x194.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-800x470.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-400x235.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10.png 2106w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-300x176.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-1024x601.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-768x451.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-1536x902.png 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-2048x1203.png 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-330x194.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-800x470.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-10-400x235.png 400w\"><\/figure>\n\n\n\n<p>In this example the playbook \u201csite.yml\u201d uses role \u201ctomcat\u201d to install required JDK, Tomcat 7.0, configure Tomcat and deploy the SpringMusic application . Here are tasks that undeploy previous version of the application and install the new one:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; auto-links: false; gutter: false; title: ; quick-code: false; notranslate\" title=\"\">\n- name: unDeploy sample app\n  file: path=\/usr\/share\/tomcat\/webapps\/spring-music.war owner=tomcat group=tomcat state=absent\n- name: wait for tomcat to undeploy the app\n  wait_for: path=\/usr\/share\/tomcat\/webapps\/spring-music\/ state=absent\n- name: Deploy sample app\n  copy: src=..\/..\/build\/libs\/spring-music.war dest=\/usr\/share\/tomcat\/webapps\/spring-music.war owner=tomcat group=tomcat\n  notify: restart tomcat\n<\/pre><\/div>\n\n\n<p>Ansible, like Terraform, operates to reach desired state on the configuration. Below is an example output of the pipeline when Tomcat was already installed on the provisioned servers and only the application was changed:<\/p>\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-11-1024x644.webp\" alt=\"example output of the pipeline when Tomcat was already installed on the provisioned servers (screenshot)\" class=\"wp-image-73526 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-11-1024x644.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-11-300x189.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-11-768x483.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-11-1536x966.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-11-2048x1287.webp 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-11-330x207.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-11-800x503.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-11-400x251.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-11-1024x644.webp\"><\/figure>\n\n\n\n<p>The result is following resources created, up and running.<\/p>\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-12-1024x738.webp\" alt=\"list of results (screenshot)\" class=\"wp-image-73527 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-12-1024x738.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-12-300x216.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-12-768x554.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-12-330x238.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-12-800x577.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-12-400x288.webp 400w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-12.webp 1151w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-12-1024x738.webp\"><\/figure>\n\n\n\n<p>And you can see the application at http:\/\/\/spring-music\/<\/p>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-13-1024x530.webp\" alt=\"application screenshot\" class=\"wp-image-73528 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-13.webp 1329w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-13-300x155.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-13-1024x530.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-13-768x398.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-13-330x171.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-13-800x414.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-13-400x207.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-13.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-13.png 1329w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-13-300x155.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-13-1024x530.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-13-768x398.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-13-330x171.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-13-800x414.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-13-400x207.png 400w\"><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusion\">Conclusion<\/h2>\n\n\n\n<p>In this example we demonstrated a simple flow that provides application deployment and infrastructure automation. While implementing Infrastructure as Code minimizes configuration drift, it is still possible that some server might deviate due to manual changes and patches. Another consideration is that scale-out process configuration should be done on the new machines and it might take time for complex playbooks. This and some other considerations will be addressed in <a href=\"https:\/\/open.microsoft.com\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/\">Part 2 of the tutorial<\/a>, focused on building&nbsp; immutable infrastructure.<\/p>\n\n\n\n<p>Check out Part 2, &#8220;Immutable infrastructure for Azure, using VSTS, Terraform, Packer and Ansible,&#8221; <a href=\"https:\/\/open.microsoft.com\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/\">here<\/a>.<\/p>\n\n\n\n<p><em>Note: There is a \u201cTerraform\u201d task available on VSTS marketplace, it has great capabilities of running templates and using storage account as a backend, but it&#8217;s currently Windows only, while Ansible task is Linux based task \u2013 for that reason this example was written to use shell scripts.<\/em><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is part 1 of a 2-part series demonstrating how to continuously build and deploy Azure infrastructure for the applications running on Azure. The first article will show how open source tools, such as Terraform and Ansible, can be leveraged to implement Infrastructure as Code.<\/p>\n","protected":false},"author":5562,"featured_media":73515,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"msxcm_post_with_no_image":false,"ep_exclude_from_search":false,"_classifai_error":"","_classifai_text_to_speech_error":"","footnotes":""},"post_tag":[87,160,2272,166,213,218,223],"content-type":[340],"topic":[2240,2241,2242,2244,2245],"programming-languages":[2259],"coauthors":[2326],"class_list":["post-73511","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-ansible","tag-linux","tag-microsoft","tag-azure","tag-spring","tag-terraform","tag-visual-studio","content-type-tutorials-and-demos","topic-application-development","topic-cloud","topic-containers","topic-devops","topic-infrastructure","programming-languages-java","review-flag-1593580428-734","review-flag-1593580415-931","review-flag-1593580771-946","review-flag-1-1593580432-963","review-flag-2-1593580437-411","review-flag-3-1593580442-169","review-flag-7-1593580463-151","review-flag-new-1593580248-669","review-flag-sprin-1593580746-499","review-flag-vm-1593580807-312"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Tutorial: CI\/CD for Azure using Terraform, Ansible and VSTS<\/title>\n<meta name=\"description\" content=\"This is part 1 of a 2-part series, demonstrating how to continuously build and deploy Azure infrastructure for the apps running on Azure. In part 1, we&#039;ll walk though how to continually build and deploy a Java Spring Boot application and its required infrastructure and middleware using Visual Studio Team Services.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Tutorial: CI\/CD for Azure using Terraform, Ansible and VSTS\" \/>\n<meta property=\"og:description\" content=\"This is part 1 of a 2-part series, demonstrating how to continuously build and deploy Azure infrastructure for the apps running on Azure. In part 1, we&#039;ll walk though how to continually build and deploy a Java Spring Boot application and its required infrastructure and middleware using Visual Studio Team Services.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/\" \/>\n<meta property=\"og:site_name\" content=\"Microsoft Open Source Blog\" \/>\n<meta property=\"article:published_time\" content=\"2018-05-22T18:00:50+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-30T10:16:06+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1428\" \/>\n\t<meta property=\"og:image:height\" content=\"507\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Elena Neroslavskaya\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@OpenAtMicrosoft\" \/>\n<meta name=\"twitter:site\" content=\"@OpenAtMicrosoft\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Elena Neroslavskaya\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 min read\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2018\\\/05\\\/22\\\/cicd-azure-terraform-ansible-vsts-java-springboot-app\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2018\\\/05\\\/22\\\/cicd-azure-terraform-ansible-vsts-java-springboot-app\\\/\"},\"author\":[{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/author\\\/elena-neroslavskaya\\\/\",\"@type\":\"Person\",\"@name\":\"Elena Neroslavskaya\"}],\"headline\":\"Tutorial: CI\\\/CD for Azure using Terraform, Ansible and VSTS\",\"datePublished\":\"2018-05-22T18:00:50+00:00\",\"dateModified\":\"2025-06-30T10:16:06+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2018\\\/05\\\/22\\\/cicd-azure-terraform-ansible-vsts-java-springboot-app\\\/\"},\"wordCount\":1269,\"commentCount\":10,\"publisher\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2018\\\/05\\\/22\\\/cicd-azure-terraform-ansible-vsts-java-springboot-app\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/wp-content\\\/uploads\\\/2018\\\/05\\\/Post-2_image-1_flow.png\",\"keywords\":[\"Ansible\",\"Linux\",\"Microsoft\",\"Microsoft Azure\",\"Spring\",\"Terraform\",\"Visual Studio\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2018\\\/05\\\/22\\\/cicd-azure-terraform-ansible-vsts-java-springboot-app\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2018\\\/05\\\/22\\\/cicd-azure-terraform-ansible-vsts-java-springboot-app\\\/\",\"url\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2018\\\/05\\\/22\\\/cicd-azure-terraform-ansible-vsts-java-springboot-app\\\/\",\"name\":\"Tutorial: CI\\\/CD for Azure using Terraform, Ansible and VSTS\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2018\\\/05\\\/22\\\/cicd-azure-terraform-ansible-vsts-java-springboot-app\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2018\\\/05\\\/22\\\/cicd-azure-terraform-ansible-vsts-java-springboot-app\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/wp-content\\\/uploads\\\/2018\\\/05\\\/Post-2_image-1_flow.png\",\"datePublished\":\"2018-05-22T18:00:50+00:00\",\"dateModified\":\"2025-06-30T10:16:06+00:00\",\"description\":\"This is part 1 of a 2-part series, demonstrating how to continuously build and deploy Azure infrastructure for the apps running on Azure. In part 1, we'll walk though how to continually build and deploy a Java Spring Boot application and its required infrastructure and middleware using Visual Studio Team Services.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2018\\\/05\\\/22\\\/cicd-azure-terraform-ansible-vsts-java-springboot-app\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2018\\\/05\\\/22\\\/cicd-azure-terraform-ansible-vsts-java-springboot-app\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2018\\\/05\\\/22\\\/cicd-azure-terraform-ansible-vsts-java-springboot-app\\\/#primaryimage\",\"url\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/wp-content\\\/uploads\\\/2018\\\/05\\\/Post-2_image-1_flow.png\",\"contentUrl\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/wp-content\\\/uploads\\\/2018\\\/05\\\/Post-2_image-1_flow.png\",\"width\":1428,\"height\":507,\"caption\":\"a flow for how to continuously build and deploy Azure infrastructure for the applications running on Azure.\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/2018\\\/05\\\/22\\\/cicd-azure-terraform-ansible-vsts-java-springboot-app\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Tutorial: CI\\\/CD for Azure using Terraform, Ansible and VSTS\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/\",\"name\":\"Microsoft Open Source Blog\",\"description\":\"Open dialogue about openness at Microsoft \u2013 open source, standards, interoperability\",\"publisher\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/#organization\",\"name\":\"Microsoft Open Source Blog\",\"url\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/08\\\/Microsoft-Logo.png\",\"contentUrl\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/08\\\/Microsoft-Logo.png\",\"width\":259,\"height\":194,\"caption\":\"Microsoft Open Source Blog\"},\"image\":{\"@id\":\"https:\\\/\\\/opensource.microsoft.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/x.com\\\/OpenAtMicrosoft\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Tutorial: CI\/CD for Azure using Terraform, Ansible and VSTS","description":"This is part 1 of a 2-part series, demonstrating how to continuously build and deploy Azure infrastructure for the apps running on Azure. In part 1, we'll walk though how to continually build and deploy a Java Spring Boot application and its required infrastructure and middleware using Visual Studio Team Services.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/","og_locale":"en_US","og_type":"article","og_title":"Tutorial: CI\/CD for Azure using Terraform, Ansible and VSTS","og_description":"This is part 1 of a 2-part series, demonstrating how to continuously build and deploy Azure infrastructure for the apps running on Azure. In part 1, we'll walk though how to continually build and deploy a Java Spring Boot application and its required infrastructure and middleware using Visual Studio Team Services.","og_url":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/","og_site_name":"Microsoft Open Source Blog","article_published_time":"2018-05-22T18:00:50+00:00","article_modified_time":"2025-06-30T10:16:06+00:00","og_image":[{"width":1428,"height":507,"url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow.png","type":"image\/png"}],"author":"Elena Neroslavskaya","twitter_card":"summary_large_image","twitter_creator":"@OpenAtMicrosoft","twitter_site":"@OpenAtMicrosoft","twitter_misc":{"Written by":"Elena Neroslavskaya","Est. reading time":"6 min read"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/#article","isPartOf":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/"},"author":[{"@id":"https:\/\/opensource.microsoft.com\/blog\/author\/elena-neroslavskaya\/","@type":"Person","@name":"Elena Neroslavskaya"}],"headline":"Tutorial: CI\/CD for Azure using Terraform, Ansible and VSTS","datePublished":"2018-05-22T18:00:50+00:00","dateModified":"2025-06-30T10:16:06+00:00","mainEntityOfPage":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/"},"wordCount":1269,"commentCount":10,"publisher":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#organization"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/#primaryimage"},"thumbnailUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow.png","keywords":["Ansible","Linux","Microsoft","Microsoft Azure","Spring","Terraform","Visual Studio"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/","url":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/","name":"Tutorial: CI\/CD for Azure using Terraform, Ansible and VSTS","isPartOf":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/#primaryimage"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/#primaryimage"},"thumbnailUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow.png","datePublished":"2018-05-22T18:00:50+00:00","dateModified":"2025-06-30T10:16:06+00:00","description":"This is part 1 of a 2-part series, demonstrating how to continuously build and deploy Azure infrastructure for the apps running on Azure. In part 1, we'll walk though how to continually build and deploy a Java Spring Boot application and its required infrastructure and middleware using Visual Studio Team Services.","breadcrumb":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/#primaryimage","url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow.png","contentUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-2_image-1_flow.png","width":1428,"height":507,"caption":"a flow for how to continuously build and deploy Azure infrastructure for the applications running on Azure."},{"@type":"BreadcrumbList","@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/opensource.microsoft.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Tutorial: CI\/CD for Azure using Terraform, Ansible and VSTS"}]},{"@type":"WebSite","@id":"https:\/\/opensource.microsoft.com\/blog\/#website","url":"https:\/\/opensource.microsoft.com\/blog\/","name":"Microsoft Open Source Blog","description":"Open dialogue about openness at Microsoft \u2013 open source, standards, interoperability","publisher":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/opensource.microsoft.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/opensource.microsoft.com\/blog\/#organization","name":"Microsoft Open Source Blog","url":"https:\/\/opensource.microsoft.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/opensource.microsoft.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Microsoft-Logo.png","contentUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Microsoft-Logo.png","width":259,"height":194,"caption":"Microsoft Open Source Blog"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/OpenAtMicrosoft"]}]}},"msxcm_animated_featured_image":null,"bloginabox_display_generated_audio":false,"distributor_meta":false,"distributor_terms":false,"distributor_media":false,"distributor_original_site_name":"Microsoft Open Source Blog","distributor_original_site_url":"https:\/\/opensource.microsoft.com\/blog","push-errors":false,"_links":{"self":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/73511","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/users\/5562"}],"replies":[{"embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/comments?post=73511"}],"version-history":[{"count":4,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/73511\/revisions"}],"predecessor-version":[{"id":97898,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/73511\/revisions\/97898"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/media\/73515"}],"wp:attachment":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/media?parent=73511"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/post_tag?post=73511"},{"taxonomy":"content-type","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/content-type?post=73511"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/topic?post=73511"},{"taxonomy":"programming-languages","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/programming-languages?post=73511"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/coauthors?post=73511"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}