<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.8.7">Jekyll</generator><link href="http://cloudscaping.co.uk/atom.xml" rel="self" type="application/atom+xml" /><link href="http://cloudscaping.co.uk/" rel="alternate" type="text/html" /><updated>2020-06-23T14:18:12+00:00</updated><id>http://cloudscaping.co.uk/atom.xml</id><title type="html">cloudscaping</title><author><name>Ben Hummerstone</name></author><entry><title type="html">Tag Management with Azure Policy</title><link href="http://cloudscaping.co.uk/2020/06/23/azure-policy-tag-governance/" rel="alternate" type="text/html" title="Tag Management with Azure Policy" /><published>2020-06-23T00:00:00+00:00</published><updated>2020-06-23T00:00:00+00:00</updated><id>http://cloudscaping.co.uk/2020/06/23/azure-policy-tag-governance</id><content type="html" xml:base="http://cloudscaping.co.uk/2020/06/23/azure-policy-tag-governance/">&lt;p&gt;Following on from my previous post about managing tags using Azure Policy and Terraform, there was some interest in how the Policies worked for both enforcing the tags, and making them inherit down from the Resource Group to the deployed resources.&lt;/p&gt;

&lt;p&gt;This is actually deceptively simple as we can utilise a few built-in Policies to do the work for us!&lt;/p&gt;

&lt;p&gt;The first Policy is called “Require a tag on resource groups”, and has the Definition ID /providers/Microsoft.Authorization/policyDefinitions/96670d01-0a4d-4649-9c89-2d3abc0a5025.&lt;/p&gt;

&lt;p&gt;The policyRule of this Definition is as follows:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nl&quot;&gt;&quot;policyRule&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;if&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;allOf&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;field&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;equals&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Microsoft.Resources/subscriptions/resourceGroups&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;field&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[concat('tags[', parameters('tagName'), ']')]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;exists&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;then&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;effect&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;deny&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The logic here is:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;If what is being deployed is a Resource Group&lt;/li&gt;
  &lt;li&gt;And a particular named tag doesn’t exist&lt;/li&gt;
  &lt;li&gt;Then deny the deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can assign this same policy multiple times, with one instance for each tag to be enforced.&lt;/p&gt;

&lt;p&gt;The Policy for the inheritance is called “Inherit a tag from the resource group”, and has Definition ID /providers/Microsoft.Authorization/policyDefinitions/cd3aa116-8754-49c9-a813-ad46512ece54.&lt;/p&gt;

&lt;p&gt;The policyRule of this Definition is as follows:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nl&quot;&gt;&quot;policyRule&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;if&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;allOf&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;field&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[concat('tags[', parameters('tagName'), ']')]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;notEquals&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[resourceGroup().tags[parameters('tagName')]]&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[resourceGroup().tags[parameters('tagName')]]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;notEquals&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;then&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;effect&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;modify&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;details&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;roleDefinitionIds&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;operations&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;operation&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;addOrReplace&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;field&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[concat('tags[', parameters('tagName'), ']')]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[resourceGroup().tags[parameters('tagName')]]&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This uses a more interesting effect known as “Modify”, which can alter resources as they are being deployed.&lt;/p&gt;

&lt;p&gt;As such, the logic is:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;If the resource being deployed doesn’t have a named tag matching one defined in the containing Resource Group&lt;/li&gt;
  &lt;li&gt;And the Resource Group tag doesn’t have an empty value&lt;/li&gt;
  &lt;li&gt;Then modify the resource to include the Resource Group’s tag value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As above, you can assign this Policy multiple times and specify the tag names to use in each case to match those enforced by the first Policy defined above.&lt;/p&gt;

&lt;p&gt;To keep this all together, I’d recommend creating a Policy Initiative called “Tag Governance” or similar and use that to group the Definitions and Assignments together, as that will make administration much more consistent.&lt;/p&gt;

&lt;p&gt;If you want to take this further, there’s no reason why you can’t create your own Policy Definitions to alter the behaviour. Next time, I’ll show an example of how we can do that using Terraform.&lt;/p&gt;</content><author><name>Ben Hummerstone</name></author><summary type="html">Following on from my previous post about managing tags using Azure Policy and Terraform, there was some interest in how the Policies worked for both enforcing the tags, and making them inherit down from the Resource Group to the deployed resources.</summary></entry><entry><title type="html">Tag Management with Azure Policy and Terraform</title><link href="http://cloudscaping.co.uk/2020/06/18/terraform-azure-policy-tags/" rel="alternate" type="text/html" title="Tag Management with Azure Policy and Terraform" /><published>2020-06-18T00:00:00+00:00</published><updated>2020-06-18T00:00:00+00:00</updated><id>http://cloudscaping.co.uk/2020/06/18/terraform-azure-policy-tags</id><content type="html" xml:base="http://cloudscaping.co.uk/2020/06/18/terraform-azure-policy-tags/">&lt;p&gt;When deploying resources via Terraform, especially as part of a CI/CD pipeline, one of the main pain points I’ve seen from a Governance perspective is how best to manage tagging. For example, say I want to define the following tags for filtering and cost management purposes:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;business_owner&lt;/li&gt;
  &lt;li&gt;cost_centre&lt;/li&gt;
  &lt;li&gt;application_name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When we’re creating an individual resource, defining these tags is easy enough:&lt;/p&gt;

&lt;div class=&quot;language-terraform highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_resource_group&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tf_tags_rg&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tftags&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;North Europe&quot;&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;business_owner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;benhu&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;cost_centre&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;12345&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;application_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;myapp&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The problem is that we have to define the same tags for every resource we create. This is fine if there’s a handful, but what if there are tens or hundreds?&lt;/p&gt;

&lt;p&gt;I’ve seen people extract the tags to a separate variables file, which is definitely better, but you still then have to remember “tags = var.tags” in all your resources. What if there was a way to define tags on a Resource Group, and then have everything automatically inherit…&lt;/p&gt;

&lt;p&gt;In Azure, there is something called Azure Policy, and this is generally the mechanism used for tag management if using native tooling:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;First Policy blocks creation of a Resource Group if Tags are not defined&lt;/li&gt;
  &lt;li&gt;Second Policy auto-filters tags from the Resource Group down to the child resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, if you are deploying via Terraform, it won’t be aware of these tags as they are applied by the Azure Policy engine and therefore won’t be in the Terraform state. You could do some manual importing, but this is not sustainable in the long run.&lt;/p&gt;

&lt;p&gt;Luckily, there’s a pretty cool way you can do this using some custom lifecycle logic in your modules.&lt;/p&gt;

&lt;p&gt;Let’s look at our example from earlier. We’ll create an Azure Policy and link it to our Subscription to enforce these tags, as well as link some built-in Policies to inherit these tags to resources. Note, you can create and apply this Policy using Terraform… but that’s for another time.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/tf-tags-policies.png&quot; alt=&quot;Screenshot of Azure Policy screen showing assigned Policies&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To manage the tag lifecycle, we then need to add some code to our Terraform modules. Here, I’ve got a module that creates an Azure Storage Account, but uses the lifecycle keyword in Terraform to ignore changes to specific Tags:&lt;/p&gt;

&lt;div class=&quot;language-terraform highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# append random string to storage account for uniqueness&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;random_string&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;stg_suffix&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;special&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;upper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_storage_account&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;stg_acct&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;random_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stg_suffix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rg_name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;account_tier&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;account_replication_type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;replication&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;business_owner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;placeholder&quot;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;cost_centre&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;placeholder&quot;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;application_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;placeholder&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;lifecycle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;ignore_changes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;business_owner&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cost_centre&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;application_name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can then call this code as per below. Note that we only define Tags on the Resource Group, and not on the Storage Account itself:&lt;/p&gt;

