{"id":73541,"date":"2018-05-23T11:37:06","date_gmt":"2018-05-23T18:37:06","guid":{"rendered":""},"modified":"2025-06-30T03:12:20","modified_gmt":"2025-06-30T10:12:20","slug":"immutable-infrastructure-azure-vsts-terraform-packer-ansible","status":"publish","type":"post","link":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/","title":{"rendered":"Tutorial: Immutable infrastructure for Azure, using VSTS, Terraform, Packer and Ansible"},"content":{"rendered":"\n<p>This is part 2 of a 2-part series on CI\/CD for &#8220;infrastructure as code&#8221; on Azure. <a href=\"https:\/\/open.microsoft.com\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/\">In part 1<\/a>, we covered a basic pipeline building application and provisioning infrastructure codified as Terraform templates and Ansible playbooks. While it demonstrated how infrastructure is treated as a code \u2013 stored, versioned, and audited \u2013 there is still room for configuration drifts and the time required to update the configuration on the server could make auto-scaling challenging. Configuration updates using Ansible also require SSH ports to be open. These and some other considerations are addressed in this tutorial.<\/p>\n\n\n\n<p>Below we&#8217;ll demonstrate how to build immutable infrastructure for Azure using <a href=\"https:\/\/www.visualstudio.com\/team-services\/\">Visual Studio Team Services<\/a> (VSTS) as continuous integration and delivery (CI\/CD) and popular HashiCorp and Red Hat tools. Some of the challenges today when building infrastructure are predictability and automated recovery. We need to promote the exact same artifact that was tested into production to ensure consistent behavior. And it is essential to be able to recover the system to the last known to work state. Solving these and other problems such as configuration drift and snowflake servers are the main benefits of building immutable infrastructure.<\/p>\n\n\n\n<p>So, what is immutable infrastructure? It\u2019s a process where instead of having to worry about updating many moving parts at all layers of the application, the whole machine image is promoted, unchanged, from environment to environment. The downside is that building an image takes a lot more time than just running the update script, but that might be solved by layering the images.<\/p>\n\n\n\n<p>In this post the following tools are used to demonstrate the power of using CI\/CD for immutable infrastructure builds:<\/p>\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" alt=\"comparison table with text, \"Ansible, Terraform, Packer\"\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-1-1024x291.webp\" alt='comparison table with text, \"Ansible, Terraform, Packer\"' class=\"wp-image-73544 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-1-1024x291.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-1-300x85.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-1-768x218.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-1-1536x437.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-1-2048x582.webp 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-1-330x94.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-1-800x227.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-1-400x114.webp 400w\" data-orig-alt=\"comparison table with text, \"Ansible, Terraform, Packer\"\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-1-1024x291.webp\"><\/figure>\n\n\n\n<p>This is the flow implemented in this post:<\/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-3_image-2-1024x427.webp\" alt=\"Git diagram\" class=\"wp-image-73545 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2-1024x427.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2-300x125.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2-768x321.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2-1536x641.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2-2048x855.webp 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2-330x138.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2-800x334.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2-400x167.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2-1024x427.webp\"><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li>DevOps commit code or configuration change<\/li>\n\n\n\n<li>VSTS Build builds and packages application<\/li>\n\n\n\n<li>VSTS Release invokes Packer to build a Linux image and store it in Managed Disks<\/li>\n\n\n\n<li>Packer invokes the Ansible Playbook provisioner to install JDK, Tomcat and SpringBoot application<\/li>\n\n\n\n<li>VSTS Release invokes Terraform to provision Infrastructure and uses Packer build image<\/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 &#8220;<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>&#8220;<\/li>\n\n\n\n<li>Service Principal with access to the Subscription<\/li>\n\n\n\n<li>Resource Group in which managed disks will be created<\/li>\n\n\n\n<li>Storage Account\/Container to save Terraform state in (update \u201cbackend.tfvars\u201d in the Terraform templates below with the &nbsp;storage account names).<br>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<\/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>The application used for this example is the Java Spring Boot application from <a href=\"https:\/\/open.microsoft.com\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/\">part 1 of this tutorial<\/a>. First, we build and package the Spring Boot application using Gradle. You can import the full build definition from this <a href=\"https:\/\/github.com\/lenisha\/vsts-terraform-ansible-packer\">GitHub repository<\/a> or create a Java Gradle project from scratch by following the steps provided in this 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; Here is outline of the steps and commands customizations:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\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).<\/li>\n\n\n\n<li>In \u201dCopy Files\u201d &#8211; customize the step to copy all required scripts directories with templates to resulting artifact.<br>ansible\/**<br>terraform\/**<br>packer\/**<\/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-3_image-3-1024x539.webp\" alt=\"Screenshot 1 - Spring Boot Application Build\" class=\"wp-image-73546 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3.webp 1783w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3-300x158.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3-1024x539.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3-768x404.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3-1536x809.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3-600x315.webp 600w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3-330x174.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3-800x421.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3-400x211.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3.png 1783w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3-300x158.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3-1024x539.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3-768x404.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3-1536x809.png 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3-600x315.png 600w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3-330x174.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3-800x421.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-3-400x211.png 400w\"><\/figure>\n\n\n\n<p>4. Add an additional \u201cCopy Files\u201d step, which will copy the Java WAR file to the resulting build artifact.<\/p>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-4-1024x603.webp\" alt=\"Screenshot 2 - Spring Boot Application Build\" class=\"wp-image-73547 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-4.webp 1506w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-4-300x177.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-4-1024x603.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-4-768x452.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-4-330x194.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-4-800x471.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-4-400x236.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-4.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-4.png 1506w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-4-300x177.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-4-1024x603.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-4-768x452.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-4-330x194.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-4-800x471.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-4-400x236.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>In this flow, Packer builds an Azure VM image and uses Ansible as the provisioner. Ansible Playbook installs the required software (Apache) and application on the server. The completed image is saved in Azure Managed disks. Terraform is used to build the infrastructure based on the Packer image.<\/p>\n\n\n\n<p>Here is the Release pipeline definition, which can be <a href=\"https:\/\/github.com\/lenisha\/vsts-terraform-ansible-packer\">imported from GitHub<\/a>.<\/p>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-1024x432.webp\" alt=\"Screenshot 3 - Infrastructure Provisioning\" class=\"wp-image-73548 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5.webp 2063w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-300x127.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-1024x432.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-768x324.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-1536x649.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-2048x865.webp 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-330x139.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-800x338.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-400x169.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5.png 2063w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-300x127.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-1024x432.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-768x324.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-1536x649.png 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-2048x865.png 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-330x139.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-800x338.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-5-400x169.png 400w\"><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"deployment-flow\">Deployment Flow<\/h2>\n\n\n\n<p>1. Start by defining Empty Release Definition, and link the build prepared above as an artifact.<\/p>\n\n\n\n<p>2. Use custom VSTS Agent from \u201cACI-Pool\u201d<\/p>\n\n\n\n<p>3. Define Variable Group with environment variables that provide connectivity to subscription \u2013 ARM_SUBSCRIPTION_ID, ARM_TENANT_ID. Service Principle \u2013 ARM_CLIENT_ID, ARM_CLIENT_SECRET. And Storage account access key \u2013 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-3_image-6-1024x479.webp\" alt=\"Screenshot 4 - Deployment Flow\" class=\"wp-image-73550 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6.webp 1836w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6-300x140.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6-1024x479.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6-768x359.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6-1536x719.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6-330x154.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6-800x374.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6-400x187.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6.png 1836w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6-300x140.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6-1024x479.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6-768x359.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6-1536x719.png 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6-330x154.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6-800x374.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-6-400x187.png 400w\"><\/figure>\n\n\n\n<p>And Variable ARM_RESOURCE_GROUP_DISKS that has the name of resource group to store the images.<\/p>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-7-1024x289.webp\" alt=\"Screenshot 5 - Deployment Flow\" class=\"wp-image-73551 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-7.webp 1444w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-7-300x85.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-7-1024x289.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-7-768x217.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-7-330x93.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-7-800x226.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-7-400x113.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-7.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-7.png 1444w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-7-300x85.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-7-1024x289.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-7-768x217.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-7-330x93.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-7-800x226.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-7-400x113.png 400w\"><\/figure>\n\n\n\n<p>4. Add these steps:<\/p>\n\n\n\n<p>a. Packer build- invoke shell script to build image<\/p>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-1024x371.webp\" alt=\"Screenshot 6 - Deployment Flow\" class=\"wp-image-73552 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8.webp 2790w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-300x109.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-1024x371.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-768x279.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-1536x557.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-2048x743.webp 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-330x120.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-800x290.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-400x145.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8.png 2790w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-300x109.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-1024x371.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-768x279.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-1536x557.png 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-2048x743.png 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-330x120.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-800x290.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-8-400x145.png 400w\"><\/figure>\n\n\n\n<p>Script executes the Packer template and sets the VSTS output variable \u201cmanageddiskname\u201d to the disk created by Packer. This image will be used by Terraform to point VM ScaleSets to.<\/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\n## execute packer build and send out to packer-build-output file\npacker build -var playbook_drop_path=$6 .\/app.json 2>&1 | tee packer-build-output.log\n## export output variable to VSTS\nexport manageddiskname=$(cat packer-build-output.log | grep ManagedImageName: | awk '{print $2}')\necho \"variable $manageddiskname\"\necho \"##vso[task.setvariable variable=manageddiskname]$manageddiskname\"\n<\/pre><\/div>\n\n\n<p>Packer template uses Azure builder to create image based on Red Hat and saves it in Managed Disk in the provided resource group (name includes timestamp for ease of identification).<\/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{\n  \"variables\": {\n    \"tenant_id\": \"{{env `ARM_TENANT_ID`}}\",\n    \"client_id\": \"{{env `ARM_CLIENT_ID`}}\",\n    \"client_secret\": \"{{env `ARM_CLIENT_SECRET`}}\",\n    \"managed_resource_group\": \"{{env `ARM_RESOURCE_GROUP_DISKS`}}\",\n    \"subscription_id\": \"{{env `ARM_SUBSCRIPTION_ID`}}\",\n    \"playbook_drop_path\": \"\",\n    \"random_path\": \"{{timestamp}}\"\n  },\n  \"builders\": [{\n    \"type\": \"azure-arm\",\n    \"client_id\": \"{{user `client_id`}}\",\n    \"client_secret\": \"{{user `client_secret`}}\",\n    \"subscription_id\": \"{{user `subscription_id`}}\",\n    \"tenant_id\": \"{{user `tenant_id`}}\",\n    \"os_type\": \"Linux\",\n    \"image_publisher\": \"RedHat\",\n    \"image_offer\": \"RHEL\",\n    \"image_sku\": \"7.3\",\n    \"managed_image_resource_group_name\": \"{{user `managed_resource_group`}}\",\n    \"managed_image_name\": \"demoPackerImage-{{isotime \\\"2006-01-02_03_04_05\\\"}}\",\n    \"location\": \"Canada Central\",\n    \"vm_size\": \"Basic_A1\"\n  }\n<\/pre><\/div>\n\n\n<p>To install the required components and application we are using Ansible Playbook. To invoke it, define a provisioner in the Packer template. First, we use shell provisioner to install Ansible, then \u201cAnsible-local\u201d to invoke the playbook on the image being created, and then shutdown the VM.<\/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\"provisioners\": [\n    {\n      \"execute_command\": \"chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'\",\n      \"inline\": [\n        \"sudo yum install \u2013f ansible\"\n       ],\n      \"inline_shebang\": \"\/bin\/sh -x\",\n      \"type\": \"shell\"\n    },\n    {\n      \"type\": \"ansible-local\",\n      \"playbook_file\": \"{{user `playbook_drop_path`}}\/ansible\/site.yml\",\n      \"playbook_dir\": \"{{user `playbook_drop_path`}}\/ansible\"\n    },\n    {\n    \"execute_command\": \"chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'\",\n    \"inline\": [\n      \"\/usr\/sbin\/waagent -force -deprovision+user && export HISTSIZE=0 && sync\"\n    ],\n    \"inline_shebang\": \"\/bin\/sh -x\",\n    \"type\": \"shell\"\n    }\n  ]\n}\n<\/pre><\/div>\n\n\n<p>Resulting image will have all the components installed using Ansible playbook. This solution does not require SSH to be enabled on the VM as it uses local provisioner.<\/p>\n\n\n\n<p>Note: For Ansible to find all the roles and subdirectories \u201cplaybook_dir\u201d should be specified. It will direct Ansible to copy all directory and subfolders to the staging directory, where Ansible provisioner is invoked in.<\/p>\n\n\n\n<p>The Ansible Playbook used in the example (same as in <a href=\"https:\/\/open.microsoft.com\/2018\/05\/22\/cicd-azure-terraform-ansible-vsts-java-springboot-app\/\">Part 1<\/a>) is running on localhost, installs JDK, Tomcat, and the Java Spring Boot 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=\"\">\n---\n- hosts: 127.0.0.1\n  become: true\n  connection: local\n  vars:\n    http_port: 8080\n    https_port: 8443\n    admin_username: admin\n    admin_password: adminsecret\n  roles:\n    - selinux\n    - tomcat\n<\/pre><\/div>\n\n\n<p>Tomcat Role snippet installing 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=\"\">\n- name: wait for tomcat to start\n  wait_for: port={{http_port}}\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=web\/build\/libs\/spring-music.war dest=\/usr\/share\/tomcat\/webapps\/spring-music.war owner=tomcat group=tomcat\n  notify: restart tomcat\n- name: Start Tomcat\n  service: name=tomcat state=started enabled=yes\n<\/pre><\/div>\n\n\n<p>As a result, we can see the image build built, the Ansible Playbook run, and the managed disk name as an output of the task.<\/p>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-1024x632.webp\" alt=\"Screenshot 7 - Deployment Flow\" class=\"wp-image-73554 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9.webp 2334w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-300x185.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-1024x632.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-768x474.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-1536x948.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-2048x1264.webp 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-330x204.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-800x494.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-400x247.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9.png 2334w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-300x185.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-1024x632.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-768x474.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-1536x948.png 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-2048x1264.png 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-330x204.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-800x494.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-9-400x247.png 400w\"><\/figure>\n\n\n\n<p>The newly created image could be verified in the resource group (\u201cmanaged-disks\u201d in our example).<\/p>\n\n\n\n<p>Each image has timestamp as a suffix that helps to identify images for rollback and promotion (could use git hash or tag for traceability to source control).<\/p>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-10-1024x402.webp\" alt=\"Screenshot 8 - Deployment Flow\" class=\"wp-image-73555 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-10.webp 1042w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-10-300x118.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-10-1024x402.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-10-768x301.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-10-330x130.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-10-800x314.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-10-400x157.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-10.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-10.png 1042w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-10-300x118.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-10-1024x402.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-10-768x301.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-10-330x130.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-10-800x314.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-10-400x157.png 400w\"><\/figure>\n\n\n\n<p>Next step is provisioning infrastructure using Terraform:<\/p>\n\n\n\n<p>b. Shell Script \u2013 Terraform Init<\/p>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11-1024x457.webp\" alt=\"Screenshot 9 - Deployment Flow\" class=\"wp-image-73556 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11.webp 2041w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11-300x134.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11-1024x457.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11-768x343.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11-1536x686.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11-330x147.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11-800x357.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11-400x179.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11.png 2041w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11-300x134.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11-1024x457.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11-768x343.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11-1536x686.png 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11-330x147.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11-800x357.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-11-400x179.png 400w\"><\/figure>\n\n\n\n<p>Terraform must initialize Azure Resource provider and the 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\"\n  backend \"azurerm\" {}\n}\n# Configure the Microsoft Azure Provider\nprovider \"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 the Terraform template, we are using a partial configuration and providing the required backend configuration in variables file &#8211; \u201cbackend.tfvars.\u201d Here a is configuration that uses a storage account we created as part of the 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 = \"vstsbuildterraform\"\ncontainer_name = \"terraform-state\"\nkey = \"demo-packer.terraform.tfstate\"\n<\/pre><\/div>\n\n\n<p>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 a successful run it will have following output indication that 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-3_image-12-1024x648.webp\" alt=\"Screenshot 10 - Deployment Flow\" class=\"wp-image-73557 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12.webp 1982w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12-300x190.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12-1024x648.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12-768x486.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12-1536x973.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12-330x209.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12-800x507.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12-400x253.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12.png 1982w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12-300x190.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12-1024x648.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12-768x486.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12-1536x973.png 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12-330x209.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12-800x507.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-12-400x253.png 400w\"><\/figure>\n\n\n\n<p>c. Shell Script \u2013 Terraform apply<\/p>\n\n\n\n<p>Terraform apply will apply the changes required to reach the desired state of the configuration as defined by \u201cmain.tf.\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-3_image-13-1024x313.webp\" alt=\"Screenshot 11 - Deployment Flow\" class=\"wp-image-73558 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13.webp 2666w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13-300x92.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13-1024x313.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13-768x234.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13-1536x469.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13-2048x625.webp 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13-330x101.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13-800x244.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13-400x122.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13.png 2666w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13-300x92.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13-1024x313.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13-768x234.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13-1536x469.png 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13-2048x625.png 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13-330x101.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13-800x244.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-13-400x122.png 400w\"><\/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-3_image-14-1024x592.webp\" alt=\"Screenshot 12 - Deployment Flow\" class=\"wp-image-73559 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14.webp 2471w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14-300x173.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14-1024x592.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14-768x444.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14-1536x888.webp 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14-2048x1184.webp 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14-330x191.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14-800x462.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14-400x231.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14.png 2471w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14-300x173.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14-1024x592.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14-768x444.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14-1536x888.png 1536w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14-2048x1184.png 2048w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14-330x191.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14-800x462.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-14-400x231.png 400w\"><\/figure>\n\n\n\n<p>The shell file executes the Terraform build and uses the build by Packer ManagedDisk name to locate the image used in the VM scale set.<\/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 -var \"manageddiskname=$6\"\n<\/pre><\/div>\n\n\n<p>The full Terraform template can be found in <a href=\"https:\/\/github.com\/lenisha\/vsts-terraform-ansible-packer\/tree\/master\/terraform\/azure\">GitHub<\/a>.<\/p>\n\n\n\n<p>It provisions the resource group, virtual network, subnet, public IP, load balancer and NAT rules, and VM scale set.<\/p>\n\n\n\n<p>Here is the definition of VM scale set, pointing to the Packer image. Resources that are not created by Terraform are referred to as \u201cdata\u201d definition as opposed to \u201cresource.\u201d<\/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# Points to Packer build image\ndata \"azurerm_image\" \"image\" {\n  name                = \"${var.manageddiskname}\"\n  resource_group_name = \"managed-images\"\n}\n# Create virtual machine sclae set\nresource \"azurerm_virtual_machine_scale_set\" \"vmss\" {\n  name                = \"vmscaleset\"\n  location            = \"${azurerm_resource_group.demo_resource_group.location}\"\n  resource_group_name = \"${azurerm_resource_group.demo_resource_group.name}\"\n  upgrade_policy_mode = \"Automatic\"\n  sku {\n    name     = \"Standard_DS1_v2\"\n    tier     = \"Standard\"\n    capacity = 2\n  }\n  storage_profile_image_reference {\n    id = \"${data.azurerm_image.image.id}\"\n  }\n  storage_profile_os_disk {\n    name              = \"\"\n    caching           = \"ReadWrite\"\n    create_option     = \"FromImage\"\n    managed_disk_type = \"Premium_LRS\"\n  }\n  os_profile {\n    computer_name_prefix = \"myvm\"\n    admin_username       = \"azureuser\"\n    admin_password       = \"xxxx\"\n  }\n  os_profile_linux_config {\n    disable_password_authentication = true\n    ssh_keys {\n      path     = \"\/home\/azureuser\/.ssh\/authorized_keys\"\n      key_data = \"ssh-rsa xxxx\"\n    }\n  }\n  network_profile {\n    name    = \"terraformnetworkprofile\"\n    primary = true\n    ip_configuration {\n      name                                   = \"IPConfiguration\"\n      subnet_id                              = \"${azurerm_subnet.demo_subnet.id}\"\n      load_balancer_backend_address_pool_ids = [\"${azurerm_lb_backend_address_pool.bpepool.id}\"]\n    }\n  }\n  tags {\n    environment = \"Terraform Demo\"\n  }\n}\n<\/pre><\/div>\n\n\n<p>Provisioned infrastructure will look like this:<\/p>\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-15-1024x769.webp\" alt=\"Screenshot 13 - Deployment Flow\" class=\"wp-image-73560 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-15.webp 1069w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-15-300x225.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-15-1024x769.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-15-768x577.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-15-330x248.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-15-800x601.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-15-400x300.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-15.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-15.png 1069w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-15-300x225.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-15-1024x769.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-15-768x577.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-15-330x248.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-15-800x601.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-15-400x300.png 400w\"><\/figure>\n\n\n\n<p>As a result of the build, we have a Spring Boot application up and running on an Azure VM scale set and it could be scaled up and down quickly, according to demand.<\/p>\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, and builds the immutable image that can be promoted between environments. The build history is stored in the Azure resource group and each image is tagged, and could be rolled back very easily by pointing the VM scale set to a previous version. In the next article we will demonstrate more complex flow, incorporating planning, approving, verifying policies, and testing infrastructure.<\/p>\n\n\n\n<p><em>Note: There is a \u201cPacker\u201d task available on VSTS marketplace, Currently it does not support managed disks, enhancement is coming and we will be able to replace the shell script.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is part 2 of a 2-part series on CI\/CD for &#8220;infrastructure as code&#8221; on Azure. In part 1, we covered a basic pipeline building application and provisioning infrastructure codified as Terraform templates and Ansible playbooks.<\/p>\n","protected":false},"author":5562,"featured_media":73545,"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-73541","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-1593580419-521","review-flag-1593580771-946","review-flag-1-1593580432-963","review-flag-2-1593580437-411","review-flag-3-1593580442-169","review-flag-4-1593580448-609","review-flag-6-1593580457-852","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.2 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Tutorial: Immutable infrastructure for Azure using VSTS, Terraform, Packer and Ansible<\/title>\n<meta name=\"description\" content=\"This is part 2 of a 2-part series on CI\/CD for &quot;infrastructure as code&quot; on Azure. In this tutorial, we&#039;ll demonstrate how to build immutable infrastructure for Azure using Visual Studio Team Services (VSTS) as continuous integration and delivery (CI\/CD) and popular HashiCorp and Red Hat tools.\" \/>\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\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Tutorial: Immutable infrastructure for Azure using VSTS, Terraform, Packer and Ansible\" \/>\n<meta property=\"og:description\" content=\"This is part 2 of a 2-part series on CI\/CD for &quot;infrastructure as code&quot; on Azure. In this tutorial, we&#039;ll demonstrate how to build immutable infrastructure for Azure using Visual Studio Team Services (VSTS) as continuous integration and delivery (CI\/CD) and popular HashiCorp and Red Hat tools.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/\" \/>\n<meta property=\"og:site_name\" content=\"Microsoft Open Source Blog\" \/>\n<meta property=\"article:published_time\" content=\"2018-05-23T18:37:06+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-30T10:12:20+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2135\" \/>\n\t<meta property=\"og:image:height\" content=\"891\" \/>\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=\"8 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\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/\"},\"author\":[{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/author\/elena-neroslavskaya\/\",\"@type\":\"Person\",\"@name\":\"Elena Neroslavskaya\"}],\"headline\":\"Tutorial: Immutable infrastructure for Azure, using VSTS, Terraform, Packer and Ansible\",\"datePublished\":\"2018-05-23T18:37:06+00:00\",\"dateModified\":\"2025-06-30T10:12:20+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/\"},\"wordCount\":1474,\"commentCount\":5,\"publisher\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2.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\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/\",\"name\":\"Tutorial: Immutable infrastructure for Azure using VSTS, Terraform, Packer and Ansible\",\"isPartOf\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2.png\",\"datePublished\":\"2018-05-23T18:37:06+00:00\",\"dateModified\":\"2025-06-30T10:12:20+00:00\",\"description\":\"This is part 2 of a 2-part series on CI\/CD for \\\"infrastructure as code\\\" on Azure. In this tutorial, we'll demonstrate how to build immutable infrastructure for Azure using Visual Studio Team Services (VSTS) as continuous integration and delivery (CI\/CD) and popular HashiCorp and Red Hat tools.\",\"breadcrumb\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/#primaryimage\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2.png\",\"contentUrl\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2.png\",\"width\":2135,\"height\":891,\"caption\":\"Git diagram\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/opensource.microsoft.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Tutorial: Immutable infrastructure for Azure, using VSTS, Terraform, Packer and Ansible\"}]},{\"@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: Immutable infrastructure for Azure using VSTS, Terraform, Packer and Ansible","description":"This is part 2 of a 2-part series on CI\/CD for \"infrastructure as code\" on Azure. In this tutorial, we'll demonstrate how to build immutable infrastructure for Azure using Visual Studio Team Services (VSTS) as continuous integration and delivery (CI\/CD) and popular HashiCorp and Red Hat tools.","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\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/","og_locale":"en_US","og_type":"article","og_title":"Tutorial: Immutable infrastructure for Azure using VSTS, Terraform, Packer and Ansible","og_description":"This is part 2 of a 2-part series on CI\/CD for \"infrastructure as code\" on Azure. In this tutorial, we'll demonstrate how to build immutable infrastructure for Azure using Visual Studio Team Services (VSTS) as continuous integration and delivery (CI\/CD) and popular HashiCorp and Red Hat tools.","og_url":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/","og_site_name":"Microsoft Open Source Blog","article_published_time":"2018-05-23T18:37:06+00:00","article_modified_time":"2025-06-30T10:12:20+00:00","og_image":[{"width":2135,"height":891,"url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2.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":"8 min read"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/#article","isPartOf":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/"},"author":[{"@id":"https:\/\/opensource.microsoft.com\/blog\/author\/elena-neroslavskaya\/","@type":"Person","@name":"Elena Neroslavskaya"}],"headline":"Tutorial: Immutable infrastructure for Azure, using VSTS, Terraform, Packer and Ansible","datePublished":"2018-05-23T18:37:06+00:00","dateModified":"2025-06-30T10:12:20+00:00","mainEntityOfPage":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/"},"wordCount":1474,"commentCount":5,"publisher":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#organization"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/#primaryimage"},"thumbnailUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2.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\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/","url":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/","name":"Tutorial: Immutable infrastructure for Azure using VSTS, Terraform, Packer and Ansible","isPartOf":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/#primaryimage"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/#primaryimage"},"thumbnailUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2.png","datePublished":"2018-05-23T18:37:06+00:00","dateModified":"2025-06-30T10:12:20+00:00","description":"This is part 2 of a 2-part series on CI\/CD for \"infrastructure as code\" on Azure. In this tutorial, we'll demonstrate how to build immutable infrastructure for Azure using Visual Studio Team Services (VSTS) as continuous integration and delivery (CI\/CD) and popular HashiCorp and Red Hat tools.","breadcrumb":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/#primaryimage","url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2.png","contentUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2018\/05\/Post-3_image-2.png","width":2135,"height":891,"caption":"Git diagram"},{"@type":"BreadcrumbList","@id":"https:\/\/opensource.microsoft.com\/blog\/2018\/05\/23\/immutable-infrastructure-azure-vsts-terraform-packer-ansible\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/opensource.microsoft.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Tutorial: Immutable infrastructure for Azure, using VSTS, Terraform, Packer and Ansible"}]},{"@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_display_generated_audio":false,"msxcm_animated_featured_image":null,"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\/73541","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=73541"}],"version-history":[{"count":3,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/73541\/revisions"}],"predecessor-version":[{"id":97896,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/73541\/revisions\/97896"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/media\/73545"}],"wp:attachment":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/media?parent=73541"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/post_tag?post=73541"},{"taxonomy":"content-type","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/content-type?post=73541"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/topic?post=73541"},{"taxonomy":"programming-languages","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/programming-languages?post=73541"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/coauthors?post=73541"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}