&lt;div class=&quot;language-terraform highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_resource_group&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tf_tags_rg&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tftags&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;North Europe&quot;&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;business_owner&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;benhu&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;cost_centre&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;12345&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;application_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;myapp&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tf_tags_stg&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;./stg_tags&quot;&lt;/span&gt;
  
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bhtftag&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tf_tags_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;rg_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tf_tags_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Standard&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;replication&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;LRS&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We see that the Resource Group gets created with the required Tags, the Storage Account is created by Terraform using the placeholder tags, and finally the Storage Account inherits the tags as per the defined Policies:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/tf-tags-stg-create.png&quot; alt=&quot;Screenshot showing storage account being created with placeholder tags&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/tf-tags-stg-inherit.png&quot; alt=&quot;Screenshot showing inherited tags&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If we want to then update the Storage Account from LRS to GRS, we can see that only the replication type is being changed, and not the Tags due to the lifecycle policy we defined in the module:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/tf-tags-stg-update.png&quot; alt=&quot;Screenshot showing updated Storage Account in Terraform&quot; /&gt;&lt;/p&gt;</content><author><name>Ben Hummerstone</name></author><summary type="html">When deploying resources via Terraform, especially as part of a CI/CD pipeline, one of the main pain points I’ve seen from a Governance perspective is how best to manage tagging. For example, say I want to define the following tags for filtering and cost management purposes: business_owner cost_centre application_name</summary></entry><entry><title type="html">Deploying a Linux VM with CMK Disk Encryption using Terraform</title><link href="http://cloudscaping.co.uk/2020/06/16/terraform-vm-disk-encryption/" rel="alternate" type="text/html" title="Deploying a Linux VM with CMK Disk Encryption using Terraform" /><published>2020-06-16T00:00:00+00:00</published><updated>2020-06-16T00:00:00+00:00</updated><id>http://cloudscaping.co.uk/2020/06/16/terraform-vm-disk-encryption</id><content type="html" xml:base="http://cloudscaping.co.uk/2020/06/16/terraform-vm-disk-encryption/">&lt;p&gt;Continuing the recent Terraform theme, I’ve also been working on an example of how to deploy a VM in Azure using the new method of &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/virtual-machines/windows/disk-encryption#customer-managed-keys&quot;&gt;Disk Encryption with Customer Managed Keys&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is comprised of a few key bits of functionality:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A Disk Encryption Set to contain the disks to be encrypted&lt;/li&gt;
  &lt;li&gt;An Azure Key Vault to store the encryption keys, as well as access policies for the Disk Encryption Set and (optionally) the user deploying the code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This uses version 0.12 of the Terraform syntax, and was tested with version 2.13.0 of the Azure Provider. You’ll need a Service Principal with the necessary rights in order to create these resources; once you have the SP, fill in the details in the provider section.&lt;/p&gt;

&lt;div class=&quot;language-terraform highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# whilst the `version` attribute is optional, we recommend pinning to a given version of the Provider&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;=2.13.0&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;features&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;client_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;client_id&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;subscription_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;subscription_id&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;tenant_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tenant_id&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;client_secret&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;client_secret&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_client_config&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;current&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_resource_group&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt_rg&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;North Europe&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_virtual_network&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt_vnet&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt_vnet&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;address_space&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.0.0.0/16&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_subnet&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt_subnet&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                 &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt_subnet&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;virtual_network_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_virtual_network&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_vnet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;address_prefixes&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.0.0.0/24&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_network_interface&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt_nic&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt_nic&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;ip_configuration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                          &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ipconfig1&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;subnet_id&lt;/span&gt;                     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_subnet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_subnet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;private_ip_address_allocation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Dynamic&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_key_vault&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt_kv&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                        &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;des-tfencrypt-keyvault&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;                    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt;         &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;tenant_id&lt;/span&gt;                   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;azurerm_client_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tenant_id&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;enabled_for_disk_encryption&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;soft_delete_enabled&lt;/span&gt;         &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;purge_protection_enabled&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;sku_name&lt;/span&gt;                    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;standard&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_key_vault_access_policy&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt_kvuserpol&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;key_vault_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_key_vault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_kv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;tenant_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;azurerm_client_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tenant_id&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;object_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;azurerm_client_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;object_id&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;key_permissions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;get&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;create&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;delete&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_key_vault_key&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt_kvkey&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;         &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;des-tfencrypt-key&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;key_vault_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_key_vault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_kv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;key_type&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;RSA&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;key_size&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2048&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;depends_on&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;azurerm_key_vault_access_policy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_kvuserpol&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;key_opts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;decrypt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;encrypt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;sign&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;unwrapKey&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;verify&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;wrapKey&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_disk_encryption_set&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt_des&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt_des&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;key_vault_key_id&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_key_vault_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_kvkey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;identity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SystemAssigned&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_key_vault_access_policy&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt_kvdiskpol&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;key_vault_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_key_vault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_kv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;tenant_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_disk_encryption_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_des&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;identity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tenant_id&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;object_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_disk_encryption_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_des&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;identity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;principal_id&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;key_permissions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;get&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;decrypt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;encrypt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;sign&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;unwrapKey&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;verify&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;wrapKey&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_linux_virtual_machine&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt_vm&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;              &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;network_interface_ids&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;azurerm_network_interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_nic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;size&lt;/span&gt;               &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Standard_F2s&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;admin_username&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;benhu&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;admin_password&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Password1234!&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;disable_password_authentication&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;source_image_reference&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;publisher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Canonical&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;offer&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;UbuntuServer&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;sku&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;18.04-LTS&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;version&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;latest&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;os_disk&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;          &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfencrypt_osdisk&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;caching&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ReadWrite&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;storage_account_type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Premium_LRS&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;disk_encryption_set_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_disk_encryption_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfencrypt_des&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Ben Hummerstone</name></author><summary type="html">Continuing the recent Terraform theme, I’ve also been working on an example of how to deploy a VM in Azure using the new method of Disk Encryption with Customer Managed Keys.</summary></entry><entry><title type="html">Deploying the Azure Monitor Agent for Linux using Terraform</title><link href="http://cloudscaping.co.uk/2020/06/05/terraform-azmonitor-agent/" rel="alternate" type="text/html" title="Deploying the Azure Monitor Agent for Linux using Terraform" /><published>2020-06-05T00:00:00+00:00</published><updated>2020-06-05T00:00:00+00:00</updated><id>http://cloudscaping.co.uk/2020/06/05/terraform-azmonitor-agent</id><content type="html" xml:base="http://cloudscaping.co.uk/2020/06/05/terraform-azmonitor-agent/">&lt;p&gt;Hashicorp Terraform is a very popular tool for deploying and managing resources, both in a cloud environment or on-premises. The support in Azure for Terraform is excellent, but I had a bit of trouble getting the Azure Monitor agent installed as a VM Extension, so thought I would share my working code here.&lt;/p&gt;

&lt;p&gt;This uses version 0.12 of the Terraform syntax, and was tested with version 2.13.0 of the Azure Provider. You’ll need a Service Principal with the necessary rights in order to create these resources; once you have the SP, fill in the details in the provider section.&lt;/p&gt;

&lt;div class=&quot;language-terraform highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;=2.13.0&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;features&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;client_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;client_id&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;subscription_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;subscription_id&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;tenant_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tenant_id&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;client_secret&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;client_secret&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;random_string&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfazmon_lga_suffix&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;special&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;upper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_resource_group&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfazmon_rg&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfazmonitor&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;North Europe&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_virtual_network&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfazmon_vnet&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfazmonitor&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;address_space&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.0.0.0/16&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfazmon_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfazmon_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_subnet&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfazmon_subnet&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                 &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfazmonitor&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfazmon_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;virtual_network_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_virtual_network&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfazmon_vnet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;address_prefixes&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.0.0.0/24&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_network_interface&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfazmon_nic&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfazmonitor&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfazmon_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfazmon_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;ip_configuration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                          &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ipconfig1&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;subnet_id&lt;/span&gt;                     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_subnet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfazmon_subnet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;private_ip_address_allocation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Dynamic&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_log_analytics_workspace&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfazmon_lga&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bhtfazmonitor&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;random_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfazmon_lga_suffix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfazmon_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfazmon_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;sku&lt;/span&gt;                 &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;PerGB2018&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;retention_in_days&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;180&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_virtual_machine&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfazmon_vm&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfazmonitor&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;              &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfazmon_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resource_group_name&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_resource_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfazmon_rg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;network_interface_ids&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;azurerm_network_interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfazmon_nic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;vm_size&lt;/span&gt;               &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Standard_F2s&quot;&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;storage_image_reference&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;publisher&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Canonical&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;offer&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;UbuntuServer&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;sku&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;18.04-LTS&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;version&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;latest&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;storage_os_disk&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;          &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfazmonitor&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;caching&lt;/span&gt;       &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ReadWrite&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;create_option&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;FromImage&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;managed_disk_type&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Premium_LRS&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;os_profile&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;computer_name&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfazmonitor&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;admin_username&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;benhu&quot;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;admin_password&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Password1234!&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;os_profile_linux_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;disable_password_authentication&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;resource&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;azurerm_virtual_machine_extension&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tfazmon_ext&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;                 &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;OmsAgentForLinux&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;virtual_machine_id&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;azurerm_virtual_machine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tfazmon_vm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;publisher&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Microsoft.EnterpriseCloud.Monitoring&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;                 &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;OmsAgentForLinux&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;type_handler_version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;1.12&quot;&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;auto_upgrade_minor_version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;settings&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SETTINGS&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
    {
        &quot;workspaceId&quot;: &quot;${azurerm_log_analytics_workspace.tfazmon_lga.workspace_id}&quot;
    }
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SETTINGS

&lt;/span&gt;    &lt;span class=&quot;nx&quot;&gt;protected_settings&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PROTECTEDSETTINGS&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
    {
        &quot;workspaceKey&quot;: &quot;${azurerm_log_analytics_workspace.tfazmon_lga.primary_shared_key}&quot;
    }
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PROTECTEDSETTINGS
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Ben Hummerstone</name></author><summary type="html">Hashicorp Terraform is a very popular tool for deploying and managing resources, both in a cloud environment or on-premises. The support in Azure for Terraform is excellent, but I had a bit of trouble getting the Azure Monitor agent installed as a VM Extension, so thought I would share my working code here.</summary></entry><entry><title type="html">Automating Azure Bastion using PowerShell Functions</title><link href="http://cloudscaping.co.uk/2019/11/29/azure-function-powershell-bastion/" rel="alternate" type="text/html" title="Automating Azure Bastion using PowerShell Functions" /><published>2019-11-29T00:00:00+00:00</published><updated>2019-11-29T00:00:00+00:00</updated><id>http://cloudscaping.co.uk/2019/11/29/azure-function-powershell-bastion</id><content type="html" xml:base="http://cloudscaping.co.uk/2019/11/29/azure-function-powershell-bastion/">&lt;p&gt;Inspired by a conversation with a colleague about “Just In Time” access to workloads, I decided to investigate how to automate provisioning and de-provisioning Azure Bastion on weekdays only to reduce the overall running costs.&lt;/p&gt;

&lt;p&gt;Here’s a quick overview of my (self-imposed) requirements for the solution:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Deploy new at 08:30 each weekday, remove at 18:00&lt;/li&gt;
  &lt;li&gt;Minimise running costs for automation&lt;/li&gt;
  &lt;li&gt;No passwords to manage&lt;/li&gt;
  &lt;li&gt;No hard-coding of values in code&lt;/li&gt;
  &lt;li&gt;Keep same public IP. In theory, Bastion can create a new Public IP for you each time, but it helps to keep this static for e.g. whitelisting purposes&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;step-1---creating-the-static-resources&quot;&gt;Step 1 - Creating the Static Resources&lt;/h2&gt;
&lt;p&gt;In order for an Azure Bastion host to be created, it requires a few pre-requisites:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Resource group in which to live&lt;/li&gt;
  &lt;li&gt;Virtual Network in a supported region (e.g. West Europe)&lt;/li&gt;
  &lt;li&gt;Subnet of a suitable size (at least /27) in that virtual network with the name AzureBastionSubnet&lt;/li&gt;
  &lt;li&gt;Public IP in the same region as the virtual network with the Standard SKU&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, the first step is to create these resources. I chose to use the Azure CLI from the Cloud Shell to do this, but use whatever method works for you:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az group create &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; bh-bastion &lt;span class=&quot;nt&quot;&gt;--location&lt;/span&gt; westeurope
az network vnet create &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; bh-bastion-vnet &lt;span class=&quot;nt&quot;&gt;--resource-group&lt;/span&gt; bh-bastion &lt;span class=&quot;nt&quot;&gt;--location&lt;/span&gt; westeurope &lt;span class=&quot;nt&quot;&gt;--address-prefixes&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'10.0.0.0/16'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--subnet-name&lt;/span&gt; AzureBastionSubnet &lt;span class=&quot;nt&quot;&gt;--subnet-prefixes&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'10.0.255.0/24'&lt;/span&gt;
az network public-ip create &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; bh-bastion-pip &lt;span class=&quot;nt&quot;&gt;--resource-group&lt;/span&gt; bh-bastion &lt;span class=&quot;nt&quot;&gt;--location&lt;/span&gt; westeurope &lt;span class=&quot;nt&quot;&gt;--allocation-method&lt;/span&gt; Static &lt;span class=&quot;nt&quot;&gt;--sku&lt;/span&gt; Standard
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Make a note of the names and Resource Group for these, as we’ll need them later.&lt;/p&gt;

&lt;h2 id=&quot;step-2---creating-the-function-app&quot;&gt;Step 2 - Creating the Function App&lt;/h2&gt;
&lt;p&gt;Now that we have our “landing zone” for the Bastion host, we need a way of deploying and destroying it on a regular basis. There are numerous different ways of doing this, but one that meets all of the requirements is doing this using PowerShell in an Azure Function App. PowerShell support went GA recently, and Azure Functions have some excellent built-in features such as Timer triggers and Managed Identities that align with our goals. We can also run this on a Consumption plan, so we only pay for the time the commands are running: perfect for requirement #2.&lt;/p&gt;

&lt;p&gt;We can also assign some variables as Application Settings in the Function to tick requirement #4.&lt;/p&gt;

&lt;p&gt;Creating a new Function App is also pretty straightforward:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az storage account create &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; bhstgfuncbastion &lt;span class=&quot;nt&quot;&gt;--location&lt;/span&gt; westeurope &lt;span class=&quot;nt&quot;&gt;--resource-group&lt;/span&gt; bh-bastion &lt;span class=&quot;nt&quot;&gt;--sku&lt;/span&gt; Standard_LRS
az functionapp create &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; bhfuncbastion &lt;span class=&quot;nt&quot;&gt;--resource-group&lt;/span&gt; bh-bastion &lt;span class=&quot;nt&quot;&gt;--consumption-plan-location&lt;/span&gt; westeurope &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; bhfuncbastion &lt;span class=&quot;nt&quot;&gt;--storage-account&lt;/span&gt; bhstgfuncbastion &lt;span class=&quot;nt&quot;&gt;--os-type&lt;/span&gt; Windows &lt;span class=&quot;nt&quot;&gt;--runtime&lt;/span&gt; powershell &lt;span class=&quot;nt&quot;&gt;--disable-app-insights&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true
&lt;/span&gt;az functionapp config appsettings &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; bhfuncbastion &lt;span class=&quot;nt&quot;&gt;--resource-group&lt;/span&gt; bh-bastion &lt;span class=&quot;nt&quot;&gt;--settings&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;BASTION_VNET_NAME=bh-bastion-vnet&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;BASTION_VNET_RG=bh-bastion&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;BASTION_PIP_NAME=bh-bastion-pip&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;BASTION_PIP_RG=bh-bastion&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;BASTION_NAME=bh-bastion&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;BASTION_RG=bh-bastion&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-3---setting-up-identity-and-access-control&quot;&gt;Step 3 - Setting up Identity and Access Control&lt;/h2&gt;
&lt;p&gt;For our Function App to be able to perform actions within Azure, it needs to have permission to deploy and remove resources from the Bastion resource group. Functions has an option to assign a Managed Identity, which is an identity for the Function App itself that exists in Azure Active Directory and can be combined with Role Based Access Control to grant permissions as required; this also ticks off requirement #3.&lt;/p&gt;

&lt;p&gt;In our case, let’s assign the identity and then give it Contributor access over the Bastion Resource Group.&lt;/p&gt;

&lt;p&gt;In an ideal world, I would use the following command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az functionapp identity assign &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; bhfuncbastion &lt;span class=&quot;nt&quot;&gt;--resource-group&lt;/span&gt; bh-bastion &lt;span class=&quot;nt&quot;&gt;--role&lt;/span&gt; Contributor &lt;span class=&quot;nt&quot;&gt;--scope&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;az group show &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; bh-bastion &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'id'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; tsv&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, this doesn’t currently work; see &lt;a href=&quot;https://github.com/Azure/azure-cli/issues/11435&quot;&gt;this GitHub Issue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As a workaround, you can enable the Managed Identity through the Portal:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Browse to your Function App in the Azure Portal&lt;/li&gt;
  &lt;li&gt;Click on Platform features&lt;/li&gt;
  &lt;li&gt;Click on Identity, which is under Networking&lt;/li&gt;
  &lt;li&gt;Change the Status to On&lt;/li&gt;
  &lt;li&gt;Hit Save, and click Yes when prompted&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Copy the object ID shown on the screen, and we can assign the Contributor role as follows:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az role assignment create &lt;span class=&quot;nt&quot;&gt;--assignee&lt;/span&gt; &amp;lt;object_id_from_function_app&amp;gt; &lt;span class=&quot;nt&quot;&gt;--role&lt;/span&gt; Contributor &lt;span class=&quot;nt&quot;&gt;--scope&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;az group show &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; bh-bastion &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'id'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; tsv&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-4---creating-the-createremove-functions&quot;&gt;Step 4 - Creating the Create/Remove Functions&lt;/h2&gt;
&lt;p&gt;In terms of creating the Functions themselves, the easiest way I’ve found to do this is either using the built-in editor in the Azure Portal, or by using Visual Studio Code. If you end up doing any extensive work with Functions I highly recommend the latter option, but for now let’s work in the Portal.&lt;/p&gt;

&lt;p&gt;For the Create Function:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Browse to your Function App in the Azure Portal&lt;/li&gt;
  &lt;li&gt;On the left hand side, under the main Function drop down, click on Functions, then on the + New function button at the top&lt;/li&gt;
  &lt;li&gt;From the templates, choose Timer trigger&lt;/li&gt;
  &lt;li&gt;Give the Function a suitable name e.g. Create_Azure_Bastion&lt;/li&gt;
  &lt;li&gt;For the Schedule, enter “0 30 8 * * 1-5”. This is cron syntax for “08:30 on days 1-5” i.e. Monday to Friday&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should now be in the in-Portal editor. Replace the code with the PowerShell located &lt;a href=&quot;https://raw.githubusercontent.com/bhummerstone/azure-function-bastion/master/powershell/Create_Azure_Bastion.ps1&quot;&gt;here&lt;/a&gt;, and hit Save.&lt;/p&gt;

&lt;p&gt;For the Remove Function:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Browse to your Function App in the Azure Portal&lt;/li&gt;
  &lt;li&gt;On the left hand side, under the main Function drop down, click on Functions, then on the + New function button at the top&lt;/li&gt;
  &lt;li&gt;From the templates, choose Timer trigger&lt;/li&gt;
  &lt;li&gt;Give the Function a suitable name e.g. Remove_Azure_Bastion&lt;/li&gt;
  &lt;li&gt;For the Schedule, enter “0 0 18 * * 1-5”. This is cron syntax for “18:00 on days 1-5” i.e. Monday to Friday&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As before, replace the code with the PowerShell located &lt;a href=&quot;https://raw.githubusercontent.com/bhummerstone/azure-function-bastion/master/powershell/Remove_Azure_Bastion.ps1&quot;&gt;here&lt;/a&gt; and hit Save.&lt;/p&gt;

&lt;h2 id=&quot;step-5---increase-function-app-timeout&quot;&gt;Step 5 - Increase Function App Timeout&lt;/h2&gt;

&lt;p&gt;One final (optional) item: in my testing, I occasionally saw the Removal take longer than the default 5 minute timeout supported by Functions (never the creation, oddly enough!). To workaround this, we can change the default timeout for our Functions to 10 minutes instead.&lt;/p&gt;

&lt;p&gt;This is achieved by editing the host.json file for your Function App. This can be done locally using Visual Studio Code and the Function Core Tools, but you can also do this in the Portal:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Browse to your Function App in the Azure Portal&lt;/li&gt;
  &lt;li&gt;Click on Platform features&lt;/li&gt;
  &lt;li&gt;Click on App Service Editor, which is under Development Tools. This should open a new tab, allowing us to edit the files in the Function App&lt;/li&gt;
  &lt;li&gt;Click on host.json, which should then open in the right hand pane&lt;/li&gt;
  &lt;li&gt;Add the following code as a new line: “functionTimeout”: “00:10:00”&lt;/li&gt;
  &lt;li&gt;The file automatically saves after you make a change, so you can now close the tab&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;There we have it: one Function App, two Functions, and all of our requirements nicely met.&lt;/p&gt;

&lt;p&gt;From a potential saving perspective, I did some rough calculations using the Azure Pricing Calculator:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Running Bastion for 730 hours (roughly 24/7 for 1 month) in West Europe comes to &lt;strong&gt;£51.69&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;With the Create/Remove method, we run for 9.5 hours/day, weekdays only, and if we say 21 weekdays per month, this is 199.5 hours, which totals &lt;strong&gt;£14.16&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s over 70% saving!&lt;/p&gt;

&lt;p&gt;Also, total running cost of our Functions? Free. Even if they run for the maximum of 10 minutes each time. Not bad for 5 lines of PowerShell.&lt;/p&gt;</content><author><name>Ben Hummerstone</name></author><summary type="html">Inspired by a conversation with a colleague about “Just In Time” access to workloads, I decided to investigate how to automate provisioning and de-provisioning Azure Bastion on weekdays only to reduce the overall running costs.</summary></entry><entry><title type="html">On-Demand, Serverless SFTP</title><link href="http://cloudscaping.co.uk/2019/04/11/on-demand-serverless-sftp/" rel="alternate" type="text/html" title="On-Demand, Serverless SFTP" /><published>2019-04-11T00:00:00+00:00</published><updated>2019-04-11T00:00:00+00:00</updated><id>http://cloudscaping.co.uk/2019/04/11/on-demand-serverless-sftp</id><content type="html" xml:base="http://cloudscaping.co.uk/2019/04/11/on-demand-serverless-sftp/">&lt;p&gt;SSH File Transfer Protocol or SFTP has been around for over 20 years, and still remains a great way to transfer files securely, specifically allowing the use of SSH keys to ensure that only the right users can perform the necessary operations.&lt;/p&gt;

&lt;p&gt;One such scenario is the ability to receive files from known third-parties. This would generally involve having a server running 24/7, and possibly left open to the internet all the time as well. Even in the cloud, having a VM (or two for HA) switched on full time for such a simple task can quickly rack up costs.&lt;/p&gt;

&lt;p&gt;So I thought about creating an on-demand SFTP service. This could easily be done in a VM by just starting and stopping it, but VMs are quite heavyweight for just receiving files; let’s see if we can create an on-demand SFTP service with no VMs required!&lt;/p&gt;

&lt;p&gt;Containers are a good choice as a stand-in for VMs: their start times are generally much faster, and they can contain (pun intended) just the services we require; in our case, OpenSSH.&lt;/p&gt;

&lt;p&gt;Microsoft Azure provides &lt;a href=&quot;https://azure.microsoft.com/en-us/services/container-instances/&quot;&gt;Azure Container Instances (ACI)&lt;/a&gt; for this purpose, which is an incredibly easy way to run a container image: just give it an image from Docker Hub and you’re pretty much set.&lt;/p&gt;

&lt;p&gt;However, since ACI abstracts away from the underlying infrastructure running these containers, we need to use some kind of remote file system for persistent storage; enter &lt;a href=&quot;https://azure.microsoft.com/en-gb/services/storage/files/&quot;&gt;Azure Files&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;ACI provides native support for mounting Azure Files shares within running containers, so this is perfect for my requirements.&lt;/p&gt;

&lt;p&gt;Since I started writing this blog post, this usage of ACI has been made into a formal Azure Sample, so a reference template can be found &lt;a href=&quot;https://azure.microsoft.com/en-gb/resources/samples/sftp-creation-template/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;

&lt;p&gt;I’ll be doing all of this in the Azure CLI, which you can download from &lt;a href=&quot;https://docs.microsoft.com/en-us/cli/azure/install-azure-cli&quot;&gt;here&lt;/a&gt; or access via the &lt;a href=&quot;https://shell.azure.com&quot;&gt;Azure Cloud Shell&lt;/a&gt;. Also, ACI isn’t available in all of the Azure regions yet, so I’ll be using West Europe; make sure you check availability in your region of choice.&lt;/p&gt;

&lt;p&gt;Firstly, let’s create a new Resource Group to hold the resources we’re going to be creating:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az group create &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; bh-rg-sftp-we-01 &lt;span class=&quot;nt&quot;&gt;--location&lt;/span&gt; westeurope
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, we need to create the storage account. This will be used to host our file shares to be mounted into the containers at runtime:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az storage account create &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; bhstgsftpwe01 &lt;span class=&quot;nt&quot;&gt;--location&lt;/span&gt; westeurope &lt;span class=&quot;nt&quot;&gt;--resource-group&lt;/span&gt; bh-rg-sftp-we-01 &lt;span class=&quot;nt&quot;&gt;-sku&lt;/span&gt; Standard_LRS &lt;span class=&quot;nt&quot;&gt;--kind&lt;/span&gt; Storage
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, let’s create the file share that we’ll mount into the ACI:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;connection_string &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;az storage account show-connection-string &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &amp;lt;storage-account&amp;gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; &amp;lt;resource-group&amp;gt; &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'connectionString'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; tsv&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
az storage share create &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; upload &lt;span class=&quot;nt&quot;&gt;--quota&lt;/span&gt; 1024 &lt;span class=&quot;nt&quot;&gt;--connection-string&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$connection_string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;choosing-our-image&quot;&gt;Choosing our image&lt;/h2&gt;

&lt;p&gt;One of the best things about containers is the incredible community that has grown as part of this movement. Central to this is the work that Docker have done to maintain central image repositories such as Docker Hub to enable people to upload and share images that they have created.&lt;/p&gt;

&lt;p&gt;With this in mind, I decided to use one of these pre-existing images for our SFTP server; specifically the sftp image created by atmoz, which is available &lt;a href=&quot;https://hub.docker.com/r/atmoz/sftp/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are a number of different ways to get the user/password details into this particular container, one of which is by using the SFTP_USERS environment variable. This needs to be in the format username:password:userID e.g. ftpuser:Password1234:1001. This environment variable will be composed of various parameters passed to a template at deployment time.&lt;/p&gt;

&lt;h2 id=&quot;creating-the-aci-template&quot;&gt;Creating the ACI template&lt;/h2&gt;

&lt;p&gt;I decided to use an Azure Resource Manager (ARM) template to create the resources required for this project. This will allow us to define the infrastructure in code, and take advantage of all the usual version control and Continuous Integration/Continuous Deployment (CI/CD) pipelines. Note that you can use any other method of your choice for deploying the ACI e.g. Azure CLI, PowerShell, Terraform etc.&lt;/p&gt;

&lt;p&gt;The template schema for creating ACIs is pretty straightforward, so let’s focus on the sections responsible for mounting the Azure File Share. First, defining the mount point into the ACI:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[variables('sftpContainerName')]&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
                &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;volumeMounts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[concat('/home/', parameters('sftpUser'), '/upload')]&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;sftpvolume&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;readOnly&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, we are telling the ACI to mount a share (yet to be defined) called “sftpvolume” at the location /home/&lt;username&gt;/upload. The username is defined as a parameter of the template, and so is dynamically set at deployment time.&lt;/username&gt;&lt;/p&gt;

&lt;p&gt;The next section defines the volume to be mounted:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;sftpvolume&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;azureFile&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;readOnly&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shareName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[parameters('existingFileShareName')]&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;storageAccountName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[parameters('existingStorageAccountName')]&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;storageAccountKey&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[listKeys(variables('storageAccountId'),'2018-02-01').keys[0].value]&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This section of the template defines the volume we referenced previously. It is of type “azureFile” i.e. an Azure Files share, and we pass the share name and storage account names as parameters at deployment time. The storage account key is required to access this type of volume, so the listKeys() function is used to extract this on the fly (as long as the user deploying the template has permissions to do so).&lt;/p&gt;

&lt;p&gt;Once we have this template defined, we can deploy it via the Azure CLI:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az group deployment create &lt;span class=&quot;nt&quot;&gt;--resource-group&lt;/span&gt; bh-rg-sftp-we-01 &lt;span class=&quot;nt&quot;&gt;--template-file&lt;/span&gt; aci-sftp.json &lt;span class=&quot;nt&quot;&gt;--existingFileShareName&lt;/span&gt; xxx &lt;span class=&quot;nt&quot;&gt;--existingStorageAccountName&lt;/span&gt; yyy ... etc.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once deployed, connect via SFTP, copy some files, and they will appear in your Azure Files share. :)&lt;/p&gt;

&lt;p&gt;Remember to delete the ACI once you have finished copying files to ensure you don’t rack up unnecessary costs:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az aci delete &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; bhsftpaci01 &lt;span class=&quot;nt&quot;&gt;--resource-group&lt;/span&gt; bh-rg-sftp-we-01
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;taking-it-further&quot;&gt;Taking it further&lt;/h2&gt;

&lt;p&gt;There are a few potential ways to extend this implementation, or to customise it to meet your requirements. For example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Use SSH instead of passwords: this can be achieved by mounting a second Azure Files share into /home/username/.ssh/keys
** example &lt;a href=&quot;https://github.com/bhummerstone/azure-templates/blob/master/compute/sftp/sftp-config-file.json&quot;&gt;here&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Create your own custom image with e.g. custom host keys defined&lt;/li&gt;
  &lt;li&gt;Use the ACI Logic Apps connector to create the ACI as part of a wider workflow&lt;/li&gt;
  &lt;li&gt;Run the container image in a Kubernetes cluster with the Azure Files share defined as a persistent volume&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One key thing to remember is that the ACI should not be a long-lived object: if you need something to always be available, consider deploying a VM to fulfil that role. However, since the SFTP service only really needs to be online for the period of the transfer, it is well worth considering the value that this on-demand SFTP can bring.&lt;/p&gt;</content><author><name>Ben Hummerstone</name></author><summary type="html">SSH File Transfer Protocol or SFTP has been around for over 20 years, and still remains a great way to transfer files securely, specifically allowing the use of SSH keys to ensure that only the right users can perform the necessary operations.</summary></entry><entry><title type="html">CI/CD with Kubernetes on ACS - Part 3 - Configuring VSTS</title><link href="http://cloudscaping.co.uk/2017/08/24/ci-cd-kubernetes-acs-pt3/" rel="alternate" type="text/html" title="CI/CD with Kubernetes on ACS - Part 3 - Configuring VSTS" /><published>2017-08-24T00:00:00+00:00</published><updated>2017-08-24T00:00:00+00:00</updated><id>http://cloudscaping.co.uk/2017/08/24/ci-cd-kubernetes-acs-pt3</id><content type="html" xml:base="http://cloudscaping.co.uk/2017/08/24/ci-cd-kubernetes-acs-pt3/">&lt;p&gt;Part 1 - &lt;a href=&quot;/2017/04/12/ci-cd-kubernetes-acs-pt1/&quot;&gt;Introduction&lt;/a&gt;&lt;br /&gt;
Part 2 - &lt;a href=&quot;/2017/04/12/ci-cd-kubernetes-acs-pt2/&quot;&gt;Creating ACS &amp;amp; ACR&lt;/a&gt;&lt;br /&gt;
Part 3 - Configuring VSTS (this post)&lt;br /&gt;
Part 4 - &lt;a href=&quot;&quot;&gt;Kubernetes-ifying Application&lt;/a&gt;&lt;br /&gt;
Part 5 - &lt;a href=&quot;&quot;&gt;Build Definition&lt;/a&gt;&lt;br /&gt;
Part 6 - &lt;a href=&quot;&quot;&gt;Release Definition&lt;/a&gt;&lt;br /&gt;
Part 7 - &lt;a href=&quot;&quot;&gt;Wrap-up&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;getting-started-with-vsts&quot;&gt;Getting Started with VSTS&lt;/h2&gt;
&lt;p&gt;Our next step is to get our Visual Studio Team Services (VSTS) environment ready to go. If you haven’t got a VSTS account already, head over to https://www.visualstudio.com/team-services/ and sign up for free.&lt;/p&gt;

&lt;p&gt;Once you have a VSTS account, create a new project; keep the default settings for now.&lt;/p&gt;

&lt;p&gt;The first thing we’ll need to do is to authorise VSTS with GitHub so that it can access your repositories. Note that it is perfectly appropriate, and arguably preferable, to use VSTS itself to host your code, but mine is in GitHub so this step is required!&lt;/p&gt;

&lt;p&gt;In your project, click on the little cog at the top to go to Settings, then select Services and New Service Endpoint -&amp;gt; GitHub. You can choose to either autorise VSTS entirely, or pre-create a Personal Access Token in GitHub for VSTS to use. I chose to “Grant Authorization”, and gave the endpoint a suitable name.&lt;/p&gt;

&lt;h2 id=&quot;add-acr-to-vsts&quot;&gt;Add ACR to VSTS&lt;/h2&gt;
&lt;p&gt;Next, we’ll need to create another Service Endpoint for our ACR. In the same Settings -&amp;gt; Services screen as before, choose New Service Endpoint -&amp;gt; Docker Registry.&lt;/p&gt;

&lt;p&gt;Choose a suitably descriptive name for the connection, and enter your full ACR name in the “Docker Registry” box i.e. myacr-microsoft.azurecr.io.&lt;/p&gt;

&lt;p&gt;I hope you noted down the appID and Password from &lt;a href=&quot;/2017/04/12/ci-cd-kubernetes-acs-pt2/&quot;&gt;Part 2&lt;/a&gt;, because you need to enter these as the Docker ID and Password respectively.&lt;/p&gt;

&lt;h2 id=&quot;add-ssh-to-vsts&quot;&gt;Add SSH to VSTS&lt;/h2&gt;
&lt;p&gt;The final service endpoint we need is an SSH connection to our k8s master. Choose New Service Endpoint -&amp;gt; SSH to create the connection.&lt;/p&gt;

&lt;p&gt;The Host name should be the DNS name associated with your Master nodes; in my example it is bhk8s.westeurope.cloudapp.azure.com.&lt;/p&gt;

&lt;p&gt;The User name will be azureuser, and you can get the Private Key by SSH-ing into the k8s master and copying it from there (it will be in ~/.ssh/id_rsa by default).&lt;/p&gt;

&lt;h2 id=&quot;create-the-vsts-build-agent&quot;&gt;Create the VSTS Build Agent&lt;/h2&gt;
&lt;p&gt;This step is optional, but I found having a custom VSTS build agent useful to understand what was going on and for debugging purposes. In addition, depending on your requirements you may need some additional software that isn’t installed on the Hosted agents, or you may burn through your free build/release time quite quickly.&lt;/p&gt;

&lt;p&gt;Anyhow, if you do decide to create your own agent, the first step is to create a new agent pool, which can be done by going to Settings -&amp;gt; Agent queues -&amp;gt; New queue…&lt;/p&gt;

&lt;p&gt;Once you have the queue/pool configured, you will need to create a VSTS Personal Access Token for you agent to use. The full steps are &lt;a href=&quot;https://www.visualstudio.com/en-us/docs/setup-admin/team-services/use-personal-access-tokens-to-authenticate&quot;&gt;here&lt;/a&gt;, and you will need to assign permissions for “Agent Pools (read, manage)” for the token. Make a note of the token for later.&lt;/p&gt;

&lt;p&gt;Finaly, it is time to set up the build agent. I used a new Ubuntu 16.04 VM (running in Azure, natch). I didn’t do anything special apart from installing latest updates and &lt;a href=&quot;https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/#install-docker-ce&quot;&gt;installing Docker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You’ll also need to install a few pre-reqs for the build agent:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; libunwind8 libcurl3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The full steps for installing the agent are &lt;a href=&quot;https://www.visualstudio.com/en-us/docs/build/actions/agents/v2-linux&quot;&gt;here&lt;/a&gt;. The documentation is pretty good and up-to-date, but remember to configure it to join the correct Agent Queue that we created earlier.&lt;/p&gt;

&lt;p&gt;Now we’ve got VSTS sorted, the next step is to turn the existing Docker Compose file into a Kubernetes YAML; join me in &lt;a href=&quot;&quot;&gt;Part 4&lt;/a&gt; for that.&lt;/p&gt;</content><author><name>Ben Hummerstone</name></author><summary type="html">Part 1 - Introduction Part 2 - Creating ACS &amp;amp; ACR Part 3 - Configuring VSTS (this post) Part 4 - Kubernetes-ifying Application Part 5 - Build Definition Part 6 - Release Definition Part 7 - Wrap-up</summary></entry><entry><title type="html">Extending Storage Spaces Volumes</title><link href="http://cloudscaping.co.uk/2017/04/22/extending-storage-spaces-volumes/" rel="alternate" type="text/html" title="Extending Storage Spaces Volumes" /><published>2017-04-22T00:00:00+00:00</published><updated>2017-04-22T00:00:00+00:00</updated><id>http://cloudscaping.co.uk/2017/04/22/extending-storage-spaces-volumes</id><content type="html" xml:base="http://cloudscaping.co.uk/2017/04/22/extending-storage-spaces-volumes/">&lt;p&gt;One of the cool features introduced in Windows Server 2012 was Storage Spaces, which is the ability to create Storage Pools that span multiple physical disks; it is sort of like software RAID on steroids. Storage Spaces was further enhanced in 2012 R2, and some additional polish has been added in 2016.&lt;/p&gt;

&lt;p&gt;The most useful application of Storage Spaces in recent times is the ability to go beyond the disk size limit in public clouds such as Azure and AWS by adding multiple virtual hard drives to your VMs and creating a Storage Pool over the top.&lt;/p&gt;

&lt;p&gt;However, what happens when you want to dynamically extend a virtual disk created on top of a Storage Pool? Do you need to shut everything down, and potentially stop one of your business critical applications from running? Happily, no: you can dynamically expand the size of your virtual disks using the following PowerShell:&lt;/p&gt;

&lt;p&gt;First, create the Storage Pool. This command gets all available data disks attached to the VM and adds them to the pool:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;New-StoragePool&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;–&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FriendlyName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;StoragePool1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-StorageSubSystemFriendlyName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Windows Storage*'&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-PhysicalDisks&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-PhysicalDisk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-CanPool&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, create a virtual disk on top of the Storage Pool and format it to get a partition:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$Disks&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-StoragePool&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;–&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FriendlyName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;StoragePool1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-IsPrimordial&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$False&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-PhysicalDisk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;New-VirtualDisk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;–&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FriendlyName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;VirtualDisk1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-ResiliencySettingName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Simple&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;–&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;NumberOfColumns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;–&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UseMaximumSize&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;–&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Interleave&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;256KB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-StoragePoolFriendlyName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;StoragePool1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-VirtualDisk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;–&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;FriendlyName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;VirtualDisk1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-Disk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Initialize-Disk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;–&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Passthru&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;New-Partition&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;–&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AssignDriveLetter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;–&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;UseMaximumSize&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Format-Volume&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;–&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AllocationUnitSize&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;64KB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A few important things to note about the above commands:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;ResiliencySettingName = Simple: this is equivalent to RAID 0 i.e. write the data over multiple disks, no additional copies required&lt;/li&gt;
  &lt;li&gt;NumberOfColumns = 2: the number of disks over which to write data simulataneously. By specifying 2 here, we’ll need to add new disks in multiples of 2 to be able to add them to the Pool&lt;/li&gt;
  &lt;li&gt;Interleave = 256KB: how much data is written to each column simultaneously; adjust to suit your workload&lt;/li&gt;
  &lt;li&gt;AllocationUnitSize = 64KB: choose the best size for your workload&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I highly recommend checking out &lt;a href=&quot;https://technet.microsoft.com/library/mt243829.aspx&quot;&gt;this article&lt;/a&gt; on Storage Spaces design to help choose the best configuration for you.&lt;/p&gt;

&lt;p&gt;Once we have the pool configured and a volume created, we can extend it further by adding more virtual disks; remember that the number of disks you will need to add depends on the Resiliency and Column settings you chose above.&lt;/p&gt;

&lt;p&gt;Add some more disks to your VM without shutting it down, and they should appear inside the OS. We can add these disks to the pool and then confirm the available size for disks:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;Get-StoragePool&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-FriendlyName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;StoragePool1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add-PhysicalDisk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-PhysicalDisks&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-PhysicalDisk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-CanPool&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-StoragePool&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-FriendlyName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;StoragePool1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-VirtualDiskSupportedSize&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-ResiliencySettingName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Simple&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, we need to increase the virtual disk to take up the additional space in the pool:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$disksize&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-VirtualDisk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-FriendlyName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;VirtualDisk1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Size&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$poolsize&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-VirtualDiskSupportedSize&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-StoragePoolFriendlyName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;StoragePool1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-ResiliencySettingName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Simple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;VirtualDiskSizeMax&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Resize-VirtualDisk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-FriendlyName&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;VirtualDisk1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Size&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$disksize&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$poolsize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, we can extend the required partitions to take up the available space:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$VirtualDisk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-VirtualDisk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;VirtualDisk1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Partition&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$VirtualDisk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-Disk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-Partition&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Where&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PartitionNumber&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-Eq&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Partition&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Resize-Partition&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-Size&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Partition&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Get-PartitionSupportedSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SizeMax&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Hope this helps!&lt;/p&gt;</content><author><name>Ben Hummerstone</name></author><summary type="html">One of the cool features introduced in Windows Server 2012 was Storage Spaces, which is the ability to create Storage Pools that span multiple physical disks; it is sort of like software RAID on steroids. Storage Spaces was further enhanced in 2012 R2, and some additional polish has been added in 2016.</summary></entry><entry><title type="html">CI/CD with Kubernetes on ACS - Part 1 - Introduction</title><link href="http://cloudscaping.co.uk/2017/04/12/ci-cd-kubernetes-acs-pt1/" rel="alternate" type="text/html" title="CI/CD with Kubernetes on ACS - Part 1 - Introduction" /><published>2017-04-12T00:00:00+00:00</published><updated>2017-04-12T00:00:00+00:00</updated><id>http://cloudscaping.co.uk/2017/04/12/ci-cd-kubernetes-acs-pt1</id><content type="html" xml:base="http://cloudscaping.co.uk/2017/04/12/ci-cd-kubernetes-acs-pt1/">&lt;p&gt;Part 1 - Introduction (this post)&lt;br /&gt;
Part 2 - &lt;a href=&quot;/2017/04/12/ci-cd-kubernetes-acs-pt2/&quot;&gt;Creating ACS &amp;amp; ACR&lt;/a&gt;&lt;br /&gt;
Part 3 - &lt;a href=&quot;/2017/08/24/ci-cd-kubernetes-acs-pt3/&quot;&gt;Configuring VSTS&lt;/a&gt;&lt;br /&gt;
Part 4 - &lt;a href=&quot;&quot;&gt;Kubernetes-ifying Application&lt;/a&gt;&lt;br /&gt;
Part 5 - &lt;a href=&quot;&quot;&gt;Build Definition&lt;/a&gt;&lt;br /&gt;
Part 6 - &lt;a href=&quot;&quot;&gt;Release Definition&lt;/a&gt;&lt;br /&gt;
Part 7 - &lt;a href=&quot;&quot;&gt;Wrap-up&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I read an &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/container-service/container-service-docker-swarm-setup-ci-cd&quot;&gt;article&lt;/a&gt; recently that discussed implementing Continuous Integration &amp;amp; Continuous Deployment (CI/CD) using Visual Studio Team Services (VSTS) to a Docker Swarm cluster on Azure Container Service (ACS); acronyms-ahoy!&lt;/p&gt;

&lt;p&gt;This seemed like an excellent use of all the associated technologies, but it got me thinking: how would this work with Kubernetes as the orchestrator rather than Docker Swarm?&lt;/p&gt;

&lt;p&gt;As a relative newcomer to container technologies and orchestrators it was a bit of an arduous task, but I finally managed to get my Build and Release pipelines sorted and deploying a (very simple) ASP.NET Core application to a Kubernetes cluster hosted on ACS.&lt;/p&gt;

&lt;p&gt;So, starting from the beginning, what you will need to follow along with this series:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;An Azure account (free trial should be sufficient if you don’t have one already)&lt;/li&gt;
  &lt;li&gt;A Visual Studio Team Services account&lt;/li&gt;
  &lt;li&gt;A Github account&lt;/li&gt;
  &lt;li&gt;Some knowledge of git &amp;amp; Linux command line&lt;/li&gt;
  &lt;li&gt;It will be helpful to understand the basics of Kubernetes, but I’ll try to explain stuff as I go along&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used the Azure CLI v2, which you can either install locally or run as a Docker container, but Azure PowerShell should work just fine as well. You can get the Azure CLI v2 from &lt;a href=&quot;https://docs.microsoft.com/en-us/cli/azure/install-azure-cli&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you’re ready to go, check out &lt;a href=&quot;/2017/04/12/ci-cd-kubernetes-acs-pt2/&quot;&gt;Part 2&lt;/a&gt;!&lt;/p&gt;</content><author><name>Ben Hummerstone</name></author><summary type="html">Part 1 - Introduction (this post) Part 2 - Creating ACS &amp;amp; ACR Part 3 - Configuring VSTS Part 4 - Kubernetes-ifying Application Part 5 - Build Definition Part 6 - Release Definition Part 7 - Wrap-up</summary></entry><entry><title type="html">CI/CD with Kubernetes on ACS - Part 2 - Creating ACS and ACR</title><link href="http://cloudscaping.co.uk/2017/04/12/ci-cd-kubernetes-acs-pt2/" rel="alternate" type="text/html" title="CI/CD with Kubernetes on ACS - Part 2 - Creating ACS and ACR" /><published>2017-04-12T00:00:00+00:00</published><updated>2017-04-12T00:00:00+00:00</updated><id>http://cloudscaping.co.uk/2017/04/12/ci-cd-kubernetes-acs-pt2</id><content type="html" xml:base="http://cloudscaping.co.uk/2017/04/12/ci-cd-kubernetes-acs-pt2/">&lt;p&gt;Part 1 - &lt;a href=&quot;/2017/04/12/ci-cd-kubernetes-acs-pt1/&quot;&gt;Introduction&lt;/a&gt;&lt;br /&gt;
Part 2 - Creating ACS &amp;amp; ACR (this post)&lt;br /&gt;
Part 3 - &lt;a href=&quot;/2017/08/24/ci-cd-kubernetes-acs-pt3/&quot;&gt;Configuring VSTS&lt;/a&gt;
Part 4 - Kubernetes-ifying Application&lt;br /&gt;
Part 5 - Build Definition&lt;br /&gt;
Part 6 - Release Definition&lt;br /&gt;
Part 7 - Wrap-up&lt;/p&gt;

&lt;h2 id=&quot;install-azure-cli&quot;&gt;Install Azure CLI&lt;/h2&gt;
&lt;p&gt;The first step is to get up and running with Azure and the Azure CLI. You can get a free trial for Azure &lt;a href=&quot;https://azure.microsoft.com/en-gb/free/&quot;&gt;here&lt;/a&gt;, which will give you more than enough credit to get started. If you already have an Azure subscription, make sure that you have the necessary permissions to create &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal#required-permissions&quot;&gt;Service Principals&lt;/a&gt; as this is required by Kubernetes for configuring the Azure resources.&lt;/p&gt;

&lt;p&gt;You can get the Azure CLI v2 from &lt;a href=&quot;https://docs.microsoft.com/en-us/cli/azure/install-azure-cli&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you are set up with an Azure account and the CLI, open up a terminal/command prompt and run:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az login
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will prompt you with a code, and you will need to open a web browser to http://aka.ms/devicelogin, enter the code, and login to your Azure account. Once this is complete, you will then be authenticated from the CLI.&lt;/p&gt;

&lt;p&gt;Alternatively, depending on your account security settings, you can login using:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az login &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; &amp;lt;username&amp;gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &amp;lt;password&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;create-the-acs-cluster&quot;&gt;Create the ACS Cluster&lt;/h2&gt;
&lt;p&gt;Next step is to create a new Resource Group in Azure to contain all of the resources we are going to be creating:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az group create &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; k8scicd &lt;span class=&quot;nt&quot;&gt;--location&lt;/span&gt; westeurope
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note: I’m using West Europe for everything as part of this series, as it contains all of the services that are required. Double check the Azure services by region page to help choose a suitable region for you.&lt;/p&gt;

&lt;p&gt;Now we have our Resource Group, it’s time to create our Azure Container Service (ACS) cluster:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az acs create  &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; k8scicd &lt;span class=&quot;nt&quot;&gt;--resource-group&lt;/span&gt; k8scicd  &lt;span class=&quot;nt&quot;&gt;--orchestrator-type&lt;/span&gt; kubernetes &lt;span class=&quot;nt&quot;&gt;--dns-prefix&lt;/span&gt; bhk8s &lt;span class=&quot;nt&quot;&gt;--verbose&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This command creates the ACS cluster using k8s as the orchestrator and configures the DNS prefix for connecting to the cluster later; this DNS prefix must be unique as it is associated with some public IPs.&lt;/p&gt;

&lt;p&gt;Once the cluster has been created, you’ll need to install kubectl, which is the CLI for k8s. You can do that by running the following:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;az acs kubernetes install-cli
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This installs kubectl to /usr/local/bin/kubectl by default, but you can change it with the –install-location parameter if required.&lt;/p&gt;

&lt;p&gt;To check that your k8s cluster is working, run:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Assuming everything has gone to plan, you should get a list back of the nodes in your ACS cluster.&lt;/p&gt;

&lt;p&gt;I’ll be honest: when I was writing this up, this command failed for me with the warning “Private key file is encrypted”. After a bit of troubleshooting, I worked out that it was because I had an old k8s config file lurking around that was trying to connect me to a different cluster.&lt;/p&gt;

&lt;p&gt;To resolve this, I removed the existing config and then copied the new config down from my k8s cluster:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; ~/.kube/config
scp azureuser@bhk8s.westeurope.cloudapp.azure.com:.kube/config &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/.kube/config
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You now have a k8s cluster up and running!&lt;/p&gt;

&lt;h2 id=&quot;create-the-acr&quot;&gt;Create the ACR&lt;/h2&gt;
&lt;p&gt;Before we move on, let’s set up our Azure Container Registry (ACR). To do this, we create a storage account, and than an ACR:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az storage account create &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; bhacrstg &lt;span class=&quot;nt&quot;&gt;--location&lt;/span&gt; westeurope &lt;span class=&quot;nt&quot;&gt;--resource-group&lt;/span&gt; k8scicd &lt;span class=&quot;nt&quot;&gt;--sku&lt;/span&gt; Standard_LRS
az acr create &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; bhacr &lt;span class=&quot;nt&quot;&gt;--location&lt;/span&gt; westeurope &lt;span class=&quot;nt&quot;&gt;--resource-group&lt;/span&gt; k8scicd &lt;span class=&quot;nt&quot;&gt;--storage-account-name&lt;/span&gt; bhacrstg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Another command, another error: this time it was “The subscription is not registered to use namespace ‘Microsoft.ContainerRegistry’”. As ACR has only recently become Generally Available, your subscription might not have been updated to register with the Resource Provider that supports it. Easily solved though:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az provider register &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt; Microsoft.ContainerRegistry
az provider show &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt; Microsoft.ContainerRegistry | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;registrationState
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;registrationState should change to “registered” when complete, which might take a couple of minutes.&lt;/p&gt;

&lt;p&gt;Finally, we need to be able to push and pull container images from our ACR. To do this, we need a new Service Principal:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;az ad sp create-for-rbac &lt;span class=&quot;nt&quot;&gt;--scopes&lt;/span&gt; /subscriptions/&amp;lt;subcription_id&amp;gt;/resourcegroups/k8scicd/providers/Microsoft.ContainerRegistry/registries/bhacr &lt;span class=&quot;nt&quot;&gt;--role&lt;/span&gt; Owner &lt;span class=&quot;nt&quot;&gt;--password&lt;/span&gt; &amp;lt;password&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note down the appID and name that are generated by this command as we will need them in the next step, which is adding these credentials to k8s:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl create secret docker-registry bhacr &lt;span class=&quot;nt&quot;&gt;--docker-server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://&amp;lt;ACR_name&amp;gt;-microsoft.azurecr.io &lt;span class=&quot;nt&quot;&gt;--docker-username&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;lt;appID&amp;gt; &lt;span class=&quot;nt&quot;&gt;--docker-password&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;lt;password&amp;gt; &lt;span class=&quot;nt&quot;&gt;--docker-email&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;lt;any_email&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Make sure you remember these settings as we will use them again as (spoliers!) part of the build definition in VSTS.&lt;/p&gt;

&lt;p&gt;Now that you’re up and running with ACS &amp;amp; ACR, check out &lt;a href=&quot;/2017/08/24/ci-cd-kubernetes-acs-pt3/&quot;&gt;Part 3&lt;/a&gt; to prepare VSTS.&lt;/p&gt;</content><author><name>Ben Hummerstone</name></author><summary type="html">Part 1 - Introduction Part 2 - Creating ACS &amp;amp; ACR (this post) Part 3 - Configuring VSTS Part 4 - Kubernetes-ifying Application Part 5 - Build Definition Part 6 - Release Definition Part 7 - Wrap-up</summary></entry></feed>