<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en" xml:base="https://danielscottraynsford.com/">
  <title>Neural Flow Blog</title>
  <subtitle>Generative AI, Agentic system, software development, Azure architecture, SaaS and much more.</subtitle>
  <link href="https://danielscottraynsford.com/feed.xml" rel="self" />
  <link href="https://danielscottraynsford.com/" />
  <updated>2026-03-02T20:56:11Z</updated>
  <id>https://danielscottraynsford.com/</id>
  <author>
    <name>Daniel Scott-Raynsford</name>
  </author>
	<entry>
      <title>Enabling GitHub Copilot Coding Agents to Access Azure</title>
      <link href="https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/" />
      <updated>2025-08-15T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/</id>
      <content type="html">
				&lt;blockquote data-callout=&quot;important&quot; class=&quot;blockquote--important&quot;&gt;&lt;p&gt;Before I start with the details, I want to make something extremely clear: Do not give coding agents access to production. We’ve all read/seen the stories on the internet about AI’s potential for causing chaos if not properly controlled. This is not specific to AI, but it’s a general rule: Don’t give developers/anyone permanent, ungoverned access to production resources without strict controls in place. I could spend the rest of this blog post lecturing about this, but then we’d never actually get to the point of this article. So warnings aside, lets get to it.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;background&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#background&quot; class=&quot;heading-anchor&quot;&gt;Background&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I’ve been experimenting with &lt;a href=&quot;https://docs.github.com/en/enterprise-cloud@latest/copilot/concepts/coding-agent/coding-agent&quot; rel=&quot;noopener&quot;&gt;GitHub Copilot coding agents&lt;/a&gt;, assigning them tasks almost every day (and night) and they’re pretty impressive. But on occasion I actually need coding agents to interact with Azure resources, usually as part of diagnosing an issue/fixing a bug that requires real-time access to those resources (only in dev/test, of course). This is also sometimes useful if I want the coding agent to be able to verify some infrastructure it’s creating Bicep or Terraform files for.&lt;/p&gt;&lt;p&gt;So, this blog post shows you how to configure your coding agents to be able to use a federated identity to access Azure resources securely, without needing to store any secrets in your repository. As well as set up RBAC permissions so that they only get the level of access they need.&lt;/p&gt;&lt;h2 id=&quot;why-this-matters&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#why-this-matters&quot; class=&quot;heading-anchor&quot;&gt;Why This Matters&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Here’s what I can do now with my Copilot agents:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Diagnose issues that might be occurring in dev/test environments&lt;/li&gt;&lt;li&gt;Access key vault secrets that might be required to access dev/test databases or other resources&lt;/li&gt;&lt;li&gt;Validate or obtain specific information about the Azure environment that is being worked with&lt;/li&gt;&lt;li&gt;Automatically provision (and of course cleanup) Azure resources as part of coding agent development process. But remember, deploying resources in Azure usually has a cost associated with it.&lt;/li&gt;&lt;li&gt;Enable your Coding Agents to use the &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/developer/azure-mcp-server/get-started&quot; rel=&quot;noopener&quot;&gt;Azure MCP Server&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;blockquote data-callout=&quot;important&quot; class=&quot;blockquote--important&quot;&gt;&lt;p&gt;The most important thing to note however, is the principle of least privilege: If your coding agent doesn’t need access to Azure, don’t provide it access. If just needs to be able to read diagnostic logs from a Log Analytics workspace, then that’s all it should be allowed to do. It’s very easy to over-provision access, so always err on the side of caution.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;azure-costs&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#azure-costs&quot; class=&quot;heading-anchor&quot;&gt;Azure Costs&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you give coding agents permission to deploy resources, be aware of the potential costs involved. Because coding agents now read the &lt;a href=&quot;https://docs.github.com/en/enterprise-cloud@latest/copilot/how-tos/configure-custom-instructions/add-repository-instructions&quot; rel=&quot;noopener&quot;&gt;copilot-instructions.md&lt;/a&gt; in your repository, it will be critical to provide cost management guidance within that file - including the fact that they should delete any resources they create once they are done with them.&lt;/p&gt;&lt;p&gt;My advice is to avoid giving them write/deploy access unless absolutely necessary, or at the very least have a method of tracking and auditing their resource usage. It would be quite simple to create a janitor (a GitHub Actions workflow) that deletes any resources that were created in a resource group that the Coding Agent has permission to create in.&lt;/p&gt;&lt;h2 id=&quot;getting-the-security-right&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#getting-the-security-right&quot; class=&quot;heading-anchor&quot;&gt;Getting the Security Right&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Before we dive into the setup, let me be clear about the security principles I follow. I’ve seen too many “quick demos” that skip these fundamentals:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Least privilege always&lt;/strong&gt;: Give only the minimum permissions needed. You can expand later.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Federated credentials only&lt;/strong&gt;: Workload identity federation is the right way to give your coding agents access to Azure.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Environment isolation&lt;/strong&gt;: Dev/test, staging need separate identities. But I shouldn’t have to repeat: don’t give coding agents access to production.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Check what your agents are doing&lt;/strong&gt;: You need to know what your agents are doing in Azure.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Your agent, your cost&lt;/strong&gt;: Coding agents don’t care about your budget (unless you tell them to and what to do about it).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;These aren’t optional “best practices”—they’re requirements if you want to sleep well at night.&lt;/p&gt;&lt;h2 id=&quot;how-to-set-this-up&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#how-to-set-this-up&quot; class=&quot;heading-anchor&quot;&gt;How to Set This Up&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The process is as follows:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Create a GitHub environment called &lt;code&gt;copilot&lt;/code&gt; for your coding agent to use in the GitHub repository.&lt;/li&gt;&lt;li&gt;Create an Entra ID &lt;strong&gt;app registration&lt;/strong&gt; for your coding agent.&lt;/li&gt;&lt;li&gt;Create a &lt;strong&gt;federated credential&lt;/strong&gt; for the &lt;em&gt;app registration&lt;/em&gt; that is usable by the coding actions GitHub workflow.&lt;/li&gt;&lt;li&gt;Assign the least access RBAC roles to the &lt;em&gt;app registration&lt;/em&gt; that coding agent will need.&lt;/li&gt;&lt;li&gt;Create the &lt;strong&gt;secrets&lt;/strong&gt; and &lt;strong&gt;variables&lt;/strong&gt; for the &lt;code&gt;copilot&lt;/code&gt; environment in the GitHub repository.&lt;/li&gt;&lt;li&gt;Configure or create the &lt;code&gt;copilot-setup-steps.yml&lt;/code&gt; workflow to use the credential to authenticate to Azure.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The next sections show the process in detail.&lt;/p&gt;&lt;h3 id=&quot;1-create-a-github-environment&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#1-create-a-github-environment&quot; class=&quot;heading-anchor&quot;&gt;1. Create a GitHub Environment&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;It is best to create an environment in GitHub that your coding agent has access to. This allows you to provide environment specific variables and secrets that your coding agent will get access to. This is required for the federated credentials to work.&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;I have called my environment &lt;code&gt;copilot&lt;/code&gt;, but you can name yours anything you like. Make sure to adjust any steps accordingly.&lt;/p&gt;&lt;/blockquote&gt;&lt;ol&gt;&lt;li&gt;Go to your GitHub repository&lt;/li&gt;&lt;li&gt;Click on &lt;strong&gt;Settings&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Scroll down to the &lt;strong&gt;Environments&lt;/strong&gt; section&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;New environment&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Name it something like &lt;code&gt;copilot&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Configure environment&lt;/strong&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Uo2vfyMc0a-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Uo2vfyMc0a-650.gif&quot; alt=&quot;Create GitHub environment&quot; title=&quot;Create a new GitHub Environment for copilot to use&quot; width=&quot;650&quot; height=&quot;319&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;Create a new GitHub Environment for copilot to use&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 id=&quot;2-create-the-entra-id-app-registration&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#2-create-the-entra-id-app-registration&quot; class=&quot;heading-anchor&quot;&gt;2. Create the Entra ID App Registration&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Next, you need an Entra ID application registration for your coding agents to use. You should create a dedicated app registration for each repository so that you can scope the permissions for each coding agent to prevent over-privileging.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Head to the &lt;a href=&quot;https://portal.azure.com&quot; rel=&quot;noopener&quot;&gt;Azure Portal&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Navigate to &lt;strong&gt;Microsoft Entra ID&lt;/strong&gt; (using the Azure search box)&lt;/li&gt;&lt;li&gt;Select the &lt;strong&gt;App registrations&lt;/strong&gt; blade under &lt;strong&gt;Manage&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;New registration&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Name it something meaningful like &lt;code&gt;gitHub-coding-agent-yourreponame&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Leave the &lt;strong&gt;Redirect URI&lt;/strong&gt; blank and the &lt;strong&gt;Supported Account Types&lt;/strong&gt; as &lt;code&gt;Accounts in this organizational directory only&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Register&lt;/strong&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/WHEddx3Gkp-650.webp 650w, https://danielscottraynsford.com/img/WHEddx3Gkp-877.webp 877w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/WHEddx3Gkp-650.png&quot; alt=&quot;Create Entra ID App Registration for Coding Agent&quot; title=&quot;Create Entra ID App Registration for Coding Agent&quot; width=&quot;877&quot; height=&quot;680&quot; srcset=&quot;https://danielscottraynsford.com/img/WHEddx3Gkp-650.png 650w, https://danielscottraynsford.com/img/WHEddx3Gkp-877.png 877w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;Create Entra ID App Registration for Coding Agent&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Once it’s created, grab these values from the &lt;strong&gt;Overview&lt;/strong&gt; page:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Application (client) ID&lt;/strong&gt; - You’ll need this for GitHub secrets&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Directory (tenant) ID&lt;/strong&gt; - Also required for GitHub secrets&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/4wPPaQOrF7-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/4wPPaQOrF7-650.png&quot; alt=&quot;Copy Entra ID App Registration Details&quot; title=&quot;Copy Entra ID App Registration Details&quot; width=&quot;650&quot; height=&quot;321&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;Copy Entra ID App Registration Details&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Example values (yours will be different)&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;AZURE_CLIENT_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;12345678-1234-1234-1234-123456789012&quot;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;AZURE_TENANT_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;87654321-4321-4321-4321-210987654321&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;3-create-a-federated-credential&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#3-create-a-federated-credential&quot; class=&quot;heading-anchor&quot;&gt;3. Create a Federated Credential&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This is where the magic happens. Federated credentials create a trust relationship between GitHub and Azure without storing any secrets. It’s way more secure than cramming service principal secrets into your repo.&lt;/p&gt;&lt;p&gt;Here’s the crucial part that tripped me up initially:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;In your app registration, go to &lt;strong&gt;Certificates &amp;amp; secrets&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;Federated credentials&lt;/strong&gt; tab&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Add credential&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Select &lt;strong&gt;GitHub Actions deploying Azure resources&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Fill in the details:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Organization&lt;/strong&gt;: Your GitHub username or organization&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt;: Your repository name&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Entity type&lt;/strong&gt;: Environment&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Environment name&lt;/strong&gt;: &lt;code&gt;copilot&lt;/code&gt; (this is crucial. It must match exactly the name of the GitHub environment you created)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Name&lt;/strong&gt;: Something descriptive like &lt;code&gt;gitHub-coding-agent-yourreponame-copilot-environment&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/TveBONf0Yc-650.webp 650w, https://danielscottraynsford.com/img/TveBONf0Yc-930.webp 930w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/TveBONf0Yc-650.gif&quot; alt=&quot;Create GitHub Federated Credential for Coding Agent&quot; title=&quot;Create GitHub Federated Credential for Coding Agent&quot; width=&quot;930&quot; height=&quot;992&quot; srcset=&quot;https://danielscottraynsford.com/img/TveBONf0Yc-650.gif 650w, https://danielscottraynsford.com/img/TveBONf0Yc-930.gif 930w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;Create GitHub Federated Credential for Coding Agent&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;blockquote data-callout=&quot;important&quot; class=&quot;blockquote--important&quot;&gt;&lt;p&gt;The environment name must be exactly &lt;code&gt;copilot&lt;/code&gt; with lowercase ‘c’. I learned this the hard way when I kept getting AADSTS7002138 errors. Azure federated identity credentials are case-sensitive, so &lt;code&gt;Copilot&lt;/code&gt; or &lt;code&gt;COPILOT&lt;/code&gt; will fail.&lt;/p&gt;&lt;/blockquote&gt;&lt;h3 id=&quot;4-set-up-azure-rbac-roles&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#4-set-up-azure-rbac-roles&quot; class=&quot;heading-anchor&quot;&gt;4. Set Up Azure RBAC Roles&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Now for the permissions. The specific roles depend on what your agents need to do, but always start with minimal permissions and expand as needed. Just remember the golden rule:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/S_uhtqwk5d-270.webp 270w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/S_uhtqwk5d-270.jpeg&quot; alt=&quot;Never give AI access to production&quot; width=&quot;270&quot; height=&quot;270&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;You can grant the RBAC roles through the Azure Portal just as you would any other application registration, user, group or managed identity, but below, I’m using Azure CLI, so have already logged into Azure using &lt;code&gt;az login&lt;/code&gt;. Just use the method you’re most comfortable with. The following are some examples of RBAC roles you might wish to give your coding agent.&lt;/p&gt;&lt;p&gt;To allow the coding agent to experiment in a single group for &lt;strong&gt;resource deployment and management&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Set environment variables&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;AZURE_CLIENT_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;12345678-1234-1234-1234-123456789012&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;AZURE_SUBSCRIPTION_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;11111111-2222-3333-4444-555555555555&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Create a resource group for the coding agent&lt;/span&gt;
az group create &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt; rg-coding-agent-sandbox &lt;span class=&quot;token parameter variable&quot;&gt;--location&lt;/span&gt; eastus

&lt;span class=&quot;token comment&quot;&gt;# Contributor role to a single existing resource group&lt;/span&gt;
az role assignment create &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--assignee&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$AZURE_CLIENT_ID&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Contributor&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--scope&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/subscriptions/&lt;span class=&quot;token variable&quot;&gt;$AZURE_SUBSCRIPTION_ID&lt;/span&gt;/resourceGroups/rg-coding-agent-sandbox&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For &lt;strong&gt;read-only monitoring&lt;/strong&gt; of a subscription:&lt;/p&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Set environment variables&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;AZURE_CLIENT_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;12345678-1234-1234-1234-123456789012&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;AZURE_SUBSCRIPTION_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;11111111-2222-3333-4444-555555555555&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Reader role for subscription&lt;/span&gt;
az role assignment create &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--assignee&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$AZURE_CLIENT_ID&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Reader&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--scope&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/subscriptions/&lt;span class=&quot;token variable&quot;&gt;$AZURE_SUBSCRIPTION_ID&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For &lt;strong&gt;Key Vault access&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;AZURE_CLIENT_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;12345678-1234-1234-1234-123456789012&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;AZURE_SUBSCRIPTION_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;11111111-2222-3333-4444-555555555555&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;RESOURCE_GROUP_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;your-resource-group-name&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;KEY_VAULT_NAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;your-keyvault-name&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Key Vault Secrets User&lt;/span&gt;
az role assignment create &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--assignee&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$AZURE_CLIENT_ID&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--role&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Key Vault Secrets User&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--scope&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/subscriptions/&lt;span class=&quot;token variable&quot;&gt;$AZURE_SUBSCRIPTION_ID&lt;/span&gt;/resourceGroups/&lt;span class=&quot;token variable&quot;&gt;$RESOURCE_GROUP_NAME&lt;/span&gt;/providers/Microsoft.KeyVault/vaults/&lt;span class=&quot;token variable&quot;&gt;$KEY_VAULT_NAME&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I can’t stress this enough: Principle of least privilege. I’ve seen too many demos where people just hand out Contributor at the subscription level. That’s asking for trouble.&lt;/p&gt;&lt;h3 id=&quot;5-add-github-repository-secrets&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#5-add-github-repository-secrets&quot; class=&quot;heading-anchor&quot;&gt;5. Add GitHub Repository Secrets&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Your Copilot agents need three pieces of info to authenticate with Azure. Store these as GitHub repository environment level secrets—not environment variables in your code.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;In your GitHub repository, go to &lt;strong&gt;Settings&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Select &lt;strong&gt;Environments&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Click on your &lt;code&gt;copilot&lt;/code&gt; environment&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Add environment secret&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Add these repository secrets, one-by-one:&lt;/li&gt;&lt;/ol&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;AZURE_CLIENT_ID: 12345678-1234-1234-1234-123456789012
AZURE_TENANT_ID: 87654321-4321-4321-4321-210987654321
AZURE_SUBSCRIPTION_ID: 11111111-2222-3333-4444-555555555555&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;If you’re not sure how to get your Azure Subscription ID, &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id&quot; rel=&quot;noopener&quot;&gt;this page&lt;/a&gt; will tell you how.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;These aren’t really “secrets” since they’re identifiers, but storing them as repository secrets keeps your config centralized and secure and it is better to treat them as secrets.&lt;/p&gt;&lt;h3 id=&quot;6-create-the-github-actions-workflow&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#6-create-the-github-actions-workflow&quot; class=&quot;heading-anchor&quot;&gt;6. Create the GitHub Actions Workflow&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Here’s where it all comes together. GitHub Copilot coding agents always look for a workflow called &lt;code&gt;.github/workflows/copilot-setup-steps.yml&lt;/code&gt; in the GitHub repository that they are working on. This is actually a GitHub Actions workflow that provides all the necessary steps to set up the environment that the coding agent will work in. In our case, this also authenticates to Azure.&lt;/p&gt;&lt;p&gt;Create &lt;code&gt;.github/workflows/copilot-setup-steps.yml&lt;/code&gt;:&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Copilot Setup Steps

&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;id-token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; write
  &lt;span class=&quot;token key atrule&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; read

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;copilot-setup-steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;id-token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; write
      &lt;span class=&quot;token key atrule&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; read
    &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; copilot  &lt;span class=&quot;token comment&quot;&gt;# This must match your federated credential environment name&lt;/span&gt;

    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout repository
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v4

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Azure login
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; azure/login@v2
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;client-id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.AZURE_CLIENT_ID &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;tenant-id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.AZURE_TENANT_ID &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;subscription-id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.AZURE_SUBSCRIPTION_ID &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Verify Azure access
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          echo &quot;Successfully authenticated to Azure!&quot;
          az account show&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This workflow provides a solid foundation that your Copilot agents can build on for various Azure tasks that might need to perform while working on an issue. The key is the &lt;code&gt;environment: copilot&lt;/code&gt; line—that’s what ties everything together with your federated credential.&lt;/p&gt;&lt;h3 id=&quot;test-it-out&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#test-it-out&quot; class=&quot;heading-anchor&quot;&gt;Test It Out&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Time to see if everything works. Let’s test the authentication flow:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Go to your GitHub repository&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Actions&lt;/strong&gt; → &lt;strong&gt;Copilot Setup Steps&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Run workflow&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Select the &lt;code&gt;main&lt;/code&gt; branch&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Run workflow&lt;/strong&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;If everything’s configured correctly, you should see:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/3nvCzmNgjC-650.webp 650w, https://danielscottraynsford.com/img/3nvCzmNgjC-960.webp 960w, https://danielscottraynsford.com/img/3nvCzmNgjC-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/3nvCzmNgjC-650.png&quot; alt=&quot;GitHub Copilot Coding Agent Run Copilot Setup Steps&quot; title=&quot;GitHub Copilot Coding Agent Run Copilot Setup Steps&quot; width=&quot;1400&quot; height=&quot;340&quot; srcset=&quot;https://danielscottraynsford.com/img/3nvCzmNgjC-650.png 650w, https://danielscottraynsford.com/img/3nvCzmNgjC-960.png 960w, https://danielscottraynsford.com/img/3nvCzmNgjC-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;GitHub Copilot Coding Agent Run Copilot Setup Steps&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;When things go wrong&lt;/strong&gt; (and they will):&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Error: &lt;code&gt;AADSTS7002138: No matching federated identity record found&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Verify the environment name is exactly &lt;code&gt;copilot&lt;/code&gt; (lowercase)&lt;/li&gt;&lt;li&gt;Check that your repository and organization names match exactly&lt;/li&gt;&lt;li&gt;Ensure the federated credential subject pattern is correct&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Error: &lt;code&gt;AADSTS70021: No matching federated identity record found for presented assertion&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Verify your GitHub repository secrets are correct&lt;/li&gt;&lt;li&gt;Check that the app registration client ID matches&lt;/li&gt;&lt;li&gt;Ensure the federated credential is properly configured&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Error: &lt;code&gt;Authorization failed&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Verify RBAC role assignments are correct&lt;/li&gt;&lt;li&gt;Check that permissions are assigned to the correct scope&lt;/li&gt;&lt;li&gt;Ensure the service principal has the necessary roles&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;beyond-the-basics&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#beyond-the-basics&quot; class=&quot;heading-anchor&quot;&gt;Beyond the Basics&lt;/a&gt;&lt;/h2&gt;&lt;h3 id=&quot;configuring-coding-agents-to-use-azure-mcp&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#configuring-coding-agents-to-use-azure-mcp&quot; class=&quot;heading-anchor&quot;&gt;Configuring Coding Agents to use Azure MCP&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;To enable your coding agents to use an Azure MCP (Model Context Protocol) server, you’ll need to first add the Azure MCP in the Copilot settings for the repository. For more information on the Azure MCP server, see &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/developer/azure-mcp-server/get-started&quot; rel=&quot;noopener&quot;&gt;this page&lt;/a&gt;. The Azure MCP server actually makes calls to Azure CLI commands which is why it can only function if the &lt;strong&gt;coding agent&lt;/strong&gt; environment is already connected to Azure.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Open the &lt;strong&gt;Settings&lt;/strong&gt; tab of your GitHub repository.&lt;/li&gt;&lt;li&gt;Expand the &lt;strong&gt;Copilot&lt;/strong&gt; section.&lt;/li&gt;&lt;li&gt;Select &lt;strong&gt;Coding agent&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;In the MCP configuration section add:&lt;/li&gt;&lt;/ol&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token property&quot;&gt;&quot;mcpServers&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;token property&quot;&gt;&quot;Azure&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;local&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;-y&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;@azure/mcp@latest&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;server&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;start&quot;&lt;/span&gt;
       &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;tools&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&lt;li&gt;Click &lt;strong&gt;Save MCP configuration&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/2QXVbxSflu-650.webp 650w, https://danielscottraynsford.com/img/2QXVbxSflu-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/2QXVbxSflu-650.png&quot; alt=&quot;GitHub Copilot Coding Agent Azure MCP Setup&quot; title=&quot;GitHub Copilot Coding Agent Azure MCP Setup&quot; width=&quot;960&quot; height=&quot;949&quot; srcset=&quot;https://danielscottraynsford.com/img/2QXVbxSflu-650.png 650w, https://danielscottraynsford.com/img/2QXVbxSflu-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;GitHub Copilot Coding Agent Azure MCP Setup&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;blockquote data-callout=&quot;important&quot; class=&quot;blockquote--important&quot;&gt;&lt;p&gt;A reminder: the Azure MCP server can only function if the &lt;strong&gt;coding agent&lt;/strong&gt; environment is already connected to Azure. It uses the application registration provided in the GitHub repository secrets and will only have permissions to do what that identity can do. So make sure your permissions are set up correctly!&lt;/p&gt;&lt;/blockquote&gt;&lt;h3 id=&quot;testing-with-the-coding-agent&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#testing-with-the-coding-agent&quot; class=&quot;heading-anchor&quot;&gt;Testing with the Coding Agent&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;To really test out the setup, we need to have the coding agent perform a task that requires it to access Azure and use the Azure MCP server. If you don’t have the Azure MCP server configured, the coding agent won’t have the MCP tools to access to Azure, but technically can still call Azure CLI commands directly.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Open your GitHub repository.&lt;/li&gt;&lt;li&gt;Go to the &lt;strong&gt;Issues&lt;/strong&gt; tab.&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;New issue&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Set the title to “Add Resource Group List” with a description that says:&lt;/li&gt;&lt;/ol&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Add a list of all the Azure Resource Groups in the subscription into a Markdown file called `resourcegroups.md`. Use the `#groups` tool if it is available, otherwise, run the `az` command.&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&lt;li&gt;Click the &lt;strong&gt;Assignee&lt;/strong&gt; button and select &lt;code&gt;Copilot&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Assign the issue to the Copilot coding agent.&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;Create&lt;/strong&gt; button.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/iMJnpJHYNR-650.webp 650w, https://danielscottraynsford.com/img/iMJnpJHYNR-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/iMJnpJHYNR-650.gif&quot; alt=&quot;Create and assign issue to Copilot coding agent&quot; title=&quot;Create and assign issue to Copilot coding agent&quot; width=&quot;960&quot; height=&quot;1016&quot; srcset=&quot;https://danielscottraynsford.com/img/iMJnpJHYNR-650.gif 650w, https://danielscottraynsford.com/img/iMJnpJHYNR-960.gif 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;Create and assign issue to Copilot coding agent&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;After a few seconds Copilot will create a new branch and a draft pull request and assign the coding agent to get to work.&lt;/li&gt;&lt;li&gt;You can watch the progress by selecting the new pull request and clicking the &lt;strong&gt;View Session&lt;/strong&gt; button.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/P8-ygS9xb4-650.webp 650w, https://danielscottraynsford.com/img/P8-ygS9xb4-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/P8-ygS9xb4-650.gif&quot; alt=&quot;Copilot coding agent completed Azure Resource Groups task&quot; title=&quot;Copilot coding agent completed Azure Resource Groups task&quot; width=&quot;960&quot; height=&quot;1016&quot; srcset=&quot;https://danielscottraynsford.com/img/P8-ygS9xb4-650.gif 650w, https://danielscottraynsford.com/img/P8-ygS9xb4-960.gif 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;Copilot coding agent completed Azure Resource Groups task&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;This is obviously a simplified example, and a very wasteful way to create this issue, but it demonstrates the potential for using GitHub Copilot coding agents to interact with Azure resources as part of a coding workflow.&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#conclusion&quot; class=&quot;heading-anchor&quot;&gt;Conclusion&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Setting up GitHub Copilot coding agents with secure Azure access opens up options for coding agents to do more than just code. They can now interact with Azure resources, diagnose issues, and even automate infrastructure management tasks—all while adhering to security best practices.&lt;/p&gt;&lt;p&gt;The federated credential approach follows enterprise security best practices while actually being easier to set up than the old “secrets everywhere” approach.&lt;/p&gt;&lt;h2 id=&quot;whats-next&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#whats-next&quot; class=&quot;heading-anchor&quot;&gt;What’s Next&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Ready to expand this? Consider:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Limit coding agents to just reader over dev/test environments&lt;/strong&gt; unless you have effective controls in place (cost, auditability)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Using copilot-instructions&lt;/strong&gt; to tell coding agents when and why to use Azure MCP tools. See &lt;a href=&quot;https://docs.github.com/enterprise-cloud@latest/copilot/how-tos/configure-custom-instructions/add-repository-instructions#creating-a-repository-custom-instructions-file&quot; rel=&quot;noopener&quot;&gt;custom instructions&lt;/a&gt;. This should include specific scenarios where Azure MCP tools are beneficial, as well as any relevant context or constraints and requirements to delete deployed resources.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Remember: the goal isn’t to replace human oversight—it’s to allow your coding agents to be able to make better code descisions or provide better debugging assistance by having access to the Azure resources they need, when they need them.&lt;/p&gt;&lt;p&gt;And one last time:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/S_uhtqwk5d-270.webp 270w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/S_uhtqwk5d-270.jpeg&quot; alt=&quot;Never give AI access to production&quot; width=&quot;270&quot; height=&quot;270&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h2 id=&quot;related-links&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enabling-github-copilot-coding-agents-to-access-azure/#related-links&quot; class=&quot;heading-anchor&quot;&gt;Related Links&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/entra/workload-id/workload-identity-federation&quot; rel=&quot;noopener&quot;&gt;Azure Workload Identity Federation Documentation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/Azure/login&quot; rel=&quot;noopener&quot;&gt;GitHub Actions Azure Login&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/azure/role-based-access-control/&quot; rel=&quot;noopener&quot;&gt;Azure RBAC Documentation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/entra/identity-platform/quickstart-register-app&quot; rel=&quot;noopener&quot;&gt;Entra ID App Registration Guide&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://docs.github.com/enterprise-cloud@latest/copilot&quot; rel=&quot;noopener&quot;&gt;GitHub Copilot Enterprise Documentation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/PlagueHO/libris-maleficarum/blob/main/.github/workflows/copilot-setup-steps.yml&quot; rel=&quot;noopener&quot;&gt;Reference Workflow Example&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
 			</content>
    </entry><entry>
      <title>Keeping Azure Bicep up-to-date the easy way with GitHub Copilot Agents</title>
      <link href="https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/" />
      <updated>2025-06-16T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/</id>
      <content type="html">
				&lt;p&gt;Nobody likes doing janitorial work, but keeping your Infrastructure as Code (IaC) up-to-date is one of those critical maintenance tasks that often gets pushed to the bottom of your to-do list. Today, I want to show you how to use GitHub Copilot Coding Agent and an experimental feature in Visual Studio Code, &lt;em&gt;Prompt Files&lt;/em&gt; to automate one of the most tedious but important maintenance tasks: updating your Azure Verified Modules (AVM) in Bicep to their latest versions.&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;Although I’m using the example of Bicep with Azure Verified Modules, the concepts and techniques discussed here can be applied to other IaC tools and module repositories as well. For example, updating ARM APIs, Terraform modules, or even DSC configurations can benefit from similar automation strategies.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;why-keep-your-infrastructure-as-code-up-to-date&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#why-keep-your-infrastructure-as-code-up-to-date&quot; class=&quot;heading-anchor&quot;&gt;Why Keep Your Infrastructure as Code Up-to-Date?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Before we dive into the solution, let’s talk about why this matters. When you’re building cloud solutions, your Infrastructure as Code isn’t just configuration—it’s the foundation of your entire system. Keeping it current means:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Security patches&lt;/strong&gt;: New versions often include critical security fixes&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New features&lt;/strong&gt;: You get access to the latest Azure capabilities and improvements&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Bug fixes&lt;/strong&gt;: Issues you might not even know exist get resolved&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Performance improvements&lt;/strong&gt;: Better resource allocation and optimization&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Compliance&lt;/strong&gt;: Meeting organizational requirements for using current versions&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The problem? Manually checking and updating dozens or hundreds of module references across multiple Bicep files is time-consuming and error-prone. So we often don’t do it as frequently as we should. And the worst reason of all? We forget about it until something breaks, and then we’re scrambling to catch up. Most engineering/platform teams are always overworked and asked to prioritize new features over maintenance tasks, which means that keeping your IaC up-to-date often falls by the wayside.&lt;/p&gt;&lt;h2 id=&quot;what-are-azure-verified-modules&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#what-are-azure-verified-modules&quot; class=&quot;heading-anchor&quot;&gt;What are Azure Verified Modules?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you’re not familiar with &lt;a href=&quot;https://aka.ms/avm&quot; rel=&quot;noopener&quot;&gt;Azure Verified Modules (AVM)&lt;/a&gt;, they’re Microsoft’s curated collection of Infrastructure as Code modules that follow consistent patterns and best practices. They wrap up common resources together along with Microsoft Well-Architected Best practices and simplify and speed up the process of defining your IaC.&lt;/p&gt;&lt;p&gt;Using AVM modules in your Bicep templates (or Terraform - yes, there are Terraform versions) means you’re building on a solid, well-maintained foundation. But like any dependency, you need to keep them current to get the full benefit.&lt;/p&gt;&lt;p&gt;For a quick primer into AVM, check out this video:&lt;/p&gt;&lt;p&gt;&lt;custom-youtube slug=&quot;JbIMrJKW5N0&quot; label=&quot;An Introduction to Azure Verified Modules (AVM)&quot; class=&quot;flow&quot;&gt;&lt;!-- component composition by: https://github.com/zachleat/zachleat.com --&gt;
&lt;style&gt;
  /* Hide without JS */
  is-land:not(:defined).video-wrapper {
    display: none;
  }
&lt;/style&gt;

  &lt;is-land on:visible class=&quot;video-wrapper&quot;&gt;
    &lt;lite-youtube videoid=&quot;JbIMrJKW5N0&quot; js-api playlabel=&quot;Play: An Introduction to Azure Verified Modules (AVM)&quot; style=&quot;background-image: var(--yt-poster-img-url); --yt-poster-img-url-lazy: url(&#39;https://v1.opengraph.11ty.dev/https%3A%2F%2Fyoutube.com%2Fwatch%3Fv%3DJbIMrJKW5N0/auto/jpeg/&#39;)&quot;&gt;&lt;/lite-youtube&gt;

    &lt;template data-island=&quot;once&quot;&gt;
      &lt;style&gt;
        lite-youtube {
          max-inline-size: 100% !important;
          background-size: cover;
        }

        is-land lite-youtube {
          background-color: #eee;
          border-radius: 0.2em;
          background-size: cover;
        }
        is-land[ready] lite-youtube {
          /* gotta set in `style` to override the 480w image from lite-youtube */
          --yt-poster-img-url: var(--yt-poster-img-url-lazy);
        }

        .video-wrapper {
          aspect-ratio: 16 / 9;
          width: 100%;
        }

        is-land.video-wrapper {
          display: block;
        }
      &lt;/style&gt;
      &lt;link rel=&quot;stylesheet&quot; href=&quot;/assets/components/lite-yt-embed.css&quot; /&gt;
      &lt;script type=&quot;module&quot; src=&quot;/assets/components/lite-yt-embed.js&quot;&gt;&lt;/script&gt;
    &lt;/template&gt;
  &lt;/is-land&gt;

&lt;custom-youtube-link label=&quot;An Introduction to Azure Verified Modules (AVM)&quot; href=&quot;https://youtube.com/watch?v=JbIMrJKW5N0&quot;&gt;&lt;style&gt;
  custom-youtube-link {
    display: flex;
    align-items: flex-start;
    gap: var(--space-xs);
    font-size: var(--size-step-min-1);
  }
  custom-youtube-link svg {
    font-size: var(--size-step-0);
    margin-block-start: 0.1em;
  }
&lt;/style&gt;

&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; ariahidden=&quot;true&quot;&gt;&lt;path d=&quot;M2.5 17a24.12 24.12 0 0 1 0-10 2 2 0 0 1 1.4-1.4 49.56 49.56 0 0 1 16.2 0A2 2 0 0 1 21.5 7a24.12 24.12 0 0 1 0 10 2 2 0 0 1-1.4 1.4 49.55 49.55 0 0 1-16.2 0A2 2 0 0 1 2.5 17&quot;&gt;&lt;/path&gt;&lt;path d=&quot;m10 15 5-3-5-3z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;

&lt;a href=&quot;https://youtube.com/watch?v=JbIMrJKW5N0&quot;&gt;An Introduction to Azure Verified Modules (AVM)&lt;/a&gt;

&lt;!-- Inspired by https://github.com/zachleat/zachleat.com  --&gt;
&lt;/custom-youtube-link&gt;
&lt;/custom-youtube&gt;&lt;/p&gt;&lt;p&gt;A Bicep module using AVM might look something like this:&lt;/p&gt;&lt;pre class=&quot;language-bicep&quot;&gt;&lt;code class=&quot;language-bicep&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt; aiSearchService &lt;span class=&quot;token string&quot;&gt;&#39;br/public:avm/res/search/search-service:0.10.0&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;ai-search-service-deployment&#39;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rg
  &lt;span class=&quot;token property&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; aiSearchServiceName
    &lt;span class=&quot;token property&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; location
    &lt;span class=&quot;token property&quot;&gt;sku&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; azureAiSearchSku
    &lt;span class=&quot;token comment&quot;&gt;// Other parameters and objects as needed&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;publicNetworkAccess&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; azureNetworkIsolation &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Disabled&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Enabled&#39;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;semanticSearch&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;standard&#39;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; tags
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In this example, the module &lt;code&gt;br/public:avm/res/search/search-service:0.10.0&lt;/code&gt; is an AVM module for deploying an Azure AI Search Service. The version &lt;code&gt;0.10.0&lt;/code&gt; is specified, and over time, newer versions will be released with improvements and fixes.&lt;/p&gt;&lt;h2 id=&quot;the-manual-update-challenge&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#the-manual-update-challenge&quot; class=&quot;heading-anchor&quot;&gt;The Manual Update Challenge&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;manually updating a Bicep file with lots of AVM resources is a real chore - but it must be done. Here’s what the typical process looks like:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Open each Bicep file that uses AVM modules&lt;/li&gt;&lt;li&gt;Identify which modules are being used and their current versions&lt;/li&gt;&lt;li&gt;Go to the AVM index page to find the latest version of each module&lt;/li&gt;&lt;li&gt;If it’s newer, update the module version in the Bicep file&lt;/li&gt;&lt;li&gt;Check the module documentation to see if any parameters have changed. An easy place for things to go wrong is if you update the module version but forget to update the parameters that may have changed in the new version&lt;/li&gt;&lt;li&gt;Update your parameter usage if needed&lt;/li&gt;&lt;li&gt;Test everything to make sure it still works - Automated tests are your best friend here, but simple linting and validation checks can help catch some issues early.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Multiply this by the number of Bicep files in your project, and it quickly becomes a significant time investment. This is exactly the kind of repetitive, rule-based task that AI can help us automate.&lt;/p&gt;&lt;p&gt;With the amount of work required it’s lucky if this is done more than once a year and often it is just done reactively when something breaks. This is not a good situation to be in, especially when you consider the security and compliance implications of running outdated modules.&lt;/p&gt;&lt;h2 id=&quot;enter-github-copilot-agent-mode-and-prompt-files&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#enter-github-copilot-agent-mode-and-prompt-files&quot; class=&quot;heading-anchor&quot;&gt;Enter GitHub Copilot Agent Mode and Prompt Files&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;GitHub Copilot’s Agent mode allows you to create custom prompts that can perform complex, multi-step tasks. The experimental &lt;a href=&quot;https://code.visualstudio.com/docs/copilot/copilot-customization#_prompt-files-experimental&quot; rel=&quot;noopener&quot;&gt;Prompt Files feature&lt;/a&gt; takes this a step further by letting you create reusable, parameterized prompts that you can apply across different files and projects.&lt;/p&gt;&lt;p&gt;Think of Prompt Files as templates for AI tasks. You define the steps, the tools the agent can use, and the parameters it needs, then you can invoke that prompt on any file or context where it makes sense.&lt;/p&gt;&lt;h2 id=&quot;the-avm-update-prompt-file&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#the-avm-update-prompt-file&quot; class=&quot;heading-anchor&quot;&gt;The AVM Update Prompt File&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Here’s the Prompt File I’ve created for updating Azure Verified Modules. This file should be saved with a &lt;code&gt;.prompt.md&lt;/code&gt; extension in the &lt;code&gt;/.github/prompts/&lt;/code&gt; directory of your project. For example, you could name it &lt;code&gt;update-avm-modules.prompt.md&lt;/code&gt;.&lt;/p&gt;&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;agent&#39;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Update the Azure Verified Module to the latest version for the Bicep infrastructure as code file.&#39;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;changes&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;codebase&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;editFiles&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;extensions&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;fetch&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;githubRepo&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;openSimpleBrowser&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;problems&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;runTasks&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;search&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;searchResults&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;terminalLastCommand&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;terminalSelection&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;testFailure&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;usages&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;vscodeAPI&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/span&gt;

Your goal is to update the Bicep file &lt;span class=&quot;token code-snippet code keyword&quot;&gt;`${file}`&lt;/span&gt; to use the latest available versions of Azure Verified Modules (AVM).
You will need to perform these steps:

&lt;span class=&quot;token list punctuation&quot;&gt;1.&lt;/span&gt; Get a list of all the Azure Verified Modules that are used in the specific &lt;span class=&quot;token code-snippet code keyword&quot;&gt;`${file}`&lt;/span&gt; Bicep file and get the module names and their current versions.
&lt;span class=&quot;token list punctuation&quot;&gt;2.&lt;/span&gt; Step through each module referenced in the Bicep file and find the latest version of the module. Do this by using the &lt;span class=&quot;token code-snippet code keyword&quot;&gt;`fetch`&lt;/span&gt; tool to get the tags list from Microsoft Container Registry. E.g. for &#39;br/public:avm/res/compute/virtual-machine&#39; fetch &lt;span class=&quot;token url&quot;&gt;[&lt;span class=&quot;token content&quot;&gt;https://mcr.microsoft.com/v2/bicep/avm/res/compute/virtual-machine/tags/list&lt;/span&gt;](&lt;span class=&quot;token url&quot;&gt;https://mcr.microsoft.com/v2/bicep/avm/res/compute/virtual-machine/tags/list&lt;/span&gt;)&lt;/span&gt; and find the latest version tag.
&lt;span class=&quot;token list punctuation&quot;&gt;3.&lt;/span&gt; If there is a newer version of the module available based on the tags list from Microsoft Container Registry than is currently used in the Bicep, use the &lt;span class=&quot;token code-snippet code keyword&quot;&gt;`fetch`&lt;/span&gt; tool to get the documentation for the module from the Azure Verified Modules index page. E.g., for &lt;span class=&quot;token code-snippet code keyword&quot;&gt;`br/public:avm/res/compute/virtual-machine`&lt;/span&gt; the docs are &lt;span class=&quot;token url&quot;&gt;[&lt;span class=&quot;token content&quot;&gt;https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/compute/virtual-machine&lt;/span&gt;](&lt;span class=&quot;token url&quot;&gt;https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/compute/virtual-machine&lt;/span&gt;)&lt;/span&gt;
&lt;span class=&quot;token list punctuation&quot;&gt;4.&lt;/span&gt; Display a list of the modules with their current versions and the latest available versions, along with the documentation links for each module to the user.
&lt;span class=&quot;token list punctuation&quot;&gt;5.&lt;/span&gt; Compare the documentation for the module to the current usage in the Bicep file and identify any changes that need to be made to the module parameters or usage.
&lt;span class=&quot;token blockquote punctuation&quot;&gt;&amp;gt;&lt;/span&gt; [!IMPORTANT]
&lt;span class=&quot;token blockquote punctuation&quot;&gt;&amp;gt;&lt;/span&gt; If the changes to the module parameters are not compatible with the current usage, are new changes that would change the behaviour, are related to security or compliance, then these changes must be reviewed and approved before being applied. So, PAUSE and ask for user input before proceeding.
&lt;span class=&quot;token list punctuation&quot;&gt;6.&lt;/span&gt; Update the Azure Verified Module version and the resource in the Bicep file to use the latest available version and apply any relevant changes based on the documentation and including guidance from the user if required, to the module parameters.
&lt;span class=&quot;token list punctuation&quot;&gt;7.&lt;/span&gt; If there are no changes to the module, leave it as is and make no other changes.
&lt;span class=&quot;token list punctuation&quot;&gt;8.&lt;/span&gt; Display a table summary of the modules detected, their current versions and whether they were updated or not, and the documentation links for each module.

&lt;span class=&quot;token title important&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;##&lt;/span&gt; IMPORTANT&lt;/span&gt;

&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; Ensure that the Bicep file is valid after the changes and that it adheres to the latest standards for Azure Verified Modules and there are no linting errors.
&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; Do not try to find the latest version of an Azure Verified Module by any other mechanism than fetching the tags list from Microsoft Container Registry.
&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; The tags list returned from Microsoft Container Registry is an array of JSON strings, so is not in version order. You will need to parse the tags and find the latest version based on the semantic versioning scheme.&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;You can create prompt files in the &lt;code&gt;/.github/prompts&lt;/code&gt; directory of your project and they’ll be available to all users accessing the repository, or you can also create prompts in a &lt;code&gt;prompts&lt;/code&gt; directory in your own User Data Directory for personal use. The latter is useful for prompts that are specific to your workflow or environment.&lt;/p&gt;&lt;/blockquote&gt;&lt;h3 id=&quot;breaking-down-the-prompt-file&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#breaking-down-the-prompt-file&quot; class=&quot;heading-anchor&quot;&gt;Breaking Down the Prompt File&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Let’s take a closer look at the key sections of this prompt file:&lt;/p&gt;&lt;h4 id=&quot;front-matter&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#front-matter&quot; class=&quot;heading-anchor&quot;&gt;Front Matter&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;Mode: Set to &lt;code&gt;agent&lt;/code&gt; to indicate that this prompt must be run in Agent mode. It can also be set to &lt;code&gt;edit&lt;/code&gt; or &lt;code&gt;ask&lt;/code&gt;. When you activate a prompt,chat will automatically switch to the appropriate mode - in this case &lt;code&gt;agent&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Description: A brief description of what the prompt does.&lt;/li&gt;&lt;li&gt;Tools: An array of MCP tools that the agent can use to perform the task. Only &lt;code&gt;agent&lt;/code&gt; mode supports tools, and the tools you specify here will be available to the agent when it runs the prompt.&lt;/li&gt;&lt;/ul&gt;&lt;h4 id=&quot;on-the-tools&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#on-the-tools&quot; class=&quot;heading-anchor&quot;&gt;On the Tools&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;There are several built-in tools including &lt;code&gt;fetch&lt;/code&gt; for retrieving data from URLs, &lt;code&gt;editFiles&lt;/code&gt; for modifying files, and others that help with retrieving information, running tasks, and interacting with the codebase. Many Visual Studio Code extensions also provide additional tools that can be used in prompts. And if you have custom tools defined in your MCP configuration, you can include those here as well.&lt;/p&gt;&lt;p&gt;Selecting the right tools for the task is crucial for the agent to be successful. If the prompt file needs a tool that the user doesn’t have installed, the agent will inform them that it can’t achieve the task that it needs to, but may try to work around it or simply stop and ask the user for guidance.&lt;/p&gt;&lt;h4 id=&quot;the-prompt-content&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#the-prompt-content&quot; class=&quot;heading-anchor&quot;&gt;The Prompt Content&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;The prompt starts with a clear goal: to update the specified Bicep file to use the latest versions of Azure Verified Modules. It also specifies the file to act on using the &lt;code&gt;${file}&lt;/code&gt; placeholder, which will be replaced with the actual file that is selected in the Copilot context.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/fPPimvFFVc-650.webp 650w, https://danielscottraynsford.com/img/fPPimvFFVc-865.webp 865w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/fPPimvFFVc-650.png&quot; alt=&quot;AVM Copilot Prompt File Update Chat Context&quot; title=&quot;AVM Copilot Prompt File Update Chat Context&quot; width=&quot;865&quot; height=&quot;178&quot; srcset=&quot;https://danielscottraynsford.com/img/fPPimvFFVc-650.png 650w, https://danielscottraynsford.com/img/fPPimvFFVc-865.png 865w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;AVM Copilot Prompt File Update Chat Context&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;After that, it outlines a series of steps that the agent should follow to achieve this goal. Each step is clearly defined, and &lt;strong&gt;explicitly&lt;/strong&gt; instructs the agent how to achieve each task, tools to use and documentation to reference.&lt;/p&gt;&lt;blockquote data-callout=&quot;important&quot; class=&quot;blockquote--important&quot;&gt;&lt;p&gt;Prompt engineering is an art and a science. The more specific and clear you can be about the steps the agent should take, the better the results will be. Being explicit rather than assuming the agent will “just know” what to do is key to getting the desired outcome. Providing explicit references/links to documentation, APIs, or other resources also ensures the agent will have the information it needs to make informed decisions.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;It outlines a series of steps that the agent should follow, including:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Parse your Bicep files to find AVM module references&lt;/li&gt;&lt;li&gt;Check the Microsoft Container Registry for the latest versions&lt;/li&gt;&lt;li&gt;Compare current vs. latest versions using semantic versioning&lt;/li&gt;&lt;li&gt;Fetch documentation to understand any parameter changes&lt;/li&gt;&lt;li&gt;Update your files with the new versions and any necessary parameter adjustments&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Finally, &lt;strong&gt;IMPORTANT&lt;/strong&gt; guardrails and notes are provided at the end of the prompt to ensure the agent follows best practices and doesn’t make changes that could break your infrastructure.&lt;/p&gt;&lt;h2 id=&quot;setting-up-and-using-prompt-files&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#setting-up-and-using-prompt-files&quot; class=&quot;heading-anchor&quot;&gt;Setting Up and Using Prompt Files&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To use this prompt file, you’ll need to:&lt;/p&gt;&lt;h3 id=&quot;pre-requisites&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#pre-requisites&quot; class=&quot;heading-anchor&quot;&gt;Pre-requisites&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Visual Studio Code&lt;/strong&gt;: Make sure you have the latest version of &lt;a href=&quot;https://aka.ms/vscode&quot; rel=&quot;noopener&quot;&gt;VS Code installed&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt;: Ensure you have GitHub Copilot enabled in your GitHub account.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Visual Studio Code GitHub Copilot Extension&lt;/strong&gt;: Make sure you have the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=GitHub.copilot&quot; rel=&quot;noopener&quot;&gt;GitHub Copilot extension installed&lt;/a&gt; in Visual Studio Code.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Visual Studio Code GitHub Copilot Chat Extension&lt;/strong&gt;: Ensure you have the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat&quot; rel=&quot;noopener&quot;&gt;GitHub Copilot Chat extension installed&lt;/a&gt; in Visual Studio Code.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/3i4prl-POW-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/3i4prl-POW-650.png&quot; alt=&quot;GitHub Copilot Chat Extension&quot; title=&quot;GitHub Copilot Chat Extension&quot; width=&quot;650&quot; height=&quot;283&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;GitHub Copilot Chat Extension&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 id=&quot;1-enable-the-experimental-feature&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#1-enable-the-experimental-feature&quot; class=&quot;heading-anchor&quot;&gt;1. Enable the Experimental Feature&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;First, enable Prompt Files in Visual Studio Code:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Open VS Code settings (&lt;code&gt;Ctrl/Cmd + ,&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;Search for “prompt files”&lt;/li&gt;&lt;li&gt;Enable the “Copilot: Enable Prompt Files” setting (for either the User or Workspace settings, depending on your preference)&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/uQpHImYEjU-650.webp 650w, https://danielscottraynsford.com/img/uQpHImYEjU-960.webp 960w, https://danielscottraynsford.com/img/uQpHImYEjU-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/uQpHImYEjU-650.png&quot; alt=&quot;Enabling Copilot Prompt Files in Visual Studio Code&quot; title=&quot;Enabling Copilot Prompt Files in Visual Studio Code&quot; width=&quot;1400&quot; height=&quot;297&quot; srcset=&quot;https://danielscottraynsford.com/img/uQpHImYEjU-650.png 650w, https://danielscottraynsford.com/img/uQpHImYEjU-960.png 960w, https://danielscottraynsford.com/img/uQpHImYEjU-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;Enabling Copilot Prompt Files in Visual Studio Code&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 id=&quot;2-create-your-prompt-file&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#2-create-your-prompt-file&quot; class=&quot;heading-anchor&quot;&gt;2. Create Your Prompt File&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;In Visual Studio Code, create a new file named &lt;code&gt;update-avm-modules.prompt.md&lt;/code&gt; in the &lt;code&gt;/.github/prompts/&lt;/code&gt; directory of your project. If you don’t have this directory, you can create it.&lt;/p&gt;&lt;p&gt;You can also create this by:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Pressing &lt;code&gt;Ctrl/Cmd + Shift + P&lt;/code&gt; to open the Command Palette&lt;/li&gt;&lt;li&gt;Typing “Chat: New Prompt File”&lt;/li&gt;&lt;li&gt;Select the &lt;code&gt;prompts&lt;/code&gt; or the &lt;code&gt;User Data Folder&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Enter the name &lt;code&gt;update-avm-modules&lt;/code&gt; (without the &lt;code&gt;.prompt.md&lt;/code&gt; and paste the content from the prompt file above.&lt;/li&gt;&lt;li&gt;Save the file.&lt;/li&gt;&lt;/ol&gt;&lt;h3 id=&quot;3-run-the-prompt&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#3-run-the-prompt&quot; class=&quot;heading-anchor&quot;&gt;3. Run the Prompt&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;To use the prompt:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Open the Bicep file you want to update&lt;/li&gt;&lt;li&gt;Open the Copilot Chat sidebar by clicking the Copilot icon in the Activity Bar or pressing &lt;code&gt;Ctrl/Cmd + Shift + I&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;&lt;blockquote data-callout=&quot;important&quot; class=&quot;blockquote--important&quot;&gt;&lt;p&gt;Before you run the prompt, make sure to enable all tools that will be used by this prompt file. You can do this by clicking on the gear icon in the Copilot Chat sidebar and ticking the tools. This will ensure that the agent has access to the &lt;code&gt;fetch&lt;/code&gt;, &lt;code&gt;editFiles&lt;/code&gt;, and other tools it needs to perform the update.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/sXzNED_YQI-650.webp 650w, https://danielscottraynsford.com/img/sXzNED_YQI-853.webp 853w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/sXzNED_YQI-650.png&quot; alt=&quot;Enabling tools in GitHub Copilot Chat Agent mode&quot; title=&quot;Enabling tools in GitHub Copilot Chat Agent mode&quot; width=&quot;853&quot; height=&quot;190&quot; srcset=&quot;https://danielscottraynsford.com/img/sXzNED_YQI-650.png 650w, https://danielscottraynsford.com/img/sXzNED_YQI-853.png 853w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;Enabling tools in GitHub Copilot Chat Agent mode&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;In the Copilot Chat sidebar, type &lt;code&gt;/&lt;/code&gt; to invoke the prompt&lt;/li&gt;&lt;li&gt;Select your &lt;code&gt;update-avm-modules&lt;/code&gt; prompt&lt;/li&gt;&lt;li&gt;Make sure to &lt;em&gt;click&lt;/em&gt; the bicep file you want to update in the Chat context so that it is used as the &lt;code&gt;${file}&lt;/code&gt; parameter in the prompt.&lt;/li&gt;&lt;li&gt;Select the appropriate model to use. In my experience, &lt;code&gt;GPT-4.1&lt;/code&gt;, &lt;code&gt;Gemini 2.5 Pro&lt;/code&gt; and &lt;code&gt;Claude Sonnet 4&lt;/code&gt; all work well for this task. But I have found reasoning models like &lt;code&gt;o4-mini&lt;/code&gt; aren’t as effective for this kind of task.&lt;/li&gt;&lt;li&gt;Start the agent by clicking the &lt;strong&gt;Send&lt;/strong&gt; button or pressing &lt;code&gt;Enter&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;the-process-in-action&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#the-process-in-action&quot; class=&quot;heading-anchor&quot;&gt;The Process in Action&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;When you run this prompt on a Bicep file, here’s what happens behind the scenes:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Analysis&lt;/strong&gt;: The agent scans your Bicep file and identifies all AVM module references&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Version Check&lt;/strong&gt;: For each module, it queries the Microsoft Container Registry to get the latest available version&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Comparison&lt;/strong&gt;: It compares your current version with the latest using semantic versioning rules&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Documentation Review&lt;/strong&gt;: If an update is needed, it fetches the module documentation to understand any breaking changes&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Update&lt;/strong&gt;: It updates your Bicep file with the new version and adjusts parameters if necessary&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Validation&lt;/strong&gt;: It ensures the updated file is valid and follows current best practices&lt;/li&gt;&lt;/ol&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;The first time you run the prompt, the agent will ask you to confirm any tool calls it needs to make, such as fetching the tags list from the Microsoft Container Registry or updating the Bicep file. This is a safety measure to ensure you have control over what the agent is doing. You can approve them for the session, workspace or globally, depending on your preference.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The entire process that might take you 30-60 minutes manually happens in just a few minutes with the AI agent.&lt;/p&gt;&lt;blockquote data-callout=&quot;important&quot; class=&quot;blockquote--important&quot;&gt;&lt;p&gt;It is possible that the agent may decide that there are significant or non-trivial changes required to a resource, in which case it will pause and ask for your feedback and direction before proceeding. This is a good thing! It means the agent is being cautious and not making potentially breaking changes without your approval.&lt;/p&gt;&lt;p&gt;If you examine the prompt file, you’ll notice that it includes instructions to ensure that this occurs, especially for changes that could affect security or compliance. This is a critical part of the process to ensure that the agent doesn’t make changes that could have unintended consequences.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;the-importance-of-test-automation&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#the-importance-of-test-automation&quot; class=&quot;heading-anchor&quot;&gt;The Importance of Test Automation&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Before you start using this kind of automated updating, make sure you have solid test automation in place. I can’t stress this enough—automated infrastructure updates are only as safe as your testing strategy.&lt;/p&gt;&lt;p&gt;You should have:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Linting&lt;/strong&gt; to ensure your Bicep files follow best practices and standards&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Bicep validation&lt;/strong&gt; - perform Bicep What-If analysis to ensure the changes won’t break your deployment&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Integration tests&lt;/strong&gt; that deploy to a test environment&lt;/li&gt;&lt;li&gt;&lt;strong&gt;End-to-End tests&lt;/strong&gt; that validate the entire system works as expected after the update&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Rollback procedures&lt;/strong&gt; (ideally roll forward) in case something goes wrong&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Tools like &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-cli#validate&quot; rel=&quot;noopener&quot;&gt;Bicep’s built-in validation&lt;/a&gt;, &lt;a href=&quot;https://github.com/Azure/arm-ttk&quot; rel=&quot;noopener&quot;&gt;Azure Resource Manager Template Test Toolkit&lt;/a&gt;, and &lt;a href=&quot;https://pester.dev/&quot; rel=&quot;noopener&quot;&gt;Pester tests for infrastructure&lt;/a&gt; can help you build a comprehensive testing strategy.&lt;/p&gt;&lt;p&gt;For a complete video walkthrough of this process, check out my YouTube video showing the end to end process:&lt;/p&gt;&lt;p&gt;&lt;custom-youtube slug=&quot;ZfYWh1qT-Us&quot; label=&quot;Keeping your Azure Bicep up-to-date the easy way with GitHub Copilot Agents&quot; class=&quot;flow&quot;&gt;&lt;!-- component composition by: https://github.com/zachleat/zachleat.com --&gt;
&lt;style&gt;
  /* Hide without JS */
  is-land:not(:defined).video-wrapper {
    display: none;
  }
&lt;/style&gt;

  &lt;is-land on:visible class=&quot;video-wrapper&quot;&gt;
    &lt;lite-youtube videoid=&quot;ZfYWh1qT-Us&quot; js-api playlabel=&quot;Play: Keeping your Azure Bicep up-to-date the easy way with GitHub Copilot Agents&quot; style=&quot;background-image: var(--yt-poster-img-url); --yt-poster-img-url-lazy: url(&#39;https://v1.opengraph.11ty.dev/https%3A%2F%2Fyoutube.com%2Fwatch%3Fv%3DZfYWh1qT-Us/auto/jpeg/&#39;)&quot;&gt;&lt;/lite-youtube&gt;

    &lt;template data-island=&quot;once&quot;&gt;
      &lt;style&gt;
        lite-youtube {
          max-inline-size: 100% !important;
          background-size: cover;
        }

        is-land lite-youtube {
          background-color: #eee;
          border-radius: 0.2em;
          background-size: cover;
        }
        is-land[ready] lite-youtube {
          /* gotta set in `style` to override the 480w image from lite-youtube */
          --yt-poster-img-url: var(--yt-poster-img-url-lazy);
        }

        .video-wrapper {
          aspect-ratio: 16 / 9;
          width: 100%;
        }

        is-land.video-wrapper {
          display: block;
        }
      &lt;/style&gt;
      &lt;link rel=&quot;stylesheet&quot; href=&quot;/assets/components/lite-yt-embed.css&quot; /&gt;
      &lt;script type=&quot;module&quot; src=&quot;/assets/components/lite-yt-embed.js&quot;&gt;&lt;/script&gt;
    &lt;/template&gt;
  &lt;/is-land&gt;

&lt;custom-youtube-link label=&quot;Keeping your Azure Bicep up-to-date the easy way with GitHub Copilot Agents&quot; href=&quot;https://youtube.com/watch?v=ZfYWh1qT-Us&quot;&gt;&lt;style&gt;
  custom-youtube-link {
    display: flex;
    align-items: flex-start;
    gap: var(--space-xs);
    font-size: var(--size-step-min-1);
  }
  custom-youtube-link svg {
    font-size: var(--size-step-0);
    margin-block-start: 0.1em;
  }
&lt;/style&gt;

&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; ariahidden=&quot;true&quot;&gt;&lt;path d=&quot;M2.5 17a24.12 24.12 0 0 1 0-10 2 2 0 0 1 1.4-1.4 49.56 49.56 0 0 1 16.2 0A2 2 0 0 1 21.5 7a24.12 24.12 0 0 1 0 10 2 2 0 0 1-1.4 1.4 49.55 49.55 0 0 1-16.2 0A2 2 0 0 1 2.5 17&quot;&gt;&lt;/path&gt;&lt;path d=&quot;m10 15 5-3-5-3z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;

&lt;a href=&quot;https://youtube.com/watch?v=ZfYWh1qT-Us&quot;&gt;Keeping your Azure Bicep up-to-date the easy way with GitHub Copilot Agents&lt;/a&gt;

&lt;!-- Inspired by https://github.com/zachleat/zachleat.com  --&gt;
&lt;/custom-youtube-link&gt;
&lt;/custom-youtube&gt;&lt;/p&gt;&lt;h2 id=&quot;beyond-avm-updates-the-bigger-picture&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#beyond-avm-updates-the-bigger-picture&quot; class=&quot;heading-anchor&quot;&gt;Beyond AVM Updates: The Bigger Picture&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;What excites me most about this approach isn’t just the AVM updates—it’s the pattern. You can create Prompt Files for any repetitive, rule-based task that involves:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Code analysis and transformation&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Documentation updates&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Security vulnerability scanning and fixing&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Code style standardization&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Dependency management&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The key is identifying tasks that are:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Well-defined&lt;/strong&gt;: Clear steps that you can articulate&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Repetitive&lt;/strong&gt;: You do them regularly across multiple files or projects&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Rule-based&lt;/strong&gt;: They follow consistent patterns and logic&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Time-consuming&lt;/strong&gt;: They take significant effort to do manually&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;wrapping-up&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/keeping-azure-bicep-up-to-date-the-easy-way-with-github-copilot-agents/#wrapping-up&quot; class=&quot;heading-anchor&quot;&gt;Wrapping Up&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;GitHub Copilot’s Prompt Files feature is a great advancement that helps automate routine maintenance tasks and much more. By creating reusable prompts for complex processes or even using it to plan out one-off tasks helps us think through and document the process before we actually start coding - this in itself increases the chances of success and reduces the time spent on trial and error.&lt;/p&gt;&lt;p&gt;The future of development tooling is moving toward AI agents that can handle increasingly complex tasks. Prompt Files give us a way to encode our domain knowledge and processes into reusable AI workflows, making our entire team more productive.&lt;/p&gt;&lt;p&gt;Have you tried using GitHub Copilot Agent mode for infrastructure maintenance? I’d love to hear about your experiences and the creative ways you’re using Prompt Files in your projects.&lt;/p&gt;&lt;p&gt;Remember: the goal isn’t to replace human judgment, but to automate the tedious parts so we can focus on the creative and strategic aspects of building great cloud solutions. Less toil, more innovation!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Deploying Foundry VTT to Azure in 5 minutes</title>
      <link href="https://danielscottraynsford.com/blog/deploying-foundry-vtt-to-azure-in-5-minutes/" />
      <updated>2025-05-26T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/deploying-foundry-vtt-to-azure-in-5-minutes/</id>
      <content type="html">
				&lt;h2 id=&quot;what-is-foundry-vtt&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploying-foundry-vtt-to-azure-in-5-minutes/#what-is-foundry-vtt&quot; class=&quot;heading-anchor&quot;&gt;What is Foundry VTT?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you’re a tabletop RPG enthusiast like me, you might have heard of Foundry Virtual Table Top (VTT). It’s a self-hosted, modern application for playing tabletop roleplaying games online with your friends. Foundry VTT gives you full control over your TTRPG gaming experience - and is super extensible with a vibrant community creating modules, systems, and content to enhance your games.&lt;/p&gt;&lt;p&gt;Many people choose to just run Foundry VTT on a machine on their home network and make it accessible via port forwarding on their router, which all works perfectly. But if you’re playing with people spread over many different locations or just like to host things in the cloud, then this is for you - that’s where Azure comes in!&lt;/p&gt;&lt;h2 id=&quot;why-deploy-foundry-vtt-to-azure&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploying-foundry-vtt-to-azure-in-5-minutes/#why-deploy-foundry-vtt-to-azure&quot; class=&quot;heading-anchor&quot;&gt;Why deploy Foundry VTT to Azure?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you’re considering cloud hosting for Foundry VTT, and happen to have access to a Microsoft Azure subscription, deploying Foundry VTT to Azure has several advantages:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Always available&lt;/strong&gt; - Your game server is always online, ready for your players to connect anytime.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;No port forwarding&lt;/strong&gt; - You keep your home network secure without needing to expose it to the internet.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt; - Azure’s global infrastructure ensures low latency for players around the world.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Scalability&lt;/strong&gt; - Need more power for a bigger campaign? Scale up easily.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Storage Reliability&lt;/strong&gt; - Your game data is stored in Azure Files, which can be replicated and backed up for durability. This also means you can leverage features of Azure storage like snapshots and backups to protect your valuable campaign data.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt; - Azure provides built-in security features like managed identities and Key Vault for sensitive information.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Of course, you may not need all of these features, and be perfectly happy running Foundry VTT on your home network. But this blog post is for those who want to take advantage of the cloud to host their Foundry VTT server.&lt;/p&gt;&lt;h2 id=&quot;how-to-deploy-the-solution-accelerator&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploying-foundry-vtt-to-azure-in-5-minutes/#how-to-deploy-the-solution-accelerator&quot; class=&quot;heading-anchor&quot;&gt;How to deploy the solution accelerator&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;So, to make deploying Foundry VTT to Azure as easy as possible, I’ve created a solution accelerator that automates the entire process. It uses the &lt;a href=&quot;https://aka.ms/azd&quot; rel=&quot;noopener&quot;&gt;Azure Developer CLI&lt;/a&gt; (azd) and &lt;a href=&quot;https://aka.ms/bicep&quot; rel=&quot;noopener&quot;&gt;Azure Bicep&lt;/a&gt;. The Azure Developer CLI is a powerful tool that simplifies the process of provisioning and managing Azure resources as well as building and deploying applications (however, in this case we don’t need to build anything). I chose to use it for this solution because it makes it simple to “stamp” out environments as well as tearing them down when they’re not needed - perfect for gaming sessions that might not run continuously - although be careful not to delete your storage account if you want to keep your game data!&lt;/p&gt;&lt;p&gt;Let’s walk through the process:&lt;/p&gt;&lt;h3 id=&quot;prerequisites&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploying-foundry-vtt-to-azure-in-5-minutes/#prerequisites&quot; class=&quot;heading-anchor&quot;&gt;Prerequisites&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Before we start, you’ll need:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;An Azure subscription (well, obviously!)&lt;ul&gt;&lt;li&gt;If you don’t have one, you can &lt;a href=&quot;https://azure.microsoft.com/free&quot; rel=&quot;noopener&quot;&gt;sign up for a free Azure account&lt;/a&gt; which gives you some credits to get started.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;A valid Foundry VTT license (purchase at &lt;a href=&quot;https://foundryvtt.com&quot; rel=&quot;noopener&quot;&gt;foundryvtt.com&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd&quot; rel=&quot;noopener&quot;&gt;Azure Developer CLI&lt;/a&gt; installed&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://git-scm.com/downloads&quot; rel=&quot;noopener&quot;&gt;Git&lt;/a&gt; installed&lt;/li&gt;&lt;/ol&gt;&lt;h3 id=&quot;deployment-steps&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploying-foundry-vtt-to-azure-in-5-minutes/#deployment-steps&quot; class=&quot;heading-anchor&quot;&gt;Deployment Steps&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Open a terminal or command prompt.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Clone the Foundry VTT in Azure repository:&lt;/p&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone https://github.com/PlagueHO/foundryvtt-azure.git
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; foundryvtt-azure&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Log in to Azure using the Azure Developer CLI:&lt;/p&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;azd auth login&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The next step is to initialize the Azure Developer CLI project by setting an environment name. This is a unique name that will be used to create the Azure resources. You can choose any name you like, but it should be unique across your Azure subscription. Run the following command, replacing &lt;code&gt;&amp;lt;UniqueEnvironmentName&amp;gt;&lt;/code&gt; with your chosen name:&lt;/p&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;azd init &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;UniqueEnvironmentName&lt;span class=&quot;token operator&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Configure the Foundry VTT environment variables that allow the deployment to access your Foundry VTT account to get the application distribution and license. You can do this by running the following commands, replacing the placeholders with your actual Foundry VTT credentials:&lt;/p&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;azd &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; FOUNDRY_USERNAME &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;your-foundry-username&amp;gt;&quot;&lt;/span&gt;
azd &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; FOUNDRY_PASSWORD &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;your-foundry-password&amp;gt;&quot;&lt;/span&gt;
azd &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; FOUNDRY_ADMIN_KEY &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;your-foundry-admin-key&amp;gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-callout=&quot;important&quot; class=&quot;blockquote--important&quot;&gt;&lt;p&gt;The &lt;code&gt;FOUNDRY_USERNAME&lt;/code&gt; and &lt;code&gt;FOUNDRY_PASSWORD&lt;/code&gt; are the credentials you use to log in to Foundry VTT website. These are used to retrieve your Foundry license and download the application distribution by the Foundry VTT Docker container. They are stored in an Azure Key Vault to protect them. The &lt;code&gt;FOUNDRY_ADMIN_KEY&lt;/code&gt; is the admin password you’ll use to log into your Foundry VTT once it is deployed.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;There are also a number of optional parameters you can also configure to control the deployment (this is just a few):&lt;/p&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;azd &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; AZURE_DEPLOY_NETWORKING &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt;
azd &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; AZURE_STORAGE_CONFIGURATION &lt;span class=&quot;token string&quot;&gt;&quot;Premium_100GB&quot;&lt;/span&gt;
azd &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; AZURE_COMPUTE_SERVICE &lt;span class=&quot;token string&quot;&gt;&quot;Web App&quot;&lt;/span&gt;
azd &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; AZURE_APP_SERVICE_PLAN_SKUNAME &lt;span class=&quot;token string&quot;&gt;&quot;P0v3&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Start the deployment:&lt;/p&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;azd provision&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;You will then be asked to select the Azure Subscription and the Azure region to deploy the resources to.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/sgBxWB2FGl-650.webp 650w, https://danielscottraynsford.com/img/sgBxWB2FGl-960.webp 960w, https://danielscottraynsford.com/img/sgBxWB2FGl-1367.webp 1367w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/sgBxWB2FGl-650.png&quot; alt=&quot;The commands used to deploy the Foundry VTT solution accelerator&quot; title=&quot;The commands used to deploy the Foundry VTT solution accelerator&quot; width=&quot;1367&quot; height=&quot;1069&quot; srcset=&quot;https://danielscottraynsford.com/img/sgBxWB2FGl-650.png 650w, https://danielscottraynsford.com/img/sgBxWB2FGl-960.png 960w, https://danielscottraynsford.com/img/sgBxWB2FGl-1367.png 1367w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;The commands used to deploy the Foundry VTT solution accelerator&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;The Azure region should be the closest to you and your players for optimal latency.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;And that’s it! In about 5 minutes (give or take a minute), you’ll have a fully deployed Foundry VTT instance running in Azure. At the end of the deployment, you’ll see the URL where your Foundry VTT server is accessible displayed in the console.&lt;/p&gt;&lt;h2 id=&quot;see-it-in-action&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploying-foundry-vtt-to-azure-in-5-minutes/#see-it-in-action&quot; class=&quot;heading-anchor&quot;&gt;See it in action&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Here’s a video walkthrough of the deployment process:&lt;/p&gt;&lt;p&gt;&lt;custom-youtube slug=&quot;asb8bu0eRmM&quot; label=&quot;Deploying Foundry VTT to Azure in 5 minutes using Azure Developer CLI&quot; class=&quot;flow&quot;&gt;&lt;!-- component composition by: https://github.com/zachleat/zachleat.com --&gt;
&lt;style&gt;
  /* Hide without JS */
  is-land:not(:defined).video-wrapper {
    display: none;
  }
&lt;/style&gt;

  &lt;is-land on:visible class=&quot;video-wrapper&quot;&gt;
    &lt;lite-youtube videoid=&quot;asb8bu0eRmM&quot; js-api playlabel=&quot;Play: Deploying Foundry VTT to Azure in 5 minutes using Azure Developer CLI&quot; style=&quot;background-image: var(--yt-poster-img-url); --yt-poster-img-url-lazy: url(&#39;https://v1.opengraph.11ty.dev/https%3A%2F%2Fyoutube.com%2Fwatch%3Fv%3Dasb8bu0eRmM/auto/jpeg/&#39;)&quot;&gt;&lt;/lite-youtube&gt;

    &lt;template data-island=&quot;once&quot;&gt;
      &lt;style&gt;
        lite-youtube {
          max-inline-size: 100% !important;
          background-size: cover;
        }

        is-land lite-youtube {
          background-color: #eee;
          border-radius: 0.2em;
          background-size: cover;
        }
        is-land[ready] lite-youtube {
          /* gotta set in `style` to override the 480w image from lite-youtube */
          --yt-poster-img-url: var(--yt-poster-img-url-lazy);
        }

        .video-wrapper {
          aspect-ratio: 16 / 9;
          width: 100%;
        }

        is-land.video-wrapper {
          display: block;
        }
      &lt;/style&gt;
      &lt;link rel=&quot;stylesheet&quot; href=&quot;/assets/components/lite-yt-embed.css&quot; /&gt;
      &lt;script type=&quot;module&quot; src=&quot;/assets/components/lite-yt-embed.js&quot;&gt;&lt;/script&gt;
    &lt;/template&gt;
  &lt;/is-land&gt;

&lt;custom-youtube-link label=&quot;Deploying Foundry VTT to Azure in 5 minutes using Azure Developer CLI&quot; href=&quot;https://youtube.com/watch?v=asb8bu0eRmM&quot;&gt;&lt;style&gt;
  custom-youtube-link {
    display: flex;
    align-items: flex-start;
    gap: var(--space-xs);
    font-size: var(--size-step-min-1);
  }
  custom-youtube-link svg {
    font-size: var(--size-step-0);
    margin-block-start: 0.1em;
  }
&lt;/style&gt;

&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; ariahidden=&quot;true&quot;&gt;&lt;path d=&quot;M2.5 17a24.12 24.12 0 0 1 0-10 2 2 0 0 1 1.4-1.4 49.56 49.56 0 0 1 16.2 0A2 2 0 0 1 21.5 7a24.12 24.12 0 0 1 0 10 2 2 0 0 1-1.4 1.4 49.55 49.55 0 0 1-16.2 0A2 2 0 0 1 2.5 17&quot;&gt;&lt;/path&gt;&lt;path d=&quot;m10 15 5-3-5-3z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;

&lt;a href=&quot;https://youtube.com/watch?v=asb8bu0eRmM&quot;&gt;Deploying Foundry VTT to Azure in 5 minutes using Azure Developer CLI&lt;/a&gt;

&lt;!-- Inspired by https://github.com/zachleat/zachleat.com  --&gt;
&lt;/custom-youtube-link&gt;
&lt;/custom-youtube&gt;&lt;/p&gt;&lt;p&gt;If the embed doesn’t work, you can &lt;a href=&quot;https://youtu.be/asb8bu0eRmM&quot; rel=&quot;noopener&quot;&gt;watch the video on YouTube&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;configuration-options&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploying-foundry-vtt-to-azure-in-5-minutes/#configuration-options&quot; class=&quot;heading-anchor&quot;&gt;Configuration options&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The solution accelerator does provide a number of configuration options you can use to customize your deployment. For example, you can change the SKU of the App Service Plan, the size of the Azure Storage account, whether to deploy a virtual network, and more. These can be set using the &lt;code&gt;azd env set&lt;/code&gt; command as shown above.&lt;/p&gt;&lt;h3 id=&quot;required-parameters&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploying-foundry-vtt-to-azure-in-5-minutes/#required-parameters&quot; class=&quot;heading-anchor&quot;&gt;Required Parameters&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;FOUNDRY_USERNAME&lt;/code&gt; - Your Foundry VTT username.&lt;/li&gt;&lt;li&gt;&lt;code&gt;FOUNDRY_PASSWORD&lt;/code&gt; - Your Foundry VTT password&lt;/li&gt;&lt;li&gt;&lt;code&gt;FOUNDRY_ADMIN_KEY&lt;/code&gt; - The admin key for Foundry VTT&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;optional-parameters&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploying-foundry-vtt-to-azure-in-5-minutes/#optional-parameters&quot; class=&quot;heading-anchor&quot;&gt;Optional Parameters&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;AZURE_COMPUTE_SERVICE&lt;/code&gt; - &lt;code&gt;Web App&lt;/code&gt; (default, recommended) or &lt;code&gt;Container Instance&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;AZURE_DEPLOY_DDB_PROXY&lt;/code&gt; - &lt;code&gt;true&lt;/code&gt; (default) or &lt;code&gt;false&lt;/code&gt; to deploy a DDB Proxy. The DDB Proxy is second Web App that runs Mr. Primates DDB-Proxy container. For more information see &lt;a href=&quot;https://github.com/PlagueHO/foundryvtt-azure/?tab=readme-ov-file#ddb-proxy&quot; rel=&quot;noopener&quot;&gt;DDB-Proxy&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;AZURE_DEPLOY_NETWORKING&lt;/code&gt; - &lt;code&gt;true&lt;/code&gt; (default) or &lt;code&gt;false&lt;/code&gt; to deploy a virtual network&lt;/li&gt;&lt;li&gt;&lt;code&gt;AZURE_STORAGE_CONFIGURATION&lt;/code&gt; - &lt;code&gt;Premium_100GB&lt;/code&gt; (default) or &lt;code&gt;Standard_100GB&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;AZURE_STORAGE_PUBLIC_ACCESS&lt;/code&gt; - &lt;code&gt;false&lt;/code&gt; (default) to restrict public access to storage, or &lt;code&gt;true&lt;/code&gt; to allow public access&lt;/li&gt;&lt;li&gt;&lt;code&gt;AZURE_APP_SERVICE_PLAN_SKUNAME&lt;/code&gt; - App Service SKU (e.g., &lt;code&gt;P0v3&lt;/code&gt; is default)&lt;/li&gt;&lt;li&gt;&lt;code&gt;AZURE_DEPLOY_DIAGNOSTICS&lt;/code&gt; - &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; (default) to deploy diagnostics&lt;/li&gt;&lt;/ul&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;There are other optional parameters you can set to control the deployment. You can find the full list of parameters in the &lt;a href=&quot;https://github.com/PlagueHO/foundryvtt-azure&quot; rel=&quot;noopener&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;what-gets-deployed&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploying-foundry-vtt-to-azure-in-5-minutes/#what-gets-deployed&quot; class=&quot;heading-anchor&quot;&gt;What gets deployed?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;When you deploy the solution accelerator, these Azure resources are provisioned:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Azure Storage Account&lt;/strong&gt;: Hosts your Foundry VTT data files, configurations, and worlds.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Azure Web App&lt;/strong&gt;: Hosts the Foundry VTT application using the Felddy Docker container.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Azure Key Vault&lt;/strong&gt;: Stores sensitive information like your Foundry VTT credentials securely.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Azure Virtual Network&lt;/strong&gt; (optional): Provides a secure network for your resources.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The solution is designed with a separation of concerns - your data storage is independent from the compute resources. This means you can rebuild, upgrade, or modify your server without risking your valuable game data.&lt;/p&gt;&lt;h2 id=&quot;advanced-deploying-with-github-actions&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploying-foundry-vtt-to-azure-in-5-minutes/#advanced-deploying-with-github-actions&quot; class=&quot;heading-anchor&quot;&gt;Advanced: Deploying with GitHub Actions&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;For those who want to automate deployments further, the solution accelerator can be integrated with GitHub Actions. This allows you to:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Version control your infrastructure configuration&lt;/li&gt;&lt;li&gt;Automate deployments when you push changes&lt;/li&gt;&lt;li&gt;Easily deploy to multiple environments (e.g., testing and production)&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;To set this up, check out the &lt;a href=&quot;https://github.com/PlagueHO/foundryvtt-azure/?tab=readme-ov-file#deploy-with-github-actions&quot; rel=&quot;noopener&quot;&gt;instructions&lt;/a&gt; in the GitHub repository. It provides a step-by-step guide on how to configure GitHub Actions to deploy your Foundry VTT instance automatically whenever you push changes to your repository.&lt;/p&gt;&lt;h2 id=&quot;what-next&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploying-foundry-vtt-to-azure-in-5-minutes/#what-next&quot; class=&quot;heading-anchor&quot;&gt;What next?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I’m planning to add support to deploy the Foundry VTT and DDB-Proxy to Azure Container Apps in the future, which will allow you to run Foundry VTT in a serverless environment. In theory this should allow you to reduce hosting costs by enabling scale-to-zero for the Foundry VTT server when it’s not in use, and scale up automatically when players connect. However, this will need some testing and evaluation to ensure it works well with the Foundry VTT application.&lt;/p&gt;&lt;p&gt;If you encounter any issues, have any questions or feature requests, please create an issue in the &lt;a href=&quot;https://github.com/PlagueHO/foundryvtt-azure/issues&quot; rel=&quot;noopener&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Happy gaming in Azure!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Blogging with GitHub Copilot&#39;s Virtual Teammates</title>
      <link href="https://danielscottraynsford.com/blog/blogging-with-github-copilots-virtual-teammates/" />
      <updated>2025-05-20T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/blogging-with-github-copilots-virtual-teammates/</id>
      <content type="html">
				&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This blog post was created entirely using GitHub Copilot Coding Agent and is AI-generated. It’s published purely for demonstration and testing purposes to explore the capabilities of GitHub Copilot Coding Agent. This transparency aligns with Responsible AI practices.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;introduction-to-github-copilot-coding-agent&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/blogging-with-github-copilots-virtual-teammates/#introduction-to-github-copilot-coding-agent&quot; class=&quot;heading-anchor&quot;&gt;Introduction to GitHub Copilot Coding Agent&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;GitHub has recently taken AI coding to a new level with the &lt;a href=&quot;https://github.blog/news-insights/product-news/github-copilot-meet-the-new-coding-agent/&quot; rel=&quot;noopener&quot;&gt;GitHub Copilot Coding Agent&lt;/a&gt;. Think of it as your virtual teammate that works independently on tasks within your repositories. Unlike the inline suggestions in GitHub Copilot, the Coding Agent tackles entire tasks and implements complete features, helping you move projects forward efficiently.&lt;/p&gt;&lt;h2 id=&quot;capabilities-that-will-change-your-workflow&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/blogging-with-github-copilots-virtual-teammates/#capabilities-that-will-change-your-workflow&quot; class=&quot;heading-anchor&quot;&gt;Capabilities That Will Change Your Workflow&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;GitHub Copilot Coding Agent extends beyond simple code suggestions with capabilities that include:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Assigning entire tasks&lt;/strong&gt;: Create issues or tasks and have the Coding Agent implement them&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Making changes across multiple files&lt;/strong&gt;: The agent can understand your codebase and make coordinated changes across files&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fixing bugs and implementing features&lt;/strong&gt;: From bug fixes to feature implementations, the agent can handle substantial pieces of work&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Running tests and validating changes&lt;/strong&gt;: The agent can test its own work, ensuring changes meet requirements&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Providing detailed explanations&lt;/strong&gt;: Get comprehensive explanations about the changes made and why they were made&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;What makes this particularly interesting is that the agent operates within the constraints of your GitHub repository’s structure and existing patterns, creating code that aligns with your project’s style and approach.&lt;/p&gt;&lt;h2 id=&quot;how-to-get-started-with-coding-agent&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/blogging-with-github-copilots-virtual-teammates/#how-to-get-started-with-coding-agent&quot; class=&quot;heading-anchor&quot;&gt;How to Get Started with Coding Agent&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Getting started with GitHub Copilot Coding Agent is simple. According to the &lt;a href=&quot;https://docs.github.com/en/enterprise-cloud@latest/copilot/using-github-copilot/using-copilot-coding-agent-to-work-on-tasks/about-assigning-tasks-to-copilot&quot; rel=&quot;noopener&quot;&gt;official documentation&lt;/a&gt;, you’ll need:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;GitHub Copilot Enterprise subscription&lt;/strong&gt;: This feature is part of the enterprise offering.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enable the feature&lt;/strong&gt;: Your organization admin needs to enable Copilot Coding Agent.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Assign a task&lt;/strong&gt;: Create an issue, then assign it to Copilot.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Review and collaborate&lt;/strong&gt;: Once Copilot creates a pull request, review it like any other PR.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The workflow integrates seamlessly into existing GitHub processes.&lt;/p&gt;&lt;h2 id=&quot;the-developer-and-agent-partnership&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/blogging-with-github-copilots-virtual-teammates/#the-developer-and-agent-partnership&quot; class=&quot;heading-anchor&quot;&gt;The Developer and Agent Partnership&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Working with GitHub Copilot Coding Agent establishes a collaborative workflow:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Task definition&lt;/strong&gt;: You define a task or issue with clear requirements.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Agent implementation&lt;/strong&gt;: The Coding Agent reviews your repository and creates a pull request with its implementation.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Developer review&lt;/strong&gt;: You review the changes, possibly requesting adjustments.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Iterative refinement&lt;/strong&gt;: The agent responds to your feedback with additional changes.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;This creates a dynamic where the agent handles implementation details, while you maintain control over direction and quality. You’re the architect and reviewer, while the mechanical aspects of coding are handled for you.&lt;/p&gt;&lt;h2 id=&quot;steering-the-agent-and-handling-roadblocks&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/blogging-with-github-copilots-virtual-teammates/#steering-the-agent-and-handling-roadblocks&quot; class=&quot;heading-anchor&quot;&gt;Steering the Agent and Handling Roadblocks&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;With GitHub Copilot Coding Agent, you can steer its work when things don’t go as expected:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Provide feedback&lt;/strong&gt;: Comment directly on the PR to clarify requirements.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Handle failed builds&lt;/strong&gt;: Ask the agent to fix issues that arose during testing.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Refine implementation&lt;/strong&gt;: Request changes to align with coding standards.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;For example, if a build fails, you might comment:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;“The build is failing because we’re missing tests for the new authentication method. Could you add unit tests for this?”&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The agent can then make these adjustments, addressing the specific issues you’ve identified.&lt;/p&gt;&lt;h2 id=&quot;the-meta-moment-ai-writing-about-ai&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/blogging-with-github-copilots-virtual-teammates/#the-meta-moment-ai-writing-about-ai&quot; class=&quot;heading-anchor&quot;&gt;The Meta Moment: AI Writing About AI&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;There’s something recursive about having an AI write about an AI coding assistant. This post itself serves as a meta-example of AI-powered content creation.&lt;/p&gt;&lt;p&gt;It raises questions about expertise and authorship:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Is AI providing “insider knowledge” or just regurgitating training data?&lt;/li&gt;&lt;li&gt;How does reading AI-created content about AI differ from human-authored content?&lt;/li&gt;&lt;li&gt;What are the implications when content can be generated without human experience?&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;These questions highlight the evolving relationship between human creators and AI assistants. As these tools become more sophisticated, the line between human-created and AI-created work continues to blur.&lt;/p&gt;&lt;h2 id=&quot;conclusion-an-experiment-in-ai-capabilities&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/blogging-with-github-copilots-virtual-teammates/#conclusion-an-experiment-in-ai-capabilities&quot; class=&quot;heading-anchor&quot;&gt;Conclusion: An Experiment in AI Capabilities&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I want to emphasize that this blog post itself is an experiment in using GitHub Copilot Coding Agent. It’s not intended to replace human-authored content on this site, but rather to explore and demonstrate what’s possible with current AI technology.&lt;/p&gt;&lt;p&gt;The post you’ve just read was created entirely by AI through GitHub Copilot Coding Agent, without direct human authoring of the content. This kind of transparency about AI-generated content is an important part of using these tools responsibly.&lt;/p&gt;&lt;p&gt;While it’s fascinating to see what AI can produce, this experiment serves primarily as a learning opportunity about the capabilities and limitations of these tools. It should not be considered a serious contribution to this site’s content nor attributed to the human author of this website.&lt;/p&gt;&lt;p&gt;The real value comes from understanding how these technologies work and finding the right balance between AI assistance and human creativity in our workflows.&lt;/p&gt;&lt;h2 id=&quot;the-process-how-this-post-was-created&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/blogging-with-github-copilots-virtual-teammates/#the-process-how-this-post-was-created&quot; class=&quot;heading-anchor&quot;&gt;The Process: How This Post Was Created&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This blog post was created by:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Opening an issue describing the requirements for a blog post about GitHub Copilot Coding Agent&lt;/li&gt;&lt;li&gt;Assigning the issue to GitHub Copilot&lt;/li&gt;&lt;li&gt;Letting GitHub Copilot Coding Agent analyze the repository structure and existing blog posts&lt;/li&gt;&lt;li&gt;Reviewing the AI-generated content and providing minimal guidance&lt;/li&gt;&lt;li&gt;Accepting the pull request with the new blog post&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The entire process was handled by GitHub Copilot Coding Agent with minimal human intervention, demonstrating the capability of AI to understand repository structure, content requirements, and writing style guidelines.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This experiment demonstrates the capabilities of GitHub Copilot Coding Agent for educational purposes only. Future content on this site will continue to be human-authored.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;video-demonstration&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/blogging-with-github-copilots-virtual-teammates/#video-demonstration&quot; class=&quot;heading-anchor&quot;&gt;Video Demonstration&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Check out this video showing me using GitHub Copilot Coding Agent to create this very blog post (yes, it’s inception!):&lt;/p&gt;&lt;p&gt;&lt;custom-youtube slug=&quot;F2XkbeazOEc&quot; label=&quot;Using GitHub Copilot Coding Agent to create a blog post about GitHub Coding Agent! Inception!&quot; class=&quot;flow&quot;&gt;&lt;!-- component composition by: https://github.com/zachleat/zachleat.com --&gt;
&lt;style&gt;
  /* Hide without JS */
  is-land:not(:defined).video-wrapper {
    display: none;
  }
&lt;/style&gt;

  &lt;is-land on:visible class=&quot;video-wrapper&quot;&gt;
    &lt;lite-youtube videoid=&quot;F2XkbeazOEc&quot; js-api playlabel=&quot;Play: Using GitHub Copilot Coding Agent to create a blog post about GitHub Coding Agent! Inception!&quot; style=&quot;background-image: var(--yt-poster-img-url); --yt-poster-img-url-lazy: url(&#39;https://v1.opengraph.11ty.dev/https%3A%2F%2Fyoutube.com%2Fwatch%3Fv%3DF2XkbeazOEc/auto/jpeg/&#39;)&quot;&gt;&lt;/lite-youtube&gt;

    &lt;template data-island=&quot;once&quot;&gt;
      &lt;style&gt;
        lite-youtube {
          max-inline-size: 100% !important;
          background-size: cover;
        }

        is-land lite-youtube {
          background-color: #eee;
          border-radius: 0.2em;
          background-size: cover;
        }
        is-land[ready] lite-youtube {
          /* gotta set in `style` to override the 480w image from lite-youtube */
          --yt-poster-img-url: var(--yt-poster-img-url-lazy);
        }

        .video-wrapper {
          aspect-ratio: 16 / 9;
          width: 100%;
        }

        is-land.video-wrapper {
          display: block;
        }
      &lt;/style&gt;
      &lt;link rel=&quot;stylesheet&quot; href=&quot;/assets/components/lite-yt-embed.css&quot; /&gt;
      &lt;script type=&quot;module&quot; src=&quot;/assets/components/lite-yt-embed.js&quot;&gt;&lt;/script&gt;
    &lt;/template&gt;
  &lt;/is-land&gt;

&lt;custom-youtube-link label=&quot;Using GitHub Copilot Coding Agent to create a blog post about GitHub Coding Agent! Inception!&quot; href=&quot;https://youtube.com/watch?v=F2XkbeazOEc&quot;&gt;&lt;style&gt;
  custom-youtube-link {
    display: flex;
    align-items: flex-start;
    gap: var(--space-xs);
    font-size: var(--size-step-min-1);
  }
  custom-youtube-link svg {
    font-size: var(--size-step-0);
    margin-block-start: 0.1em;
  }
&lt;/style&gt;

&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;1em&quot; height=&quot;1em&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; ariahidden=&quot;true&quot;&gt;&lt;path d=&quot;M2.5 17a24.12 24.12 0 0 1 0-10 2 2 0 0 1 1.4-1.4 49.56 49.56 0 0 1 16.2 0A2 2 0 0 1 21.5 7a24.12 24.12 0 0 1 0 10 2 2 0 0 1-1.4 1.4 49.55 49.55 0 0 1-16.2 0A2 2 0 0 1 2.5 17&quot;&gt;&lt;/path&gt;&lt;path d=&quot;m10 15 5-3-5-3z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;

&lt;a href=&quot;https://youtube.com/watch?v=F2XkbeazOEc&quot;&gt;Using GitHub Copilot Coding Agent to create a blog post about GitHub Coding Agent! Inception!&lt;/a&gt;

&lt;!-- Inspired by https://github.com/zachleat/zachleat.com  --&gt;
&lt;/custom-youtube-link&gt;
&lt;/custom-youtube&gt;&lt;/p&gt;&lt;p&gt;If the embed doesn’t work, you can &lt;a href=&quot;https://youtu.be/F2XkbeazOEc&quot; rel=&quot;noopener&quot;&gt;watch the video on YouTube&lt;/a&gt;.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Using AzureDefaultCredential with Semantic Kernel in Python</title>
      <link href="https://danielscottraynsford.com/blog/using-azuredefaultcredential-with-semantic-kernel-in-python/" />
      <updated>2025-05-17T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/using-azuredefaultcredential-with-semantic-kernel-in-python/</id>
      <content type="html">
				&lt;h2 id=&quot;background&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/using-azuredefaultcredential-with-semantic-kernel-in-python/#background&quot; class=&quot;heading-anchor&quot;&gt;Background&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I am working on a project for simplifying the deployment of a zero-trust &lt;a href=&quot;https://learn.microsoft.com/azure/ai-foundry/how-to/create-secure-ai-hub&quot; rel=&quot;noopener&quot;&gt;secure Azure AI Foundry environment&lt;/a&gt;. As part of this project I am creating some &lt;a href=&quot;https://github.com/PlagueHO/azure-ai-foundry-jumpstart/tree/main/scripts/data-generators#readme&quot; rel=&quot;noopener&quot;&gt;Python scripts to generate synthetic data&lt;/a&gt; for use as sample data.&lt;/p&gt;&lt;h2 id=&quot;securing-with-managed-identities&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/using-azuredefaultcredential-with-semantic-kernel-in-python/#securing-with-managed-identities&quot; class=&quot;heading-anchor&quot;&gt;Securing with Managed Identities&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;It is always best practice to use managed identities to authenticate to Azure services wherever possible, especially in production. I’m very familiar with using managed identities in C# and .NET, but I haven’t used them much in Python version of Semantic Kernel before. So, it’s time to learn how to do that.&lt;/p&gt;&lt;p&gt;To do this, I needed to use the &lt;code&gt;DefaultAzureCredential&lt;/code&gt; class from the &lt;a href=&quot;https://learn.microsoft.com/python/api/overview/azure/identity-readme?view=azure-python&quot; rel=&quot;noopener&quot;&gt;Azure Identity SDK&lt;/a&gt; to provide a token provider to the &lt;code&gt;AzureChatCompletion&lt;/code&gt; class from the &lt;a href=&quot;https://github.com/microsoft/semantic-kernel/tree/main/python#readme&quot; rel=&quot;noopener&quot;&gt;Semantic Kernel Python SDK&lt;/a&gt;.&lt;/p&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; azure&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;identity &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; DefaultAzureCredential&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; get_bearer_token_provider
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; semantic_kernel &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; sk
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; semantic_kernel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connectors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ai&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;open_ai &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; AzureChatCompletion

azure_openai_deployment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;your_deployment_name&amp;gt;&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# The name of your Azure OpenAI deployment&lt;/span&gt;
azure_openai_endpoint &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://&amp;lt;your_endpoint&amp;gt;.openai.azure.com&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# The endpoint for your Azure OpenAI Service&lt;/span&gt;

kernel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Kernel&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Create the Semantic Kernel instance&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Create the Entra ID token provider using the DefaultAzureCredential&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# https://learn.microsoft.com/python/api/azure-identity/azure.identity.defaultazurecredential?view=azure-python&lt;/span&gt;
token_provider &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; get_bearer_token_provider&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    DefaultAzureCredential&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;https://cognitiveservices.azure.com/.default&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# The scope for Azure OpenAI Service&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Attach an AzureChatCompletion service to the kernel using the token provider&lt;/span&gt;
service &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; AzureChatCompletion&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    deployment_name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;azure_openai_deployment&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    endpoint&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;azure_openai_endpoint&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    ad_token_provider&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;token_provider&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Pass the token provider to the service&lt;/span&gt;
    service_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;azure_open_ai&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That’s it. It is fairly straight forward to get away from using API keys to authenticate to your Azure OpenAI Service endpoints. You can see the complete implementation in the &lt;a href=&quot;https://github.com/PlagueHO/azure-ai-foundry-jumpstart/blob/main/scripts/data-generators/synthetic_data_generator.py&quot; rel=&quot;noopener&quot;&gt;Azure AI Foundry jumpstart data generator&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/using-azuredefaultcredential-with-semantic-kernel-in-python/#conclusion&quot; class=&quot;heading-anchor&quot;&gt;Conclusion&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This is a fairly simple example and probably super obvious to many people, but I couldn’t find this clearly documented anywhere, and even my trusty GenAI tooling wasn’t getting it right. I suspect this is most likely because Semantic Kernel is undergoing rapid development.&lt;/p&gt;&lt;p&gt;Chances are I’ll run into this again in the future, so I thought it was worth documenting. But hopefully someone else finds it useful too.&lt;/p&gt;&lt;h2 id=&quot;related-links&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/using-azuredefaultcredential-with-semantic-kernel-in-python/#related-links&quot; class=&quot;heading-anchor&quot;&gt;Related links&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/microsoft/semantic-kernel/tree/main/python#readme&quot; rel=&quot;noopener&quot;&gt;Semantic Kernel Python SDK&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/python/api/overview/azure/identity-readme?view=azure-python&quot; rel=&quot;noopener&quot;&gt;Azure Identity SDK for Python&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/python/api/azure-identity/azure.identity.defaultazurecredential?view=azure-python&quot; rel=&quot;noopener&quot;&gt;DefaultAzureCredential Class in Python&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/PlagueHO/azure-ai-foundry-jumpstart/tree/main/scripts/data-generators#readme&quot; rel=&quot;noopener&quot;&gt;Python scripts to generate synthetic data&lt;/a&gt; that demonstrate this approach.&lt;/li&gt;&lt;/ul&gt;
 			</content>
    </entry><entry>
      <title>Assigning Roles for Azure Enterprise Apps using Bicep</title>
      <link href="https://danielscottraynsford.com/blog/assigning-roles-for-azure-enterprise-apps-using-bicep/" />
      <updated>2025-05-11T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/assigning-roles-for-azure-enterprise-apps-using-bicep/</id>
      <content type="html">
				&lt;h2 id=&quot;welcome-back&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/assigning-roles-for-azure-enterprise-apps-using-bicep/#welcome-back&quot; class=&quot;heading-anchor&quot;&gt;Welcome back&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;It’s been a while since I last posted, but with the amount of things going on in the world of AI, I thought it was time to get back into the swing of things. Today, I’m going to share a problem I faced recently and how I solved it with Bicep.&lt;/p&gt;&lt;h2 id=&quot;the-problem&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/assigning-roles-for-azure-enterprise-apps-using-bicep/#the-problem&quot; class=&quot;heading-anchor&quot;&gt;The problem&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I am working on a project for simplifying the deployment of a zero-trust &lt;a href=&quot;https://learn.microsoft.com/azure/ai-foundry/how-to/create-secure-ai-hub&quot; rel=&quot;noopener&quot;&gt;secure Azure AI Foundry environment&lt;/a&gt;, an &lt;a href=&quot;https://github.com/PlagueHO/azure-ai-foundry-jumpstart&quot; rel=&quot;noopener&quot;&gt;Azure AI Foundry jumpstart&lt;/a&gt; if you will. This project deploys the resources using &lt;a href=&quot;http://aka.ms/bicep&quot; rel=&quot;noopener&quot;&gt;Bicep&lt;/a&gt;, but one of the requirements is to assign the &lt;code&gt;reader&lt;/code&gt; role to the service principal for the Enterprise Application added &lt;code&gt;Azure Machine Learning&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;However, this is not as straightforward as it seems. The &lt;code&gt;Azure Machine Learning&lt;/code&gt; Enterprise Application is a multi-tenant application, and the service principal object ID is different across all tenants. This means that you cannot hardcode the Object ID in your Bicep template and you can’t use the Application ID of the &lt;code&gt;Azure Machine Learning&lt;/code&gt; Enterprise Application either, as the role assignement requires the Object ID of the service principal in your tenant.&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;I’m using the &lt;code&gt;Azure Machine Learning&lt;/code&gt; Enterprise Application as an example, but this applies to any multi-tenant application in Entra ID that you want to assign roles to in your tenant. For more information on application and service principals objects in Entra ID, see &lt;a href=&quot;https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser#relationship-between-application-objects-and-service-principals&quot; rel=&quot;noopener&quot;&gt;this article&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;the-solution&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/assigning-roles-for-azure-enterprise-apps-using-bicep/#the-solution&quot; class=&quot;heading-anchor&quot;&gt;The solution&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The solution to this is to use the experiemental &lt;code&gt;extensibility&lt;/code&gt; feature of Bicep that allows you to use the &lt;a href=&quot;https://learn.microsoft.com/en-us/graph/templates/bicep/reference/overview?view=graph-bicep-1.0&quot; rel=&quot;noopener&quot;&gt;Microsoft Graph Extension&lt;/a&gt; to work with Entra ID resources, such as service principals, groups, and users. This feature is still in preview, but it works well for this use case.&lt;/p&gt;&lt;p&gt;Here is a list of the steps we’re going to follow:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Enable the extensibility feature of Bicep by adding a &lt;code&gt;bicepconfig.json&lt;/code&gt; file to your project.&lt;/li&gt;&lt;li&gt;Add the Microsoft Graph Bicep extension to your Bicep template.&lt;/li&gt;&lt;li&gt;Create the service principal resource for the &lt;code&gt;Azure Machine Learning&lt;/code&gt; Enterprise Application.&lt;/li&gt;&lt;li&gt;Create the role assignment for the service principal using the &lt;code&gt;id&lt;/code&gt; property of the service principal resource.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;And now for the fun part! Let’s get started with the code.&lt;/p&gt;&lt;h3 id=&quot;step-1-enable-bicep-extensibility&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/assigning-roles-for-azure-enterprise-apps-using-bicep/#step-1-enable-bicep-extensibility&quot; class=&quot;heading-anchor&quot;&gt;Step 1: Enable Bicep extensibility&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;To enable the extensibility feature of Bicep, you need to add a &lt;code&gt;bicepconfig.json&lt;/code&gt; file to your Bicep project. In general this file should be in the same folder as your Bicep files. If you’re using Visual Studio Code you open the Command Palette ([CTRL/CMD]+[SHIFT]+P) and select &lt;code&gt;Bicep: Create Bicep Configuration File&lt;/code&gt; to create the file. To the &lt;code&gt;bicepconfig.json&lt;/code&gt; file, you need to add the following settings:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;&quot;extensibility&quot;: true&lt;/code&gt; to the &lt;code&gt;experimentalFeaturesEnabled&lt;/code&gt; section to enable the extensibility feature.&lt;/li&gt;&lt;li&gt;&lt;code&gt;&quot;microsoftGraphV1&quot;: &quot;br:mcr.microsoft.com/bicep/extensions/microsoftgraph/v1.0:0.2.0-preview&quot;&lt;/code&gt; to the &lt;code&gt;extensions&lt;/code&gt; section to use the Microsoft Graph Bicep extension.&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// See https://aka.ms/bicep/config for more information on Bicep configuration options&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Press CTRL+SPACE at any location to see Intellisense suggestions&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;analyzers&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;core&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;rules&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;no-unused-params&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;level&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;warning&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;experimentalFeaturesEnabled&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;extensibility&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;extensions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;microsoftGraphV1&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;br:mcr.microsoft.com/bicep/extensions/microsoftgraph/v1.0:0.2.0-preview&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;Once the extensibility feature is out of preview, setting the &lt;code&gt;&quot;extensibility&quot;: true&lt;/code&gt; will no longer be required.&lt;/p&gt;&lt;/blockquote&gt;&lt;h3 id=&quot;step-2-add-the-microsoft-graph-bicep-extension&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/assigning-roles-for-azure-enterprise-apps-using-bicep/#step-2-add-the-microsoft-graph-bicep-extension&quot; class=&quot;heading-anchor&quot;&gt;Step 2: Add the Microsoft Graph Bicep extension&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;At the beginning of your Bicep template, you need to add the Microsoft Graph Bicep extension. This is done by adding the following line:&lt;/p&gt;&lt;pre class=&quot;language-bicep&quot;&gt;&lt;code class=&quot;language-bicep&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Use the Microsoft Graph Bicep extension to work with Entra ID resources&lt;/span&gt;
extension microsoftGraphV1&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;step-3-reference-the-existing-service-principal-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/assigning-roles-for-azure-enterprise-apps-using-bicep/#step-3-reference-the-existing-service-principal-resource&quot; class=&quot;heading-anchor&quot;&gt;Step 3: Reference the existing service principal resource&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;You can use the existing &lt;a href=&quot;https://learn.microsoft.com/en-us/graph/templates/bicep/reference/serviceprincipals?view=graph-bicep-1.0&quot; rel=&quot;noopener&quot;&gt;Microsoft.Graph/servicePrincipals@v1.0&lt;/a&gt; resource that has the &lt;code&gt;appId&lt;/code&gt; property set to the GUID of the &lt;code&gt;Azure Machine Learning&lt;/code&gt; Enterprise Application.&lt;/p&gt;&lt;pre class=&quot;language-bicep&quot;&gt;&lt;code class=&quot;language-bicep&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// The Service Principal of the Azure Machine Learning service.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource&lt;/span&gt; azureMachineLearningServicePrincipal &lt;span class=&quot;token string&quot;&gt;&#39;Microsoft.Graph/servicePrincipals@v1.0&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;existing&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;appId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;0736f41a-0425-4b46-bdb5-1563eff02385&#39;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Azure Machine Learning service principal&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;If you don’t know the &lt;code&gt;appId&lt;/code&gt; of the Enterprise Application you want to assign, you can find it in the Azure portal by going to &lt;code&gt;Microsoft Entra ID&lt;/code&gt; &amp;gt; &lt;code&gt;Enterprise applications&lt;/code&gt; and searching for &lt;code&gt;Azure Machine Learning&lt;/code&gt;. The &lt;code&gt;appId&lt;/code&gt; is the &lt;code&gt;Application ID&lt;/code&gt; of the Enterprise Application. You might need to remove the &lt;code&gt;Application type == Enterprise Applications&lt;/code&gt; filter to see the &lt;code&gt;Azure Machine Learning&lt;/code&gt; Enterprise Application in the list.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/fdx07--gke-650.webp 650w, https://danielscottraynsford.com/img/fdx07--gke-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/fdx07--gke-650.png&quot; alt=&quot;The Azure Machine Learning enterprise application in Entra ID&quot; title=&quot;The Azure Machine Learning enterprise application in Entra ID&quot; width=&quot;960&quot; height=&quot;299&quot; srcset=&quot;https://danielscottraynsford.com/img/fdx07--gke-650.png 650w, https://danielscottraynsford.com/img/fdx07--gke-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;figcaption&gt;The Azure Machine Learning enterprise application in Entra ID&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 id=&quot;step-4-create-the-role-assignment&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/assigning-roles-for-azure-enterprise-apps-using-bicep/#step-4-create-the-role-assignment&quot; class=&quot;heading-anchor&quot;&gt;Step 4: Create the role assignment&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Once the &lt;code&gt;azureMachineLearningServicePrincipal&lt;/code&gt; resource is available to your Bicep template, you can use the &lt;code&gt;id&lt;/code&gt; property of the service principal to assign the &lt;code&gt;reader&lt;/code&gt; role to the resource for the Enterprise Application. In this example, I’m using assigning the &lt;code&gt;reader&lt;/code&gt; role to an existing &lt;code&gt;Azure AI Search&lt;/code&gt; service, but you can assign it to any role to any resource, existing or being created in the template.&lt;/p&gt;&lt;pre class=&quot;language-bicep&quot;&gt;&lt;code class=&quot;language-bicep&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// The existing Azure AI Search service (can be a new or existing resource).&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource&lt;/span&gt; azureAiSearch &lt;span class=&quot;token string&quot;&gt;&#39;Microsoft.Search/searchServices@2025-02-01-preview&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;existing&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;my-azure-ai-search&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The role assignment for the Azure AI Search service to grant reader role to Azure Machine Learning.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource&lt;/span&gt; roleAssignment &lt;span class=&quot;token string&quot;&gt;&#39;Microsoft.Authorization/roleAssignments@2020-04-01-preview&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;guid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subscriptionId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; azureAiSearch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;acdd72a7-3385-48ef-bd42-f606fba81ae7&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; azureAiSearch
  &lt;span class=&quot;token property&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;principalType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;ServicePrincipal&#39;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;principalId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; azureMachineLearningServicePrincipal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id
    &lt;span class=&quot;token property&quot;&gt;roleDefinitionId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;acdd72a7-3385-48ef-bd42-f606fba81ae7&#39;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Role definition ID for Reader role&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;the-complete-bicep-template&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/assigning-roles-for-azure-enterprise-apps-using-bicep/#the-complete-bicep-template&quot; class=&quot;heading-anchor&quot;&gt;The complete Bicep template&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The complete Bicep template looks like this:&lt;/p&gt;&lt;pre class=&quot;language-bicep&quot;&gt;&lt;code class=&quot;language-bicep&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Use the Microsoft Graph Bicep extension to work with Entra ID resources&lt;/span&gt;
extension microsoftGraphV1

&lt;span class=&quot;token comment&quot;&gt;// The Service Principal of the Azure Machine Learning service.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource&lt;/span&gt; azureMachineLearningServicePrincipal &lt;span class=&quot;token string&quot;&gt;&#39;Microsoft.Graph/servicePrincipals@v1.0&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;existing&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;appId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;0736f41a-0425-4b46-bdb5-1563eff02385&#39;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Azure Machine Learning service principal&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The existing Azure AI Search service (can be a new or existing resource).&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource&lt;/span&gt; azureAiSearch &lt;span class=&quot;token string&quot;&gt;&#39;Microsoft.Search/searchServices@2025-02-01-preview&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;existing&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;my-azure-ai-search&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The role assignment for the Azure AI Search service to grant reader role to Azure Machine Learning.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource&lt;/span&gt; roleAssignment &lt;span class=&quot;token string&quot;&gt;&#39;Microsoft.Authorization/roleAssignments@2020-04-01-preview&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;guid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subscriptionId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; azureAiSearch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;acdd72a7-3385-48ef-bd42-f606fba81ae7&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; azureAiSearch
  &lt;span class=&quot;token property&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;principalType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;ServicePrincipal&#39;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;principalId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; azureMachineLearningServicePrincipal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id
    &lt;span class=&quot;token property&quot;&gt;roleDefinitionId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;acdd72a7-3385-48ef-bd42-f606fba81ae7&#39;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Role definition ID for Reader role&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can find the Github Gist containing the code above &lt;a href=&quot;https://gist.github.com/PlagueHO/0f3a2b1c4d5e7f8a6c9b8e4d5e7f8a6&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;To see the entire complete process in action, check out the Bicep for the &lt;a href=&quot;https://github.com/PlagueHO/azure-ai-foundry-jumpstart/blob/main/infra/main.bicep&quot; rel=&quot;noopener&quot;&gt;Azure AI Foundry Jumpstart&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/assigning-roles-for-azure-enterprise-apps-using-bicep/#conclusion&quot; class=&quot;heading-anchor&quot;&gt;Conclusion&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In this post, I showed you how to assign the &lt;code&gt;reader&lt;/code&gt; role to the service principal for the &lt;code&gt;Azure Machine Learning&lt;/code&gt; Enterprise Application using Bicep. This is a common scenario deploying services in Azure that are consumed by multi-tenant applications (such as Azure Machine Learning). Using experiemental features in production enviornments is generally not recommended, but hopefully the extensions feature of Bicep will be available in a stable release soon. So keep an eye out for that!&lt;/p&gt;&lt;h2 id=&quot;related-links&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/assigning-roles-for-azure-enterprise-apps-using-bicep/#related-links&quot; class=&quot;heading-anchor&quot;&gt;Related links&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://aka.ms/bicep&quot; rel=&quot;noopener&quot;&gt;Bicep documentation&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/Azure/bicep/blob/main/docs/experimental-features.md&quot; rel=&quot;noopener&quot;&gt;Bicep experimental features&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-config&quot; rel=&quot;noopener&quot;&gt;Creating a Bicep configuration file&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/graph/templates/bicep/reference/overview?view=graph-bicep-1.0&quot; rel=&quot;noopener&quot;&gt;Microsoft Graph Bicep resource reference&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/entra/identity-platform/app-objects-and-service-principals?tabs=browser#relationship-between-application-objects-and-service-principals&quot; rel=&quot;noopener&quot;&gt;Relationship between application objects and service principals&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
 			</content>
    </entry><entry>
      <title>Protect your Environment from Malicious Pipeline Changes in Azure DevOps</title>
      <link href="https://danielscottraynsford.com/blog/protect-your-environment-from-malicious-pipeline-changes-in-azure-devops/" />
      <updated>2021-01-24T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/protect-your-environment-from-malicious-pipeline-changes-in-azure-devops/</id>
      <content type="html">
				&lt;p&gt;I’ve recently been looking into ways to increase &lt;strong&gt;control&lt;/strong&gt; and &lt;strong&gt;governance&lt;/strong&gt; of continuous delivery practices when Azure DevOps Pipelines when using &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started/pipelines-get-started?view=azure-devops#define-pipelines-using-yaml-syntax&quot; rel=&quot;noopener&quot;&gt;multi-stage YAML pipelines&lt;/a&gt;. My reason for investigating this area is that there are certain gaps in control when you’re just relying on “pipeline as code”.&lt;/p&gt;&lt;p&gt;In this post I’ll demonstrate how a number of different features in Azure and Azure DevOps can be combined to provide a very high level of &lt;strong&gt;control&lt;/strong&gt; and &lt;strong&gt;governance&lt;/strong&gt; over your environments. This is especially important when working in industries that require adherence to specific compliance standards and controls (e.g. health and finance).&lt;/p&gt;&lt;p&gt;Once you’ve read through this post you should have a good understanding of the different features in Azure DevOps that can be combined to meet whatever controls you need.&lt;/p&gt;&lt;p&gt;This post is &lt;strong&gt;not&lt;/strong&gt; going to be an in-depth look at &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/organizations/security/about-permissions&quot; rel=&quot;noopener&quot;&gt;Azure DevOps security and permissions&lt;/a&gt;. That would take far too long and is not the goal of this post. However, it is important to remember that if you don’t set the appropriate permissions on the entities (branches, branch policies, service connections, variable groups, pipelines etc.) then users will be able to bypass the controls you set up. Therefor it is necessary to take security and permissions into account when you’re planning your controls.&lt;/p&gt;&lt;p&gt;In this post I’ll be focusing on &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/approvals&quot; rel=&quot;noopener&quot;&gt;Pipeline Approvals&lt;/a&gt; and how they can be enabled in different ways when combined with &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/environments&quot; rel=&quot;noopener&quot;&gt;Environments&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints&quot; rel=&quot;noopener&quot;&gt;Service Connections&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/library/variable-groups&quot; rel=&quot;noopener&quot;&gt;Variable Groups&lt;/a&gt; and &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/library/variable-groups?view=azure-devops&amp;amp;tabs=yaml#link-secrets-from-an-azure-key-vault&quot; rel=&quot;noopener&quot;&gt;Azure Key Vault&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;a-potential-gap-in-control&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/protect-your-environment-from-malicious-pipeline-changes-in-azure-devops/#a-potential-gap-in-control&quot; class=&quot;heading-anchor&quot;&gt;A Potential Gap in Control&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The reason I decided to write this post is that it is not as straight to protect your secrets and environments when you’re implementing Pipeline as Code in Azure DevOps.&lt;/p&gt;&lt;p&gt;By way of example, consider you have a Git repository with a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started/pipelines-get-started?view=azure-devops#define-pipelines-using-yaml-syntax&quot; rel=&quot;noopener&quot;&gt;multi-stage Azure DevOps YAML pipeline&lt;/a&gt; defined in it:&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;main&#39;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;pr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none

&lt;span class=&quot;token key atrule&quot;&gt;stages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Build
    &lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; templates/build.yml

  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; QA
    &lt;span class=&quot;token key atrule&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Quality Assurance&#39;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;deployment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; deploy_qa
        &lt;span class=&quot;token key atrule&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Deploy to QA&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;vmImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Ubuntu-16.04&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;QA Secrets&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;runOnce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;deploy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; AzureResourceManagerTemplateDeployment@3
                  &lt;span class=&quot;token key atrule&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Deploy Azure Resources&#39;&lt;/span&gt;
                  &lt;span class=&quot;token key atrule&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;azureResourceManagerConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Azure QA&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;subscriptionId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;redacted&amp;gt;&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;resourceGroupName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;dsr-qa-rg&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;East US&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;csmFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;$(Pipeline.Workspace)/arm/azuredeploy.json&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;overrideParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;-sqlServerName dsr-qa-sql -sqlDatabaseName dsrqadb -sqlAdministratorLoginUsername $(SQLAdministratorLoginUsername) -sqlAdministratorLoginPassword $(SQLAdministratorLoginPassword) -hostingPlanName &quot;dsr-qa-asp&quot; -webSiteName &quot;dsrqaapp&quot;&#39;&lt;/span&gt;

  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Production
    &lt;span class=&quot;token key atrule&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Release to Production&#39;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;deployment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; deploy_production
        &lt;span class=&quot;token key atrule&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Deploy to Production&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;vmImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Ubuntu-16.04&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;PRODUCTION Secrets&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;runOnce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;deploy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; AzureResourceManagerTemplateDeployment@3
                  &lt;span class=&quot;token key atrule&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Deploy Azure Resources&#39;&lt;/span&gt;
                  &lt;span class=&quot;token key atrule&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;azureResourceManagerConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Azure PRODUCTION&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;subscriptionId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;redacted&amp;gt;&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;resourceGroupName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;dsr-production-rg&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;East US&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;csmFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;$(Pipeline.Workspace)/arm/azuredeploy.json&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;overrideParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;-sqlServerName dsr-production-sql -sqlDatabaseName dsrproductiondb -sqlAdministratorLoginUsername $(SQLAdministratorLoginUsername) -sqlAdministratorLoginPassword $(SQLAdministratorLoginPassword) -hostingPlanName &quot;dsr-production-asp&quot; -webSiteName &quot;dsrproductionapp&quot;&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This definition is &lt;strong&gt;triggered&lt;/strong&gt; to only run on ‘main’ branch and never from a pull request. The pipeline also references &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/library/variable-groups&quot; rel=&quot;noopener&quot;&gt;Variable Groups&lt;/a&gt; and &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints&quot; rel=&quot;noopener&quot;&gt;Service Connections&lt;/a&gt;, which should be considered protected resources, especially for the Production environment.&lt;/p&gt;&lt;p&gt;We also have an Azure DevOps Pipeline called &lt;strong&gt;Environment Continuous Delivery&lt;/strong&gt; that uses the YAML file:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_pipelineyaml.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/l5b-sW4tYn-650.webp 650w, https://danielscottraynsford.com/img/l5b-sW4tYn-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/l5b-sW4tYn-650.png&quot; alt width=&quot;960&quot; height=&quot;354&quot; srcset=&quot;https://danielscottraynsford.com/img/l5b-sW4tYn-650.png 650w, https://danielscottraynsford.com/img/l5b-sW4tYn-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Azure DevOps Pipeline ‘Environment Continuous Delivery’ linked to azure-pipelines.yml&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;triggers&lt;/strong&gt; are not being overridden:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_pipelinetriggers.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/eL75Jz6cOZ-650.webp 650w, https://danielscottraynsford.com/img/eL75Jz6cOZ-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/eL75Jz6cOZ-650.png&quot; alt width=&quot;960&quot; height=&quot;320&quot; srcset=&quot;https://danielscottraynsford.com/img/eL75Jz6cOZ-650.png 650w, https://danielscottraynsford.com/img/eL75Jz6cOZ-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The pipeline triggers are not being overridden&lt;/p&gt;&lt;p&gt;The fact that the pipeline triggers are not being overridden means that the triggers defined in the YAML will always be used.&lt;/p&gt;&lt;p&gt;Finally, we have also &lt;strong&gt;locked&lt;/strong&gt; main branch to prevent pushing code directly to it without an approved pull request. There is also a &lt;strong&gt;branch policy&lt;/strong&gt; enabled that runs a simple CI build:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_branchpolicy.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/rJknjihZQ6-650.webp 650w, https://danielscottraynsford.com/img/rJknjihZQ6-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/rJknjihZQ6-650.png&quot; alt width=&quot;960&quot; height=&quot;861&quot; srcset=&quot;https://danielscottraynsford.com/img/rJknjihZQ6-650.png 650w, https://danielscottraynsford.com/img/rJknjihZQ6-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The branch policy and lock prevents direct commits/pushes to main branch.&lt;/p&gt;&lt;p&gt;However, the branch policy specifics aren’t actually important here.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;So, what is the problem?&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The problem is that &lt;strong&gt;any user&lt;/strong&gt; may create a new branch off &lt;strong&gt;main&lt;/strong&gt; and add malicious (or accidental) code to the &lt;strong&gt;azure-pipelines.yml&lt;/strong&gt;. For example, if I create a new branch called &lt;em&gt;malicious-change&lt;/em&gt; with &lt;strong&gt;azure-pipelines.yml&lt;/strong&gt; changed to:&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;main&#39;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;malicious-change&#39;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;pr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none

&lt;span class=&quot;token key atrule&quot;&gt;stages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Build
    &lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Malicious_Activities
        &lt;span class=&quot;token key atrule&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;vmImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Ubuntu-16.04&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;continueOnError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;PRODUCTION Secrets&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; echo &#39;Send $(SQLAdministratorLoginUsername) to Pastebin or some external location&#39;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; AzurePowerShell@5
            &lt;span class=&quot;token key atrule&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Run malicious code in Azure Production envrionment&#39;&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;azureSubscription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Azure PRODUCTION&#39;&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;ScriptType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; InlineScript
              &lt;span class=&quot;token key atrule&quot;&gt;Inline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;# Run some malicious code with access to Azure Production&#39;&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;azurePowerShellVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; latestVersion&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_gitmaliciousbranch.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/xwgkCHmg6y-650.webp 650w, https://danielscottraynsford.com/img/xwgkCHmg6y-960.webp 960w, https://danielscottraynsford.com/img/xwgkCHmg6y-1228.webp 1228w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/xwgkCHmg6y-650.png&quot; alt width=&quot;1228&quot; height=&quot;508&quot; srcset=&quot;https://danielscottraynsford.com/img/xwgkCHmg6y-650.png 650w, https://danielscottraynsford.com/img/xwgkCHmg6y-960.png 960w, https://danielscottraynsford.com/img/xwgkCHmg6y-1228.png 1228w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Create a new branch with malicious changes to pipeline definition.&lt;/p&gt;&lt;p&gt;If we then push that new &lt;em&gt;malicious-change&lt;/em&gt; branch to Azure DevOps Git repo, then …&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;… the Azure DevOps pipeline &lt;strong&gt;Environment Continuous Delivery&lt;/strong&gt; will automatically execute against this new branch with malicious/dangerous pipeline changes.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_gitmaliciousbranchruns.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/16G6gskmib-650.webp 650w, https://danielscottraynsford.com/img/16G6gskmib-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/16G6gskmib-650.png&quot; alt width=&quot;960&quot; height=&quot;139&quot; srcset=&quot;https://danielscottraynsford.com/img/16G6gskmib-650.png 650w, https://danielscottraynsford.com/img/16G6gskmib-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The pipeline runs and has access to all resources that this pipeline normally has.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_maliciouspipelinelogs.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/cj2VmYfHhD-650.webp 650w, https://danielscottraynsford.com/img/cj2VmYfHhD-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/cj2VmYfHhD-650.png&quot; alt width=&quot;960&quot; height=&quot;367&quot; srcset=&quot;https://danielscottraynsford.com/img/cj2VmYfHhD-650.png 650w, https://danielscottraynsford.com/img/cj2VmYfHhD-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Pipeline is able to run commands with access to Azure Production resources.&lt;/p&gt;&lt;p&gt;Now that we know where the gaps are in our controls we can look for potential solutions.&lt;/p&gt;&lt;h2 id=&quot;a-less-than-ideal-solution&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/protect-your-environment-from-malicious-pipeline-changes-in-azure-devops/#a-less-than-ideal-solution&quot; class=&quot;heading-anchor&quot;&gt;A Less than Ideal Solution&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;There is a “quick and dirty” solution to this issue, but it does move us away from true “pipeline as code”. To implement this we simply need to override the triggers section in the pipeline so that it is no longer controlled by the &lt;strong&gt;azure-pipelines.yml&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_overridetriggers.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/aOMmXrWOHp-650.webp 650w, https://danielscottraynsford.com/img/aOMmXrWOHp-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/aOMmXrWOHp-650.png&quot; alt width=&quot;960&quot; height=&quot;561&quot; srcset=&quot;https://danielscottraynsford.com/img/aOMmXrWOHp-650.png 650w, https://danielscottraynsford.com/img/aOMmXrWOHp-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Overriding the triggers in the pipeline prevents the triggers section in azure-pipelines.yml from being used.&lt;/p&gt;&lt;p&gt;Although this solution is easy to implement it means that we’re not fully defining our &lt;strong&gt;pipelines with code&lt;/strong&gt;. This means that someone with permissions to edit the pipeline would need to make any changes to branch or path filters, even when they are legitimate. Plus, there is a gap in the Azure DevOps UI which prevents us from overriding pull request triggers.&lt;/p&gt;&lt;p&gt;Alternatively, we could use Azure DevOps security to prevent creation of new branches by unapproved users but this will limit productivity and increases complexity, so I’m not even considering this a solution worth exploring.&lt;/p&gt;&lt;p&gt;So, let’s look at some better ways to protect our environments, secrets and service connections.&lt;/p&gt;&lt;h2 id=&quot;increasing-controls-the-right-way&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/protect-your-environment-from-malicious-pipeline-changes-in-azure-devops/#increasing-controls-the-right-way&quot; class=&quot;heading-anchor&quot;&gt;Increasing Controls the Right Way&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I’m going to increase the level of control and governance over the pipelines by implementing the following changes:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Putting secrets into an &lt;strong&gt;Azure Key Vault&lt;/strong&gt; and using a &lt;strong&gt;service connection&lt;/strong&gt; with &lt;strong&gt;approvals &amp;amp; checks&lt;/strong&gt; enabled on it. We’ll then create a &lt;strong&gt;variable group&lt;/strong&gt; linked to the Key Vault.&lt;/li&gt;&lt;li&gt;Adding &lt;strong&gt;approvals &amp;amp; checks&lt;/strong&gt; to deployment &lt;strong&gt;service connections&lt;/strong&gt; and allowing them to only be used within &lt;strong&gt;approved pipelines&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Defining &lt;strong&gt;environments&lt;/strong&gt; with &lt;strong&gt;approvals &amp;amp; checks&lt;/strong&gt; and using them in your pipelines.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;So, lets look at each of these in more detail.&lt;/p&gt;&lt;h2 id=&quot;move-secrets-into-a-key-vault&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/protect-your-environment-from-malicious-pipeline-changes-in-azure-devops/#move-secrets-into-a-key-vault&quot; class=&quot;heading-anchor&quot;&gt;Move Secrets into a Key Vault&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The first task is to &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/key-vault/general/quick-create-cli&quot; rel=&quot;noopener&quot;&gt;create an Azure Key Vault&lt;/a&gt; and &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/key-vault/secrets/quick-create-cli#add-a-secret-to-key-vault&quot; rel=&quot;noopener&quot;&gt;add all the secrets&lt;/a&gt; that are used in a pipeline into an &lt;strong&gt;Azure Key Vault&lt;/strong&gt;. In my case, I added SQL server login details as two secrets:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_azurekeyvaultsecrets.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/QKLnMiRofC-650.webp 650w, https://danielscottraynsford.com/img/QKLnMiRofC-960.webp 960w, https://danielscottraynsford.com/img/QKLnMiRofC-1245.webp 1245w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/QKLnMiRofC-650.png&quot; alt width=&quot;1245&quot; height=&quot;307&quot; srcset=&quot;https://danielscottraynsford.com/img/QKLnMiRofC-650.png 650w, https://danielscottraynsford.com/img/QKLnMiRofC-960.png 960w, https://danielscottraynsford.com/img/QKLnMiRofC-1245.png 1245w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;My production Azure Key Vault&lt;/p&gt;&lt;p&gt;In my case, I have two environments, QA and PRODUCTION. So, I created a resource group and a Key Vault for each. This is so that I can implement different levels of controls over QA to PRODUCTION.&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;As part of this process you should also use other techniques such as &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/key-vault/general/azure-policy?tabs=certificates&quot; rel=&quot;noopener&quot;&gt;governance with Azure Policy&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/key-vault/general/howto-logging&quot; rel=&quot;noopener&quot;&gt;sending logs to Azure Log Analytics&lt;/a&gt; to harden and protect your Key Vaults. But this is beyond the scope of this post.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Next, I need to &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&amp;amp;tabs=yaml#create-a-service-connection&quot; rel=&quot;noopener&quot;&gt;create a Service Connection to Azure Resource Manager to the resource group&lt;/a&gt; I created the Key Vault in:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_azurekeyvaultserviceconnection.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Q13pzwXCrx-478.webp 478w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Q13pzwXCrx-478.png&quot; alt width=&quot;478&quot; height=&quot;627&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Take note that I limited the connection to the Resource Group and didn’t grant permission to all pipelines.&lt;/p&gt;&lt;p&gt;I then need to edit the &lt;strong&gt;security&lt;/strong&gt; for the Service Connection to grant access to it from specific pipelines:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_azurekeyvaultserviceconnectionpermissions.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ho_QlZaE43-650.webp 650w, https://danielscottraynsford.com/img/ho_QlZaE43-901.webp 901w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ho_QlZaE43-650.png&quot; alt width=&quot;901&quot; height=&quot;777&quot; srcset=&quot;https://danielscottraynsford.com/img/ho_QlZaE43-650.png 650w, https://danielscottraynsford.com/img/ho_QlZaE43-901.png 901w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;What we then need to do is add &lt;strong&gt;approvals and checks&lt;/strong&gt; to the &lt;strong&gt;service connection&lt;/strong&gt;. This will cause these checks to be run any time a pipeline tries to use the service connection:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_azurekeyvaultserviceconnectionapprovals.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/q3wmRekN3x-650.webp 650w, https://danielscottraynsford.com/img/q3wmRekN3x-891.webp 891w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/q3wmRekN3x-650.png&quot; alt width=&quot;891&quot; height=&quot;374&quot; srcset=&quot;https://danielscottraynsford.com/img/q3wmRekN3x-650.png 650w, https://danielscottraynsford.com/img/q3wmRekN3x-891.png 891w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Adding Approvals and checks to a Service Connection.&lt;/p&gt;&lt;p&gt;There is one approval type (getting approval from a user or group) and several checks that can be enabled:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_approvalsandchecks.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Wl2Wu1bEGi-477.webp 477w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Wl2Wu1bEGi-477.png&quot; alt width=&quot;477&quot; height=&quot;670&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Adding approvals and checks on a Service Connection.&lt;/p&gt;&lt;p&gt;Depending on the level of control you’d like to implement on each environment, you might configure these checks differently. In my case, for &lt;strong&gt;QA&lt;/strong&gt; I only used a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/approvals?view=azure-devops&amp;amp;tabs=check-pass#branch-control&quot; rel=&quot;noopener&quot;&gt;branch control&lt;/a&gt; to only allow the connection to run against &lt;strong&gt;main&lt;/strong&gt; branch.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_keyvaultqachecks.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/FjLUAmcoIM-650.webp 650w, https://danielscottraynsford.com/img/FjLUAmcoIM-943.webp 943w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/FjLUAmcoIM-650.png&quot; alt width=&quot;943&quot; height=&quot;173&quot; srcset=&quot;https://danielscottraynsford.com/img/FjLUAmcoIM-650.png 650w, https://danielscottraynsford.com/img/FjLUAmcoIM-943.png 943w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The Azure Key Vault QA service connection can only be accessed when run within main branch.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_branchcontrol.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/LQZyyhTqX9-478.webp 478w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/LQZyyhTqX9-478.png&quot; alt width=&quot;478&quot; height=&quot;419&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Take care to format the Allowed branches using the full ref path.&lt;/p&gt;&lt;p&gt;By enabling &lt;strong&gt;verify branch protection&lt;/strong&gt; it will ensure the service connection is only available if the branch protection for the branch is enabled. It should be ticked for QA and PRODUCTION.&lt;/p&gt;&lt;p&gt;For, &lt;strong&gt;PRODUCTION&lt;/strong&gt;, I enabled both a &lt;strong&gt;branch control&lt;/strong&gt; and &lt;strong&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/approvals?view=azure-devops&amp;amp;tabs=check-pass#approvals&quot; rel=&quot;noopener&quot;&gt;Approvals&lt;/a&gt;&lt;/strong&gt; from a security group:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_keyvaultprodchecks.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/kpZtVieY0n-650.webp 650w, https://danielscottraynsford.com/img/kpZtVieY0n-948.webp 948w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/kpZtVieY0n-650.png&quot; alt width=&quot;948&quot; height=&quot;210&quot; srcset=&quot;https://danielscottraynsford.com/img/kpZtVieY0n-650.png 650w, https://danielscottraynsford.com/img/kpZtVieY0n-948.png 948w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The Azure Key Vault QA service connection can only be accessed when run within main branch and also requires approval from a group.&lt;/p&gt;&lt;p&gt;For the Approval gate I had a group defined called &lt;strong&gt;Production Environment Approvers&lt;/strong&gt;. I could have used an Active Directory group here instead. Using a group is recommended rather than specifying individual users because only a single member of each group needs to approve. See &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/approvals?view=azure-devops&amp;amp;tabs=check-pass#approvals&quot; rel=&quot;noopener&quot;&gt;this document&lt;/a&gt; for more information on setting approvers.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_keyvaultapproval.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/v2cI8aZ_Xf-475.webp 475w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/v2cI8aZ_Xf-475.png&quot; alt width=&quot;475&quot; height=&quot;498&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;To enforce separation of duties, make sure approvers can not approve their own runs.&lt;/p&gt;&lt;p&gt;The final task is to &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/library/variable-groups?view=azure-devops&amp;amp;tabs=yaml#link-secrets-from-an-azure-key-vault&quot; rel=&quot;noopener&quot;&gt;create the Variable Groups linked to the Azure Key Vault&lt;/a&gt;, using our &lt;strong&gt;service connections&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_variablegroups.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/nmsyOVUt0T-650.webp 650w, https://danielscottraynsford.com/img/nmsyOVUt0T-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/nmsyOVUt0T-650.png&quot; alt width=&quot;960&quot; height=&quot;202&quot; srcset=&quot;https://danielscottraynsford.com/img/nmsyOVUt0T-650.png 650w, https://danielscottraynsford.com/img/nmsyOVUt0T-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Variable groups linked to Azure Key Vaults.&lt;/p&gt;&lt;p&gt;To keep this post short(er), I won’t describe the exact steps here. You can get more detail on the exact process on &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/library/variable-groups?view=azure-devops&amp;amp;tabs=yaml#link-secrets-from-an-azure-key-vault&quot; rel=&quot;noopener&quot;&gt;this page&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_variablegroupsecrets.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/0SJc12YiKn-650.webp 650w, https://danielscottraynsford.com/img/0SJc12YiKn-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/0SJc12YiKn-650.png&quot; alt width=&quot;960&quot; height=&quot;799&quot; srcset=&quot;https://danielscottraynsford.com/img/0SJc12YiKn-650.png 650w, https://danielscottraynsford.com/img/0SJc12YiKn-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;It is important to not allow access from all pipelines.&lt;/p&gt;&lt;p&gt;Because we unticked the &lt;strong&gt;allow access to all pipelines&lt;/strong&gt; box, it will mean the owner (or someone with enough permissions) will be asked to approve the use of the &lt;strong&gt;variable group&lt;/strong&gt; and Key Vault &lt;strong&gt;service connection&lt;/strong&gt; the first time the pipeline is run:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_pipelinerequestpermit.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Y4PfwpX0Wp-650.webp 650w, https://danielscottraynsford.com/img/Y4PfwpX0Wp-960.webp 960w, https://danielscottraynsford.com/img/Y4PfwpX0Wp-1347.webp 1347w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Y4PfwpX0Wp-650.png&quot; alt width=&quot;1347&quot; height=&quot;693&quot; srcset=&quot;https://danielscottraynsford.com/img/Y4PfwpX0Wp-650.png 650w, https://danielscottraynsford.com/img/Y4PfwpX0Wp-960.png 960w, https://danielscottraynsford.com/img/Y4PfwpX0Wp-1347.png 1347w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Both the Azure Key Vault QA service connection and QA Secrets variable group need to be granted permission on the first run.&lt;/p&gt;&lt;p&gt;Subsequent runs of this pipeline won’t require permission.&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;Variable Group&lt;/strong&gt; gates and approvals only work if linking it to an Azure Key Vault - which is another good reason to use them.&lt;/p&gt;&lt;p&gt;Now we have a much higher level of governance over our pipeline secrets, so let’s move on to the next improvement we can make.&lt;/p&gt;&lt;h2 id=&quot;add-approvals-and-checks-to-service-connections&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/protect-your-environment-from-malicious-pipeline-changes-in-azure-devops/#add-approvals-and-checks-to-service-connections&quot; class=&quot;heading-anchor&quot;&gt;Add Approvals &amp;amp; Checks to Service Connections&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The next method we’ll implement is to add approvals &amp;amp; checks to our PRODUCTION (and QA) service connections. This is just the same as I did in the previous section for the Azure Key Vault service connections:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_productionserviceconnectionapprovals.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/_tn3a5NTBH-650.webp 650w, https://danielscottraynsford.com/img/_tn3a5NTBH-960.webp 960w, https://danielscottraynsford.com/img/_tn3a5NTBH-1303.webp 1303w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/_tn3a5NTBH-650.png&quot; alt width=&quot;1303&quot; height=&quot;209&quot; srcset=&quot;https://danielscottraynsford.com/img/_tn3a5NTBH-650.png 650w, https://danielscottraynsford.com/img/_tn3a5NTBH-960.png 960w, https://danielscottraynsford.com/img/_tn3a5NTBH-1303.png 1303w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;PRODUCTION Service Connection approvals and checks.&lt;/p&gt;&lt;p&gt;We could implement similar &lt;strong&gt;approvals &amp;amp; checks&lt;/strong&gt; to any service connection, not just to Azure. For example, we might do this for &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&amp;amp;tabs=yaml#common-service-connection-types&quot; rel=&quot;noopener&quot;&gt;connections&lt;/a&gt; to &lt;strong&gt;Kubernetes&lt;/strong&gt; clusters, &lt;strong&gt;Service Fabric&lt;/strong&gt; clusters, SSH or Docker hosts, or any other service.&lt;/p&gt;&lt;p&gt;Next, we also want to limit the service connection to only be accessible to specific pipelines, just as we did for the Key Vault connections.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_serviceconnectionsecurity.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/qCakoL4YcC-650.webp 650w, https://danielscottraynsford.com/img/qCakoL4YcC-960.webp 960w, https://danielscottraynsford.com/img/qCakoL4YcC-1258.webp 1258w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/qCakoL4YcC-650.png&quot; alt width=&quot;1258&quot; height=&quot;583&quot; srcset=&quot;https://danielscottraynsford.com/img/qCakoL4YcC-650.png 650w, https://danielscottraynsford.com/img/qCakoL4YcC-960.png 960w, https://danielscottraynsford.com/img/qCakoL4YcC-1258.png 1258w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Grant permissions to pipelines we want to allow this Service Connection to be used in.&lt;/p&gt;&lt;p&gt;We now have individual controls over secrets and resources used within our continuous delivery pipelines.&lt;/p&gt;&lt;h3 id=&quot;multiple-resource-approvals&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/protect-your-environment-from-malicious-pipeline-changes-in-azure-devops/#multiple-resource-approvals&quot; class=&quot;heading-anchor&quot;&gt;Multiple Resource Approvals&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;If we have a pipeline with a stage that requires access to multiple service connections or environments protected with approvals we don’t need to approve them all individually:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_multiapproval.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/6IOuHPrhjt-479.webp 479w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/6IOuHPrhjt-479.png&quot; alt width=&quot;479&quot; height=&quot;481&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;We can approve all gates together or individually.&lt;/p&gt;&lt;p&gt;However, you can only approve when you’re a member of the Approvers that were specified in the Approval gate.&lt;/p&gt;&lt;h2 id=&quot;environments-with-approvals-and-checks&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/protect-your-environment-from-malicious-pipeline-changes-in-azure-devops/#environments-with-approvals-and-checks&quot; class=&quot;heading-anchor&quot;&gt;Environments with Approvals &amp;amp; Checks&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The final improvement is to make use of the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/environments?view=azure-devops&quot; rel=&quot;noopener&quot;&gt;Azure DevOps Environments feature&lt;/a&gt;. This allows us to define an environment to target when using a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/deployment-jobs?view=azure-devops&quot; rel=&quot;noopener&quot;&gt;deployment job&lt;/a&gt; of an Azure DevOps Multi-stage YAML pipeline. With the environment defined, we can assign &lt;strong&gt;approvals &amp;amp; checks&lt;/strong&gt; to that, just like we did with the Service Connections and limit permissions to the environment to specific pipelines.&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;An environment can be used to define deployment targets for specific resources types such as Kubernetes namespaces and Virtual Machines. However, these are not required and you can still get a good deal of value from using environments without defining resources. See &lt;a href=&quot;https://devblogs.microsoft.com/premier-developer/azure-devops-pipelines-multi-stage-pipelines-and-yaml-for-continuous-delivery/&quot; rel=&quot;noopener&quot;&gt;this blog post&lt;/a&gt; for more details.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;In my case, I defined two environments, one for QA and one for PRODUCTION:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_environments.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Uu0A9lLiIs-650.webp 650w, https://danielscottraynsford.com/img/Uu0A9lLiIs-960.webp 960w, https://danielscottraynsford.com/img/Uu0A9lLiIs-1343.webp 1343w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Uu0A9lLiIs-650.png&quot; alt width=&quot;1343&quot; height=&quot;249&quot; srcset=&quot;https://danielscottraynsford.com/img/Uu0A9lLiIs-650.png 650w, https://danielscottraynsford.com/img/Uu0A9lLiIs-960.png 960w, https://danielscottraynsford.com/img/Uu0A9lLiIs-1343.png 1343w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;PRODUCTION and QA environments do not need to contain resources, but can still add value.&lt;/p&gt;&lt;p&gt;Just like before, I grant permissions to the environment for specific pipelines:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_environmentpermissions.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ahNC-VN9LA-650.webp 650w, https://danielscottraynsford.com/img/ahNC-VN9LA-960.webp 960w, https://danielscottraynsford.com/img/ahNC-VN9LA-1306.webp 1306w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ahNC-VN9LA-650.png&quot; alt width=&quot;1306&quot; height=&quot;626&quot; srcset=&quot;https://danielscottraynsford.com/img/ahNC-VN9LA-650.png 650w, https://danielscottraynsford.com/img/ahNC-VN9LA-960.png 960w, https://danielscottraynsford.com/img/ahNC-VN9LA-1306.png 1306w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Limit environments to be used by specific pipelines.&lt;/p&gt;&lt;p&gt;I also define &lt;strong&gt;approvals &amp;amp; checks&lt;/strong&gt; for the PRODUCTION environment just like before, but I also added an &lt;strong&gt;Exclusive Lock&lt;/strong&gt; check that will prevent more than one pipeline deploying to the PRODUCTION environment at the same time. This isn’t strictly a governance control, but will reduce the risk of conflicting deployments occurring.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_environmentapprovals.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/hUr2MUm543-650.webp 650w, https://danielscottraynsford.com/img/hUr2MUm543-960.webp 960w, https://danielscottraynsford.com/img/hUr2MUm543-1301.webp 1301w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/hUr2MUm543-650.png&quot; alt width=&quot;1301&quot; height=&quot;236&quot; srcset=&quot;https://danielscottraynsford.com/img/hUr2MUm543-650.png 650w, https://danielscottraynsford.com/img/hUr2MUm543-960.png 960w, https://danielscottraynsford.com/img/hUr2MUm543-1301.png 1301w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Prevent multiple deployments to this environment at the same time with the Exclusive Lock.&lt;/p&gt;&lt;p&gt;Finally, we need to update the &lt;strong&gt;azure-pipeline.yml&lt;/strong&gt; to make use of the &lt;strong&gt;environment&lt;/strong&gt; and the &lt;strong&gt;variable&lt;/strong&gt; group:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_yamlwithvariablesandenvrionment.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/RDqRzlLMGi-650.webp 650w, https://danielscottraynsford.com/img/RDqRzlLMGi-821.webp 821w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/RDqRzlLMGi-650.png&quot; alt width=&quot;821&quot; height=&quot;449&quot; srcset=&quot;https://danielscottraynsford.com/img/RDqRzlLMGi-650.png 650w, https://danielscottraynsford.com/img/RDqRzlLMGi-821.png 821w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Setting the environment to PRODUCTION in a deployment job.&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;main&#39;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;pr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none

&lt;span class=&quot;token key atrule&quot;&gt;stages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Build
    &lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; templates/build.yml

  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; QA
    &lt;span class=&quot;token key atrule&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Quality Assurance&#39;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;deployment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; deploy_qa
        &lt;span class=&quot;token key atrule&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Deploy to QA&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;vmImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Ubuntu-16.04&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;QA&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;QA Secrets&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;runOnce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;deploy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; AzureResourceManagerTemplateDeployment@3
                  &lt;span class=&quot;token key atrule&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Deploy Azure Resources&#39;&lt;/span&gt;
                  &lt;span class=&quot;token key atrule&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;azureResourceManagerConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Azure QA&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;subscriptionId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;72ad9153-ecab-48c9-8a7a-d61f2390df78&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;resourceGroupName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;dsr-qa-rg&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;East US&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;csmFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;$(Pipeline.Workspace)/arm/azuredeploy.json&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;overrideParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;-sqlServerName dsr-qa-sql -sqlDatabaseName dsrqadb -sqlAdministratorLoginUsername $(SQLAdministratorLoginUsername) -sqlAdministratorLoginPassword $(SQLAdministratorLoginPassword) -hostingPlanName &quot;dsr-qa-asp&quot; -webSiteName &quot;dsrqaapp&quot;&#39;&lt;/span&gt;

  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Production
    &lt;span class=&quot;token key atrule&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Release to Production&#39;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;deployment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; deploy_production
        &lt;span class=&quot;token key atrule&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Deploy to Production&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;vmImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Ubuntu-16.04&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;PRODUCTION&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;PRODUCTION Secrets&#39;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;runOnce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;deploy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; AzureResourceManagerTemplateDeployment@3
                  &lt;span class=&quot;token key atrule&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Deploy Azure Resources&#39;&lt;/span&gt;
                  &lt;span class=&quot;token key atrule&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;azureResourceManagerConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Azure PRODUCTION&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;subscriptionId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;72ad9153-ecab-48c9-8a7a-d61f2390df78&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;resourceGroupName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;dsr-production-rg&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;East US&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;csmFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;$(Pipeline.Workspace)/arm/azuredeploy.json&#39;&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;overrideParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;-sqlServerName dsr-production-sql -sqlDatabaseName dsrproductiondb -sqlAdministratorLoginUsername $(SQLAdministratorLoginUsername) -sqlAdministratorLoginPassword $(SQLAdministratorLoginPassword) -hostingPlanName &quot;dsr-production-asp&quot; -webSiteName &quot;dsrproductionapp&quot;&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can now get now also get a single view of all deployments to an environment:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_environmentdeployments.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/nSpHuI8gor-650.webp 650w, https://danielscottraynsford.com/img/nSpHuI8gor-960.webp 960w, https://danielscottraynsford.com/img/nSpHuI8gor-1336.webp 1336w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/nSpHuI8gor-650.png&quot; alt width=&quot;1336&quot; height=&quot;633&quot; srcset=&quot;https://danielscottraynsford.com/img/nSpHuI8gor-650.png 650w, https://danielscottraynsford.com/img/nSpHuI8gor-960.png 960w, https://danielscottraynsford.com/img/nSpHuI8gor-1336.png 1336w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;All environment deployments to PRODUCTION.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Because environments aren’t defined across projects, this is another reason to &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/organizations/projects/about-projects?view=azure-devops&quot; rel=&quot;noopener&quot;&gt;limit the number of Azure DevOps projects you’re creating&lt;/a&gt;. See my previous blog post on &lt;a href=&quot;https://dscottraynsford.wordpress.com/2020/09/19/12-things-you-should-know-when-implementing-azure-devops-in-your-organization/&quot; rel=&quot;noopener&quot;&gt;12 Things you Should Know when Implementing Azure DevOps in your Organization.&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;putting-it-all-together&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/protect-your-environment-from-malicious-pipeline-changes-in-azure-devops/#putting-it-all-together&quot; class=&quot;heading-anchor&quot;&gt;Putting it all together&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Now that we’ve completed all these additional checks and approvals, let’s see what happens when we attempt to get some malicious changes to run inside our &lt;strong&gt;Environment Deployment Pipeline&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_successfullyblockedmaliciousbuild.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Gp-R9ENtka-650.webp 650w, https://danielscottraynsford.com/img/Gp-R9ENtka-960.webp 960w, https://danielscottraynsford.com/img/Gp-R9ENtka-1346.webp 1346w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Gp-R9ENtka-650.png&quot; alt width=&quot;1346&quot; height=&quot;610&quot; srcset=&quot;https://danielscottraynsford.com/img/Gp-R9ENtka-650.png 650w, https://danielscottraynsford.com/img/Gp-R9ENtka-960.png 960w, https://danielscottraynsford.com/img/Gp-R9ENtka-1346.png 1346w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;After creating a new branch called &lt;strong&gt;Malicious_Activites&lt;/strong&gt; off main with adjustments to azure-pipelines.yml the build fails.&lt;/p&gt;&lt;p&gt;As we can see from the screenshot above, the following things have happened:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The &lt;strong&gt;Environment Continuous Delivery&lt;/strong&gt; pipeline was triggered automatically by our commit to the new &lt;strong&gt;Malicious_Activities&lt;/strong&gt; branch. This was expected and is the same as before.&lt;/li&gt;&lt;li&gt;This time all our &lt;strong&gt;Branch control&lt;/strong&gt; checks on the &lt;strong&gt;Service Connections&lt;/strong&gt; that were maliciously trying to be accessed have caused the build to &lt;strong&gt;fail&lt;/strong&gt; because this is not &lt;strong&gt;main&lt;/strong&gt; branch.&lt;/li&gt;&lt;li&gt;The &lt;strong&gt;Approvals&lt;/strong&gt; to access the &lt;strong&gt;service connections&lt;/strong&gt; have been requested still, but because I created the commit that triggered this, I can’t approve them. This results in implementation of &lt;em&gt;separation of duties&lt;/em&gt; control.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;For a member of the &lt;strong&gt;Production Environment Approvers&lt;/strong&gt; group it looks like this:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_approverallowed.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/-GSo2h4AX2-650.webp 650w, https://danielscottraynsford.com/img/-GSo2h4AX2-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/-GSo2h4AX2-650.png&quot; alt width=&quot;960&quot; height=&quot;528&quot; srcset=&quot;https://danielscottraynsford.com/img/-GSo2h4AX2-650.png 650w, https://danielscottraynsford.com/img/-GSo2h4AX2-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Approval allowed, even though the job has failed.&lt;/p&gt;&lt;p&gt;Even after the approving the job checks will still fail and the job won’t proceed. So, this means our PRODUCTION environment has been protected.&lt;/p&gt;&lt;p&gt;If we run the pipeline against main branch (either manually or via a commit via a Pull Request) then we will get the standard approvals:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_approverallowedtoprod.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/TtoBb-_k4d-650.webp 650w, https://danielscottraynsford.com/img/TtoBb-_k4d-960.webp 960w, https://danielscottraynsford.com/img/TtoBb-_k4d-1265.webp 1265w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/TtoBb-_k4d-650.png&quot; alt width=&quot;1265&quot; height=&quot;683&quot; srcset=&quot;https://danielscottraynsford.com/img/TtoBb-_k4d-650.png 650w, https://danielscottraynsford.com/img/TtoBb-_k4d-960.png 960w, https://danielscottraynsford.com/img/TtoBb-_k4d-1265.png 1265w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;QA checks passed automatically, and PRODUCTION Branch controls have passed. PRODUCTION approval is waiting.&lt;/p&gt;&lt;h3 id=&quot;a-quick-note-about-approvals&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/protect-your-environment-from-malicious-pipeline-changes-in-azure-devops/#a-quick-note-about-approvals&quot; class=&quot;heading-anchor&quot;&gt;A Quick Note About Approvals&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;By default approval notifications will be e-mailed to anyone who is in an Approval list. You can disable this by configuring your &lt;strong&gt;Notifications&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_azdopipelinecontrols_notifications.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/pGPuf5HU1X-650.webp 650w, https://danielscottraynsford.com/img/pGPuf5HU1X-960.webp 960w, https://danielscottraynsford.com/img/pGPuf5HU1X-1274.webp 1274w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/pGPuf5HU1X-650.png&quot; alt width=&quot;1274&quot; height=&quot;898&quot; srcset=&quot;https://danielscottraynsford.com/img/pGPuf5HU1X-650.png 650w, https://danielscottraynsford.com/img/pGPuf5HU1X-960.png 960w, https://danielscottraynsford.com/img/pGPuf5HU1X-1274.png 1274w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Enable/Disable Run stage waiting for approval notifications.&lt;/p&gt;&lt;p&gt;You can also choose to have &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/integrations/microsoft-teams?view=azure-devops&quot; rel=&quot;noopener&quot;&gt;notifications delivered to you in Microsoft Teams&lt;/a&gt;, if you use it. This is the best way to experience these features and you’re less likely to miss an important approval.&lt;/p&gt;&lt;h2 id=&quot;wrapping-up&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/protect-your-environment-from-malicious-pipeline-changes-in-azure-devops/#wrapping-up&quot; class=&quot;heading-anchor&quot;&gt;Wrapping Up&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;It is important to remember that all of these controls and methods are &lt;strong&gt;optional&lt;/strong&gt;. If you don’t need this level of control and governance over your environments then you shouldn’t add the complexity that goes with it. That said, it is always good to know what you can do with the tools, even if you don’t need to use it.&lt;/p&gt;&lt;p&gt;I hope you found this (long, but hopefully not too long) post useful.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>AKS Announcements Roll-up from Microsoft Ignite 2020</title>
      <link href="https://danielscottraynsford.com/blog/aks-announcements-roll-up-from-microsoft-ignite-2020/" />
      <updated>2020-09-23T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/aks-announcements-roll-up-from-microsoft-ignite-2020/</id>
      <content type="html">
				&lt;p&gt;There were a whole lot of announcements around Azure Kubernetes Service (AKS) at Ignite 2020. I thought I’d quickly sum them all up and provide links:&lt;/p&gt;&lt;h2 id=&quot;brendan-burns-post-on-aks-updates&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/aks-announcements-roll-up-from-microsoft-ignite-2020/#brendan-burns-post-on-aks-updates&quot; class=&quot;heading-anchor&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://techcommunity.microsoft.com/t5/azure-developer-community-blog/enterprise-grade-kubernetes-on-azure/ba-p/1659386&quot; rel=&quot;noopener&quot;&gt;Brendan Burn’s post on AKS Updates&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;A great summary of recent investments in AKS from Kubernetes co-creator, Brendan Burns.&lt;/p&gt;&lt;h2 id=&quot;preview-aks-now-available-on-azure-stack-hci&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/aks-announcements-roll-up-from-microsoft-ignite-2020/#preview-aks-now-available-on-azure-stack-hci&quot; class=&quot;heading-anchor&quot;&gt;Preview: &lt;/a&gt;&lt;a href=&quot;https://azure.microsoft.com/en-us/blog/bring-innovation-anywhere-with-azures-multicloud-multiedge-hybrid-capabilities/&quot; rel=&quot;noopener&quot;&gt;AKS now available on Azure Stack HCI&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;AKS on Azure Stack HCI enables customers to deploy and manage containerized apps at scale on Azure Stack HCI, just as they can run AKS within Azure.&lt;/p&gt;&lt;h2 id=&quot;public-preview-aks-stop/start-cluster&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/aks-announcements-roll-up-from-microsoft-ignite-2020/#public-preview-aks-stop/start-cluster&quot; class=&quot;heading-anchor&quot;&gt;Public Preview: &lt;/a&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/aks/start-stop-cluster&quot; rel=&quot;noopener&quot;&gt;AKS Stop/Start&amp;nbsp;Cluster&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Pause an AKS cluster and pick up where they left off&amp;nbsp;later&amp;nbsp;with a switch of a button, saving time and cost.&lt;/p&gt;&lt;h2 id=&quot;ga-azure-policy-add-on-for-aks&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/aks-announcements-roll-up-from-microsoft-ignite-2020/#ga-azure-policy-add-on-for-aks&quot; class=&quot;heading-anchor&quot;&gt;GA: &lt;/a&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/governance/policy/concepts/policy-for-kubernetes#install-azure-policy-add-on-for-aks&quot; rel=&quot;noopener&quot;&gt;Azure Policy&amp;nbsp;add&amp;nbsp;on for AKS&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Azure Policy add on for AKS allows customers to audit and enforce policies to their Kubernetes resources.&lt;/p&gt;&lt;h2 id=&quot;public-preview-confidential-computing-nodes-on-azure-kubernetes-service&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/aks-announcements-roll-up-from-microsoft-ignite-2020/#public-preview-confidential-computing-nodes-on-azure-kubernetes-service&quot; class=&quot;heading-anchor&quot;&gt;Public Preview: &lt;/a&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-nodes-aks-overview&quot; rel=&quot;noopener&quot;&gt;Confidential computing nodes on Azure Kubernetes Service&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Azure Kubernetes Service (AKS) supports adding&amp;nbsp;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-computing-enclaves&quot; rel=&quot;noopener&quot;&gt;DCsv2 confidential computing nodes&lt;/a&gt;&amp;nbsp;on Intel SGX.&lt;/p&gt;&lt;h2 id=&quot;ga-aks-support-for-new-base-image-ubuntu-1804&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/aks-announcements-roll-up-from-microsoft-ignite-2020/#ga-aks-support-for-new-base-image-ubuntu-1804&quot; class=&quot;heading-anchor&quot;&gt;GA: &lt;/a&gt;&lt;a href=&quot;https://azure.microsoft.com/en-us/updates/ga-aks-support-for-new-base-image-ubuntu-1804/&quot; rel=&quot;noopener&quot;&gt;AKS support for new Base image Ubuntu 18.04&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;You can now create Node Pools using Ubuntu 18.04.&lt;/p&gt;&lt;h2 id=&quot;ga-mutate-default-storage-class&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/aks-announcements-roll-up-from-microsoft-ignite-2020/#ga-mutate-default-storage-class&quot; class=&quot;heading-anchor&quot;&gt;GA: &lt;/a&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/aks/azure-files-dynamic-pv#create-a-storage-class&quot; rel=&quot;noopener&quot;&gt;Mutate default storage class&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;You can now use a different storage class in place of the default storage class to better fit their workload needs.&lt;/p&gt;&lt;h2 id=&quot;public-preview-kubernetes-119-support&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/aks-announcements-roll-up-from-microsoft-ignite-2020/#public-preview-kubernetes-119-support&quot; class=&quot;heading-anchor&quot;&gt;Public preview: &lt;/a&gt;&lt;a href=&quot;https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.19.md#whats-new-major-themes&quot; rel=&quot;noopener&quot;&gt;Kubernetes 1.19 support&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;AKS now supports Kubernetes release 1.19 in public preview. Kubernetes release 1.19 includes several new features and enhancements such as support for TLS 1.3, Ingress and seccomp feature GA, and others.&lt;/p&gt;&lt;h2 id=&quot;public-preview-rbac-for-k8s-auth&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/aks-announcements-roll-up-from-microsoft-ignite-2020/#public-preview-rbac-for-k8s-auth&quot; class=&quot;heading-anchor&quot;&gt;Public preview: &lt;/a&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/aks/manage-azure-rbac&quot; rel=&quot;noopener&quot;&gt;RBAC for K8s auth&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;With this capability, you can now manage RBAC for AKS and its resources using Azure or native Kubernetes mechanisms. When enabled, Azure AD users will be validated exclusively by Azure RBAC while regular Kubernetes service accounts are exclusively validated by Kubernetes RBAC.&lt;/p&gt;&lt;h2 id=&quot;public-preview-vscode-ext-diagperiscope&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/aks-announcements-roll-up-from-microsoft-ignite-2020/#public-preview-vscode-ext-diagperiscope&quot; class=&quot;heading-anchor&quot;&gt;Public Preview: &lt;/a&gt;&lt;a href=&quot;https://azure.microsoft.com/en-us/updates/public-preview-visual-studio-code-extension-diagnostics-periscope/&quot; rel=&quot;noopener&quot;&gt;VSCode ext. diag+periscope&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This Visual Studio Code extension enables developers to use AKS periscope and AKS diagnostics in their development workflow to quickly diagnose and troubleshoot their clusters.This Visual Studio Code extension enables developers to use AKS periscope and AKS diagnostics in their development workflow to quickly diagnose and troubleshoot their clusters.&lt;/p&gt;&lt;h2 id=&quot;enhanced-protection-for-containers&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/aks-announcements-roll-up-from-microsoft-ignite-2020/#enhanced-protection-for-containers&quot; class=&quot;heading-anchor&quot;&gt;&lt;strong&gt;Enhanced protection for containers&lt;/strong&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Enhanced protection for containers&lt;/strong&gt;: As containers and specifically Kubernetes are becoming more widely used, the Azure Defender for Kubernetes offering has been extended to include Kubernetes-level policy management, hardening and enforcement with admission control to make sure that Kubernetes workloads are secured by default. In addition, container image scanning by Azure Defender for Container Registries will now support continuous scanning of container images to minimize the exploitability of running containers&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Learn more about&amp;nbsp;&lt;a href=&quot;https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Faka.ms%2FAA9g2sn&amp;amp;data=02%7C01%7CDaniel.ScottRaynsford%40microsoft.com%7C9a03bbafdd804e73eb3f08d85f60dd3d%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637364216928474975&amp;amp;sdata=2tanwckRc6o2N2x7t%2F%2Bl41mU%2B0V6XLlnbPGdk%2FRyHEc%3D&amp;amp;reserved=0&quot; rel=&quot;noopener&quot;&gt;Microsoft Defender&lt;/a&gt;,&amp;nbsp;&lt;a href=&quot;https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Faka.ms%2FAA9k0nf&amp;amp;data=02%7C01%7CDaniel.ScottRaynsford%40microsoft.com%7C9a03bbafdd804e73eb3f08d85f60dd3d%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637364216928484933&amp;amp;sdata=8Vnt%2FZeq0kjHxTu0SRKC6ZbVEiB3AQ1mcimiy7gm%2FAc%3D&amp;amp;reserved=0&quot; rel=&quot;noopener&quot;&gt;Azure Defender&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href=&quot;https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Faka.ms%2FAA9jt2s&amp;amp;data=02%7C01%7CDaniel.ScottRaynsford%40microsoft.com%7C9a03bbafdd804e73eb3f08d85f60dd3d%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637364216928484933&amp;amp;sdata=eeYpp2%2BXp0VjCM%2FbLMQvq7nxBtpLPBvMoPlmqWl1Cqs%3D&amp;amp;reserved=0&quot; rel=&quot;noopener&quot;&gt;Azure Sentinel&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;There may indeed been more, and I’ll update them as they come to hand. Hope this roll up helps.&lt;/p&gt;&lt;p&gt;Head over to &lt;a href=&quot;https://myignite.microsoft.com&quot; rel=&quot;noopener&quot;&gt;https://myignite.microsoft.com&lt;/a&gt; and watch some of the AKS content to get even an even better view of the updates.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>12 Things you Should Know when Implementing Azure DevOps in your Organization</title>
      <link href="https://danielscottraynsford.com/blog/12-things-you-should-know-when-implementing-azure-devops-in-your-organization/" />
      <updated>2020-09-19T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/12-things-you-should-know-when-implementing-azure-devops-in-your-organization/</id>
      <content type="html">
				&lt;p&gt;&lt;a href=&quot;https://azure.microsoft.com/en-us/services/devops/&quot; rel=&quot;noopener&quot;&gt;Azure DevOps&lt;/a&gt; is a really fantastic part of any DevOps tool chain. But when you’re first starting out with it in an organization, there are a few things you should know that will make it even better… and &lt;em&gt;avoid making some doing some things you’ll later regret.&lt;/em&gt; These tips are most important if you’re implementing it &lt;strong&gt;across multiple teams&lt;/strong&gt; or in a &lt;strong&gt;medium to large organization&lt;/strong&gt;. Even if you’re implementing it in a small start-up, most of these tips will still help.&lt;/p&gt;&lt;p&gt;These tips are all based on my experience with implementing and using &lt;strong&gt;Azure DevOps&lt;/strong&gt;, &lt;strong&gt;Visual Studio Online&lt;/strong&gt; (VSO) and &lt;strong&gt;Visual Studio Team Services&lt;/strong&gt; (VSTS). These are all things &lt;strong&gt;I wish I’d known&lt;/strong&gt; &lt;strong&gt;earlier&lt;/strong&gt; as they would have saved me time, made my life easier or kept me more secure. They are also &lt;strong&gt;just my opinion&lt;/strong&gt;, so I encourage you to investigate further and decide what is &lt;em&gt;best for you in your situation/environment&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;This is by no means an exhaustive list either and they are in no particular order.&lt;/p&gt;&lt;p&gt;So, let’s get into it:&lt;/p&gt;&lt;h2 id=&quot;1-projects-less-is-better&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/12-things-you-should-know-when-implementing-azure-devops-in-your-organization/#1-projects-less-is-better&quot; class=&quot;heading-anchor&quot;&gt;1. Projects: less is better&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/yzwMLDE1PT-650.webp 650w, https://danielscottraynsford.com/img/yzwMLDE1PT-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/yzwMLDE1PT-650.png&quot; alt width=&quot;960&quot; height=&quot;495&quot; srcset=&quot;https://danielscottraynsford.com/img/yzwMLDE1PT-650.png 650w, https://danielscottraynsford.com/img/yzwMLDE1PT-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Less projects are better&lt;/p&gt;&lt;p&gt;Most things (work items, repositories, pipelines etc.) in Azure DevOps are organized into containers called &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/organizations/projects/about-projects&quot; rel=&quot;noopener&quot;&gt;Projects&lt;/a&gt;. It is tempting to try to break your work into lots of small projects (e.g. one for each library, or one per team, or one per department). This results in &lt;strong&gt;a lot of management overhead&lt;/strong&gt; trying to keep everything organized and adds little value &lt;em&gt;in most cases&lt;/em&gt; (there are exceptions). Implementing a project per team or a project per software component is &lt;em&gt;usually wrong&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Recommendation: The less projects you have the better.&lt;/strong&gt; Use &lt;strong&gt;Area Paths&lt;/strong&gt; (covered next) to organize work in your project.&lt;/p&gt;&lt;p&gt;Documentation Reference: &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/organizations/projects/about-projects?view=azure-devops#reasons-to-add-another-project&quot; rel=&quot;noopener&quot;&gt;When to add another project&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;2-area-paths-organize-work&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/12-things-you-should-know-when-implementing-azure-devops-in-your-organization/#2-area-paths-organize-work&quot; class=&quot;heading-anchor&quot;&gt;2. Area Paths: Organize work&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/5yRGth0dph-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/5yRGth0dph-650.png&quot; alt width=&quot;650&quot; height=&quot;510&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Organizing Area Paths&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/organizations/settings/set-area-pathshttps://docs.microsoft.com/en-us/azure/devops/organizations/settings/set-area-paths&quot; rel=&quot;noopener&quot;&gt;Area Paths&lt;/a&gt; allow you to divide the &lt;strong&gt;work items&lt;/strong&gt; and &lt;strong&gt;test plans&lt;/strong&gt; into a hierarchy to make them easier to manage. Teams can be assigned to one or more &lt;strong&gt;area paths&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Area’s are easily moved around, so they are much better suited to arranging your work by software component/product and organizational hierarchies.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Recommendation: For your project, set up Area Paths and assign teams to them&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Documentation Reference: &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/organizations/settings/set-area-paths&quot; rel=&quot;noopener&quot;&gt;Define area paths for your project&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;3-identity-integrate-with-azure-ad&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/12-things-you-should-know-when-implementing-azure-devops-in-your-organization/#3-identity-integrate-with-azure-ad&quot; class=&quot;heading-anchor&quot;&gt;3. Identity: Integrate with Azure AD&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/HJZrJstVqu-339.webp 339w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/HJZrJstVqu-339.png&quot; alt width=&quot;339&quot; height=&quot;153&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Connect AAD.&lt;/p&gt;&lt;p&gt;If you are using Azure AD as your primary identity source for your organization, then you should &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/connect-organization-to-azure-ad&quot; rel=&quot;noopener&quot;&gt;connect your Azure DevOps organization to Azure AD&lt;/a&gt;. This will allow your Azure AD identities to be used within Azure DevOps.&lt;/p&gt;&lt;p&gt;If you aren’t using Azure AD, but have Active Directory, consider setting up &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/hybrid/whatis-hybrid-identity&quot; rel=&quot;noopener&quot;&gt;hybrid identity with Azure AD&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;You should &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/manage-azure-active-directory-groups&quot; rel=&quot;noopener&quot;&gt;manage access to your Azure DevOps organization&lt;/a&gt; and to projects and other resources (e.g. Service Connections) using Azure AD Groups. I’d also strongly recommend reading the documentation on &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/organizations/security/add-manage-security-groups&quot; rel=&quot;noopener&quot;&gt;security groups&lt;/a&gt; and &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/organizations/security/about-permissions&quot; rel=&quot;noopener&quot;&gt;permissions&lt;/a&gt; for Azure DevOps as there are a lot of nuance to these and they deserve an entire post on their own.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Recommendation: Use Azure AD as the identity source for Azure DevOps&lt;/strong&gt;. &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/fundamentals/add-users-azure-active-directory&quot; rel=&quot;noopener&quot;&gt;Create and manage users&lt;/a&gt; and &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/organizations/security/add-ad-aad-built-in-security-groups&quot; rel=&quot;noopener&quot;&gt;security groups&lt;/a&gt; within Azure AD.&lt;/p&gt;&lt;p&gt;Documentation Reference: &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/connect-organization-to-azure-ad&quot; rel=&quot;noopener&quot;&gt;Connect organization to Azure Active Directory&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/manage-azure-active-directory-groups&quot; rel=&quot;noopener&quot;&gt;Access with Active Directory Groups&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;4-git-or-tfvc&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/12-things-you-should-know-when-implementing-azure-devops-in-your-organization/#4-git-or-tfvc&quot; class=&quot;heading-anchor&quot;&gt;4. Git or TFVC?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/uEIFGhv178-480.webp 480w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/uEIFGhv178-480.png&quot; alt width=&quot;480&quot; height=&quot;475&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Importing a TFVC repository as Git&lt;/p&gt;&lt;p&gt;This might be controversial, but it shouldn’t be. Unless you have a legacy TFVC repository that you need to keep around for historic/support reasons or some tools that only support TFVC that you can’t live without (or can’t replace) then &lt;strong&gt;you should be using&lt;/strong&gt; &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/learn/git/what-is-git&quot; rel=&quot;noopener&quot;&gt;Git as your version control system&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;If you do have legacy TFVC repositories that you need to bring over, consider &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/learn/git/migrate-from-tfvc-to-git&quot; rel=&quot;noopener&quot;&gt;importing them as Git repositories&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Recommendation: Use Git.&lt;/strong&gt; Make sure all your teams &lt;a href=&quot;https://try.github.io/&quot; rel=&quot;noopener&quot;&gt;know how to use Git well&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Documentation Reference: &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/organizations/projects/about-projects?view=azure-devops#reasons-to-add-another-project&quot; rel=&quot;noopener&quot;&gt;When to add another project&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;5-create-a-sandbox-project&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/12-things-you-should-know-when-implementing-azure-devops-in-your-organization/#5-create-a-sandbox-project&quot; class=&quot;heading-anchor&quot;&gt;5. Create a Sandbox Project&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/w_8_0myaXN-462.webp 462w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/w_8_0myaXN-462.png&quot; alt width=&quot;462&quot; height=&quot;268&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Create a Sandbox Project&lt;/p&gt;&lt;p&gt;You and your teams will often need a place to &lt;strong&gt;experiment&lt;/strong&gt; and &lt;strong&gt;learn&lt;/strong&gt; about Azure DevOps safely. A &lt;strong&gt;sandbox project&lt;/strong&gt; is a great place to do this. You can create a sandbox project and &lt;strong&gt;give teams higher levels of permissions&lt;/strong&gt; over it project to allow them to experiment with different settings (for example try out an alternate area path structure)&lt;/p&gt;&lt;p&gt;Don’t confuse a sandbox project with a project for building proof-of-concepts/experimental code: you should not use a Sandbox project for creating anything that could end up in production or anything that has any value. &lt;strong&gt;Content in sandbox projects often accidentally get deleted.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Recommendation: Create a Sandbox Project&lt;/strong&gt;. Assign an image and description for the project to make it clear that it is a Sandbox and what it can be used for.&lt;/p&gt;&lt;p&gt;Documentation Reference: &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/organizations/projects/about-projects?view=azure-devops#reasons-to-add-another-project&quot; rel=&quot;noopener&quot;&gt;When to add another project&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;6-install-extensions-wisely&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/12-things-you-should-know-when-implementing-azure-devops-in-your-organization/#6-install-extensions-wisely&quot; class=&quot;heading-anchor&quot;&gt;6. Install extensions… wisely&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/BscQPOabYr-650.webp 650w, https://danielscottraynsford.com/img/BscQPOabYr-960.webp 960w, https://danielscottraynsford.com/img/BscQPOabYr-1203.webp 1203w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/BscQPOabYr-650.png&quot; alt width=&quot;1203&quot; height=&quot;275&quot; srcset=&quot;https://danielscottraynsford.com/img/BscQPOabYr-650.png 650w, https://danielscottraynsford.com/img/BscQPOabYr-960.png 960w, https://danielscottraynsford.com/img/BscQPOabYr-1203.png 1203w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;The Azure DevOps Extensions Marketplace&lt;/p&gt;&lt;p&gt;The &lt;a href=&quot;https://marketplace.visualstudio.com/azuredevops?utm_source=vstsproduct&amp;amp;utm_medium=L1BrowseMarketplace&quot; rel=&quot;noopener&quot;&gt;Azure DevOps marketplace&lt;/a&gt; is filled with &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-devlabs.FolderManagement&quot; rel=&quot;noopener&quot;&gt;many&lt;/a&gt; &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=azsdktm.AzSDK-task&quot; rel=&quot;noopener&quot;&gt;great&lt;/a&gt; &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-devlabs.FolderManagement&quot; rel=&quot;noopener&quot;&gt;extensions&lt;/a&gt; that really enhance the value of Azure DevOps. It is well worth &lt;strong&gt;browsing through the extensions&lt;/strong&gt; created by both &lt;a href=&quot;https://marketplace.visualstudio.com/publishers/Microsoft&quot; rel=&quot;noopener&quot;&gt;Microsoft&lt;/a&gt;, &lt;a href=&quot;https://marketplace.visualstudio.com/publishers/Microsoft%20DevLabs&quot; rel=&quot;noopener&quot;&gt;Microsoft DevLabs&lt;/a&gt; and the hundreds of &lt;strong&gt;3rd party&lt;/strong&gt; ones to really experience the full power of Azure DevOps.&lt;/p&gt;&lt;p&gt;You should set up a formal process around &lt;strong&gt;validating&lt;/strong&gt;, &lt;strong&gt;on-boarding&lt;/strong&gt; and &lt;strong&gt;off-boarding&lt;/strong&gt; extensions from your organization. It is all too easy to end up with “extension sprawl” that results in a management nightmare, especially if you have strict security or governance practices (you might be familiar with this if you’ve ever managed Jenkins within a large organization).&lt;/p&gt;&lt;p&gt;It is also tempting to install pipeline extensions for any minor task that you might want to execute in a CI/CD pipeline. But you should consider if the governance &amp;amp; management of a task &lt;strong&gt;is worth the time&lt;/strong&gt; that might be saved using it, especially when a short &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/bash&quot; rel=&quot;noopener&quot;&gt;Bash&lt;/a&gt; or &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/powershell&quot; rel=&quot;noopener&quot;&gt;PowerShell&lt;/a&gt; task might do just as well.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Recommendation: Install important extensions from marketplace&lt;/strong&gt;. Formalize a process for &lt;strong&gt;validating&lt;/strong&gt;, &lt;strong&gt;on-boarding&lt;/strong&gt; and &lt;strong&gt;off-boarding&lt;/strong&gt; extensions.&lt;/p&gt;&lt;p&gt;Documentation Reference: &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/organizations/projects/about-projects?view=azure-devops#reasons-to-add-another-project&quot; rel=&quot;noopener&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/azuredevops?utm_source=vstsproduct&amp;amp;utm_medium=L1BrowseMarketplace&quot; rel=&quot;noopener&quot;&gt;Azure DevOps marketplace&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/marketplace/install-extension&quot; rel=&quot;noopener&quot;&gt;Install Azure DevOps Extension&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;7-use-multi-stage-yaml-pipelines&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/12-things-you-should-know-when-implementing-azure-devops-in-your-organization/#7-use-multi-stage-yaml-pipelines&quot; class=&quot;heading-anchor&quot;&gt;7. Use Multi-stage YAML Pipelines&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/30zlsrTxQn-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/30zlsrTxQn-650.png&quot; alt width=&quot;650&quot; height=&quot;459&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Do NOT use “Classic Editor” and create pipelines without YAML&lt;/p&gt;&lt;p&gt;Early on the evolution of Azure DevOps pipelines, all pipelines had to created using a visual designer. The structure of this visually designed pipeline &lt;strong&gt;was not stored in code&lt;/strong&gt;, rather it was stored separately without proper version control. This is no longer the case.&lt;/p&gt;&lt;p&gt;You should always create new &lt;strong&gt;build pipelines&lt;/strong&gt; using YAML and store them in your repository with your source code (pipeline as code). You can still use the &lt;strong&gt;assistant&lt;/strong&gt; to help you design your YAML:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/CQ8NcUd6rM-650.webp 650w, https://danielscottraynsford.com/img/CQ8NcUd6rM-941.webp 941w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/CQ8NcUd6rM-650.png&quot; alt width=&quot;941&quot; height=&quot;604&quot; srcset=&quot;https://danielscottraynsford.com/img/CQ8NcUd6rM-650.png 650w, https://danielscottraynsford.com/img/CQ8NcUd6rM-941.png 941w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Click Show Assistant to edit your YAML.&lt;/p&gt;&lt;p&gt;The exception is &lt;strong&gt;release pipelines&lt;/strong&gt; which don’t support YAML and being stored in version control.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Recommendation: Create all pipelines a multi-stage YAML pipelines.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Documentation Reference: &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started/pipelines-get-started?view=azure-devops#define-pipelines-using-yaml-syntax&quot; rel=&quot;noopener&quot;&gt;Define pipelines using YAML syntax&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;9-release-pipelines-in-code&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/12-things-you-should-know-when-implementing-azure-devops-in-your-organization/#9-release-pipelines-in-code&quot; class=&quot;heading-anchor&quot;&gt;9. Release Pipelines… in code?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Release Pipelines are awesome, but are they worth missing out on pipeline as code?&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Release pipelines&lt;/strong&gt; don’t support YAML. However, in &lt;em&gt;many cases&lt;/em&gt; you don’t need release pipelines. Instead, you can use your &lt;strong&gt;multi-stage YAML build pipeline&lt;/strong&gt; to &lt;strong&gt;release&lt;/strong&gt; your software as well by adding a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/deployment-jobs&quot; rel=&quot;noopener&quot;&gt;deployment job&lt;/a&gt;. This also &lt;strong&gt;aligns much more closely to GitHub&lt;/strong&gt;, where there is no concept of a Release Pipeline and would make moving to GitHub Actions much easier &lt;em&gt;should you want to&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;As of writing this post, there are &lt;strong&gt;two key feature that are missing from YAML build pipelines&lt;/strong&gt;: &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/release/approvals/gates&quot; rel=&quot;noopener&quot;&gt;Gates&lt;/a&gt; and &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/deployment-group-phases&quot; rel=&quot;noopener&quot;&gt;Deployment Group jobs&lt;/a&gt;. Also, the release pipeline &lt;strong&gt;visualization&lt;/strong&gt; and &lt;strong&gt;dashboard&lt;/strong&gt; widgets are quite useful, so you may prefer these over the build pipeline visualization. But in my opinion the visualization is not worth losing version control over your pipeline.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Recommendation: Use &lt;strong&gt;multi-stage YAML pipeline&lt;/strong&gt; deployments if you don’t need Gates or Deployment Group Jobs&lt;/strong&gt;. Use &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/conditions&quot; rel=&quot;noopener&quot;&gt;conditions&lt;/a&gt; to determine if a deployment job should be executed. Use &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/approvals?view=azure-devops&amp;amp;tabs=check-pass#approvals&quot; rel=&quot;noopener&quot;&gt;approvals&lt;/a&gt; and checks on the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/environments?view=azure-devops&quot; rel=&quot;noopener&quot;&gt;environment&lt;/a&gt; to control deployment.&lt;/p&gt;&lt;p&gt;Documentation Reference: &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/deployment-jobs&quot; rel=&quot;noopener&quot;&gt;Deployment Job&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/conditions&quot; rel=&quot;noopener&quot;&gt;Conditions&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/approvals?view=azure-devops&amp;amp;tabs=check-pass#approvals&quot; rel=&quot;noopener&quot;&gt;Approvals&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/process/environments?view=azure-devops&quot; rel=&quot;noopener&quot;&gt;Environments&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;10-deployment-group-agents&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/12-things-you-should-know-when-implementing-azure-devops-in-your-organization/#10-deployment-group-agents&quot; class=&quot;heading-anchor&quot;&gt;10. Deployment Group Agents?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/235ZCgYAEs-650.webp 650w, https://danielscottraynsford.com/img/235ZCgYAEs-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/235ZCgYAEs-650.png&quot; alt width=&quot;960&quot; height=&quot;600&quot; srcset=&quot;https://danielscottraynsford.com/img/235ZCgYAEs-650.png 650w, https://danielscottraynsford.com/img/235ZCgYAEs-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Add a machine to a Deployment Group.&lt;/p&gt;&lt;p&gt;If the applications you are building and releasing need to be deployed to a physical or virtual machine (e.g. not to a Kubernetes cluster or managed service) that is &lt;strong&gt;not accessible by an Azure DevOps Hosted agent&lt;/strong&gt;, then you can use a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/release/deployment-groups/howto-provision-deployment-group-agents&quot; rel=&quot;noopener&quot;&gt;Deployment Group agent&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;This is just the Azure DevOps Hosted agent installed onto the machine and registered with Azure DevOps as a &lt;strong&gt;Deployment Group agent&lt;/strong&gt; in a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/release/deployment-groups&quot; rel=&quot;noopener&quot;&gt;Deployment Group&lt;/a&gt;. Deployment Group agents only require outbound connectivity to Azure DevOps services.&lt;/p&gt;&lt;p&gt;This is a good solution if you’re deploy to machines on-premises or on machines where inbound internet connectivity is blocked, but outbound internet is allowed.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Recommendation: If you have to deploy your application to a machine that&lt;/strong&gt; &lt;strong&gt;is not accessible from Azure DevOps Microsoft Hosted Agents.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Documentation Reference: &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/release/deployment-groups/howto-provision-deployment-group-agents&quot; rel=&quot;noopener&quot;&gt;Deployment Group agent&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/release/deployment-groups&quot; rel=&quot;noopener&quot;&gt;Deployment Group&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;11-automate-automate&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/12-things-you-should-know-when-implementing-azure-devops-in-your-organization/#11-automate-automate&quot; class=&quot;heading-anchor&quot;&gt;11. Automate. Automate&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/O5JQMtBbcW-650.webp 650w, https://danielscottraynsford.com/img/O5JQMtBbcW-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/O5JQMtBbcW-650.png&quot; alt width=&quot;960&quot; height=&quot;530&quot; srcset=&quot;https://danielscottraynsford.com/img/O5JQMtBbcW-650.png 650w, https://danielscottraynsford.com/img/O5JQMtBbcW-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Get a list of Azure DevOps projects using Azure DevOps CLI&lt;/p&gt;&lt;p&gt;Just like most other Microsoft tools, you can automate them from the command line using either PowerShell, CMD or Bash (or even REST API). If you have to perform repetitive tasks in Azure DevOps, you might want to consider &lt;strong&gt;automating these processes&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;This is also a good way to control certain processes and practices, such as creating &lt;strong&gt;Service Connections&lt;/strong&gt; from code in a repository, or &lt;strong&gt;rolling secrets in a Library Variable Group&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;You can also use these tools to &lt;strong&gt;interact with Azure DevOps from within Azure DevOps pipelines&lt;/strong&gt;, leading to some interesting techniques such as release orchestrations (beyond the scope of this doc).&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Recommendation: Use Azure DevOps CLI or the VSTeams PowerShell module (created by &lt;a href=&quot;https://twitter.com/DonovanBrown&quot; rel=&quot;noopener&quot;&gt;Donovan Brown&lt;/a&gt;) to automate Azure DevOps.&lt;/strong&gt; Alternatively, use Azure DevOps REST API.&lt;/p&gt;&lt;p&gt;Documentation Reference: &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/cli/?view=azure-devops&quot; rel=&quot;noopener&quot;&gt;Azure DevOps CLI&lt;/a&gt;, &lt;a href=&quot;https://github.com/MethodsAndPractices/vsteam&quot; rel=&quot;noopener&quot;&gt;VSTeam PowerShell module&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/rest/api/azure/devops/&quot; rel=&quot;noopener&quot;&gt;Azure DevOps REST API&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;12-get-practical-experience&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/12-things-you-should-know-when-implementing-azure-devops-in-your-organization/#12-get-practical-experience&quot; class=&quot;heading-anchor&quot;&gt;12. Get Practical Experience&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/eLW5tM3d1C-650.webp 650w, https://danielscottraynsford.com/img/eLW5tM3d1C-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/eLW5tM3d1C-650.png&quot; alt width=&quot;960&quot; height=&quot;211&quot; srcset=&quot;https://danielscottraynsford.com/img/eLW5tM3d1C-650.png 650w, https://danielscottraynsford.com/img/eLW5tM3d1C-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;The best way to learn Azure DevOps is to get hands-on practical experience. &lt;a href=&quot;https://www.azuredevopslabs.com/&quot; rel=&quot;noopener&quot;&gt;Azure DevOps Labs&lt;/a&gt; provides &lt;strong&gt;free hands-on labs environments&lt;/strong&gt; (via your own DevOps organization) and covers practically everything you could ever want to know. The &lt;a href=&quot;https://docs.microsoft.com/en-us/learn/browse/?products=azure-devops&quot; rel=&quot;noopener&quot;&gt;Azure DevOps content on Microsoft Learn&lt;/a&gt; also has detailed walk throughs of the product and processes.&lt;/p&gt;&lt;p&gt;Making sure everyone in your organization has the skills/knowledge to work with Azure DevOps will help them be more successful and happy.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Recommendation: Do some of the hands-on labs and complete some Microsoft Learn learning pathways.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Documentation Reference: &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/release/deployment-groups/howto-provision-deployment-group-agents&quot; rel=&quot;noopener&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://www.azuredevopslabs.com/&quot; rel=&quot;noopener&quot;&gt;Azure DevOps Labs&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/learn/browse/?products=azure-devops&quot; rel=&quot;noopener&quot;&gt;Azure DevOps content on Microsoft Learn&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;wrapping-up&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/12-things-you-should-know-when-implementing-azure-devops-in-your-organization/#wrapping-up&quot; class=&quot;heading-anchor&quot;&gt;Wrapping Up&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;There are definitely lots more recommendations and considerations I could suggest, especially security and DevOps best-practices but to keep this (reasonably) short, I’ll leave them for another post.&lt;/p&gt;&lt;p&gt;I hope you find this useful and it helps you avoid some of my mistakes.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Automate on-boarding Azure Log Analytics Container Monitoring of any Linux Docker Host using Azure Arc</title>
      <link href="https://danielscottraynsford.com/blog/automate-on-boarding-azure-log-analytics-container-monitoring-of-any-linux-docker-host-using-azure-arc/" />
      <updated>2020-08-23T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/automate-on-boarding-azure-log-analytics-container-monitoring-of-any-linux-docker-host-using-azure-arc/</id>
      <content type="html">
				&lt;p&gt;That title is a bit of a mouthful, but this post will show how easy it is to configure a Linux Docker host to be monitored by &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-monitor/overview&quot; rel=&quot;noopener&quot;&gt;Azure Monitor&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Azure Monitor can be used to monitor machines that are running in &lt;strong&gt;Azure&lt;/strong&gt;, in &lt;strong&gt;any cloud&lt;/strong&gt; or &lt;strong&gt;on-premises&lt;/strong&gt;. For a machine to be monitored by Azure Monitor, it needs to have the &lt;a href=&quot;https://docs.microsoft.com/en-us/services-hub/health/mma-setup&quot; rel=&quot;noopener&quot;&gt;Microsoft Monitoring Agent&lt;/a&gt; (MMA) installed. The machine either needs to be able to connect to Azure directly or via a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-monitor/platform/gateway&quot; rel=&quot;noopener&quot;&gt;Log Analytics Gateway&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;But to make things a lot easier, we’re going to set up the Docker host to allow it to be managed in Azure using &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-arc/servers/overview&quot; rel=&quot;noopener&quot;&gt;Azure Arc&lt;/a&gt;. This will allow Azure Arc to install MMA for us. The Linux Docker host will appear in the Azure portal like other Azure resources:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/YBqx7FpdPT-650.webp 650w, https://danielscottraynsford.com/img/YBqx7FpdPT-960.webp 960w, https://danielscottraynsford.com/img/YBqx7FpdPT-1326.webp 1326w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/YBqx7FpdPT-650.png&quot; alt width=&quot;1326&quot; height=&quot;665&quot; srcset=&quot;https://danielscottraynsford.com/img/YBqx7FpdPT-650.png 650w, https://danielscottraynsford.com/img/YBqx7FpdPT-960.png 960w, https://danielscottraynsford.com/img/YBqx7FpdPT-1326.png 1326w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Azure Arc managed machines running outside of Azure&lt;/p&gt;&lt;p&gt;We will also add the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-monitor/insights/containers&quot; rel=&quot;noopener&quot;&gt;Container Monitoring solution&lt;/a&gt; to our Azure Monitor Log Analytics workspace. The Container Monitoring solution will set up the Log Analytics workspace to &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-monitor/insights/containers#container-records&quot; rel=&quot;noopener&quot;&gt;record telemetry data&lt;/a&gt; from your Linux Docker host and add a &lt;strong&gt;container monitoring dashboard&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/J3VdZ2WtaC-650.webp 650w, https://danielscottraynsford.com/img/J3VdZ2WtaC-960.webp 960w, https://danielscottraynsford.com/img/J3VdZ2WtaC-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/J3VdZ2WtaC-650.png&quot; alt width=&quot;1400&quot; height=&quot;588&quot; srcset=&quot;https://danielscottraynsford.com/img/J3VdZ2WtaC-650.png 650w, https://danielscottraynsford.com/img/J3VdZ2WtaC-960.png 960w, https://danielscottraynsford.com/img/J3VdZ2WtaC-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Container Monitoring Solution dashboard in an Azure Monitor Log Analytics Workspace&lt;/p&gt;&lt;p&gt;To enable collection and sending of telemetry from all containers running on the Docker host, a &lt;a href=&quot;https://hub.docker.com/r/microsoft/oms&quot; rel=&quot;noopener&quot;&gt;microsoft/oms container&lt;/a&gt; is run on it. This Docker container will connect to the Azure Monitor Log Analytics workspace and send logs and performance counters from all Docker containers running on the host.&lt;/p&gt;&lt;p&gt;Once we have completed the configuration of the Docker Host, the following telemetry will be sent to your Log Analytics workspace:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Host diagnostics/logs.&lt;/li&gt;&lt;li&gt;Host performance metrics.&lt;/li&gt;&lt;li&gt;Diagnostics/logs from any Docker containers on the host.&lt;/li&gt;&lt;li&gt;Performance metrics from any Docker containers on the host.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The cost of sending this telemetry to your Azure Monitor Log Analytics workspace will depend on the &lt;a href=&quot;https://azure.microsoft.com/en-us/pricing/details/monitor/&quot; rel=&quot;noopener&quot;&gt;volume of data ingested&lt;/a&gt;. You can control this by reducing the frequency with which performance counters are transmitted. By default this is sent every 60 seconds. You can configure this through the Log Analytics workspace.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/nq9pYFNXxV-650.webp 650w, https://danielscottraynsford.com/img/nq9pYFNXxV-960.webp 960w, https://danielscottraynsford.com/img/nq9pYFNXxV-1326.webp 1326w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/nq9pYFNXxV-650.png&quot; alt width=&quot;1326&quot; height=&quot;665&quot; srcset=&quot;https://danielscottraynsford.com/img/nq9pYFNXxV-650.png 650w, https://danielscottraynsford.com/img/nq9pYFNXxV-960.png 960w, https://danielscottraynsford.com/img/nq9pYFNXxV-1326.png 1326w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Configuring performance counter frequency&lt;/p&gt;&lt;h2 id=&quot;what-you-need&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/automate-on-boarding-azure-log-analytics-container-monitoring-of-any-linux-docker-host-using-azure-arc/#what-you-need&quot; class=&quot;heading-anchor&quot;&gt;What you need&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;These instructions assumes you have the following:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;An &lt;strong&gt;Azure&lt;/strong&gt; account - &lt;a href=&quot;https://azure.microsoft.com/en-us/free/&quot; rel=&quot;noopener&quot;&gt;get a free account here&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;A &lt;strong&gt;Resource Group&lt;/strong&gt; to contain the machines you register with Azure Arc - &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-portal#create-resource-groups&quot; rel=&quot;noopener&quot;&gt;instructions on how to create one&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;A &lt;strong&gt;Log Analytics Workspace&lt;/strong&gt; - &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-monitor/learn/quick-create-workspace&quot; rel=&quot;noopener&quot;&gt;instructions on how to create one&lt;/a&gt;. This is where all the diagnostic and metric data will be sent from the Docker hosts.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Azure Cloud Shell&lt;/strong&gt; enabled - &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/cloud-shell/using-the-shell-window&quot; rel=&quot;noopener&quot;&gt;how to use Azure Cloud Shell&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;SSH access&lt;/strong&gt; to your Docker host to connect to &lt;strong&gt;Azure Arc&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Important:&lt;/strong&gt; the Linux host you are going to install MMA on must have Python installed. If it is not installed you will receive an “&lt;em&gt;Install failed with exit code 52 Installation failed due to missing dependencies.&lt;/em&gt;” error when you install the MMA Agent.&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;connect-linux-host-to-azure-arc&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/automate-on-boarding-azure-log-analytics-container-monitoring-of-any-linux-docker-host-using-azure-arc/#connect-linux-host-to-azure-arc&quot; class=&quot;heading-anchor&quot;&gt;Connect Linux Host to Azure Arc&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The first step is to connect our Linux host to Azure Arc so that we can use it to perform all the other steps directly from the Azure Portal. We are going to use a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-arc/servers/onboard-service-principal&quot; rel=&quot;noopener&quot;&gt;service principal for onboarding the machine&lt;/a&gt; as this will make it easier to automate.&lt;/p&gt;&lt;p&gt;We are going to run &lt;a href=&quot;https://danielscottraynsford.com/blog/automate-on-boarding-azure-log-analytics-container-monitoring-of-any-linux-docker-host-using-azure-arc/64a2fd67489ea22b3ca09cd5bf3a0782&quot;&gt;this Azure Arc Onboarding script generator PowerShell script&lt;/a&gt; in &lt;strong&gt;Azure Cloud Shell&lt;/strong&gt; to create the &lt;strong&gt;Service Principal&lt;/strong&gt; and generate the Linux Shell script for us. It can also generate a PowerShell script for onboarding &lt;strong&gt;Windows machines&lt;/strong&gt; to Azure Arc.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Open Azure Cloud Shell and ensure you’re &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/cloud-shell/using-the-shell-window#swap-between-bash-and-powershell-environments&quot; rel=&quot;noopener&quot;&gt;using PowerShell&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Download the script by running:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Invoke-WebRequest&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Uri https:&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;gist&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;githubusercontent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com/PlagueHO/64a2fd67489ea22b3ca09cd5bf3a0782/raw/&lt;span class=&quot;token function&quot;&gt;Get-AzureArcOnboardingScript&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;OutFile ~&#92;&lt;span class=&quot;token function&quot;&gt;Get-AzureArcOnboardingScript&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Scms5EKnHa-650.webp 650w, https://danielscottraynsford.com/img/Scms5EKnHa-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Scms5EKnHa-650.png&quot; alt width=&quot;960&quot; height=&quot;66&quot; srcset=&quot;https://danielscottraynsford.com/img/Scms5EKnHa-650.png 650w, https://danielscottraynsford.com/img/Scms5EKnHa-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Run the script by executing the following command and setting the &lt;code&gt;TenantId&lt;/code&gt;, &lt;code&gt;SubscriptionId&lt;/code&gt;, &lt;code&gt;Location&lt;/code&gt; and &lt;code&gt;ResourceGroup&lt;/code&gt; parameters:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureArcOnboardingScript&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TenantId &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;TENANT ID&amp;gt;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SubscriptionId &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;SUBSCRIPTION ID&amp;gt;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Location &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;LOCATION&amp;gt;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroup &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;RESOURCE GROUP&amp;gt;&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/YI_vPXn7AG-650.webp 650w, https://danielscottraynsford.com/img/YI_vPXn7AG-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/YI_vPXn7AG-650.png&quot; alt width=&quot;960&quot; height=&quot;299&quot; srcset=&quot;https://danielscottraynsford.com/img/YI_vPXn7AG-650.png 650w, https://danielscottraynsford.com/img/YI_vPXn7AG-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;You will need to &lt;a href=&quot;https://microsoft.github.io/AzureTipsAndTricks/blog/tip153.html&quot; rel=&quot;noopener&quot;&gt;get your Tenant ID from the Azure Portal&lt;/a&gt;. The &lt;strong&gt;Subscription Id&lt;/strong&gt; and &lt;strong&gt;Resource Group&lt;/strong&gt; is the subscription and resource group respectively to register the machine in. The &lt;strong&gt;Location&lt;/strong&gt; is the Azure region that the machine metadata will be stored.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Copy the script that was produced. We will execute it on any Linux machine we want to onboard.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;SSH into the Linux Host and run (paste) the script:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/1iM3VcyQLm-650.webp 650w, https://danielscottraynsford.com/img/1iM3VcyQLm-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/1iM3VcyQLm-650.gif&quot; alt width=&quot;960&quot; height=&quot;634&quot; srcset=&quot;https://danielscottraynsford.com/img/1iM3VcyQLm-650.gif 650w, https://danielscottraynsford.com/img/1iM3VcyQLm-960.gif 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/veCsXsCC27-650.webp 650w, https://danielscottraynsford.com/img/veCsXsCC27-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/veCsXsCC27-650.png&quot; alt width=&quot;960&quot; height=&quot;220&quot; srcset=&quot;https://danielscottraynsford.com/img/veCsXsCC27-650.png 650w, https://danielscottraynsford.com/img/veCsXsCC27-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;br&gt;In a real production environment you’d probably automate this process and you’d also need to protect the secrets in the script.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Once the installation is complete, the machine will appear in the Azure Portal in the resource group:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ICK8MVYDu3-650.webp 650w, https://danielscottraynsford.com/img/ICK8MVYDu3-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ICK8MVYDu3-650.png&quot; alt width=&quot;960&quot; height=&quot;617&quot; srcset=&quot;https://danielscottraynsford.com/img/ICK8MVYDu3-650.png 650w, https://danielscottraynsford.com/img/ICK8MVYDu3-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Now that the machine is &lt;strong&gt;onboarded into Azure Arc&lt;/strong&gt;, we can use it to install Microsoft Monitoring Agent (MMA) and then run the &lt;a href=&quot;https://hub.docker.com/r/microsoft/oms&quot; rel=&quot;noopener&quot;&gt;microsoft/oms Docker container&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Further Improvements:&lt;/strong&gt; we could easily have used something like &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/scripting/dsc/overview/overview?view=powershell-7&quot; rel=&quot;noopener&quot;&gt;PowerShell DSC&lt;/a&gt; or Ansible to apply the Azure Arc onboarding configuration to the machine, but this is beyond the scope of this post. In a fully mature practice, there would be &lt;strong&gt;no need for logging directly into the host&lt;/strong&gt; at any point in this process.&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;installing-mma-with-azure-arc&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/automate-on-boarding-azure-log-analytics-container-monitoring-of-any-linux-docker-host-using-azure-arc/#installing-mma-with-azure-arc&quot; class=&quot;heading-anchor&quot;&gt;Installing MMA with Azure Arc&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;At the time of writing this blog post, there wasn’t an Azure PowerShell module or AzCLI extension for Azure Arc. So automating this process right now will require the use of an ARM template:&lt;/p&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;contentVersion&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;parameters&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;MachineName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;String&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;Location&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;String&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;WorkspaceId&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;String&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;WorkspaceKey&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;String&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;variables&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;resources&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Microsoft.HybridCompute/machines/extensions&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;apiVersion&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2019-12-12&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[concat(parameters(&#39;MachineName&#39;), &#39;/OMSAgentForLinux&#39;)]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;location&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;Location&#39;)]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;publisher&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Microsoft.EnterpriseCloud.Monitoring&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;OmsAgentForLinux&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;autoUpgradeMinorVersion&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;settings&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;&quot;workspaceId&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;WorkspaceId&#39;)]&quot;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;protectedSettings&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;&quot;workspaceKey&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;WorkspaceKey&#39;)]&quot;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Before you attempt this step, make sure the machine you are deploying MMA to has Python installed on it. If it is not installed you will receive an “&lt;em&gt;Install failed with exit code 52 Installation failed due to missing dependencies.&lt;/em&gt;” error when you install the MMA Agent.&lt;/p&gt;&lt;p&gt;To apply the ARM Template in &lt;strong&gt;Azure Cloud Shell&lt;/strong&gt;:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Run this command to download the ARM Template:&lt;/li&gt;&lt;/ol&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Invoke-WebRequest&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Uri https:&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;gist&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;githubusercontent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com/PlagueHO/74c5035543c454daf3d28f33ea91cde0/raw/AzureArcLinuxMonitoringExtensions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;OutFile ~&#92;AzureArcLinuxMonitoringExtensions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Apply the ARM Template to an Azure Arc machine by running this command (replacing the values in the strings):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;New-AzResourceGroupDeployment&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;NAME OF RESOURCE GROUP CONTAINING ARC MACHINES&amp;gt;&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TemplateFile ~&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;AzureArcLinuxMonitoringExtensions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TemplateParameterObject @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    MachineName = &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;NAME OF AZURE ARC MACHINE&amp;gt;&#39;&lt;/span&gt;
    Location = &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;LOCATION OF AZURE ARM MACHINE&amp;gt;&#39;&lt;/span&gt;
    WorkspaceId = &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;WORKSPACE ID OF LOG ANALYTICS WORKSPACE&amp;gt;&#39;&lt;/span&gt;
    WorkspaceKey = &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;WORKSPACE KEY OF LOG ANALYTICS WORKSPACE&amp;gt;&#39;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/tS35bH79ao-650.webp 650w, https://danielscottraynsford.com/img/tS35bH79ao-960.webp 960w, https://danielscottraynsford.com/img/tS35bH79ao-1325.webp 1325w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/tS35bH79ao-650.png&quot; alt width=&quot;1325&quot; height=&quot;719&quot; srcset=&quot;https://danielscottraynsford.com/img/tS35bH79ao-650.png 650w, https://danielscottraynsford.com/img/tS35bH79ao-960.png 960w, https://danielscottraynsford.com/img/tS35bH79ao-1325.png 1325w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;br&gt;You can get the &lt;code&gt;WorkspaceId&lt;/code&gt; and &lt;code&gt;WorkspaceKey&lt;/code&gt; values by locating your Log Analytics Workspace in the Azure Portal and clicking &lt;strong&gt;Agents Management&lt;/strong&gt; in the side bar.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Important:&lt;/strong&gt; If you’re automating this, you’ll want to take care not to expose the &lt;strong&gt;Workspace Key&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;You can navigate to the &lt;strong&gt;Azure Arc Machine&lt;/strong&gt; resource in the &lt;strong&gt;Azure Portal&lt;/strong&gt; and select the extension to see that it is “creating”. It will take a between 5 and 10 minutes before installation of the extension is &lt;strong&gt;completed&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Once installation has &lt;strong&gt;completed&lt;/strong&gt;, you can navigate to your &lt;strong&gt;Azure Monitor Log Analytics Workspace&lt;/strong&gt;, click &lt;strong&gt;Agents Management&lt;/strong&gt; in the side bar and select &lt;strong&gt;Linux Agents&lt;/strong&gt;. You should notice that the number of agents has increased:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/WzJTy47awW-650.webp 650w, https://danielscottraynsford.com/img/WzJTy47awW-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/WzJTy47awW-650.png&quot; alt width=&quot;960&quot; height=&quot;402&quot; srcset=&quot;https://danielscottraynsford.com/img/WzJTy47awW-650.png 650w, https://danielscottraynsford.com/img/WzJTy47awW-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Clicking &lt;strong&gt;Go to logs&lt;/strong&gt; will show all &lt;strong&gt;Linux Machines&lt;/strong&gt; that Azure Monitor Log Analytics has received a &lt;strong&gt;Heartbeat&lt;/strong&gt; from:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/kqE88ecIY0-650.webp 650w, https://danielscottraynsford.com/img/kqE88ecIY0-960.webp 960w, https://danielscottraynsford.com/img/kqE88ecIY0-1325.webp 1325w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/kqE88ecIY0-650.png&quot; alt width=&quot;1325&quot; height=&quot;719&quot; srcset=&quot;https://danielscottraynsford.com/img/kqE88ecIY0-650.png 650w, https://danielscottraynsford.com/img/kqE88ecIY0-960.png 960w, https://danielscottraynsford.com/img/kqE88ecIY0-1325.png 1325w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;enable-container-telemetry&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/automate-on-boarding-azure-log-analytics-container-monitoring-of-any-linux-docker-host-using-azure-arc/#enable-container-telemetry&quot; class=&quot;heading-anchor&quot;&gt;Enable Container Telemetry&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;So far, so good. We’ve onboarded the machine to Azure Arc and enabled host logging to a Azure Monitor Log Analytics workspace. However, we’re only getting telemetry data from the host, not any of the containers. So the next thing we need to do is execute the following command on the host:&lt;/p&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; run &lt;span class=&quot;token parameter variable&quot;&gt;--privileged&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt; /var/run/docker.sock:/var/run/docker.sock &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt; /var/lib/docker/containers:/var/lib/docker/containers &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;WSID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;YOUR WORKSPACE ID&amp;gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;YOUR WORKSPACE KEY&amp;gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-h&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;127.0&lt;/span&gt;.0.1:25225:25225 &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;omsagent&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--restart&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;always microsoft/oms:1.8.1-256&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will download and run the &lt;a href=&quot;https://hub.docker.com/r/microsoft/oms&quot; rel=&quot;noopener&quot;&gt;microsoft/oms container image&lt;/a&gt; on the host and configure it to send telemetry for all containers running on this host to your &lt;strong&gt;Azure Monitor Log Analytics workspace&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Important:&lt;/strong&gt; If you are installing onto Ubuntu server, you can avoid problems in this stage by making sure you’ve installed Docker using the &lt;a href=&quot;https://docs.docker.com/engine/install/ubuntu/&quot; rel=&quot;noopener&quot;&gt;official Docker repository and instructions&lt;/a&gt;. I had used Snap on Ubuntu 18.05, which resulted in this error ‘&lt;strong&gt;Error response from daemon: error while creating mount source path ‘/var/lib/docker/containers’: mkdir /var/lib/docker: read-only file system.&lt;/strong&gt;’ when running the script.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;The way to automate the installation of this on the host is to again use an ARM Template, but this time use the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/custom-script-linux&quot; rel=&quot;noopener&quot;&gt;Linux Custom Script extension&lt;/a&gt; to execute the above command. You can see the &lt;a href=&quot;https://danielscottraynsford.com/blog/automate-on-boarding-azure-log-analytics-container-monitoring-of-any-linux-docker-host-using-azure-arc/c3f09056cace496dded18da8bc1ed589&quot;&gt;ARM Template here&lt;/a&gt;. This ARM template could easily be combined into the ARM template from the preceding stage, but I kept them separate for the purposes of showing the process.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Run this command to download the ARM Template:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Invoke-WebRequest&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Uri https:&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;gist&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;githubusercontent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com/PlagueHO/c3f09056cace496dded18da8bc1ed589/raw/AzureArcLinuxCustomScriptExtensions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;OutFile ~&#92;AzureArcLinuxCustomScriptExtensions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Apply the ARM Template to an Azure Arc machine by running this command (replacing the values in the strings with the same ones as before):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;New-AzResourceGroupDeployment&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;NAME OF RESOURCE GROUP CONTAINING ARC MACHINES&amp;gt;&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TemplateFile ~&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;AzureArcLinuxCustomScriptExtensions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TemplateParameterObject @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    MachineName = &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;NAME OF AZURE ARC MACHINE&amp;gt;&#39;&lt;/span&gt;
    Location = &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;LOCATION OF AZURE ARM MACHINE&amp;gt;&#39;&lt;/span&gt;
    WorkspaceId = &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;WORKSPACE ID OF LOG ANALYTICS WORKSPACE&amp;gt;&#39;&lt;/span&gt;
    WorkspaceKey = &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;WORKSPACE KEY OF LOG ANALYTICS WORKSPACE&amp;gt;&#39;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/QayoZ-V_fN-650.webp 650w, https://danielscottraynsford.com/img/QayoZ-V_fN-960.webp 960w, https://danielscottraynsford.com/img/QayoZ-V_fN-1325.webp 1325w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/QayoZ-V_fN-650.png&quot; alt width=&quot;1325&quot; height=&quot;719&quot; srcset=&quot;https://danielscottraynsford.com/img/QayoZ-V_fN-650.png 650w, https://danielscottraynsford.com/img/QayoZ-V_fN-960.png 960w, https://danielscottraynsford.com/img/QayoZ-V_fN-1325.png 1325w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;After a few minutes installation of the &lt;strong&gt;CustomScript&lt;/strong&gt; extension should have completed and should show &lt;strong&gt;Succeeded&lt;/strong&gt; in the Azure Portal.&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/t6Er-KpPET-650.webp 650w, https://danielscottraynsford.com/img/t6Er-KpPET-868.webp 868w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/t6Er-KpPET-650.png&quot; alt width=&quot;868&quot; height=&quot;75&quot; srcset=&quot;https://danielscottraynsford.com/img/t6Er-KpPET-650.png 650w, https://danielscottraynsford.com/img/t6Er-KpPET-868.png 868w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;If you SSH into the Linux Container host and run &lt;code&gt;sudo docker ps&lt;/code&gt; you will see that the &lt;strong&gt;omsagent&lt;/strong&gt; container is running:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/RNQ1z6p460-650.webp 650w, https://danielscottraynsford.com/img/RNQ1z6p460-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/RNQ1z6p460-650.png&quot; alt width=&quot;960&quot; height=&quot;81&quot; srcset=&quot;https://danielscottraynsford.com/img/RNQ1z6p460-650.png 650w, https://danielscottraynsford.com/img/RNQ1z6p460-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The process is now complete and we’re getting telemetry from both the host and the containers running on it. We only needed to log into the host initially to onboard it into Azure Arc, but after that all other steps were performed by Azure. We could have performed the onboarding using automation as well and that would be the &lt;strong&gt;recommended&lt;/strong&gt; pattern to use in a production environment.&lt;/p&gt;&lt;h2 id=&quot;configure-performance-counters-sample-interval&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/automate-on-boarding-azure-log-analytics-container-monitoring-of-any-linux-docker-host-using-azure-arc/#configure-performance-counters-sample-interval&quot; class=&quot;heading-anchor&quot;&gt;Configure Performance Counters Sample Interval&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The final (and optional) step is to configure &lt;strong&gt;sample interval&lt;/strong&gt; that performance counters will be collected on each Linux host. To do this:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Open the &lt;a href=&quot;https://portal.azure.com/&quot; rel=&quot;noopener&quot;&gt;Azure Portal&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Navigate to your &lt;strong&gt;Azure Monitor Log Analytics Workspace&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Advanced Settings&lt;/strong&gt;:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/1ugsi8ZCcD-432.webp 432w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/1ugsi8ZCcD-432.png&quot; alt width=&quot;432&quot; height=&quot;462&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Select &lt;strong&gt;Data&lt;/strong&gt;, then &lt;strong&gt;Linux Performance Counters&lt;/strong&gt;:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/X-e157RCTv-650.webp 650w, https://danielscottraynsford.com/img/X-e157RCTv-960.webp 960w, https://danielscottraynsford.com/img/X-e157RCTv-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/X-e157RCTv-650.png&quot; alt width=&quot;1400&quot; height=&quot;749&quot; srcset=&quot;https://danielscottraynsford.com/img/X-e157RCTv-650.png 650w, https://danielscottraynsford.com/img/X-e157RCTv-960.png 960w, https://danielscottraynsford.com/img/X-e157RCTv-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Configure the &lt;strong&gt;Sample Interval&lt;/strong&gt; and click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The updated counter sample interval will be updated in the Microsoft Monitoring Agent configuration on the host.&lt;/p&gt;&lt;h2 id=&quot;see-it-all-in-action&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/automate-on-boarding-azure-log-analytics-container-monitoring-of-any-linux-docker-host-using-azure-arc/#see-it-all-in-action&quot; class=&quot;heading-anchor&quot;&gt;See It All In Action&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Now that everything is all set up, let’s see what it looks like in Azure Monitor.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Open the &lt;a href=&quot;https://portal.azure.com/&quot; rel=&quot;noopener&quot;&gt;Azure Portal&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Navigate to your &lt;strong&gt;Azure Monitor Log Analytics Workspace&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Workspace Summary&lt;/strong&gt; in the side bar.&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Container Monitoring Solution&lt;/strong&gt; in the workspace overview:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/-gGlahhj2t-358.webp 358w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/-gGlahhj2t-358.png&quot; alt width=&quot;358&quot; height=&quot;179&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;You can now browse through the Container Monitoring Solution dashboard and see your hosts are being monitored as well as see performance information from your containers:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/wR6_fLhhSn-650.webp 650w, https://danielscottraynsford.com/img/wR6_fLhhSn-960.webp 960w, https://danielscottraynsford.com/img/wR6_fLhhSn-1280.webp 1280w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/wR6_fLhhSn-650.gif&quot; alt width=&quot;1280&quot; height=&quot;720&quot; srcset=&quot;https://danielscottraynsford.com/img/wR6_fLhhSn-650.gif 650w, https://danielscottraynsford.com/img/wR6_fLhhSn-960.gif 960w, https://danielscottraynsford.com/img/wR6_fLhhSn-1280.gif 1280w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;It really is fairly easy to get set up and once configured will give you much greater visibility over your entire estate, no matter where it is running.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Enable AKS Azure Active Directory integration with a Managed Identity from an ARM template</title>
      <link href="https://danielscottraynsford.com/blog/enable-aks-azure-active-directory-integration-with-a-managed-identity-from-an-arm-template/" />
      <updated>2020-08-08T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/enable-aks-azure-active-directory-integration-with-a-managed-identity-from-an-arm-template/</id>
      <content type="html">
				&lt;p&gt;When you’re deploying an Azure Kubernetes Service (AKS) cluster in Azure, it is common that you’ll want to integrate it into Azure Active Directory (AAD) to use it as an authentication provider.&lt;/p&gt;&lt;p&gt;The &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/aks/azure-ad-integration-cli&quot; rel=&quot;noopener&quot;&gt;original (legacy) method for enabling this&lt;/a&gt; was to manually create a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal&quot; rel=&quot;noopener&quot;&gt;Service Principal&lt;/a&gt; and use that to grant your AKS cluster access to AAD. The problem with this approach was that you would need to manage this manually and as well as rolling worry about rolling secrets.&lt;/p&gt;&lt;p&gt;More recently an improved method of integrating your AKS cluster into AAD was announced: &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal&quot; rel=&quot;noopener&quot;&gt;AKS-managed Azure Active Directory integration&lt;/a&gt;. This method allows your AKS cluster resource provider to take over the task of integrating to AAD for you. This simplifies things significantly.&lt;/p&gt;&lt;p&gt;You can easily do this integration by running PowerShell or Bash scripts, but if you’d prefer to use an ARM template, here is what you need to know.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;You will need to have an &lt;code&gt;object id&lt;/code&gt; of an &lt;strong&gt;Azure Active Directory group&lt;/strong&gt; to use as your Cluster Admins.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$clusterAdminGroupObjectIds&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;New-AzADGroup&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DisplayName &lt;span class=&quot;token string&quot;&gt;&quot;AksClusterAdmin&quot;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MailNickname &lt;span class=&quot;token string&quot;&gt;&quot;AksClusterAdmin&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Id&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/AXG3O6Tn32-550.webp 550w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/AXG3O6Tn32-550.png&quot; alt=&quot;ss_aksaadintegration_createaadgroup&quot; width=&quot;550&quot; height=&quot;137&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This will return the object Id for the newly create group in the variable &lt;code&gt;$clusterAdminGroupObjectIds&lt;/code&gt;. You will need to pass this variable into your ARM template.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;You need to add an &lt;code&gt;aadProfile&lt;/code&gt; block into the &lt;code&gt;properties&lt;/code&gt; of your AKS cluster deployment definition:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/QYYX9tmdJQ-605.webp 605w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/QYYX9tmdJQ-605.png&quot; alt width=&quot;605&quot; height=&quot;94&quot;&gt;&lt;/picture&gt;&lt;br&gt;For example:&lt;/p&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://schema.management.azure.com/schemas/2019-08-01/deploymentTemplate.json#&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;contentVersion&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;parameters&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;clusterAdminGroupObjectIds&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;defaultValue&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;array&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;metadata&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Array of Azure AD Group object Ids to use for cluster administrators.&quot;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;resources&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MyAksCluster&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Microsoft.ContainerService/managedClusters&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;apiVersion&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2020-04-01&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;location&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;eastus&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;kubernetesVersion&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.18.4&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;enableRBAC&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;aadProfile&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;&quot;managed&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;&quot;adminGroupObjectIds&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;clusterAdminGroupObjectIds&#39;)]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;&quot;tenantId&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[subscription().tenantId]&quot;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// Other cluster properties here...&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;identity&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SystemAssigned&quot;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;When you deploy the ARM template (using whatever method you choose), you’ll need to pass the &lt;code&gt;$clusterAdminGroupObjectIds&lt;/code&gt;​as a parameter. For example:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;New-AzResourceGroupDeployment&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token string&quot;&gt;&#39;MyAksWithAad_RG `
    -TemplateFile &#39;&lt;/span&gt;ArmTemplateAksClusterWithManagedAadIntegration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json&#39; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TemplateParameterObject @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        clusterAdminGroupObjectIds = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$clusterAdminGroupObjectIds&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;That is all you really need to get AKS-managed AAD integration going with your AKS cluster.&lt;/p&gt;&lt;p&gt;For a fully formed ARM template for that will deploy an AKS cluster with AKS-managed AAD integration plus a whole lot more, &lt;a href=&quot;https://github.com/PlagueHO/Workshop-AKS-Advanced-with-AGIC/blob/master/src/infrastructure/azuredeploy.json&quot; rel=&quot;noopener&quot;&gt;check out this ARM template&lt;/a&gt;. It will deploy an AKS cluster including the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A Log Analytics workspace integrated into the cluster with Cluster Insights and Diagnostics.&lt;/li&gt;&lt;li&gt;A VNET for the cluster nodes.&lt;/li&gt;&lt;li&gt;An ACR integrated into the VNET with Private Link and Diagnostics into the Log Analytics Workspace.&lt;/li&gt;&lt;li&gt;Multiple node pools spanning availability zones:&lt;ul&gt;&lt;li&gt;A system node pool including automatic node scaler.&lt;/li&gt;&lt;li&gt;A Linux user node pool including automatic node scaler.&lt;/li&gt;&lt;li&gt;A Windows node pool including automatic node scaler and taints.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Thanks for reading.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Refactoring PowerShell - Switch Statements</title>
      <link href="https://danielscottraynsford.com/blog/refactoring-powershell-switch-statements/" />
      <updated>2019-10-05T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/refactoring-powershell-switch-statements/</id>
      <content type="html">
				&lt;p&gt;Regardless of your experience within technology, the process of creating code usually starts out with a solution that is just enough to get the job done. The solution is then typically tweaked and improved continuously until it is either “&lt;em&gt;production worthy&lt;/em&gt;” or “&lt;em&gt;good enough for the requirements&lt;/em&gt;”. This process of improvement is called &lt;strong&gt;refactoring&lt;/strong&gt;. Refactoring is a skill that all technical professionals should become proficient in, regardless if you are an IT Pro, a developer or just someone who needs to automate things.&lt;/p&gt;&lt;p&gt;There are many reasons to refactor code, including:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Add new features&lt;/li&gt;&lt;li&gt;Remove unused features&lt;/li&gt;&lt;li&gt;Improve performance&lt;/li&gt;&lt;li&gt;Increase readability, maintainability or test-ability&lt;/li&gt;&lt;li&gt;Improve design&lt;/li&gt;&lt;li&gt;Improve adherence to standards or best practices&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;refactoring-in-code-reviews&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/refactoring-powershell-switch-statements/#refactoring-in-code-reviews&quot; class=&quot;heading-anchor&quot;&gt;Refactoring in Code Reviews&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;One of the jobs of a code reviewer is to suggest areas that could be refactored to improve some of the areas above. I’ve often found that I’ll suggest the same set of refactorings in my reviews. So rather than just putting the suggesting into the code review, I thought I’d start writing them down here in a series that I could refer contributors to as well as help anyone else who happens to come across these.&lt;/p&gt;&lt;h2 id=&quot;unit-tests-and-refactoring&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/refactoring-powershell-switch-statements/#unit-tests-and-refactoring&quot; class=&quot;heading-anchor&quot;&gt;Unit Tests and Refactoring&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Because refactoring requires changing code, how can we be sure that we’re not breaking functionality or introducing bugs? That is where &lt;a href=&quot;https://devblogs.microsoft.com/scripting/unit-testing-powershell-code-with-pester/&quot; rel=&quot;noopener&quot;&gt;unit testing&lt;/a&gt; comes in. With PowerShell, we’ll typically use the &lt;a href=&quot;https://github.com/pester/Pester&quot; rel=&quot;noopener&quot;&gt;PowerShell Pester module&lt;/a&gt; to create unit tests that allow us to more safely refactor code. Unit testing is beyond the scope of this post.&lt;/p&gt;&lt;h2 id=&quot;switch-statement-refactoring&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/refactoring-powershell-switch-statements/#switch-statement-refactoring&quot; class=&quot;heading-anchor&quot;&gt;Switch Statement Refactoring&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;One of the common patterns I’ve come across is &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_switch?view=powershell-6&quot; rel=&quot;noopener&quot;&gt;PowerShell switch statements&lt;/a&gt; being used is to map from one values to another set of values. For example:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$colourName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;green&#39;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$colourValue&lt;/span&gt; = &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$colourName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;red&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 0xFF0000&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;green&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 0x00FF00&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;blue&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 0x0000FF&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;white&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 0xFFFFFF&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    default &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 0x0 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$colourValue&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This converts a colour name (e.g. &lt;em&gt;red&lt;/em&gt;, &lt;em&gt;green&lt;/em&gt;, &lt;em&gt;blue&lt;/em&gt;, &lt;em&gt;white&lt;/em&gt;) to a colour value. If a colour name can not be matched then it returns &lt;em&gt;0x0&lt;/em&gt; (&lt;em&gt;black&lt;/em&gt;). Admittedly, this is a bit of an unlikely example, but it demonstrates the pattern.&lt;/p&gt;&lt;p&gt;This is by no means incorrect or “bad code”. It is completely valid and solves the problem perfectly well. But as you can see this requires a lot of code to perform a simple mapping.&lt;/p&gt;&lt;h3 id=&quot;mapping-using-a-hash-table&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/refactoring-powershell-switch-statements/#mapping-using-a-hash-table&quot; class=&quot;heading-anchor&quot;&gt;Mapping using a Hash Table&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;An alternative to using a switch statement is to use a &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_hash_tables?view=powershell-6&quot; rel=&quot;noopener&quot;&gt;hash table&lt;/a&gt;:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$colourName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;green&#39;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$colourMap&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    red = 0xFF0000
    green = 0x00FF00
    blue = 0x0000FF
    white = 0xFFFFFF
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$colourValue&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$colourMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$colourName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$colourValue&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This can simplify the code slightly by removing the &lt;strong&gt;break&lt;/strong&gt; &lt;strong&gt;statement&lt;/strong&gt; and &lt;strong&gt;braces&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Note: The break statement is not strictly required &lt;em&gt;in this example&lt;/em&gt; from a functional perspective, but including them increases overall performance of the switch statement.&lt;/p&gt;&lt;p&gt;You may have noticed that the hash table above does not quite match the behavior of the switch statement: the default mapping to &lt;em&gt;0x0&lt;/em&gt; is not handled. So, in this case, we’d need to include additional code to handle this:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$colourName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;green&#39;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$colourMap&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    red = 0xFF0000
    green = 0x00FF00
    blue = 0x0000FF
    white = 0xFFFFFF
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$colourValue&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$colourMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$colourName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0x0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1 &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Null Coalescing&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$colourValue&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To handle the default we’re using a quasi &lt;a href=&quot;https://en.wikipedia.org/wiki/Null_coalescing_operator&quot; rel=&quot;noopener&quot;&gt;null coalescing operator&lt;/a&gt;. PowerShell doesn’t have a native null coalescing operator like many languages, but it does have a way of simulating it using the line:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$notNull&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$y&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1 &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Null Coalescing in PowerShell&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You could definitely argue that using a &lt;strong&gt;hash table&lt;/strong&gt; mapping with a &lt;strong&gt;null coalescing operator&lt;/strong&gt; does not make the code easier to read or maintain. But the purpose here is not to define which approach is best, rather to offer alternative patterns.&lt;/p&gt;&lt;p&gt;One other benefit of using a &lt;strong&gt;hash table&lt;/strong&gt; for mapping is that it can be separated out into a separate&amp;nbsp;&lt;strong&gt;psd1&lt;/strong&gt; file. This allows editing of the mapping table elements without having to edit the code itself:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$colourName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;green&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$colourMap&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Import-LocalizedData&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FileName mapping&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;psd1
&lt;span class=&quot;token variable&quot;&gt;$colourValue&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$colourMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$colourName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0x0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1 &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Null Coalescing&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$colourValue&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;strong&gt;psd1&lt;/strong&gt; file containing the mapping data (mapping.psd1):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;@&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    red = 0xFF0000
    green = 0x00FF00
    blue = 0x0000FF
    white = 0xFFFFFF
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;reversing-the-mapping&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/refactoring-powershell-switch-statements/#reversing-the-mapping&quot; class=&quot;heading-anchor&quot;&gt;Reversing the Mapping&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;How do we use a similar pattern to reverse the mapping? For example, mapping a colour value back to a colour name:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$colourValue&lt;/span&gt; = 0x00FF00

&lt;span class=&quot;token variable&quot;&gt;$colourName&lt;/span&gt; = &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$colourValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    0xFF0000 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;red&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    0x00FF00 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;green&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    0x0000FF &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;blue&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    0xFFFFFF &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;white&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    default &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;none&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$colourName&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To implement the same functionality using a &lt;strong&gt;hash table&lt;/strong&gt; also including the&amp;nbsp;&lt;strong&gt;null coalescing operator&lt;/strong&gt; you could use:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$colourValue&lt;/span&gt; = 0x00FF00

&lt;span class=&quot;token variable&quot;&gt;$colourMap&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    0xFF0000 = &lt;span class=&quot;token string&quot;&gt;&#39;red&#39;&lt;/span&gt;
    0x00FF00 = &lt;span class=&quot;token string&quot;&gt;&#39;green&#39;&lt;/span&gt;
    0x0000FF = &lt;span class=&quot;token string&quot;&gt;&#39;blue&#39;&lt;/span&gt;
    0xFFFFFF = &lt;span class=&quot;token string&quot;&gt;&#39;white&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$colourName&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$colourMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$colourValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0x0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1 &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Null Coalescing&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$colourName&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;using-a-hash-table-with-script-values&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/refactoring-powershell-switch-statements/#using-a-hash-table-with-script-values&quot; class=&quot;heading-anchor&quot;&gt;Using a Hash Table with Script Values&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Switch blocks may contain more than just a single statement. For example:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$VerbosePreference&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;Continue&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$action&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;New&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$path&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;c:&#92;somefile.txt&#39;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$result&lt;/span&gt; = &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;New&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Execute New-Item&#39;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;New-Item&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$path&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token string&quot;&gt;&#39;Remove&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Execute Remove-Item&#39;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Remove-Item&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$path&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token string&quot;&gt;&#39;Get&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Execute Get-Item&#39;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Get-Item&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$path&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    Default &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Invalid Action&#39;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$result&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If your switch blocks do more than just perform a mapping, you can assign &lt;strong&gt;script blocks&lt;/strong&gt; to the &lt;strong&gt;hash table&lt;/strong&gt; values instead:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$VerbosePreference&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;Continue&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$action&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;New&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$path&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;c:&#92;somefile.txt&#39;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$actions&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;New&#39;&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Execute New-Item&#39;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;New-Item&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$path&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token string&quot;&gt;&#39;Remove&#39;&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Execute Remove-Item&#39;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Remove-Item&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$path&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token string&quot;&gt;&#39;Get&#39;&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Execute Get-Item&#39;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Get-Item&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$path&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$actions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Invalid Action&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1 &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Invoke&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Instead of containing a value in each &lt;strong&gt;hash table&lt;/strong&gt; item, a &lt;strong&gt;script block&lt;/strong&gt; is specified. The &lt;strong&gt;Invoke()&lt;/strong&gt; method can then be called on the &lt;strong&gt;script block.&lt;/strong&gt;&lt;/p&gt;&lt;h3 id=&quot;enumerated-types&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/refactoring-powershell-switch-statements/#enumerated-types&quot; class=&quot;heading-anchor&quot;&gt;Enumerated Types&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;If you’re using PowerShell 5.0 or above (you hopefully are), you’re also able to use the &lt;strong&gt;enum&lt;/strong&gt; keyword to define an &lt;strong&gt;enumerated type&lt;/strong&gt; that can also be used to replace switch statements in some situations.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$colourName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;green&#39;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Only needs to be declared once&lt;/span&gt;
enum colour &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    red = 0xFF0000 
    green = 0x00FF00
    blue = 0x0000FF
    white = 0xFFFFFF
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;[colour]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$colourName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value__&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The&amp;nbsp;&lt;strong&gt;enumerated type&lt;/strong&gt; only needs to be declared once.&lt;/p&gt;&lt;p&gt;But what do we need to do if we want to have a default returned if the value is invalid in the mapping? In that case we need to use the &lt;strong&gt;TryParse&lt;/strong&gt; method of the &lt;strong&gt;enumerated type&lt;/strong&gt; to try and parse the value and return a default value if it is invalid:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$colourName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;grey&#39;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Only needs to be declared once&lt;/span&gt;
enum colour &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    red = 0xFF0000 
    green = 0x00FF00
    blue = 0x0000FF
    white = 0xFFFFFF
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$colourValue&lt;/span&gt; = &lt;span class=&quot;token namespace&quot;&gt;[colour]&lt;/span&gt;::white

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-not&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;[colour]&lt;/span&gt;::TryParse&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$colourName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;[ref]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$colourValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; 0x0
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$colourValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value__
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, we can’t assign&amp;nbsp;&lt;strong&gt;scriptblocks&lt;/strong&gt; to the values in an&amp;nbsp;&lt;strong&gt;enumerated type&lt;/strong&gt; - only constant values may be assigned. This means we can’t implement scenarios where we’d like to have the value contain more than one instruction. But this shouldn’t be too much of a problem, because if you do find yourself being limited by this, then you should probably be looking to use more advanced&amp;nbsp;&lt;strong&gt;object oriented programming&lt;/strong&gt; patterns such as&amp;nbsp;&lt;strong&gt;polymorphism&lt;/strong&gt; - which is well beyond the scope of this post. But if you’re interested to know more, review &lt;a href=&quot;https://refactoring.guru/replace-conditional-with-polymorphism&quot; rel=&quot;noopener&quot;&gt;this article&lt;/a&gt; (not PowerShell specific).&lt;/p&gt;&lt;h2 id=&quot;wrapping-up&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/refactoring-powershell-switch-statements/#wrapping-up&quot; class=&quot;heading-anchor&quot;&gt;Wrapping Up&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This post is all about looking at different ways of writing the same code. It isn’t trying to say which way is better or worse. If you have a preference and it works for you, by all means, keep on using it. This is simply to provide alternative methods that may or may not make code more readable and maintainable.&lt;/p&gt;&lt;p&gt;Feel free to comment with alternative methods of refactoring switch statements.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Deploy Sonarqube to Azure App Service Linux Containers using an Azure DevOps Pipeline</title>
      <link href="https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/" />
      <updated>2019-06-03T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/</id>
      <content type="html">
				&lt;p&gt;&lt;em&gt;Update 2020-10-12: I have updated the &lt;a href=&quot;https://github.com/Azure/azure-quickstart-templates/pull/8410&quot; rel=&quot;noopener&quot;&gt;101-webapp-linux-sonarqube-azuresql&lt;/a&gt; Azure Resource Manager quick start template to default to &lt;strong&gt;7.7-community&lt;/strong&gt; edition and prevent deployment of versions that aren’t currently compatible with Azure App Service Web App Containers.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Update 2020-10-09: It was pointed out to me that the process in this post had stopped working. The container was not starting up correctly. Upon investigation I found that this was because newer versions of the Sonarqube container includes ElasticSearch which requires additional heap memory to be assigned. Therefore the latest versions of Sonarqube can’t be used with this process. I am working on a full resolution to this issue, but in the meantime ensure you’re only using Sonarqube &lt;strong&gt;7.7-community&lt;/strong&gt; edition. I have updated the ARM template to no longer default to&amp;nbsp;&lt;strong&gt;latest&lt;/strong&gt; for the &lt;strong&gt;sonarqubeImageVersion&lt;/strong&gt; parameter. There is also an &lt;a href=&quot;https://github.com/Azure/azure-quickstart-templates/issues/7481&quot; rel=&quot;noopener&quot;&gt;issue in GitHub&lt;/a&gt; against the ARM template.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.sonarqube.org/&quot; rel=&quot;noopener&quot;&gt;Sonarqube&lt;/a&gt; is a web application that development teams typically use during the application development process to continuous validate the quality of the code.&lt;/p&gt;&lt;p&gt;This post is not specifically about Sonarqube and how it works. It is intended to show Developers &amp;amp; IT Pros how to deploy a service to Azure using contemporary &lt;strong&gt;infrastructure as code&lt;/strong&gt; and &lt;strong&gt;DevOps&lt;/strong&gt; patterns.&lt;/p&gt;&lt;h2 id=&quot;the-implementation&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#the-implementation&quot; class=&quot;heading-anchor&quot;&gt;The Implementation&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;A Sonarqube installation is made up of a web application front end backed by database.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/7Zci-2TkZQ-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/7Zci-2TkZQ-650.png&quot; alt=&quot;ss_sonarqube_architecture&quot; width=&quot;650&quot; height=&quot;442&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Sonarqube supports many &lt;a href=&quot;https://docs.sonarqube.org/latest/setup/install-server/&quot; rel=&quot;noopener&quot;&gt;different types of databases&lt;/a&gt;, but I chose to use &lt;a href=&quot;https://azure.microsoft.com/en-in/services/sql-database/&quot; rel=&quot;noopener&quot;&gt;Azure SQL Database&lt;/a&gt;. I decided to use Azure SQL Database for the following reasons:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;It is a &lt;strong&gt;managed service&lt;/strong&gt;, so I don’t have to worry about patching, securing and looking after SQL servers.&lt;ol&gt;&lt;li&gt;I can &lt;strong&gt;scale&lt;/strong&gt; the database performance up and down easily with code. This allows me to balance my performance requirements with the cost to run the server or even dial performance right back at times when the service is not being used.&lt;/li&gt;&lt;li&gt;I can make use of the new &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/sql-database/sql-database-serverless&quot; rel=&quot;noopener&quot;&gt;Azure SQL Database serverless&lt;/a&gt; (spoiler alert: there are still SQL servers). This allows the SQL Database to be &lt;strong&gt;paused&lt;/strong&gt; when not being accessed by the Sonarqube front end. It can be used to further reduce costs running Sonarqube by allowing me to delete the front end every night and only pay for the storage costs when developers aren’t developing code.&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/w0F3FWuhEB-511.png 511w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/w0F3FWuhEB-511.webp 511w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/w0F3FWuhEB-511.jpeg&quot; alt=&quot;ss_sonarqube_sql_server_serverless&quot; width=&quot;511&quot; height=&quot;310&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;For the front end web application I decided to use the &lt;a href=&quot;https://azure.microsoft.com/en-in/services/app-service/containers/&quot; rel=&quot;noopener&quot;&gt;Azure Web App for Containers&lt;/a&gt; running a Linux container using the official &lt;a href=&quot;https://hub.docker.com/_/sonarqube/&quot; rel=&quot;noopener&quot;&gt;Sonarqube Docker image&lt;/a&gt;. Because the Sonarqube web application is stateless it is a great target for being able to be delete and recreate from code. The benefits to using Azure Web App for Containers are:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Azure Web App for Containers is a &lt;strong&gt;managed service&lt;/strong&gt;, so again, no patching, securing or taking care of servers.&lt;/li&gt;&lt;li&gt;I can &lt;strong&gt;scale&lt;/strong&gt; the performance &lt;strong&gt;up&lt;/strong&gt; and &lt;strong&gt;down&lt;/strong&gt; and &lt;strong&gt;in&lt;/strong&gt; and &lt;strong&gt;out&lt;/strong&gt; from within my pipeline. This allows me to quickly and easily tune my performance/cost, even on a schedule.&lt;/li&gt;&lt;li&gt;I can &lt;strong&gt;delete and rebuild&lt;/strong&gt; my front end web application by running the pipeline in under 3 minutes. So I can completely delete my front end and save money when it is not in use (e.g. when teams aren’t developing in the middle of the night).&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;architectural-considerations&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#architectural-considerations&quot; class=&quot;heading-anchor&quot;&gt;Architectural Considerations&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The Sonarqube web application, as it has been architected, is accessible from the &lt;strong&gt;public internet&lt;/strong&gt;. This might not meet your security requirements, so you might wish to change the architecture in the following ways:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Putting an &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/application-gateway/overview&quot; rel=&quot;noopener&quot;&gt;Azure Application Gateway&lt;/a&gt; (a layer 7 router) in front of the service.&lt;/li&gt;&lt;li&gt;Isolate the service in a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/virtual-network/virtual-networks-overview&quot; rel=&quot;noopener&quot;&gt;Azure Virtual Network&lt;/a&gt; from the internet and make it only accessible to your development services. This may also require &lt;a href=&quot;https://azure.microsoft.com/en-us/services/expressroute/&quot; rel=&quot;noopener&quot;&gt;Azure ExpressRoute&lt;/a&gt; or other VPN technologies to be used.&lt;/li&gt;&lt;li&gt;We are using the SQL Server administrator account for the Sonarqube front end to connect to the backend. This is not advised for a production service - instead, a user account specifically for the use of Sonarqube should be created and the password stored in an Azure Key Vault.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;These architectural changes are beyond the scope of this document though as I wanted to keep the services simple. But the pattern defined in this post will work equally well with these architectures.&lt;/p&gt;&lt;h2 id=&quot;techniques&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#techniques&quot; class=&quot;heading-anchor&quot;&gt;Techniques&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Before we get into the good stuff, it is important to understand why I chose to &lt;strong&gt;orchestrate&lt;/strong&gt; the deployment of these services using an &lt;a href=&quot;https://azure.microsoft.com/en-us/services/devops/pipelines/&quot; rel=&quot;noopener&quot;&gt;Azure Pipeline&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I could have quite easily built the infrastructure manually straight into the Azure Portal or using some &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/azure/overview&quot; rel=&quot;noopener&quot;&gt;Azure PowerShell&lt;/a&gt; automation or the &lt;a href=&quot;https://docs.microsoft.com/en-us/cli/azure/?view=azure-cli-latest&quot; rel=&quot;noopener&quot;&gt;Azure CLI&lt;/a&gt;, so why do it this way?&lt;/p&gt;&lt;p&gt;There are a number of reasons that I’ll list below, but this is the most mature way to deploy applications and services.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/oEFbj7vxaT-650.webp 650w, https://danielscottraynsford.com/img/oEFbj7vxaT-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/oEFbj7vxaT-650.png&quot; alt=&quot;ss_sonarqube_journey_of_an_Azure_professional&quot; width=&quot;960&quot; height=&quot;529&quot; srcset=&quot;https://danielscottraynsford.com/img/oEFbj7vxaT-650.png 650w, https://danielscottraynsford.com/img/oEFbj7vxaT-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;I wanted to define my services using &lt;strong&gt;infrastructure as code&lt;/strong&gt; using an &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-authoring-templates&quot; rel=&quot;noopener&quot;&gt;Azure Resource Manager template&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;I wanted the infrastructure as code under &lt;strong&gt;version&lt;/strong&gt; &lt;strong&gt;control&lt;/strong&gt; using &lt;a href=&quot;https://azure.microsoft.com/en-us/services/devops/repos/&quot; rel=&quot;noopener&quot;&gt;Azure Repos&lt;/a&gt;. I could have easily used GitHub here or one of a number of other Git repositories, but I’m using Azure Repos for simplicity.&lt;/li&gt;&lt;li&gt;I wanted to be able to &lt;strong&gt;orchestrate the deployment&lt;/strong&gt; of the service using a CI/CD pipeline using &lt;a href=&quot;https://azure.microsoft.com/en-us/services/devops/pipelines/&quot; rel=&quot;noopener&quot;&gt;Azure Pipelines&lt;/a&gt; so that the process was &lt;strong&gt;secure&lt;/strong&gt;, &lt;strong&gt;repeatable&lt;/strong&gt; and &lt;strong&gt;auditable&lt;/strong&gt;. I also wanted to &lt;strong&gt;parameterize&lt;/strong&gt; my pipeline so that I could configure the parameters of the service (such as size of the resources and web site name) outside of version control. This would also allow me to scale the services by tweaking the parameters and simply redeploying.&lt;/li&gt;&lt;li&gt;I wanted to use a &lt;a href=&quot;https://devblogs.microsoft.com/devops/whats-new-with-azure-pipelines/&quot; rel=&quot;noopener&quot;&gt;YAML multi-stage pipeline&lt;/a&gt; so that the pipeline definition was stored in &lt;strong&gt;version&lt;/strong&gt; &lt;strong&gt;control&lt;/strong&gt; (a.k.a. &lt;strong&gt;pipeline as code&lt;/strong&gt;). This also enabled me to break the process of deployment into two stages:&lt;ul&gt;&lt;li&gt;Build - publish a copy of the Azure Resource Manager templates as an artifact.&lt;/li&gt;&lt;li&gt;Deploy to Dev - deploy the resources to Azure using the artifact produced in the build.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;em&gt;Note: I’ve made my version of all these components &lt;strong&gt;public&lt;/strong&gt;, so you can see how everything is built. You can find my Azure DevOps repository &lt;a href=&quot;https://dev.azure.com/dscottraynsford/_git/Sonarqube-Azure&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; and the Azure Pipeline definition &lt;a href=&quot;https://dev.azure.com/dscottraynsford/Sonarqube-Azure/_build&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;step-1-create-a-project-in-azure-devops&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#step-1-create-a-project-in-azure-devops&quot; class=&quot;heading-anchor&quot;&gt;Step 1 - Create a project in Azure DevOps&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;First up we need to have an &lt;strong&gt;Azure DevOps organization&lt;/strong&gt;. You can sign up for a &lt;strong&gt;completely free&lt;/strong&gt; one that will everything you need by going &lt;a href=&quot;https://azure.microsoft.com/en-us/services/devops/&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; and clicking &lt;strong&gt;start free&lt;/strong&gt;. I’m going to assume you have your DevOps organization all set up.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;In your browser, log in to your Azure DevOps organization.&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;+ Create project&lt;/strong&gt; to create a new project.&lt;/li&gt;&lt;li&gt;Enter a &lt;strong&gt;Project Name&lt;/strong&gt; and optionally a &lt;strong&gt;Description&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Select &lt;strong&gt;Public&lt;/strong&gt; if you want to allow anyone to view your project (they can’t contribute or change it). Otherwise leave it as &lt;strong&gt;Private&lt;/strong&gt; to make it only visible to you.&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ZvtWt84eOX-650.webp 650w, https://danielscottraynsford.com/img/ZvtWt84eOX-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ZvtWt84eOX-650.gif&quot; alt=&quot;ss_sonarqube_createproject&quot; width=&quot;960&quot; height=&quot;466&quot; srcset=&quot;https://danielscottraynsford.com/img/ZvtWt84eOX-650.gif 650w, https://danielscottraynsford.com/img/ZvtWt84eOX-960.gif 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;You’ve now got an &lt;strong&gt;Azure&lt;/strong&gt; &lt;strong&gt;Repo&lt;/strong&gt; (version control) as well as a place to create &lt;strong&gt;Azure Pipelines&lt;/strong&gt; as well as a whole lot of other tools, such as Azure Boards, that we’re not going to be using for this project.&lt;/p&gt;&lt;h2 id=&quot;step-2-add-arm-template-files-to-the-repo&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#step-2-add-arm-template-files-to-the-repo&quot; class=&quot;heading-anchor&quot;&gt;Step 2 - Add ARM Template Files to the Repo&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Next, we need to initialize our repository and then add the &lt;strong&gt;Azure Resource Manager&lt;/strong&gt; &lt;strong&gt;(ARM) template&lt;/strong&gt; files and the &lt;strong&gt;Azure Pipeline definition&lt;/strong&gt; (YAML) file. We’re going to be adding all the files to the repository directly in the browser, but if you’re comfortable using Git, then I’d suggest using that.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Select &lt;strong&gt;Repos&lt;/strong&gt; &amp;gt; &lt;strong&gt;Files&lt;/strong&gt; from the nav bar.&lt;/li&gt;&lt;li&gt;Make sure &lt;strong&gt;Add a README&lt;/strong&gt; is ticked and click &lt;strong&gt;Initialize&lt;/strong&gt;.&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/XBOYKDqV3F-650.webp 650w, https://danielscottraynsford.com/img/XBOYKDqV3F-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/XBOYKDqV3F-650.gif&quot; alt=&quot;ss_sonarqube_initializerepo&quot; width=&quot;960&quot; height=&quot;466&quot; srcset=&quot;https://danielscottraynsford.com/img/XBOYKDqV3F-650.gif 650w, https://danielscottraynsford.com/img/XBOYKDqV3F-960.gif 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;ellipsis&lt;/strong&gt; (…) next to the &lt;strong&gt;repo&lt;/strong&gt; name and select &lt;strong&gt;Create a new folder&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Set the &lt;strong&gt;Folder name&lt;/strong&gt; to &lt;strong&gt;infrastructure.&lt;/strong&gt; The name matters because the pipeline definition expects to find the ARM template files in that folder.&lt;/li&gt;&lt;li&gt;Enter a &lt;strong&gt;checkin&lt;/strong&gt; &lt;strong&gt;comment&lt;/strong&gt; of “Added infrastructure folder”.&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Create&lt;/strong&gt;.&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/k8kPS-joEf-650.webp 650w, https://danielscottraynsford.com/img/k8kPS-joEf-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/k8kPS-joEf-650.gif&quot; alt=&quot;ss_sonarqube_createinfrastructurefolder&quot; width=&quot;960&quot; height=&quot;466&quot; srcset=&quot;https://danielscottraynsford.com/img/k8kPS-joEf-650.gif 650w, https://danielscottraynsford.com/img/k8kPS-joEf-960.gif 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Once the folder has been created, we need to add two files to it:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;sonarqube.json&lt;/strong&gt; - The ARM template representing the infrastructure to deploy.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;sonarqube.parameters.json&lt;/strong&gt; - The ARM template default parameters.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Click &lt;a href=&quot;https://dev.azure.com/dscottraynsford/9e9a4415-9d4d-4e6d-bc2f-933025d16ed6/_apis/git/repositories/00b39c50-2a21-408f-9ae2-7e66b7fb2506/Items?path=%2Finfrastructure%2Fsonarqube.json&amp;amp;versionDescriptor%5BversionOptions%5D=0&amp;amp;versionDescriptor%5BversionType%5D=0&amp;amp;versionDescriptor%5Bversion%5D=master&amp;amp;download=true&amp;amp;resolveLfs=true&amp;amp;%24format=octetStream&amp;amp;api-version=5.0-preview.1&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; to download a copy of the sonarqube.json. You can see the content of this file &lt;a href=&quot;https://dev.azure.com/dscottraynsford/_git/Sonarqube-Azure?path=%2Finfrastructure%2Fsonarqube.json&amp;amp;version=GBmaster&amp;amp;line=1&amp;amp;lineStyle=plain&amp;amp;lineEnd=2&amp;amp;lineStartColumn=1&amp;amp;lineEndColumn=1&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Click &lt;a href=&quot;https://dev.azure.com/dscottraynsford/9e9a4415-9d4d-4e6d-bc2f-933025d16ed6/_apis/git/repositories/00b39c50-2a21-408f-9ae2-7e66b7fb2506/Items?path=%2Finfrastructure%2Fsonarqube.parameters.json&amp;amp;versionDescriptor%5BversionOptions%5D=0&amp;amp;versionDescriptor%5BversionType%5D=0&amp;amp;versionDescriptor%5Bversion%5D=master&amp;amp;download=true&amp;amp;resolveLfs=true&amp;amp;%24format=octetStream&amp;amp;api-version=5.0-preview.1&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; to download a copy of the sonarqube.parameters.json. You can see the content of this file &lt;a href=&quot;https://dev.azure.com/dscottraynsford/_git/Sonarqube-Azure?path=%2Finfrastructure%2Fsonarqube.parameters.json&amp;amp;version=GBmaster&amp;amp;line=1&amp;amp;lineStyle=plain&amp;amp;lineEnd=2&amp;amp;lineStartColumn=1&amp;amp;lineEndColumn=1&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;ellipsis (…)&lt;/strong&gt; next to the &lt;strong&gt;infrastructure folder&lt;/strong&gt; and select &lt;strong&gt;Upload file(s)&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;Browse&lt;/strong&gt; button and select the &lt;strong&gt;sonarqube.json&lt;/strong&gt; and &lt;strong&gt;sonarqube.parameters.json&lt;/strong&gt; files you downloaded.&lt;/li&gt;&lt;li&gt;Set the &lt;strong&gt;Comment&lt;/strong&gt; to something like “Added ARM template”.&lt;/li&gt;&lt;li&gt;Ensure &lt;strong&gt;Branch name&lt;/strong&gt; is set to &lt;strong&gt;master&lt;/strong&gt; (it should be if you’re following along).&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Commit&lt;/strong&gt;.&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/xUA1iUj5SA-650.webp 650w, https://danielscottraynsford.com/img/xUA1iUj5SA-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/xUA1iUj5SA-650.gif&quot; alt=&quot;ss_sonarqube_uploadarmtemplate&quot; width=&quot;960&quot; height=&quot;466&quot; srcset=&quot;https://danielscottraynsford.com/img/xUA1iUj5SA-650.gif 650w, https://danielscottraynsford.com/img/xUA1iUj5SA-960.gif 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;We’ve now got the ARM Template in the repository and under version control. So we can track any changes to them.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; When we created the &lt;strong&gt;infrastructure&lt;/strong&gt; folder through the Azure DevOps portal a file called _PlaceHolderFile.md was automatically created. This is created because Git doesn’t allow storing empty folders. You can safely delete this file from your repo if you want.&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;step-3-create-your-multi-stage-build-pipeline&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#step-3-create-your-multi-stage-build-pipeline&quot; class=&quot;heading-anchor&quot;&gt;Step 3 - Create your Multi-stage Build Pipeline&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Now that we’ve got a repository we can create our &lt;strong&gt;mulit-stage build pipeline&lt;/strong&gt;. This build pipeline will package the &lt;strong&gt;infrastructure files&lt;/strong&gt; and store them and then perform a deployment. The &lt;strong&gt;multi-stage build&lt;/strong&gt; &lt;strong&gt;pipeline&lt;/strong&gt; is defined in a file called &lt;strong&gt;azure-pipelines.yml&lt;/strong&gt; that we’ll put into the root folde of our repository.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Click &lt;a href=&quot;https://dev.azure.com/dscottraynsford/9e9a4415-9d4d-4e6d-bc2f-933025d16ed6/_apis/git/repositories/00b39c50-2a21-408f-9ae2-7e66b7fb2506/Items?path=%2Fazure-pipelines.yml&amp;amp;versionDescriptor%5BversionOptions%5D=0&amp;amp;versionDescriptor%5BversionType%5D=0&amp;amp;versionDescriptor%5Bversion%5D=master&amp;amp;download=true&amp;amp;resolveLfs=true&amp;amp;%24format=octetStream&amp;amp;api-version=5.0-preview.1&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; to download a copy of the &lt;strong&gt;azure-pipelines.yml&lt;/strong&gt;. You can see the content of this file &lt;a href=&quot;https://dev.azure.com/dscottraynsford/_git/Sonarqube-Azure?path=%2Fazure-pipelines.yml&amp;amp;version=GBmaster&amp;amp;line=1&amp;amp;lineStyle=plain&amp;amp;lineEnd=2&amp;amp;lineStartColumn=1&amp;amp;lineEndColumn=1&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;ellipsis (…)&lt;/strong&gt; button next to the repository name and select &lt;strong&gt;Upload file(s)&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Browse&lt;/strong&gt; and select the &lt;strong&gt;azure-pipelines.yml&lt;/strong&gt; file you dowloaded.&lt;/li&gt;&lt;li&gt;Set the &lt;strong&gt;Comment&lt;/strong&gt; to something like “Added Pipeline Defnition”.&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Commit&lt;/strong&gt;.&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/7tdHZzSb6v-650.webp 650w, https://danielscottraynsford.com/img/7tdHZzSb6v-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/7tdHZzSb6v-650.gif&quot; alt=&quot;ss_sonarqube_uploadpipelinefile&quot; width=&quot;960&quot; height=&quot;466&quot; srcset=&quot;https://danielscottraynsford.com/img/7tdHZzSb6v-650.gif 650w, https://danielscottraynsford.com/img/7tdHZzSb6v-960.gif 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Set up build&lt;/strong&gt; button.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Azure Pipelines&lt;/strong&gt; will automatically detect the &lt;strong&gt;azure-pipelines.yml&lt;/strong&gt; file in the root of our repository and configure our pipeline.&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;Run&lt;/strong&gt; button. The build will fail because we haven’t yet created the &lt;strong&gt;service connection&lt;/strong&gt; called &lt;strong&gt;Sonarqube-Azure&lt;/strong&gt; to allow our pipeline to deploy to Azure. We also still still need to configure the &lt;strong&gt;parameters&lt;/strong&gt; for the pipeline.&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/cVq0HVbid0-650.webp 650w, https://danielscottraynsford.com/img/cVq0HVbid0-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/cVq0HVbid0-650.gif&quot; alt=&quot;ss_sonarqube_createbuildpipeline&quot; width=&quot;960&quot; height=&quot;466&quot; srcset=&quot;https://danielscottraynsford.com/img/cVq0HVbid0-650.gif 650w, https://danielscottraynsford.com/img/cVq0HVbid0-960.gif 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;em&gt;Note: I’ll break down the contents of the &lt;strong&gt;azure-pipelines.yml&lt;/strong&gt; at the end of this post so you get a feel for how a &lt;strong&gt;multi-stage build pipeline&lt;/strong&gt; can be defined.&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;step-4-create-service-connection-to-azure&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#step-4-create-service-connection-to-azure&quot; class=&quot;heading-anchor&quot;&gt;Step 4 - Create Service Connection to Azure&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;For &lt;strong&gt;Azure Pipelines&lt;/strong&gt; to be able to deploy to Azure (or access other external services) it needs a &lt;strong&gt;service connection&lt;/strong&gt; defined. In this step we’ll configure the service sonnection called &lt;strong&gt;Sonarqube-Azure&lt;/strong&gt; that is referred to in the &lt;strong&gt;azure-pipelines.yml&lt;/strong&gt; file. I won’t go into too much detail about what happens when we create a service connection as Azure Pipelines takes care of the details for you, but if you want to know more, read &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/library/connect-to-azure?view=azure-devops&quot; rel=&quot;noopener&quot;&gt;this page&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Important: This step assumes you have permissions to create &lt;strong&gt;service connections&lt;/strong&gt; in the project and permissions to Azure to create a new Serivce Principal account with contributor permissions within the subscription. Many users won’t have this, so you might need to get a user with the enough permissions to the Azure subscription to do this step for you.&lt;/em&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Click the &lt;strong&gt;Project settings&lt;/strong&gt; button in your project.&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Service connections&lt;/strong&gt; under the &lt;strong&gt;Pipelines&lt;/strong&gt; section.&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;New service connection&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Select &lt;strong&gt;Azure Resource Manager&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Make sure &lt;strong&gt;Service Principal Authentication&lt;/strong&gt; is selected.&lt;/li&gt;&lt;li&gt;Enter &lt;strong&gt;Sonarqube-Azure&lt;/strong&gt; for the &lt;strong&gt;Connection name&lt;/strong&gt;. &lt;em&gt;This must be exact, otherwise it won’t match the value in the &lt;strong&gt;azure-pipelines.yml&lt;/strong&gt; file.&lt;/em&gt;&lt;/li&gt;&lt;li&gt;Set &lt;strong&gt;Scope level&lt;/strong&gt; to &lt;strong&gt;Subscription&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;From the &lt;strong&gt;Subscription&lt;/strong&gt; box, select your &lt;strong&gt;Azure Subscription&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Make sure the &lt;strong&gt;Resource group&lt;/strong&gt; box is empty.&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;An authorization box will pop up requesting that you authenticate with the Azure subscription you want to deploy to.&lt;/li&gt;&lt;li&gt;Enter the account details of a user who has &lt;strong&gt;permissions&lt;/strong&gt; to create a &lt;strong&gt;Service Principal&lt;/strong&gt; with &lt;strong&gt;contributor&lt;/strong&gt; access to the &lt;strong&gt;subscription&lt;/strong&gt; selected above**.**&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/MPyYPzux3u-650.webp 650w, https://danielscottraynsford.com/img/MPyYPzux3u-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/MPyYPzux3u-650.gif&quot; alt=&quot;ss_sonarqube_createserviceconnection&quot; width=&quot;960&quot; height=&quot;466&quot; srcset=&quot;https://danielscottraynsford.com/img/MPyYPzux3u-650.gif 650w, https://danielscottraynsford.com/img/MPyYPzux3u-960.gif 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;You now have a &lt;strong&gt;service connection&lt;/strong&gt; to Azure that any build pipeline (including the one we created earlier) in this project can use to deploy services to Azure.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: You can restrict the use of this &lt;strong&gt;Service connection&lt;/strong&gt; by changing the Roles on the &lt;strong&gt;Service connection&lt;/strong&gt;. See &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&amp;amp;tabs=yaml#secure-a-service-connection&quot; rel=&quot;noopener&quot;&gt;this page&lt;/a&gt; for more information.&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;step-5-configure-pipeline-parameters&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#step-5-configure-pipeline-parameters&quot; class=&quot;heading-anchor&quot;&gt;Step 5 - Configure Pipeline Parameters&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The &lt;strong&gt;ARM template&lt;/strong&gt; contains a number of parameters which allow us to configure some of the things about the Azure resources we’re going to deploy, such as the Location (data center) to deploy to, the size of the resources and the site name our Sonarqube service will be exposed on.&lt;/p&gt;&lt;p&gt;In the &lt;strong&gt;azure-pipelines.yml&lt;/strong&gt; file we &lt;a href=&quot;https://dev.azure.com/dscottraynsford/_git/Sonarqube-Azure?path=%2Fazure-pipelines.yml&amp;amp;version=GBmaster&quot; rel=&quot;noopener&quot;&gt;configure the parameters&lt;/a&gt; that are passed to the ARM template from &lt;strong&gt;pipeline&lt;/strong&gt; &lt;strong&gt;variables&lt;/strong&gt;. &lt;em&gt;Note: There are additional ARM template parameters that are exposed (such as&lt;/em&gt; &lt;em&gt;sqlDatabaseSkuSizeGB and sonarqubeImageVersion), but we’ll leave the configuration of those parameters as a seprate exercise.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;The parameters that are exposed as &lt;strong&gt;pipeline variables&lt;/strong&gt; are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;siteName&lt;/strong&gt; - The name of the web site. This will result in the Sonarqube web site being hosted at [&lt;em&gt;siteName&lt;/em&gt;].azurewebsites.net.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;sqlServerAdministratorUsername&lt;/strong&gt; - The administrator username that will be used to administer this SQL database and for the Sonarqube front end to connct using. &lt;em&gt;Note: for a Production service we should actually create another account for Sonarqube to use.&lt;/em&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;sqlServerAdministratorPassword&lt;/strong&gt; - The password that will be used by Sonarqube to connect to the database.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;servicePlanCapacity&lt;/strong&gt; - The number of App Service plan nodes to use to run this Sonarqube service. Recommend leaving it at 1 unless you’ve got really heavy load.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;servicePlanPricingTier&lt;/strong&gt; - This is the App Service plan pricing tier to use for the service. Suggest S1 for testing, but for systems requiring greater performance then S2, S3, P1V2, P2V2 or P3V2.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;sqlDatabaseSkuName&lt;/strong&gt; - this is the performance of the SQL Server. There are a number of different performance options &lt;a href=&quot;https://dev.azure.com/dscottraynsford/_git/Sonarqube-Azure?path=%2Finfrastructure%2Fsonarqube.json&amp;amp;version=GBmaster&amp;amp;line=63&amp;amp;lineStyle=plain&amp;amp;lineEnd=64&amp;amp;lineStartColumn=1&amp;amp;lineEndColumn=1&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; and what you chose will need to depend on your load.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;location&lt;/strong&gt; - this is the code for the data center to deploy to. I use &lt;strong&gt;WestUS2&lt;/strong&gt;, but chose whatever datacenter you wish.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The great thing is, you can change these variables at any time and then run your pipeline again and your infrastructure will be changed (scaled up/down/in/out) accordingly - without losing data. &lt;em&gt;Note: You can’t change location or siteName after first deployment however.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;To create your variables:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Click &lt;strong&gt;Pipelines&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;SonarqubeInAzure&lt;/strong&gt; pipeline.&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;Edit&lt;/strong&gt; button.&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;menu&lt;/strong&gt; button (vertical &lt;strong&gt;ellipsis&lt;/strong&gt;) and select &lt;strong&gt;Variables&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;Add&lt;/strong&gt; button and add the following parameters and values:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;siteName&lt;/strong&gt; - The globally unique name for your site. This will deploy the service to [&lt;em&gt;siteName&lt;/em&gt;].azurewebsites.net. If this does not result in a globally unique name an error will occur during deployment.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;sqlServerAdministratorUsername&lt;/strong&gt; - Set to &lt;strong&gt;sonarqube&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;sqlServerAdministratorPassword&lt;/strong&gt; - Set to a strong password consisting of at least 8 characters including upper and lower case, numbers and symbols. &lt;em&gt;Make sure you click the &lt;strong&gt;lock symbol&lt;/strong&gt; to let Azure DevOps know this is a password and to treat it accordignly.&lt;/em&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;servicePlanCapacity&lt;/strong&gt; - Set to &lt;strong&gt;1&lt;/strong&gt; for now (you can always change and scale up later).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;servicePlanPricingTier&lt;/strong&gt; - Set to &lt;strong&gt;S1&lt;/strong&gt; for now (you can always change and scale up later).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;sqlDatabaseSkuName&lt;/strong&gt; - Set to &lt;strong&gt;GP_Gen5_2&lt;/strong&gt; for now (you can always change and scale up later). &lt;em&gt;If you want to use the SQL Serverless database, use &lt;strong&gt;GP_S_Gen5_1&lt;/strong&gt;, &lt;strong&gt;GP_S_Gen5_2&lt;/strong&gt; or &lt;strong&gt;GP_S_Gen5_4&lt;/strong&gt;.&lt;/em&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;location&lt;/strong&gt; - set to &lt;strong&gt;WestUS2&lt;/strong&gt; or whatever the code is for your preferred data center.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;You can also click the &lt;strong&gt;Settable at Queue&lt;/strong&gt; time box against any of the parameters you want to be able to set when the job is manually queued.&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/3yGtbKGc_J-650.webp 650w, https://danielscottraynsford.com/img/3yGtbKGc_J-960.webp 960w, https://danielscottraynsford.com/img/3yGtbKGc_J-1320.webp 1320w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/3yGtbKGc_J-650.gif&quot; alt=&quot;ss_sonarqube_createvariables&quot; width=&quot;1320&quot; height=&quot;618&quot; srcset=&quot;https://danielscottraynsford.com/img/3yGtbKGc_J-650.gif 650w, https://danielscottraynsford.com/img/3yGtbKGc_J-960.gif 960w, https://danielscottraynsford.com/img/3yGtbKGc_J-1320.gif 1320w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/FkZYmNe-1h-650.webp 650w, https://danielscottraynsford.com/img/FkZYmNe-1h-960.webp 960w, https://danielscottraynsford.com/img/FkZYmNe-1h-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/FkZYmNe-1h-650.png&quot; alt=&quot;ss_sonarqube_variables&quot; width=&quot;1400&quot; height=&quot;596&quot; srcset=&quot;https://danielscottraynsford.com/img/FkZYmNe-1h-650.png 650w, https://danielscottraynsford.com/img/FkZYmNe-1h-960.png 960w, https://danielscottraynsford.com/img/FkZYmNe-1h-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;Save and Queue&lt;/strong&gt; button and select &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;We are now ready to deploy our service by triggering the pipeline.&lt;/p&gt;&lt;h2 id=&quot;step-6-run-the-pipeline&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#step-6-run-the-pipeline&quot; class=&quot;heading-anchor&quot;&gt;Step 6 - Run the Pipeline&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The most common way an &lt;strong&gt;Azure Pipeline&lt;/strong&gt; is going to get &lt;strong&gt;triggered&lt;/strong&gt; is by &lt;strong&gt;committing a change&lt;/strong&gt; to the repository the build pipeline is linked to. But in this case we are just going to trigger a &lt;strong&gt;manual build&lt;/strong&gt;:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Click &lt;strong&gt;Pipelines&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;SonarqubeInAzure&lt;/strong&gt; pipeline.&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;Run pipeline&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Set any of the variables we want to change (for example if we wanted to scale up our services).&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Run&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;You can then watch the &lt;strong&gt;build&lt;/strong&gt; and &lt;strong&gt;deploy&lt;/strong&gt; stages complete.&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/V08UstJ1BG-650.webp 650w, https://danielscottraynsford.com/img/V08UstJ1BG-960.webp 960w, https://danielscottraynsford.com/img/V08UstJ1BG-1320.webp 1320w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/V08UstJ1BG-650.gif&quot; alt=&quot;ss_sonarqube_runpipeline.gif&quot; width=&quot;1320&quot; height=&quot;618&quot; srcset=&quot;https://danielscottraynsford.com/img/V08UstJ1BG-650.gif 650w, https://danielscottraynsford.com/img/V08UstJ1BG-960.gif 960w, https://danielscottraynsford.com/img/V08UstJ1BG-1320.gif 1320w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Your &lt;strong&gt;pipeline&lt;/strong&gt; should have completed and your resources will be on thier way to being deployed to Azure. You can rerun this pipeline at any time with different variables to scale your services. You could even delete the front end app service completely and use this pipeline to redeploy the service again - saving lots of precious $$$.&lt;/p&gt;&lt;h2 id=&quot;step-7-checkout-your-new-sonarqube-service&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#step-7-checkout-your-new-sonarqube-service&quot; class=&quot;heading-anchor&quot;&gt;Step 7 - Checkout your new Sonarqube Service&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;You can login to the Azure Portal to see the new resource group and resources that have been deployed.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Open the &lt;a href=&quot;https://portal.azure.com&quot; rel=&quot;noopener&quot;&gt;Azure portal&lt;/a&gt; and log in.&lt;/li&gt;&lt;li&gt;You will see a new resource group named &lt;strong&gt;[siteName]-rg&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Open the &lt;strong&gt;[siteName]-rg&lt;/strong&gt;.&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/4uFHI8ymrU-650.webp 650w, https://danielscottraynsford.com/img/4uFHI8ymrU-960.webp 960w, https://danielscottraynsford.com/img/4uFHI8ymrU-1324.webp 1324w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/4uFHI8ymrU-650.png&quot; alt=&quot;ss_sonarqube_resources&quot; width=&quot;1324&quot; height=&quot;731&quot; srcset=&quot;https://danielscottraynsford.com/img/4uFHI8ymrU-650.png 650w, https://danielscottraynsford.com/img/4uFHI8ymrU-960.png 960w, https://danielscottraynsford.com/img/4uFHI8ymrU-1324.png 1324w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Select the Web App with the name &lt;strong&gt;[siteName]&lt;/strong&gt;.&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/XB-rN7fB7l-650.webp 650w, https://danielscottraynsford.com/img/XB-rN7fB7l-960.webp 960w, https://danielscottraynsford.com/img/XB-rN7fB7l-1324.webp 1324w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/XB-rN7fB7l-650.png&quot; alt=&quot;ss_sonarqube_webapp&quot; width=&quot;1324&quot; height=&quot;731&quot; srcset=&quot;https://danielscottraynsford.com/img/XB-rN7fB7l-650.png 650w, https://danielscottraynsford.com/img/XB-rN7fB7l-960.png 960w, https://danielscottraynsford.com/img/XB-rN7fB7l-1324.png 1324w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;URL&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Your &lt;strong&gt;Sonarqube&lt;/strong&gt; application will open after a few seconds.&lt;br&gt;&lt;em&gt;Note: It may take a little while to load the first time depending on the performance you configured on your SQL database.&lt;/em&gt;&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/4YnmI7fIZB-650.webp 650w, https://danielscottraynsford.com/img/4YnmI7fIZB-960.webp 960w, https://danielscottraynsford.com/img/4YnmI7fIZB-1324.webp 1324w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/4YnmI7fIZB-650.png&quot; alt=&quot;ss_sonarqube_theapplication&quot; width=&quot;1324&quot; height=&quot;731&quot; srcset=&quot;https://danielscottraynsford.com/img/4YnmI7fIZB-650.png 650w, https://danielscottraynsford.com/img/4YnmI7fIZB-960.png 960w, https://danielscottraynsford.com/img/4YnmI7fIZB-1324.png 1324w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Log in to Sonarqube with the username &lt;strong&gt;admin&lt;/strong&gt; and the password &lt;strong&gt;admin&lt;/strong&gt;.&lt;br&gt;&lt;strong&gt;Remember to change this password immediately.&lt;/strong&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;You are now ready to use Sonarqube in your build pipelines.&lt;/p&gt;&lt;h2 id=&quot;step-8-scaling-your-sonarqube-services&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#step-8-scaling-your-sonarqube-services&quot; class=&quot;heading-anchor&quot;&gt;Step 8 - Scaling your Sonarqube Services&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;One of the purposes of this process was to enable the resources to be scaled easily and non-destructively. All we need to do is:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Click &lt;strong&gt;Pipelines&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;SonarqubeInAzure&lt;/strong&gt; pipeline.&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Run pipeline&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Adjust any variables to scale the service up, down, in, or out.&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Run&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Watch the &lt;strong&gt;build&lt;/strong&gt; and &lt;strong&gt;deploy&lt;/strong&gt; stages complete.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Of course you could do a lot of the scaling with &lt;strong&gt;Azure Automation&lt;/strong&gt;, which is a better idea in the long term than using your build pipeline to scale the services because you’ll end up with hundreds of deployment records over time.&lt;/p&gt;&lt;h2 id=&quot;a-closer-look-at-the-multi-stage-build-pipeline-yaml&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#a-closer-look-at-the-multi-stage-build-pipeline-yaml&quot; class=&quot;heading-anchor&quot;&gt;A Closer look at the Multi-stage Build Pipeline YAML&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;At the time of writing this post, the &lt;strong&gt;Multi-stage Build Pipeline YAML&lt;/strong&gt; was relatively new and still in a preview state. This means that it is not fully documented. So, I’ll break down the file and highlight the interesting pieces:&lt;/p&gt;&lt;h3 id=&quot;trigger&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#trigger&quot; class=&quot;heading-anchor&quot;&gt;Trigger&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/B-HztQHlLE-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/B-HztQHlLE-650.png&quot; alt=&quot;ss_sonarqube_yamltrigger&quot; width=&quot;650&quot; height=&quot;67&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This section ensures the pipeline will only be triggered on changes to the &lt;strong&gt;master&lt;/strong&gt; branch.&lt;/p&gt;&lt;h3 id=&quot;stages&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#stages&quot; class=&quot;heading-anchor&quot;&gt;Stages&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ZZGnfcF8oI-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ZZGnfcF8oI-650.png&quot; alt=&quot;ss_sonarqube_yamlstages&quot; width=&quot;650&quot; height=&quot;16&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This section contains the two stages: &lt;strong&gt;Build&lt;/strong&gt; and &lt;strong&gt;Deploy&lt;/strong&gt;. We could have as many stages as we like. For example: Build, Deploy Test, Deploy Prod.&lt;/p&gt;&lt;h3 id=&quot;build-stage&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#build-stage&quot; class=&quot;heading-anchor&quot;&gt;Build Stage&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/AD91Qi3tl_-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/AD91Qi3tl_-650.png&quot; alt=&quot;ss_sonarqube_yamlbuildstage&quot; width=&quot;650&quot; height=&quot;157&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This defines the steps to run in the build stage. It also requires the execution of the stage on an &lt;strong&gt;Azure DevOps&lt;/strong&gt; agent in the &lt;strong&gt;vs2017-win2016&lt;/strong&gt; pool.&lt;/p&gt;&lt;h3 id=&quot;build-stage-checkout-step&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#build-stage-checkout-step&quot; class=&quot;heading-anchor&quot;&gt;Build Stage Checkout Step&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Bfk-Yk4fzw-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Bfk-Yk4fzw-650.png&quot; alt=&quot;ss_sonarqube_yamlbuildcheckout&quot; width=&quot;650&quot; height=&quot;36&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This step causes the &lt;strong&gt;repository&lt;/strong&gt; to be checked out onto the &lt;strong&gt;Azure DevOps&lt;/strong&gt; agent.&lt;/p&gt;&lt;h3 id=&quot;build-stage-publish-artifacts-step&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#build-stage-publish-artifacts-step&quot; class=&quot;heading-anchor&quot;&gt;Build Stage Publish Artifacts Step&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/a6ywqfv-6H-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/a6ywqfv-6H-650.png&quot; alt=&quot;ss_sonarqube_yamlbuildpublish&quot; width=&quot;650&quot; height=&quot;88&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This step takes the &lt;strong&gt;infrastructure&lt;/strong&gt; folder from the checked out &lt;strong&gt;repository&lt;/strong&gt; and stores it as an artifact that will always be accessible as long as the build record is stored. The artifact will also be made available to the next stage (the &lt;strong&gt;Deploy Stage&lt;/strong&gt;). The purpose of this step is to ensure we have an &lt;strong&gt;immutable artifact&lt;/strong&gt; available that we could always use to redeploy this exact build.&lt;/p&gt;&lt;h3 id=&quot;deploy-stage&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#deploy-stage&quot; class=&quot;heading-anchor&quot;&gt;Deploy Stage&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/PL224zQT5x-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/PL224zQT5x-650.png&quot; alt=&quot;ss_sonarqube_yamldeploystage&quot; width=&quot;650&quot; height=&quot;211&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;The deploy stage takes the artifact produced in the &lt;strong&gt;build stage&lt;/strong&gt; and deploys it. It runs on an Azure DevOps agent in the &lt;strong&gt;vs2017-win2016&lt;/strong&gt; pool.&lt;/p&gt;&lt;p&gt;It also specifies that this is a deployment to an environment called “&lt;strong&gt;dev&lt;/strong&gt;”. This will cause the environment to show up in the &lt;strong&gt;environments&lt;/strong&gt; section under &lt;strong&gt;pipelines&lt;/strong&gt; in &lt;strong&gt;Azure DevOps&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/cfNOM5kzmt-650.webp 650w, https://danielscottraynsford.com/img/cfNOM5kzmt-960.webp 960w, https://danielscottraynsford.com/img/cfNOM5kzmt-1324.webp 1324w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/cfNOM5kzmt-650.png&quot; alt=&quot;ss_sonarqube_environments.png&quot; width=&quot;1324&quot; height=&quot;731&quot; srcset=&quot;https://danielscottraynsford.com/img/cfNOM5kzmt-650.png 650w, https://danielscottraynsford.com/img/cfNOM5kzmt-960.png 960w, https://danielscottraynsford.com/img/cfNOM5kzmt-1324.png 1324w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;strategy&lt;/strong&gt; and &lt;strong&gt;runOnce&lt;/strong&gt; define that this deployment should only execute once each time the pipeline is triggered.&lt;/p&gt;&lt;h3 id=&quot;deploy-stage-azure-resource-group-deployment-step&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#deploy-stage-azure-resource-group-deployment-step&quot; class=&quot;heading-anchor&quot;&gt;Deploy Stage Azure Resource Group Deployment Step&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/yS9A7hRtka-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/yS9A7hRtka-650.png&quot; alt=&quot;ss_sonarqube_yamldeploystep&quot; width=&quot;650&quot; height=&quot;324&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This deploy step takes the ARM template from the &lt;strong&gt;infrastructure&lt;/strong&gt; artifact and deploys it to the &lt;strong&gt;Sonarqube-Azure&lt;/strong&gt; Service connection. It overrides the parameters (using the &lt;strong&gt;overrideParameters&lt;/strong&gt;) property using &lt;strong&gt;build variables&lt;/strong&gt; (e.g. &lt;em&gt;$(siteName)&lt;/em&gt;, &lt;em&gt;$(servicePlanCapacity)&lt;/em&gt;).&lt;/p&gt;&lt;h2 id=&quot;but-what-about-azure-blueprints&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#but-what-about-azure-blueprints&quot; class=&quot;heading-anchor&quot;&gt;But what about Azure Blueprints?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;One final thing to consider: this deployment could be a great use case for implementing with &lt;a href=&quot;https://azure.microsoft.com/en-in/services/blueprints/&quot; rel=&quot;noopener&quot;&gt;Azure Blueprints&lt;/a&gt;. I would strongly suggest taking a look at using your build pipeline to deploy an Azure Blueprint containing the ARM template above.&lt;/p&gt;&lt;p&gt;Thank you very much for reading this and I hope you found it interesting.&lt;/p&gt;&lt;h3 id=&quot;installing-the-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline/#installing-the-resource&quot; class=&quot;heading-anchor&quot;&gt;Installing the Resource&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;If you have installed &lt;strong&gt;WMF 5.0&lt;/strong&gt; you can just download this&amp;nbsp;directly from the &lt;a href=&quot;https://www.powershellgallery.com/&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt; by running this command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;src&#92;posts&#92;2019&#92;06&#92;2019-06-03-deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline.md&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name Az &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AllowClobber &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Scope CurrentUser&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can use the Azure CLI as well if you prefer:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;src&#92;posts&#92;2019&#92;06&#92;2019-06-03-deploy-sonarqube-to-azure-app-service-linux-containers-using-an-azure-devops-pipeline.md&lt;/span&gt;
az login
az &lt;span class=&quot;token function&quot;&gt;group&lt;/span&gt; create &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;name &amp;lt;resource-&lt;span class=&quot;token function&quot;&gt;group-name&lt;/span&gt;&amp;gt; &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;location &amp;lt;location&amp;gt;
az deployment &lt;span class=&quot;token function&quot;&gt;group&lt;/span&gt; create &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;resource-&lt;span class=&quot;token function&quot;&gt;group&lt;/span&gt; &amp;lt;resource-&lt;span class=&quot;token function&quot;&gt;group-name&lt;/span&gt;&amp;gt; &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;template-file infrastructure/sonarqube&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;parameters infrastructure/sonarqube&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json&lt;/code&gt;&lt;/pre&gt;
 			</content>
    </entry><entry>
      <title>Azure Resource Manager Templates Hands-on Lab and #GlobalAzure 2019</title>
      <link href="https://danielscottraynsford.com/blog/azure-resource-manager-templates-hands-on-lab-and-globalazure-2019/" />
      <updated>2019-05-04T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/azure-resource-manager-templates-hands-on-lab-and-globalazure-2019/</id>
      <content type="html">
				&lt;p&gt;Recently I helped organize and present at the 2019 &lt;a href=&quot;https://global.azurebootcamp.net/&quot; rel=&quot;noopener&quot;&gt;Global Azure Bootcamp&lt;/a&gt; in Auckland. The Global Azure Bootcamp is an huge event run by Azure communities throughout the world all on the same day every year. It is an opportunity for anyone with an interest in Azure to come and learn from experts and presenters and share their knowledge. If you’re new to Azure or even if you’re an expert it is well worth your time to attend these free events.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/K44KSEhwHT-650.webp 650w, https://danielscottraynsford.com/img/K44KSEhwHT-960.webp 960w, https://danielscottraynsford.com/img/K44KSEhwHT-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/K44KSEhwHT-650.jpeg&quot; alt=&quot;AucklandGAB2019-1&quot; width=&quot;1400&quot; height=&quot;1050&quot; srcset=&quot;https://danielscottraynsford.com/img/K44KSEhwHT-650.jpeg 650w, https://danielscottraynsford.com/img/K44KSEhwHT-960.jpeg 960w, https://danielscottraynsford.com/img/K44KSEhwHT-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;Global Azure Bootcamp&lt;/strong&gt; is also an awful lot of fun to be a part of and I got to meet some fantastic people!&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://twitter.com/i/status/1122002366041968640&quot; rel=&quot;noopener&quot;&gt;https://twitter.com/i/status/1122002366041968640&lt;/a&gt;&lt;/p&gt;&lt;p&gt;We also got to contribute to the &lt;a href=&quot;https://global.azurebootcamp.net/global-azure-science-lab-2019/&quot; rel=&quot;noopener&quot;&gt;Global Azure Bootcamp Science lab&lt;/a&gt;, which was a really great way to learn Azure as well as contribute to the goal of finding potential exosolar planets (how cool is that?). A global &lt;a href=&quot;https://gablabdashboard.azurewebsites.net/&quot; rel=&quot;noopener&quot;&gt;dashboard&lt;/a&gt; was made available where all locations could compare their contributions. The Auckland Team did fantastically well, given the size of Auckland comparatively: We managed to get 8th on the &lt;a href=&quot;https://gablabdashboard.azurewebsites.net/Results/Teams&quot; rel=&quot;noopener&quot;&gt;team leaderboard&lt;/a&gt;:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/2KUgeDOmFw-650.webp 650w, https://danielscottraynsford.com/img/2KUgeDOmFw-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/2KUgeDOmFw-650.png&quot; alt=&quot;AucklandGAB2019-TeamLeaderboard&quot; width=&quot;960&quot; height=&quot;583&quot; srcset=&quot;https://danielscottraynsford.com/img/2KUgeDOmFw-650.png 650w, https://danielscottraynsford.com/img/2KUgeDOmFw-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h2 id=&quot;hands-on-workshop-material&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/azure-resource-manager-templates-hands-on-lab-and-globalazure-2019/#hands-on-workshop-material&quot; class=&quot;heading-anchor&quot;&gt;Hands-On Workshop Material&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As part of my session this year, I produced a &lt;strong&gt;Hands-on workshop&lt;/strong&gt; and &lt;strong&gt;presentation&lt;/strong&gt; showing attendees the basics of using &lt;strong&gt;Azure Resource Manager templates&lt;/strong&gt; as well as some of the more advanced topics such as linked/nested templates and security.&lt;/p&gt;&lt;p&gt;The topics covered are:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/U_vwwN9bbi-650.webp 650w, https://danielscottraynsford.com/img/U_vwwN9bbi-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/U_vwwN9bbi-650.png&quot; alt=&quot;AucklandGAB2019-ARMTemplatesWorkshop&quot; width=&quot;960&quot; height=&quot;540&quot; srcset=&quot;https://danielscottraynsford.com/img/U_vwwN9bbi-650.png 650w, https://danielscottraynsford.com/img/U_vwwN9bbi-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;I’ve made all of this material &lt;strong&gt;open&lt;/strong&gt; and &lt;strong&gt;free for the community&lt;/strong&gt; to use to run your own sessions or modify and improve.&lt;/p&gt;&lt;p&gt;You can find the material in GitHub here:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/PlagueHO/Workshop-ARM-Templates&quot; rel=&quot;noopener&quot;&gt;https://github.com/PlagueHO/Workshop-ARM-Templates&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Thanks for reading and hope to see some of you at a future &lt;strong&gt;Global Azure Bootcamp&lt;/strong&gt;!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Allow Integer Parameter to Accept Null in a PowerShell Function</title>
      <link href="https://danielscottraynsford.com/blog/allow-integer-parameter-to-accept-null-in-a-powershell-function/" />
      <updated>2019-04-06T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/allow-integer-parameter-to-accept-null-in-a-powershell-function/</id>
      <content type="html">
				&lt;p&gt;One of the great things about PowerShell being based on .NET is that we get access to the huge number of types built into the framework.&lt;/p&gt;&lt;p&gt;A problem I came across today was that I needed to have a function that took a mandatory integer parameter, but that parameter needed to allow &lt;code&gt;Null&lt;/code&gt;. In .NET, there is a generic type &lt;code&gt;System.Nullable&amp;lt;T&amp;gt;&lt;/code&gt; that allows other types to take on a null value.&lt;/p&gt;&lt;p&gt;Here’s how I solved it:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;src&#92;posts&#92;2019&#92;04&#92;2019-04-06-allow-integer-parameter-to-accept-null-in-a-powershell-function.md&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Set-AdapterVlan&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[CmdletBinding()]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $true)]&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Adapter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $true)]&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[AllowNull()]&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[Nullable[System.Int32]]&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$VlanId&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$VlanId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# If VlanId is null, clear the VLAN ID from the adapter&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Adapter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Set-VMNetworkAdapterVlan&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Untagged
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Otherwise, set the VLAN ID&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Adapter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Set-VMNetworkAdapterVlan&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VlanId &lt;span class=&quot;token variable&quot;&gt;$VlanId&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Access
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This allows me to call the function above like this:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Set-AdapterVlan&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Adapter &lt;span class=&quot;token variable&quot;&gt;$adapter&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VlanId &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Which will clear the VLAN ID from the virtual network adapter.&lt;/p&gt;&lt;p&gt;The magic is in the parameter definition:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $true)]&lt;/span&gt;
&lt;span class=&quot;token namespace&quot;&gt;[AllowNull()]&lt;/span&gt;
&lt;span class=&quot;token namespace&quot;&gt;[Nullable[System.Int32]]&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$VlanId&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;[AllowNull()]&lt;/code&gt; attribute allows the &lt;code&gt;$VlanId&lt;/code&gt; parameter to accept a null even though it is mandatory, and the &lt;code&gt;[Nullable[System.Int32]]&lt;/code&gt; allows &lt;code&gt;$VlanId&lt;/code&gt; to be assigned a null value.&lt;/p&gt;&lt;p&gt;This isn’t something I use often, but I thought it was worth sharing!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Enable CORS Support in Cosmos DB using PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/enable-cors-support-in-cosmos-db-using-powershell/" />
      <updated>2018-12-26T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/enable-cors-support-in-cosmos-db-using-powershell/</id>
      <content type="html">
				&lt;p&gt;Support for &lt;strong&gt;Cross-Origin Resource Sharing (CORS)&lt;/strong&gt; was &lt;a href=&quot;https://azure.microsoft.com/en-us/blog/azure-cosmos-now-supports-cross-origin-resource-sharing-cors/&quot; rel=&quot;noopener&quot;&gt;recently added&lt;/a&gt; to Cosmos DB. If you want to enable CORS on an existing Cosmos DB account or create a new Cosmos DB account with CORS enabled it is very easy to do with &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/cosmos-db/how-to-configure-cross-origin-resource-sharing#enable-cors-support-from-resource-manager-template&quot; rel=&quot;noopener&quot;&gt;Azure Resource Manager&lt;/a&gt; (ARM) templates or the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/cosmos-db/how-to-configure-cross-origin-resource-sharing#enable-cors-support-from-azure-portal&quot; rel=&quot;noopener&quot;&gt;Azure Portal&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;But what if you’re wanting to find out the state of the CORS setting on an account or set it using PowerShell? Well, look no further.&lt;/p&gt;&lt;p&gt;The &lt;a href=&quot;https://www.powershellgallery.com/packages/CosmosDB&quot; rel=&quot;noopener&quot;&gt;Cosmos DB PowerShell module&lt;/a&gt; (version 3.0.0 and above) supports creating Cosmos DB accounts with CORS enabled as well as updating and removing the CORS headers setting on an existing account. You can also retrieve the CORS setting for an existing Cosmos DB account.&lt;/p&gt;&lt;h2 id=&quot;installing-the-cosmosdb-module&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enable-cors-support-in-cosmos-db-using-powershell/#installing-the-cosmosdb-module&quot; class=&quot;heading-anchor&quot;&gt;Installing the CosmosDB Module&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The first thing you need to do is install the &lt;a href=&quot;https://www.powershellgallery.com/packages/CosmosDB&quot; rel=&quot;noopener&quot;&gt;CosmosDB PowerShell&lt;/a&gt; module from the PowerShell Gallery by running this in a PowerShell console:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name CosmosDB &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MinimumVersion 3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ZbR9jlkV_6-650.webp 650w, https://danielscottraynsford.com/img/ZbR9jlkV_6-842.webp 842w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ZbR9jlkV_6-650.png&quot; alt=&quot;ss_cosmosdbcors_installmodule&quot; width=&quot;842&quot; height=&quot;155&quot; srcset=&quot;https://danielscottraynsford.com/img/ZbR9jlkV_6-650.png 650w, https://danielscottraynsford.com/img/ZbR9jlkV_6-842.png 842w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This will also install the &lt;strong&gt;Az PowerShell modules&lt;/strong&gt; &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/az.accounts/?view=azps-1.0.0#profile&quot; rel=&quot;noopener&quot;&gt;Az.Accounts&lt;/a&gt; and &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/az.resources/?view=azps-1.0.0#resources&quot; rel=&quot;noopener&quot;&gt;Az.Resources&lt;/a&gt; modules if they are not installed on your machine. The &lt;em&gt;*-CosmosDbAccount&lt;/em&gt; functions in the CosmosDB module are dependent on these modules.&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;The CosmosDB PowerShell module and the&amp;nbsp;Az PowerShell modules are completely &lt;strong&gt;cross-platform&lt;/strong&gt; and support &lt;strong&gt;Linux&lt;/strong&gt;, &lt;strong&gt;MacOS&lt;/strong&gt; and &lt;strong&gt;Windows&lt;/strong&gt;. Running in either &lt;strong&gt;Windows PowerShell&lt;/strong&gt; (Windows) or &lt;strong&gt;PowerShell Core&lt;/strong&gt; (cross-platform) is supported.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Versions of the CosmosDB PowerShell module earlier than 3.0.0.0 use the older AzureRm/AzureRm.NetCore modules and do not support the CORS setting.&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;authenticating-to-azure-with-az&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enable-cors-support-in-cosmos-db-using-powershell/#authenticating-to-azure-with-az&quot; class=&quot;heading-anchor&quot;&gt;Authenticating to Azure with ‘Az’&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Before using the CosmosDB PowerShell module &lt;strong&gt;accounts&lt;/strong&gt; functions to work with CORS settings you’ll first need to &lt;strong&gt;authenticate to Azure&lt;/strong&gt; using the &lt;strong&gt;Az&lt;/strong&gt; &lt;strong&gt;PowerShell Modules&lt;/strong&gt;. If you’re planning on automating this process you’ll want to &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/azure/authenticate-azureps?view=azps-1.0.0#sign-in-with-a-service-principal&quot; rel=&quot;noopener&quot;&gt;authenticate to Azure using a Service Principal identity&lt;/a&gt;.&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;If you’re using this module in an Azure DevOps build/release pipeline the Azure PowerShell task will take care of the Service Principal authentication process for you:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/-1TO00T8dL-650.webp 650w, https://danielscottraynsford.com/img/-1TO00T8dL-922.webp 922w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/-1TO00T8dL-650.png&quot; alt=&quot;ss_cosmosdbcors_azuredevopspowershelltask&quot; width=&quot;922&quot; height=&quot;80&quot; srcset=&quot;https://danielscottraynsford.com/img/-1TO00T8dL-650.png 650w, https://danielscottraynsford.com/img/-1TO00T8dL-922.png 922w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;But if you’re just doing a little bit of experimentation then you can just use an &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/azure/authenticate-azureps?view=azps-1.0.0#sign-in-interactively&quot; rel=&quot;noopener&quot;&gt;interactive authentication process&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;To use the interactive authentication process just enter into your PowerShell console:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Connect-AzAccount&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;then follow the instructions.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/fSQ3Us6RC5-650.webp 650w, https://danielscottraynsford.com/img/fSQ3Us6RC5-960.webp 960w, https://danielscottraynsford.com/img/fSQ3Us6RC5-1367.webp 1367w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/fSQ3Us6RC5-650.png&quot; alt=&quot;ss_cosmosdbcors_authenticateaz.png&quot; width=&quot;1367&quot; height=&quot;837&quot; srcset=&quot;https://danielscottraynsford.com/img/fSQ3Us6RC5-650.png 650w, https://danielscottraynsford.com/img/fSQ3Us6RC5-960.png 960w, https://danielscottraynsford.com/img/fSQ3Us6RC5-1367.png 1367w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h2 id=&quot;create-a-cosmos-db-account-with-cors-enabled&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enable-cors-support-in-cosmos-db-using-powershell/#create-a-cosmos-db-account-with-cors-enabled&quot; class=&quot;heading-anchor&quot;&gt;Create a Cosmos DB Account with CORS enabled&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Once you have authenticated to Azure, you can use the &lt;strong&gt;New-CosmosDbAccount&lt;/strong&gt; function to create a new account:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;New-CosmosDbAccount&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&#39;dsrcosmosdbtest&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token string&quot;&gt;&#39;dsrcosmosdbtest-rgp&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Location &lt;span class=&quot;token string&quot;&gt;&#39;westus&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AllowedOrigin &lt;span class=&quot;token string&quot;&gt;&#39;https://www.fabrikam.com&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://www.contoso.com&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Q3K0tOdvKZ-650.webp 650w, https://danielscottraynsford.com/img/Q3K0tOdvKZ-960.webp 960w, https://danielscottraynsford.com/img/Q3K0tOdvKZ-1368.webp 1368w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Q3K0tOdvKZ-650.png&quot; alt=&quot;ss_cosmosdbcors_newcosmosdbaccount&quot; width=&quot;1368&quot; height=&quot;99&quot; srcset=&quot;https://danielscottraynsford.com/img/Q3K0tOdvKZ-650.png 650w, https://danielscottraynsford.com/img/Q3K0tOdvKZ-960.png 960w, https://danielscottraynsford.com/img/Q3K0tOdvKZ-1368.png 1368w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;This will create a new Cosmos DB account with the name &lt;strong&gt;dsrcosmosdbtest&lt;/strong&gt; in the resource group &lt;strong&gt;dsrcosmosdbtest-rgp&lt;/strong&gt; in the &lt;strong&gt;West US&lt;/strong&gt; location and with CORS allowed origins of &lt;strong&gt;&lt;a href=&quot;https://www.fabrikam.com&quot; rel=&quot;noopener&quot;&gt;https://www.fabrikam.com&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href=&quot;https://www.contoso.com&quot; rel=&quot;noopener&quot;&gt;https://www.contoso.com&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;&lt;blockquote data-callout=&quot;important&quot; class=&quot;blockquote--important&quot;&gt;&lt;p&gt;The &lt;strong&gt;New-CosmosDbAccount&lt;/strong&gt; command assumes the resource group that is specified in the&amp;nbsp;&lt;strong&gt;ResourceGroup&lt;/strong&gt; parameter already exists and you have &lt;strong&gt;contributor&lt;/strong&gt; access to it. If the resource group doesn’t exist then you can create it using the &lt;strong&gt;New-AzResourceGroup&lt;/strong&gt; function or some other method.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;It will take Azure a few minutes to create the new Cosmos DB account for you.&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;But if you want your PowerShell automation or script to be able to get on and do other tasks in the meantime, then add the &lt;strong&gt;-AsJob&lt;/strong&gt; parameter to the &lt;strong&gt;New-CosmosDbAccount&lt;/strong&gt;call. This will cause the function to immediately return and provide you a &lt;strong&gt;Job&lt;/strong&gt; object that you can use to periodically query the state of the Job. More information on using PowerShell Jobs can be found &lt;a href=&quot;https://blogs.technet.microsoft.com/heyscriptingguy/2012/12/31/using-windows-powershell-jobs/&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Be aware, you won’t be able to use the Cosmos DB account until the Job is completed.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;If you look in the Azure Portal, you will find the new Cosmos DB account with the CORS allowed origin values set as per your command:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/4ZI1bZIbsX-650.webp 650w, https://danielscottraynsford.com/img/4ZI1bZIbsX-960.webp 960w, https://danielscottraynsford.com/img/4ZI1bZIbsX-1240.webp 1240w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/4ZI1bZIbsX-650.png&quot; alt=&quot;ss_cosmosdbcors_cosmosdbinportalwithcors&quot; width=&quot;1240&quot; height=&quot;675&quot; srcset=&quot;https://danielscottraynsford.com/img/4ZI1bZIbsX-650.png 650w, https://danielscottraynsford.com/img/4ZI1bZIbsX-960.png 960w, https://danielscottraynsford.com/img/4ZI1bZIbsX-1240.png 1240w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h2 id=&quot;get-the-cors-allowed-origins-on-a-cosmos-db-account&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enable-cors-support-in-cosmos-db-using-powershell/#get-the-cors-allowed-origins-on-a-cosmos-db-account&quot; class=&quot;heading-anchor&quot;&gt;Get the CORS Allowed Origins on a Cosmos DB Account&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Getting the current CORS Allowed Origins value on an account is easy too. Just run the following PowerShell command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-CosmosDbAccount&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&#39;dsrcosmosdbtest&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token string&quot;&gt;&#39;dsrcosmosdbtest-rgp&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Properties&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Cors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AllowedOrigins&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/kjsNleGHMW-650.webp 650w, https://danielscottraynsford.com/img/kjsNleGHMW-960.webp 960w, https://danielscottraynsford.com/img/kjsNleGHMW-1367.webp 1367w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/kjsNleGHMW-650.png&quot; alt=&quot;ss_cosmosdbcors_getcosmosdbcors&quot; width=&quot;1367&quot; height=&quot;127&quot; srcset=&quot;https://danielscottraynsford.com/img/kjsNleGHMW-650.png 650w, https://danielscottraynsford.com/img/kjsNleGHMW-960.png 960w, https://danielscottraynsford.com/img/kjsNleGHMW-1367.png 1367w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This will return a string containing all the CORS Allowed Origins for the Cosmos DB account &lt;strong&gt;dsrcosmosdbtest&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;You could easily split this string into an array variable by using:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$corsAllowedOrigins&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-CosmosDbAccount&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&#39;dsrcosmosdbtest&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token string&quot;&gt;&#39;dsrcosmosdbtest-rgp&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Properties&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Cors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AllowedOrigins &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;split &lt;span class=&quot;token string&quot;&gt;&#39;,&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/qZT_pQbwye-650.webp 650w, https://danielscottraynsford.com/img/qZT_pQbwye-960.webp 960w, https://danielscottraynsford.com/img/qZT_pQbwye-1367.webp 1367w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/qZT_pQbwye-650.png&quot; alt=&quot;ss_cosmosdbcors_getcosmosdbcorssplit&quot; width=&quot;1367&quot; height=&quot;180&quot; srcset=&quot;https://danielscottraynsford.com/img/qZT_pQbwye-650.png 650w, https://danielscottraynsford.com/img/qZT_pQbwye-960.png 960w, https://danielscottraynsford.com/img/qZT_pQbwye-1367.png 1367w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h2 id=&quot;update-the-cors-allowed-origins-on-an-existing-cosmos-db-account&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enable-cors-support-in-cosmos-db-using-powershell/#update-the-cors-allowed-origins-on-an-existing-cosmos-db-account&quot; class=&quot;heading-anchor&quot;&gt;Update the CORS Allowed Origins on an existing Cosmos DB Account&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To set the CORS Allowed Origins on an existing account use the &lt;strong&gt;Set-CosmosDbAccount&lt;/strong&gt; function:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Set-CosmosDbAccount&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&#39;dsrcosmosdbtest&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token string&quot;&gt;&#39;dsrcosmosdbtest-rgp&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AllowedOrigin &lt;span class=&quot;token string&quot;&gt;&#39;http://www.mycompany.com&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/9jBjQrHXqS-650.webp 650w, https://danielscottraynsford.com/img/9jBjQrHXqS-960.webp 960w, https://danielscottraynsford.com/img/9jBjQrHXqS-1368.webp 1368w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/9jBjQrHXqS-650.png&quot; alt=&quot;ss_cosmosdbcors_setcosmosdbcors&quot; width=&quot;1368&quot; height=&quot;238&quot; srcset=&quot;https://danielscottraynsford.com/img/9jBjQrHXqS-650.png 650w, https://danielscottraynsford.com/img/9jBjQrHXqS-960.png 960w, https://danielscottraynsford.com/img/9jBjQrHXqS-1368.png 1368w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This will take a few minutes to update. So you can use the &lt;strong&gt;-AsJob&lt;/strong&gt; parameter to run this as a Job.&lt;/p&gt;&lt;h2 id=&quot;remove-the-cors-allowed-origins-from-an-existing-cosmos-db-account&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enable-cors-support-in-cosmos-db-using-powershell/#remove-the-cors-allowed-origins-from-an-existing-cosmos-db-account&quot; class=&quot;heading-anchor&quot;&gt;Remove the CORS Allowed Origins from an existing Cosmos DB Account&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;You can remove the CORS Allowed Origins setting by setting using the &lt;strong&gt;Set-CosmosDbAccount&lt;/strong&gt; function but passing in an empty string to the &lt;strong&gt;AllowedOrigin&lt;/strong&gt; parameter:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Set-CosmosDbAccount&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&#39;dsrcosmosdbtest&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token string&quot;&gt;&#39;dsrcosmosdbtest-rgp&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AllowedOrigin &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ZCJ3mttb-a-650.webp 650w, https://danielscottraynsford.com/img/ZCJ3mttb-a-960.webp 960w, https://danielscottraynsford.com/img/ZCJ3mttb-a-1367.webp 1367w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ZCJ3mttb-a-650.png&quot; alt=&quot;ss_cosmosdbcors_removecosmosdbcors&quot; width=&quot;1367&quot; height=&quot;170&quot; srcset=&quot;https://danielscottraynsford.com/img/ZCJ3mttb-a-650.png 650w, https://danielscottraynsford.com/img/ZCJ3mttb-a-960.png 960w, https://danielscottraynsford.com/img/ZCJ3mttb-a-1367.png 1367w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This will take a few minutes to update as well. As always, you can use the &lt;strong&gt;-AsJob&lt;/strong&gt; parameter to run this as a Job.&lt;/p&gt;&lt;h2 id=&quot;final-words&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/enable-cors-support-in-cosmos-db-using-powershell/#final-words&quot; class=&quot;heading-anchor&quot;&gt;Final Words&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Hopefully, you can see it is fairly simple to &lt;strong&gt;automate&lt;/strong&gt; and work with the &lt;strong&gt;Cosmos DB CORS Allowed Origins&lt;/strong&gt; setting using the &lt;strong&gt;PowerShell Cosmos DB module&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;If you have any &lt;a href=&quot;https://github.com/PlagueHO/CosmosDB/issues&quot; rel=&quot;noopener&quot;&gt;issues&lt;/a&gt; or queries or would like to contribute to the PowerShell Cosmos DB module, please head over to the &lt;a href=&quot;https://github.com/PlagueHO/CosmosDB&quot; rel=&quot;noopener&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Use Pester to Test Azure Resource Manager Templates for Best Practices</title>
      <link href="https://danielscottraynsford.com/blog/use-pester-to-test-azure-resource-manager-templates-for-best-practices/" />
      <updated>2018-10-13T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/use-pester-to-test-azure-resource-manager-templates-for-best-practices/</id>
      <content type="html">
				&lt;p&gt;Recently I came across the amazing &lt;a href=&quot;https://azure.microsoft.com/en-gb/resources/videos/azure-friday-getting-started-with-the-secure-devops-kit-for-azure-azsk/&quot; rel=&quot;noopener&quot;&gt;Secure DevOps Kit for Azure (AzSK)&lt;/a&gt;. This contains a really useful &lt;a href=&quot;https://www.powershellgallery.com/packages/AzSK&quot; rel=&quot;noopener&quot;&gt;AzSK PowerShell Module&lt;/a&gt; that contains cmdlets for performing different types of security scanning on Azure Resources, Subscriptions, and Resource Manager Templates.&lt;/p&gt;&lt;p&gt;The feature of this module that I was most interested in for my current project was being able to scan ARM templates for best practice violations.&lt;/p&gt;&lt;p&gt;To install the module, open a PowerShell Window and run:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;src&#92;posts&#92;2018&#92;10&#92;2018-10-13-use-pester-to-test-azure-resource-manager-templates-for-best-practices.md&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name AzSK&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; At the time of writing this post, the AzSK module has dependencies on the &lt;strong&gt;AzureRM.Profile&lt;/strong&gt; and other AzureRM.* PowerShell modules. As of December 2018, the &lt;strong&gt;AzureRM.*&lt;/strong&gt; PowerShell Modules are going to be renamed to &lt;strong&gt;Az.*&lt;/strong&gt; (see &lt;a href=&quot;https://github.com/Azure/azure-powershell/blob/preview/documentation/announcing-az-module.md&quot; rel=&quot;noopener&quot;&gt;this post&lt;/a&gt;). The AzureRM and Az modules &lt;strong&gt;cannot&lt;/strong&gt; be installed side-by-side, so if you’ve installed the Az PowerShell modules on your system, then the installation of AzSK will fail because the AzureRM modules will also be installed and a conflict will occur.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The cmdlet we’re most interested in is the &lt;strong&gt;Get-AzSKARMTemplateSecurityStatus&lt;/strong&gt;. It can be used to scan one or more ARM templates or entire folders of ARM templates for best practice violations:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/q4xQ85LJew-650.webp 650w, https://danielscottraynsford.com/img/q4xQ85LJew-960.webp 960w, https://danielscottraynsford.com/img/q4xQ85LJew-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/q4xQ85LJew-650.png&quot; alt=&quot;ss_azsk_scanning&quot; width=&quot;1400&quot; height=&quot;750&quot; srcset=&quot;https://danielscottraynsford.com/img/q4xQ85LJew-650.png 650w, https://danielscottraynsford.com/img/q4xQ85LJew-960.png 960w, https://danielscottraynsford.com/img/q4xQ85LJew-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This will scan the ARM templates and produce a &lt;strong&gt;CSV report&lt;/strong&gt; in a folder &lt;strong&gt;Microsoft&#92;AzSKLogs&#92;ARMChecker&lt;/strong&gt; within your &lt;strong&gt;$ENV:LOCALAPPDATA&lt;/strong&gt; folder and open the folder in Explorer. This isn’t ideal for automation scenarios or using during Continuous Integration or Continuous Delivery pipelines. I’ve raised &lt;a href=&quot;https://github.com/azsk/DevOpsKit/issues/267&quot; rel=&quot;noopener&quot;&gt;an issue&lt;/a&gt; with the AzSK team on GitHub to see if this can be improved.&lt;/p&gt;&lt;p&gt;In my case, I wanted to be able to use the &lt;a href=&quot;https://github.com/pester/Pester&quot; rel=&quot;noopener&quot;&gt;PowerShell Pester Module&lt;/a&gt;, a PowerShell testing framework, to execute tests on the output and then use the NUnit output Pester generates to publish into a Continuous Integration pipeline. To do that, I needed to create a custom test script that would take the CSV report, count the failures of each level (High, Medium, or Low), and fail if any are counted in the specific level.&lt;/p&gt;&lt;p&gt;This is what the script looks like:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;src&#92;posts&#92;2018&#92;10&#92;2018-10-13-use-pester-to-test-azure-resource-manager-templates-for-best-practices.md&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;#PSScriptInfo
.VERSION 1.0.0
.GUID bf41177f-4d1e-481a-a126-5f0c07dd9aae
.AUTHOR Daniel Scott-Raynsford
.COMPANYNAME
.COPYRIGHT (c) 2018 Daniel Scott-Raynsford. All rights reserved.
.TAGS AzSK, Pester, Test
.LICENSEURI https://gist.github.com/PlagueHO/1af35ee65a2276ca90b3a8a5b224a5d4
.PROJECTURI https://gist.github.com/PlagueHO/1af35ee65a2276ca90b3a8a5b224a5d4
.ICONURI
.EXTERNALMODULEDEPENDENCIES
.REQUIREDSCRIPTS
.EXTERNALSCRIPTDEPENDENCIES
.RELEASENOTES First version.
.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core
#&amp;gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;#requires -Modules @{ ModuleName=&quot;AzSK&quot;; ModuleVersion=&quot;3.6.1&quot; }&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#requires -Modules @{ ModuleName=&quot;Pester&quot;; ModuleVersion=&quot;4.3.0&quot; }&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;#
    .SYNOPSIS
    Pester test for validating ARM template meets best-practices

    .DESCRIPTION
    This Pester test will validate one or more ARM templates in the specified
    file path to validate that they meet the best practices.

    .PARAMETER TemplatePath
    The full path to the ARM template to check. This may be a path with
    wild cards to check multiple files.

    .PARAMETER Severity
    An array of severity values that will count as failed tests. Any violation
    found in the ARM template that matches a severity in this list will cause
    the Pester test to count as failed. Defaults to &#39;High&#39; and &#39;Medium&#39;.

    .PARAMETER SkipControlsFromFile
    The path to a controls file that can be used to suppress rules.
#&amp;gt;&lt;/span&gt;
&lt;span class=&quot;token namespace&quot;&gt;[CmdletBinding()]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $true)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$TemplatePath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter()]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String[]]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$Severity&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;High&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Medium&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter()]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$SkipControlsFromFile&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

Describe &lt;span class=&quot;token string&quot;&gt;&#39;ARM template best practices&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Tag &lt;span class=&quot;token string&quot;&gt;&#39;AzSK&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Context &lt;span class=&quot;token string&quot;&gt;&#39;When AzSK module is installed and run on all files in the Templates folder&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$resultPath&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-AzSKARMTemplateSecurityStatus&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ARMTemplatePath &lt;span class=&quot;token variable&quot;&gt;$TemplatePath&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Preview:&lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DoNotOpenOutputFolder `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SkipControlsFromFile &lt;span class=&quot;token variable&quot;&gt;$SkipControlsFromFile&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Recurse
        &lt;span class=&quot;token variable&quot;&gt;$resultFile&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$resultPath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Filter&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;ARMCheckerResults_*.csv&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;FullName

        It &lt;span class=&quot;token string&quot;&gt;&#39;Should produce a valid CSV results file &#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$resultFile&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should &lt;span class=&quot;token operator&quot;&gt;-Not&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;BeNullOrEmpty
            &lt;span class=&quot;token function&quot;&gt;Test-Path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$resultFile&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Be &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$script&lt;/span&gt;:resultsContent = &lt;span class=&quot;token function&quot;&gt;Get-Content&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$resultFile&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ConvertFrom-Csv&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token variable&quot;&gt;$groupedResults&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$script&lt;/span&gt;:resultsContent &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Where-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Property Status &lt;span class=&quot;token operator&quot;&gt;-EQ&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Failed&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Group-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Property Severity

        &lt;span class=&quot;token variable&quot;&gt;$testCases&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$Severity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Severity = &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        It &lt;span class=&quot;token string&quot;&gt;&#39;Should have 0 failed Severity:&amp;lt;Severity&amp;gt; results&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TestCases &lt;span class=&quot;token variable&quot;&gt;$testCases&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Severity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$failedCount&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$groupedResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Where&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Severity&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Count
            &lt;span class=&quot;token variable&quot;&gt;$failedCount&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Be 0
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can download the script from GitHub Gist directly or get it from the &lt;a href=&quot;https://www.powershellgallery.com/packages/AzSKARMTemplateSecurityStatus.Test/1.0.0&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt; by running:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Install-Script&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name AzSKARMTemplateSecurityStatus&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Test&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To use it, you will need to install Pester 4.3.0 and AzSK 3.6.1 modules:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name Pester &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MinimumVersion 4&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;0
&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name AzSK &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MinimumVersion 3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;6&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once that is done, you can use &lt;strong&gt;Invoke-Pester&lt;/strong&gt; and pass in the &lt;strong&gt;TemplatePath&lt;/strong&gt; and &lt;strong&gt;Severity&lt;/strong&gt; parameters to the test script:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Invoke-Pester&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Script @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Path = &lt;span class=&quot;token string&quot;&gt;&#39;d:&#92;Invoke-AzSKARMTemplateSecurityStatusPesterTest.ps1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; Parameters = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; TemplatePath = &lt;span class=&quot;token string&quot;&gt;&#39;D:&#92;101-webapp-basic-windows&#92;azuredeploy.json&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will execute the Pester tests in the file above on the specified ARM template. The tests will fail when there are any best practice violations with the specified &lt;strong&gt;Severity&lt;/strong&gt; or above. If you didn’t pass in a &lt;strong&gt;Severity&lt;/strong&gt;, then it will default to failing on &lt;strong&gt;Medium&lt;/strong&gt; and &lt;strong&gt;High&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/hsV84M0nlN-650.webp 650w, https://danielscottraynsford.com/img/hsV84M0nlN-960.webp 960w, https://danielscottraynsford.com/img/hsV84M0nlN-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/hsV84M0nlN-650.png&quot; alt=&quot;ss_azsk_invokepester&quot; width=&quot;1400&quot; height=&quot;855&quot; srcset=&quot;https://danielscottraynsford.com/img/hsV84M0nlN-650.png 650w, https://danielscottraynsford.com/img/hsV84M0nlN-960.png 960w, https://danielscottraynsford.com/img/hsV84M0nlN-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;If you use the &lt;strong&gt;OutputFile&lt;/strong&gt; and &lt;strong&gt;OutputFormat&lt;/strong&gt; parameters, Pester can output an NUnit format file that most Continuous Integration tools will happily accept and use to display the output of the tests.&lt;/p&gt;&lt;p&gt;If you installed the script from the PowerShell Gallery, you can also run the tests like this:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;AzSKARMTemplateSecurityStatus&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Test &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TemplatePath D:&#92;101-webapp-basic-windows&#92;azuredeploy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, if you’re using Azure DevOps, you can also get this function as part of the Secure DevOps Kit (AzSK) CICD Extensions for Azure in the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=azsdktm.AzSDK-task&quot; rel=&quot;noopener&quot;&gt;Azure DevOps Marketplace&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Whichever way you choose to consume AzSK, it is a great module and well worth including in your CI/CD pipelines to ensure your ARM templates meet best practices.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Converting a PowerShell Project to use Azure DevOps Pipelines</title>
      <link href="https://danielscottraynsford.com/blog/converting-a-powershell-project-to-use-azure-devops-pipelines/" />
      <updated>2018-09-25T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/converting-a-powershell-project-to-use-azure-devops-pipelines/</id>
      <content type="html">
				&lt;p&gt;I wrote an article on converting my Cosmos DB PowerShell module to use a CI process in Azure DevOps over on the awesome PowerShell Magazine. &lt;a href=&quot;https://www.powershellmagazine.com/2018/09/20/converting-a-powershell-project-to-use-azure-devops-pipelines/&quot; rel=&quot;noopener&quot;&gt;Check it out here&lt;/a&gt;.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>List Global Assembly Cache using PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/list-global-assembly-cache-using-powershell/" />
      <updated>2018-09-10T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/list-global-assembly-cache-using-powershell/</id>
      <content type="html">
				&lt;p&gt;The list of assemblies stored in the Global Assembly Cache (GAC) can be found in the registry under the &lt;strong&gt;HKEY_CLASSES_ROOT&#92;Installer&#92;Assemblies&#92;Global&lt;/strong&gt; key.&lt;/p&gt;&lt;p&gt;If you want to get a list of the assemblies registered in the GAC using PowerShell you can use this snippet:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;New-PSDrive&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name HKCR &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PSProvider &lt;span class=&quot;token string&quot;&gt;&#39;Microsoft.PowerShell.Core&#92;Registry&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Root HKEY_CLASSES_ROOT
&lt;span class=&quot;token function&quot;&gt;Get-ItemProperty&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&#39;HKCR:&#92;Installer&#92;Assemblies&#92;Global&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Get-Member&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MemberType NoteProperty&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Vd5bMmLsV--650.webp 650w, https://danielscottraynsford.com/img/Vd5bMmLsV--960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Vd5bMmLsV--650.png&quot; alt=&quot;ss_gac_getcontent&quot; width=&quot;960&quot; height=&quot;541&quot; srcset=&quot;https://danielscottraynsford.com/img/Vd5bMmLsV--650.png 650w, https://danielscottraynsford.com/img/Vd5bMmLsV--960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;The first line registers a new drive called &lt;strong&gt;HKCR&lt;/strong&gt; in PowerShell that maps to the &lt;strong&gt;HKEY_CLASSES_ROOT&lt;/strong&gt; in the registry. This is required because, by default only the &lt;strong&gt;HKEY_CURRENT_USER&lt;/strong&gt; and &lt;strong&gt;HKEY_LOCAL_MACHINE&lt;/strong&gt; registry hives are registered as drives in PowerShell.&lt;/p&gt;&lt;p&gt;The second line just gets the list of registry properties in the &lt;strong&gt;HKEY_CLASSES_ROOT&#92;Installer&#92;Assemblies&#92;Global&lt;/strong&gt; key and displays them.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Install Windows Admin Center (WAC) using DSC</title>
      <link href="https://danielscottraynsford.com/blog/install-windows-admin-center-wac-using-dsc/" />
      <updated>2018-07-06T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/install-windows-admin-center-wac-using-dsc/</id>
      <content type="html">
				&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows-server/manage/windows-admin-center/understand/windows-admin-center&quot; rel=&quot;noopener&quot;&gt;Windows Admin Center (WAC)&lt;/a&gt; is a locally deployed, browser-based app for managing servers, clusters, hyper-converged infrastructure, and Windows 10 PCs. It was previously known as &lt;strong&gt;Project Honolulu&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ZUEjUZkLyh-650.webp 650w, https://danielscottraynsford.com/img/ZUEjUZkLyh-960.webp 960w, https://danielscottraynsford.com/img/ZUEjUZkLyh-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ZUEjUZkLyh-650.png&quot; alt=&quot;ss_wacdsc_overview&quot; width=&quot;1400&quot; height=&quot;813&quot; srcset=&quot;https://danielscottraynsford.com/img/ZUEjUZkLyh-650.png 650w, https://danielscottraynsford.com/img/ZUEjUZkLyh-960.png 960w, https://danielscottraynsford.com/img/ZUEjUZkLyh-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;WAC really shines when being used to manage headless Windows Servers (e.g., Windows Server Core). The &lt;a href=&quot;https://cloudblogs.microsoft.com/windowsserver/2018/07/05/server-core-and-server-with-desktop-which-one-is-best-for-you/&quot; rel=&quot;noopener&quot;&gt;benefits of deploying Windows Server Core&lt;/a&gt; are huge, but it can be a bit daunting to system administrators that have only used the Windows GUI experience to manage servers.&lt;/p&gt;&lt;p&gt;It is pretty easy to install WAC, but if you want to install it with PowerShell DSC, then here is a configuration for you to use:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;configuration WindowsAdminCenter &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$WacProductId&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;{7019BE31-3389-46FB-A077-B813D53C1266}&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        
        &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$WacDownloadPath&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;https://download.microsoft.com/download/1/0/5/1059800B-F375-451C-B37E-758FFC7C8C8B/WindowsAdminCenter1809.5.msi&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;token namespace&quot;&gt;[System.Int16]&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Port&lt;/span&gt; = 6516&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        
        &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Thumbprint&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ModuleName PSDscResources
    
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;::IsNullOrEmpty&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Thumbprint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$wacInstallArguments&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&quot;/qn /l*v c:&#92;windows&#92;temp&#92;windowsadmincenter.msiinstall.log SME_PORT=&lt;span class=&quot;token variable&quot;&gt;$Port&lt;/span&gt; SSL_CERTIFICATE_OPTION=generate&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$wacInstallArguments&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&quot;/qn /l*v c:&#92;windows&#92;temp&#92;windowsadmincenter.msiinstall.log SME_PORT=&lt;span class=&quot;token variable&quot;&gt;$Port&lt;/span&gt; SME_THUMBPRINT=&lt;span class=&quot;token variable&quot;&gt;$Thumbprint&lt;/span&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    Node localhost &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        MsiPackage InstallWindowsAdminCenter &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            ProductId = &lt;span class=&quot;token variable&quot;&gt;$WacProductId&lt;/span&gt;
            Path      = &lt;span class=&quot;token variable&quot;&gt;$WacDownloadPath&lt;/span&gt;
            Arguments = &lt;span class=&quot;token variable&quot;&gt;$wacInstallArguments&lt;/span&gt;
            Ensure    = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The configuration is parameterized and supports specifying the port for WAC to listen on and using either a &lt;em&gt;self-signed certificate&lt;/em&gt; or a &lt;em&gt;local machine certificate&lt;/em&gt; by specifying a thumbprint.&lt;/p&gt;&lt;p&gt;To apply the DSC using a self-signed certificate and on the default port of 6516, run the following in an Administrator PowerShell console:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Invoke-WebRequest&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Uri &lt;span class=&quot;token string&quot;&gt;&#39;https://gist.githubusercontent.com/PlagueHO/e8120e1cc01b447d084322eb2ad14c95/raw/2aff9e1a8d94cdb6f8a7409874a3bdbfcf234f8e/WindowsAdminCenterDscConfiguration.ps1&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;OutFile &lt;span class=&quot;token string&quot;&gt;&#39;WindowsAdminCenterDscConfiguration.ps1&#39;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name PSDscResources
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;WindowsAdminCenterDscConfiguration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1
WindowsAdminCenter
&lt;span class=&quot;token function&quot;&gt;Start-DscConfiguration&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;WindowsAdminCenter&#92; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ComputerName localhost &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Wait &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/lckZDPxne8-650.webp 650w, https://danielscottraynsford.com/img/lckZDPxne8-960.webp 960w, https://danielscottraynsford.com/img/lckZDPxne8-1233.webp 1233w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/lckZDPxne8-650.png&quot; alt=&quot;ss_wacdsc_defaultport&quot; width=&quot;1233&quot; height=&quot;983&quot; srcset=&quot;https://danielscottraynsford.com/img/lckZDPxne8-650.png 650w, https://danielscottraynsford.com/img/lckZDPxne8-960.png 960w, https://danielscottraynsford.com/img/lckZDPxne8-1233.png 1233w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;You can run this on a Windows Server Core machine by logging in and typing &lt;strong&gt;powershell&lt;/strong&gt; to start a PowerShell console, then entering the commands above.&lt;/p&gt;&lt;p&gt;To apply the DSC configuration specifying a certificate with a thumbprint from the local machine store and on Port 4000, run these commands instead:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Invoke-WebRequest&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Uri &lt;span class=&quot;token string&quot;&gt;&#39;https://gist.githubusercontent.com/PlagueHO/e8120e1cc01b447d084322eb2ad14c95/raw/2aff9e1a8d94cdb6f8a7409874a3bdbfcf234f8e/WindowsAdminCenterDscConfiguration.ps1&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;OutFile &lt;span class=&quot;token string&quot;&gt;&#39;WindowsAdminCenterDscConfiguration.ps1&#39;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name PSDscResources
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;WindowsAdminCenterDscConfiguration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1
WindowsAdminCenter &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Port 4000 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Thumbprint &lt;span class=&quot;token string&quot;&gt;&#39;fddfec2150b2a1c0d1166debffdbed1d55798485&#39;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Start-DscConfiguration&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;WindowsAdminCenter&#92; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ComputerName localhost &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Wait &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/O9Op445Cg--650.webp 650w, https://danielscottraynsford.com/img/O9Op445Cg--960.webp 960w, https://danielscottraynsford.com/img/O9Op445Cg--1233.webp 1233w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/O9Op445Cg--650.png&quot; alt=&quot;ss_wacdsc_installwiththumbprint&quot; width=&quot;1233&quot; height=&quot;959&quot; srcset=&quot;https://danielscottraynsford.com/img/O9Op445Cg--650.png 650w, https://danielscottraynsford.com/img/O9Op445Cg--960.png 960w, https://danielscottraynsford.com/img/O9Op445Cg--1233.png 1233w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This DSC configuration can also be used on Virtual Machines deployed to Azure, using either the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/dsc-overview&quot; rel=&quot;noopener&quot;&gt;Azure DSC Extension Handler&lt;/a&gt; or an &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/automation/automation-dsc-overview&quot; rel=&quot;noopener&quot;&gt;Azure Automation DSC Pull Server&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Easy as all that. Now you can use the awesome WAC GUI and still run headless while also taking advantage of the benefits that DSC brings.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Get the ForceChangePassword Office 365 User Setting with PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/get-the-forcechangepassword-office-365-user-setting-with-powershell/" />
      <updated>2018-06-22T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/get-the-forcechangepassword-office-365-user-setting-with-powershell/</id>
      <content type="html">
				&lt;p&gt;Recently I was asked by a friend if I knew of a way to get the value of the setting that forces a user to change their password when they next log in to Office 365. The friend wanted to get this value for all users using PowerShell.&lt;/p&gt;&lt;p&gt;Changing this setting is fairly straight forward either in the Office 365 portal or using the &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/msonline/set-msoluserpassword&quot; rel=&quot;noopener&quot;&gt;Set-MsolUserPassword&lt;/a&gt; cmdlet in the MSOnline PowerShell module:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/CjH4WAxUhn-650.webp 650w, https://danielscottraynsford.com/img/CjH4WAxUhn-960.webp 960w, https://danielscottraynsford.com/img/CjH4WAxUhn-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/CjH4WAxUhn-650.png&quot; alt=&quot;ss_o365_setmsoluserpassword&quot; width=&quot;1400&quot; height=&quot;95&quot; srcset=&quot;https://danielscottraynsford.com/img/CjH4WAxUhn-650.png 650w, https://danielscottraynsford.com/img/CjH4WAxUhn-960.png 960w, https://danielscottraynsford.com/img/CjH4WAxUhn-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;However, retrieving the current value of the setting isn’t possible using &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/msonline/get-msoluser&quot; rel=&quot;noopener&quot;&gt;Get-MsolUser&lt;/a&gt; cmdlet—the attribute does not appear in the returned object:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/hXA7C-N9qR-650.webp 650w, https://danielscottraynsford.com/img/hXA7C-N9qR-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/hXA7C-N9qR-650.png&quot; alt=&quot;ss_o365_getmsoluser&quot; width=&quot;960&quot; height=&quot;481&quot; srcset=&quot;https://danielscottraynsford.com/img/hXA7C-N9qR-650.png 650w, https://danielscottraynsford.com/img/hXA7C-N9qR-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Instead, we need to use the &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/azuread/get-azureaduser&quot; rel=&quot;noopener&quot;&gt;Get-AzureADUser&lt;/a&gt; cmdlet in the &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/azuread&quot; rel=&quot;noopener&quot;&gt;AzureAD PowerShell Module&lt;/a&gt; to query the Azure Active Directory for the Office 365 tenant.&lt;/p&gt;&lt;p&gt;If you don’t have the AzureAD module installed, use &lt;strong&gt;Install-Module&lt;/strong&gt; cmdlet to install it from the PowerShell Gallery:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name AzureAD&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then connect to AzureAD using the &lt;strong&gt;Connect-AzureAD&lt;/strong&gt; cmdlet. Once connected, you can run the following command to get the user object and show only the appropriate property (&lt;strong&gt;ForceChangePasswordNextLogin&lt;/strong&gt; of the &lt;strong&gt;PasswordProfile&lt;/strong&gt; object):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Connect-AzureAD&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureADUser&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SearchString &lt;span class=&quot;token string&quot;&gt;&#39;williammurderface@contoso.onmicrosoft.com&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PasswordProfile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ForceChangePasswordNextLogin&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/kLJDV9x-bD-650.webp 650w, https://danielscottraynsford.com/img/kLJDV9x-bD-960.webp 960w, https://danielscottraynsford.com/img/kLJDV9x-bD-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/kLJDV9x-bD-650.png&quot; alt=&quot;ss_o365_getazureaduser&quot; width=&quot;1400&quot; height=&quot;233&quot; srcset=&quot;https://danielscottraynsford.com/img/kLJDV9x-bD-650.png 650w, https://danielscottraynsford.com/img/kLJDV9x-bD-960.png 960w, https://danielscottraynsford.com/img/kLJDV9x-bD-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;If you wanted to get a list of all users with the &lt;strong&gt;ForceChangePasswordNextLogin&lt;/strong&gt;&amp;nbsp;property set to &lt;strong&gt;true&lt;/strong&gt;, you could use:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureADUser&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Where-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FilterScript &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PasswordProfile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ForceChangePasswordNextLogin &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/H0BFcXgqm4-650.webp 650w, https://danielscottraynsford.com/img/H0BFcXgqm4-960.webp 960w, https://danielscottraynsford.com/img/H0BFcXgqm4-1201.webp 1201w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/H0BFcXgqm4-650.png&quot; alt=&quot;ss_o365_getazureadallforcechangepasswordnextlogin&quot; width=&quot;1201&quot; height=&quot;172&quot; srcset=&quot;https://danielscottraynsford.com/img/H0BFcXgqm4-650.png 650w, https://danielscottraynsford.com/img/H0BFcXgqm4-960.png 960w, https://danielscottraynsford.com/img/H0BFcXgqm4-1201.png 1201w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This is all fairly straight forward once you figure out which object in Azure AD contains the information required.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Disable TLS 1.0, TLS 1.1 and 3DES in Azure API Management using an ARM Template</title>
      <link href="https://danielscottraynsford.com/blog/disable-tls-10-tls-11-and-3des-in-azure-api-management-using-an-arm-template/" />
      <updated>2018-04-07T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/disable-tls-10-tls-11-and-3des-in-azure-api-management-using-an-arm-template/</id>
      <content type="html">
				&lt;p&gt;Recently, I’ve been putting together a continuous delivery pipeline (using VSTS) for our &lt;a href=&quot;https://azure.microsoft.com/en-us/services/api-management/&quot; rel=&quot;noopener&quot;&gt;Azure API Management service&lt;/a&gt; using &lt;strong&gt;Azure Resource Manager&lt;/strong&gt; (ARM) templates. One of the things I needed to be able to do to &lt;em&gt;secure this service properly&lt;/em&gt; is to disable &lt;strong&gt;TLS 1.0&lt;/strong&gt;, &lt;strong&gt;TLS 1.1&lt;/strong&gt; and &lt;strong&gt;3DES&lt;/strong&gt;. This is pretty easy to do in the portal:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/RlKB7PyCQ5-634.webp 634w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/RlKB7PyCQ5-634.png&quot; alt=&quot;ss_apim_disabletls3des&quot; width=&quot;634&quot; height=&quot;451&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;However, if you only allow changes to be made via your continuous delivery pipeline (a good thing by the way), then you have to change the ARM template.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Side note:&lt;/strong&gt; Disabling &lt;strong&gt;TLS 1.0&lt;/strong&gt;, &lt;strong&gt;TLS 1.1&lt;/strong&gt;&amp;nbsp;and &lt;strong&gt;3DES&lt;/strong&gt; is pretty important for keeping your system secure. But if you have an Azure Application Gateway in front of your API Management service, then you’ll also need to configure the Azure Application Gateway to disable &lt;strong&gt;TLS 1.0&lt;/strong&gt; and &lt;strong&gt;TLS 1.1&lt;/strong&gt;. This is done in a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/application-gateway/application-gateway-ssl-policy-overview&quot; rel=&quot;noopener&quot;&gt;slightly different way&lt;/a&gt;, but can also be done in an ARM Template (post a comment if you’re not sure how to do this and I’ll write another post).&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;I found the documentation for the API Management service resource &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/templates/microsoft.apimanagement/service&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;. This shows it can be done by setting the &lt;strong&gt;customProperties&lt;/strong&gt; object in the ARM Template. But the documentation isn’t completely clear.&lt;/p&gt;&lt;p&gt;But after a little bit of trial and error I managed to figure it out and get it working. What you need to do is add the following &lt;strong&gt;customProperties&lt;/strong&gt; to the &lt;strong&gt;properties&lt;/strong&gt; of the API Management service resource:&lt;/p&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&quot;customProperties&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;false&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is what the complete ARM template looks like:&lt;/p&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;contentVersion&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;parameters&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;publisherEmail&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;minLength&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;metadata&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The email address of the owner of the service&quot;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;publisherName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;minLength&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;metadata&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The name of the owner of the service&quot;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;sku&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;allowedValues&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;Developer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;Standard&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;Premium&quot;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;defaultValue&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Developer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;metadata&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The pricing tier of this API Management service&quot;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;skuCount&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;allowedValues&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&quot;2&quot;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;defaultValue&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;metadata&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The instance size of this API Management service.&quot;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;variables&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;apiManagementServiceName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[concat(&#39;apiservice&#39;, uniqueString(resourceGroup().id))]&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;resources&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;apiVersion&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2017-03-01&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[variables(&#39;apiManagementServiceName&#39;)]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Microsoft.ApiManagement/service&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;location&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;West US&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;tags&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;sku&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;sku&#39;)]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;capacity&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;skuCount&#39;)]&quot;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;publisherEmail&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;publisherEmail&#39;)]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;publisherName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;publisherName&#39;)]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;customProperties&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;&quot;Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;&quot;Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;&quot;Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;false&quot;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;the template above is based off the &lt;a href=&quot;https://github.com/Azure/azure-quickstart-templates/blob/master/101-azure-api-management-create/azuredeploy.json&quot; rel=&quot;noopener&quot;&gt;Azure Quickstart Template for API Management&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Hopefully you find this if you’re looking for an example of how to do this and it saves you some time.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Managing Users and Permissions in Cosmos DB with PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/managing-users-and-permissions-in-cosmos-db-with-powershell/" />
      <updated>2018-01-14T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/managing-users-and-permissions-in-cosmos-db-with-powershell/</id>
      <content type="html">
				&lt;p&gt;If you’re just getting started with &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/cosmos-db/&quot; rel=&quot;noopener&quot;&gt;Cosmos DB&lt;/a&gt;, you might not have come across &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/cosmos-db/database-security&quot; rel=&quot;noopener&quot;&gt;users and permissions in a Cosmos DB database&lt;/a&gt;. However, there are certain use cases where managing &lt;strong&gt;users&lt;/strong&gt; and &lt;strong&gt;permissions&lt;/strong&gt; are necessary. For example, if you’re wanting to be able to limit access to a particular resource (e.g. a collection, document, stored procedure) by user.&lt;/p&gt;&lt;p&gt;The most common usage scenario for users and permissions is if you’re implementing a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/cosmos-db/secure-access-to-data&quot; rel=&quot;noopener&quot;&gt;Resource Token Broker&lt;/a&gt; type pattern, allowing client applications to directly access the Cosmos DB database.&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;The Cosmos DB implementation of &lt;strong&gt;users&lt;/strong&gt; and &lt;strong&gt;permissions&lt;/strong&gt; only provides &lt;strong&gt;authorization&lt;/strong&gt; - it does not provide &lt;strong&gt;authentication&lt;/strong&gt;. It would be up to your own implementation to manage the &lt;strong&gt;authentication&lt;/strong&gt;. In most cases you’d use something like &lt;strong&gt;Azure Active Directory&lt;/strong&gt; to provide an &lt;strong&gt;authentication&lt;/strong&gt; layer.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;But if you go hunting through the Azure Management Portal &lt;strong&gt;Cosmos DB data explorer&lt;/strong&gt; (or &lt;a href=&quot;https://azure.microsoft.com/en-us/features/storage-explorer/&quot; rel=&quot;noopener&quot;&gt;Azure Storage Explorer&lt;/a&gt;) you won’t find any way to configure or even view&amp;nbsp;&lt;strong&gt;users&lt;/strong&gt; and&amp;nbsp;&lt;strong&gt;permissions&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/yL4BxB0d3n-650.webp 650w, https://danielscottraynsford.com/img/yL4BxB0d3n-960.webp 960w, https://danielscottraynsford.com/img/yL4BxB0d3n-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/yL4BxB0d3n-650.png&quot; alt=&quot;ss_cdb_cosmosdbdataexplorer&quot; width=&quot;1400&quot; height=&quot;737&quot; srcset=&quot;https://danielscottraynsford.com/img/yL4BxB0d3n-650.png 650w, https://danielscottraynsford.com/img/yL4BxB0d3n-960.png 960w, https://danielscottraynsford.com/img/yL4BxB0d3n-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;To manage &lt;strong&gt;users&lt;/strong&gt; and &lt;strong&gt;permissions&lt;/strong&gt; you need to use the &lt;a href=&quot;https://docs.microsoft.com/en-us/rest/api/documentdb/users&quot; rel=&quot;noopener&quot;&gt;Cosmos DB API&lt;/a&gt; directly or one of the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/cosmos-db/sql-api-sdk-dotnet&quot; rel=&quot;noopener&quot;&gt;SDKs&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;But to make &lt;strong&gt;Cosmos DB&lt;/strong&gt; &lt;strong&gt;users&lt;/strong&gt; and &lt;strong&gt;permissions&lt;/strong&gt; easier to manage from PowerShell, I created the &lt;a href=&quot;https://www.powershellgallery.com/packages/CosmosDB&quot; rel=&quot;noopener&quot;&gt;Cosmos DB PowerShell module&lt;/a&gt;. This is an open source project hosted on &lt;a href=&quot;https://github.com/PlagueHO/CosmosDB&quot; rel=&quot;noopener&quot;&gt;GitHub&lt;/a&gt;. The Cosmos DB module allows you to manage &lt;a href=&quot;https://github.com/PlagueHO/CosmosDB#introduction&quot; rel=&quot;noopener&quot;&gt;much more&lt;/a&gt; than just &lt;strong&gt;users&lt;/strong&gt; and &lt;strong&gt;permissions&lt;/strong&gt;, but for this post I just wanted to start with these.&lt;/p&gt;&lt;h2 id=&quot;requirements&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/managing-users-and-permissions-in-cosmos-db-with-powershell/#requirements&quot; class=&quot;heading-anchor&quot;&gt;Requirements&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This module works on &lt;strong&gt;PowerShell 5.x&lt;/strong&gt; and &lt;strong&gt;PowerShell Core 6.0.0&lt;/strong&gt;. It probably works on &lt;strong&gt;PowerShell 3&lt;/strong&gt; and&amp;nbsp;&lt;strong&gt;4&lt;/strong&gt;, but I don’t have any more machines running this version to test on.&lt;/p&gt;&lt;p&gt;The Cosmos DB module does not have any dependencies, &lt;strong&gt;except&lt;/strong&gt; if you call the &lt;strong&gt;New-CosmosDbContext&lt;/strong&gt; function with the &lt;strong&gt;ResourceGroup&lt;/strong&gt; parameter specified as this will use the &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/azure/install-azurerm-ps?view=azurermps-5.1.1&quot; rel=&quot;noopener&quot;&gt;AzureRM PowerShell modules&lt;/a&gt; to read the &lt;strong&gt;Master Key&lt;/strong&gt; for the connection directly from your &lt;strong&gt;Cosmos DB&lt;/strong&gt; account. So I’d recommend &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/azure/install-azurerm-ps&quot; rel=&quot;noopener&quot;&gt;installing the Azure PowerShell modules&lt;/a&gt; or if you’re using PowerShell 6.0, install the &lt;a href=&quot;https://www.powershellgallery.com/packages/AzureRM.Netcore/0.9.1&quot; rel=&quot;noopener&quot;&gt;AzureRM.NetCore modules&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;installing-the-module&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/managing-users-and-permissions-in-cosmos-db-with-powershell/#installing-the-module&quot; class=&quot;heading-anchor&quot;&gt;Installing the Module&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The best way to install the &lt;a href=&quot;https://www.powershellgallery.com/packages/CosmosDB&quot; rel=&quot;noopener&quot;&gt;Cosmos DB PowerShell module&lt;/a&gt; is from the &lt;a href=&quot;https://www.powershellgallery.com&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt;. To install it for only your user account execute this PowerShell command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;src&#92;posts&#92;2018&#92;01&#92;2018-01-14-managing-users-permissions-in-cosmosdb-with-powershell.md&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name CosmosDB &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Scope CurrentUser&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/5w-I-_BAst-650.webp 650w, https://danielscottraynsford.com/img/5w-I-_BAst-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/5w-I-_BAst-650.png&quot; alt=&quot;ss_cdb_cosmosdbinstallmodulecurrentuser&quot; width=&quot;960&quot; height=&quot;44&quot; srcset=&quot;https://danielscottraynsford.com/img/5w-I-_BAst-650.png 650w, https://danielscottraynsford.com/img/5w-I-_BAst-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Or to install it for all users on the machine (requires administrator permissions):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;src&#92;posts&#92;2018&#92;01&#92;2018-01-14-managing-users-permissions-in-cosmosdb-with-powershell.md&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name CosmosDB&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/sCj7pqPyO3-650.webp 650w, https://danielscottraynsford.com/img/sCj7pqPyO3-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/sCj7pqPyO3-650.png&quot; alt=&quot;ss_cdb_cosmosdbinstallmoduleallusers&quot; width=&quot;960&quot; height=&quot;45&quot; srcset=&quot;https://danielscottraynsford.com/img/sCj7pqPyO3-650.png 650w, https://danielscottraynsford.com/img/sCj7pqPyO3-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h2 id=&quot;context-variable&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/managing-users-and-permissions-in-cosmos-db-with-powershell/#context-variable&quot; class=&quot;heading-anchor&quot;&gt;Context Variable&lt;/a&gt;&lt;/h2&gt;&lt;blockquote&gt;&lt;p&gt;Update 2018-03-06&lt;/p&gt;&lt;p&gt;As of Cosmos DB module v2.0.1, the &lt;strong&gt;connection&lt;/strong&gt; parameter has been renamed to &lt;strong&gt;context&lt;/strong&gt; and the &lt;strong&gt;New-CosmosDbConnection&lt;/strong&gt; function has been renamed &lt;strong&gt;New-CosmosDbContext&lt;/strong&gt;. This was to be more inline with naming adopted by the Azure PowerShell project. The old &lt;strong&gt;connection&lt;/strong&gt; parameters and &lt;strong&gt;New-CosmosDbConnection&lt;/strong&gt; function is still available as an alias, so older scripts won’t break. But these should be changed to use the new naming if possible as I plan to deprecate the &lt;strong&gt;connection&lt;/strong&gt; version at some point in the future.&lt;/p&gt;&lt;p&gt;This post was updated to specify the new naming, but screenshots still show the &lt;strong&gt;Connection&lt;/strong&gt; aliases.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Before you get down to the process of working with Cosmos DB resources, you’ll need to create a &lt;strong&gt;context&lt;/strong&gt; variable containing the information required to connect. This requires the following information:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The Cosmos DB Account name&lt;/li&gt;&lt;li&gt;The Cosmos DB Database name&lt;/li&gt;&lt;li&gt;The Master Key for the account (you can have the Cosmos DB PowerShell module get this directly from your Azure account if you wish).&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;To create the &lt;strong&gt;connection variable&lt;/strong&gt; we just use the &lt;strong&gt;New-CosmosDbContext&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$account&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;MyCosmosDBAccount&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$database&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;MyDatabase&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$key&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;ConvertTo-SecureString&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;String &lt;span class=&quot;token string&quot;&gt;&#39;this is your master key, get it from the Azure portal&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AsPlainText &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force
&lt;span class=&quot;token variable&quot;&gt;$context&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-CosmosDbContext&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Account &lt;span class=&quot;token variable&quot;&gt;$account&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Database &lt;span class=&quot;token variable&quot;&gt;$database&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Key &lt;span class=&quot;token variable&quot;&gt;$key&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/sF4xlEpc0c-650.webp 650w, https://danielscottraynsford.com/img/sF4xlEpc0c-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/sF4xlEpc0c-650.png&quot; alt=&quot;ss_cdb_cosmosdbnewconnection&quot; width=&quot;960&quot; height=&quot;112&quot; srcset=&quot;https://danielscottraynsford.com/img/sF4xlEpc0c-650.png 650w, https://danielscottraynsford.com/img/sF4xlEpc0c-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;If you do not wish to specify your &lt;strong&gt;master key&lt;/strong&gt;, you can have the &lt;strong&gt;New-CosmosDbContext&lt;/strong&gt; function pull your &lt;strong&gt;master key&lt;/strong&gt; from the &lt;strong&gt;Azure Management Portal&lt;/strong&gt; directly:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Add-AzureRmAccount&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$account&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;MyCosmosDBAccount&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$database&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;MyDatabase&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$resourceGroup&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;MyCosmosDBResourceGroup&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$context&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-CosmosDbContext&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Account &lt;span class=&quot;token variable&quot;&gt;$account&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Database &lt;span class=&quot;token variable&quot;&gt;$database&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroup &lt;span class=&quot;token variable&quot;&gt;$resourceGroup&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/n61Nxx5efC-650.webp 650w, https://danielscottraynsford.com/img/n61Nxx5efC-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/n61Nxx5efC-650.png&quot; alt=&quot;ss_cdb_cosmosdbnewconnectionviaportal&quot; width=&quot;960&quot; height=&quot;347&quot; srcset=&quot;https://danielscottraynsford.com/img/n61Nxx5efC-650.png 650w, https://danielscottraynsford.com/img/n61Nxx5efC-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: This requires the &lt;strong&gt;AzureRM.Profile&lt;/strong&gt; and &lt;strong&gt;AzureRM.Resources&lt;/strong&gt; module on &lt;strong&gt;Windows PowerShell 5.x&lt;/strong&gt; or &lt;strong&gt;AzureRM.Profile.NetCore&lt;/strong&gt;&amp;nbsp;and&amp;nbsp;&lt;strong&gt;AzureRM.Resources.NetCore&lt;/strong&gt; on &lt;strong&gt;PowerShell Core 6.0.0&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;managing-users&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/managing-users-and-permissions-in-cosmos-db-with-powershell/#managing-users&quot; class=&quot;heading-anchor&quot;&gt;Managing Users&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To &lt;strong&gt;add a user&lt;/strong&gt; to the Cosmos DB Database use the &lt;strong&gt;New-CosmosDbUser&lt;/strong&gt; function:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;New-CosmosDbUser&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Context &lt;span class=&quot;token variable&quot;&gt;$context&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Id &lt;span class=&quot;token string&quot;&gt;&#39;daniel&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ILKE5o4RJ2-650.webp 650w, https://danielscottraynsford.com/img/ILKE5o4RJ2-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ILKE5o4RJ2-650.png&quot; alt=&quot;ss_cdb_cosmosdbnewuser&quot; width=&quot;960&quot; height=&quot;114&quot; srcset=&quot;https://danielscottraynsford.com/img/ILKE5o4RJ2-650.png 650w, https://danielscottraynsford.com/img/ILKE5o4RJ2-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;To &lt;strong&gt;get a list of users&lt;/strong&gt; in the database:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-CosmosDbUser&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Context &lt;span class=&quot;token variable&quot;&gt;$context&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/6FYZk5JFYp-650.webp 650w, https://danielscottraynsford.com/img/6FYZk5JFYp-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/6FYZk5JFYp-650.png&quot; alt=&quot;ss_cdb_cosmosdbgetusers&quot; width=&quot;960&quot; height=&quot;130&quot; srcset=&quot;https://danielscottraynsford.com/img/6FYZk5JFYp-650.png 650w, https://danielscottraynsford.com/img/6FYZk5JFYp-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;To &lt;strong&gt;get a specific&lt;/strong&gt; user:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-CosmosDbUser&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Context &lt;span class=&quot;token variable&quot;&gt;$context&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Id &lt;span class=&quot;token string&quot;&gt;&#39;daniel&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/9h82EKTnqc-650.webp 650w, https://danielscottraynsford.com/img/9h82EKTnqc-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/9h82EKTnqc-650.png&quot; alt=&quot;ss_cdb_cosmosdbgetuser&quot; width=&quot;960&quot; height=&quot;114&quot; srcset=&quot;https://danielscottraynsford.com/img/9h82EKTnqc-650.png 650w, https://danielscottraynsford.com/img/9h82EKTnqc-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;To &lt;strong&gt;remove a user&lt;/strong&gt; (this will also remove all permissions assigned to the user):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Remove-CosmosDbUser&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Context &lt;span class=&quot;token variable&quot;&gt;$context&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Id &lt;span class=&quot;token string&quot;&gt;&#39;daniel&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ASJUdq9jlx-650.webp 650w, https://danielscottraynsford.com/img/ASJUdq9jlx-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ASJUdq9jlx-650.png&quot; alt=&quot;ss_cdb_cosmosdbremoveuser&quot; width=&quot;960&quot; height=&quot;44&quot; srcset=&quot;https://danielscottraynsford.com/img/ASJUdq9jlx-650.png 650w, https://danielscottraynsford.com/img/ASJUdq9jlx-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h2 id=&quot;managing-permissions&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/managing-users-and-permissions-in-cosmos-db-with-powershell/#managing-permissions&quot; class=&quot;heading-anchor&quot;&gt;Managing Permissions&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Permissions in Cosmos DB are granted to a user for a specific resource. For example, you could grant a user access to just a single document, an entire collection or to a stored procedure.&lt;/p&gt;&lt;p&gt;To grant a permission you need to provide four pieces of information:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The &lt;strong&gt;Id&lt;/strong&gt; of the &lt;strong&gt;user&lt;/strong&gt; to grant the permission to.&lt;/li&gt;&lt;li&gt;An &lt;strong&gt;Id&lt;/strong&gt; for the &lt;strong&gt;permission&lt;/strong&gt; to create. This is just string to uniquely identify the permission.&lt;/li&gt;&lt;li&gt;The permission mode to the permission: &lt;strong&gt;All&lt;/strong&gt; or &lt;strong&gt;Read&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;The &lt;strong&gt;Id&lt;/strong&gt; of the resource to grant access to. This can be generated from one of the &lt;strong&gt;Get-CosmosDb*ResourcePath&lt;/strong&gt; functions in the &lt;strong&gt;CosmosDB PowerShell module&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;In the following example, we’ll grant the user &lt;strong&gt;daniel&lt;/strong&gt; &lt;em&gt;all&lt;/em&gt; access to the &lt;strong&gt;TestCollection&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$userId&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;TestUserId&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$resourcePath&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-CosmosDbCollectionResourcePath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Database &lt;span class=&quot;token string&quot;&gt;&#39;TestDatabase&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Id &lt;span class=&quot;token string&quot;&gt;&#39;TestCollection&#39;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;New-CosmosDbPermission&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Context &lt;span class=&quot;token variable&quot;&gt;$context&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Id &lt;span class=&quot;token string&quot;&gt;&#39;AccessTestCollection&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;UserId &lt;span class=&quot;token variable&quot;&gt;$userId&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PermissionMode All &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Resource &lt;span class=&quot;token variable&quot;&gt;$resourcePath&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/02y2_sZ9K0-650.webp 650w, https://danielscottraynsford.com/img/02y2_sZ9K0-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/02y2_sZ9K0-650.png&quot; alt=&quot;ss_cdb_cosmosdbnewpermission&quot; width=&quot;960&quot; height=&quot;181&quot; srcset=&quot;https://danielscottraynsford.com/img/02y2_sZ9K0-650.png 650w, https://danielscottraynsford.com/img/02y2_sZ9K0-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Once a &lt;strong&gt;permission&lt;/strong&gt; has been &lt;strong&gt;granted&lt;/strong&gt;, you can use the &lt;strong&gt;Get-CosmosDbPermission&lt;/strong&gt; function to retrieve the permission &lt;em&gt;and with it&lt;/em&gt; the &lt;strong&gt;Resource Token&lt;/strong&gt; that can be used to access the resource for a limited amount of time (between 10 minutes and 5 hours).&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: as you have the &lt;strong&gt;Master Key&lt;/strong&gt; already, using the &lt;strong&gt;Resource Token&lt;/strong&gt; isn’t required.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;For example, to retrieve all permissions for the user with &lt;strong&gt;Id&lt;/strong&gt; daniel and a resource token expiration of 600 seconds:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-CosmosDbPermission&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Context &lt;span class=&quot;token variable&quot;&gt;$context&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;UserId &lt;span class=&quot;token string&quot;&gt;&#39;daniel&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TokenExpiry &lt;span class=&quot;token string&quot;&gt;&#39;600&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; 
    &lt;span class=&quot;token function&quot;&gt;Format-List&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/zvNT2r99EW-650.webp 650w, https://danielscottraynsford.com/img/zvNT2r99EW-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/zvNT2r99EW-650.png&quot; alt=&quot;ss_cdb_cosmosdbgetpermission&quot; width=&quot;960&quot; height=&quot;384&quot; srcset=&quot;https://danielscottraynsford.com/img/zvNT2r99EW-650.png 650w, https://danielscottraynsford.com/img/zvNT2r99EW-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;You can as expected delete a permission by using the &lt;strong&gt;Remove-CosmosDbPermission&lt;/strong&gt; function:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Remove-CosmosDbPermission&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Context &lt;span class=&quot;token variable&quot;&gt;$context&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;UserId &lt;span class=&quot;token string&quot;&gt;&#39;daniel&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Id &lt;span class=&quot;token string&quot;&gt;&#39;AccessTestCollection&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/VMcaw5enVK-650.webp 650w, https://danielscottraynsford.com/img/VMcaw5enVK-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/VMcaw5enVK-650.png&quot; alt=&quot;ss_cdb_cosmosdbremovepermission&quot; width=&quot;960&quot; height=&quot;59&quot; srcset=&quot;https://danielscottraynsford.com/img/VMcaw5enVK-650.png 650w, https://danielscottraynsford.com/img/VMcaw5enVK-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h2 id=&quot;final-thoughts&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/managing-users-and-permissions-in-cosmos-db-with-powershell/#final-thoughts&quot; class=&quot;heading-anchor&quot;&gt;Final Thoughts&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;So this is pretty much all there is to managing &lt;strong&gt;users&lt;/strong&gt; and &lt;strong&gt;permissions&lt;/strong&gt; using the &lt;strong&gt;Cosmos DB PowerShell&lt;/strong&gt; module. This module can also be used to manage the following Cosmos DB resources:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Attachments&lt;/li&gt;&lt;li&gt;Collections&lt;/li&gt;&lt;li&gt;Databases&lt;/li&gt;&lt;li&gt;Documents&lt;/li&gt;&lt;li&gt;Offers&lt;/li&gt;&lt;li&gt;Stored procedures&lt;/li&gt;&lt;li&gt;Triggers&lt;/li&gt;&lt;li&gt;User Defined Functions&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;You can find additional documentation and examples of how to manage these resources over in the &lt;a href=&quot;https://github.com/PlagueHO/CosmosDB/blob/dev/README.md&quot; rel=&quot;noopener&quot;&gt;Cosmos DB PowerShell module readme file&lt;/a&gt; on GitHub.&lt;/p&gt;&lt;p&gt;Hopefully this will help you in any &lt;strong&gt;Cosmos DB&lt;/strong&gt; automation tasks you might need to implement.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Configure Azure SQL Server Automatic Tuning with PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/configure-azure-sql-server-automatic-tuning-with-powershell/" />
      <updated>2017-12-25T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/configure-azure-sql-server-automatic-tuning-with-powershell/</id>
      <content type="html">
				&lt;p&gt;One thing I’ve found with configuring Azure services using automation (e.g. &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/azure/overview&quot; rel=&quot;noopener&quot;&gt;Azure PowerShell Modules&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-authoring-templates&quot; rel=&quot;noopener&quot;&gt;Azure Resource Manager template&lt;/a&gt;) is that the automation features are a little bit behind the feature set. For example, the Azure PowerShell modules may not yet implement settings for new or preview features. This can be a an issue if you’re strictly deploying everything via code (e.g. infrastructure as code). But if you run into a problem like this, all is not lost. So read on for an example of how to solve this issue.&lt;/p&gt;&lt;h2 id=&quot;azure-rest-apis&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/configure-azure-sql-server-automatic-tuning-with-powershell/#azure-rest-apis&quot; class=&quot;heading-anchor&quot;&gt;Azure REST APIs&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;One of the great things about Azure is that everything is configurable by making direct requests to the &lt;a href=&quot;https://docs.microsoft.com/en-us/rest/api/&quot; rel=&quot;noopener&quot;&gt;Azure REST APIs&lt;/a&gt;, even if it is not available in ARM templates or Azure PowerShell.&lt;/p&gt;&lt;p&gt;Depending on the feature/configuration you can sometimes use the &lt;strong&gt;Set-AzureRmResource&lt;/strong&gt; cmdlets to make calls to the REST APIs. But this cmdlet is limited to using an HTTP method of POST. So if you need to use PATCH, you’ll need to find an alternate way to make the call.&lt;/p&gt;&lt;p&gt;So, what you need then is to use the &lt;strong&gt;Invoke-RestMethod&lt;/strong&gt; cmdlet to create a custom call to the REST API. This is the process I needed to use to configure the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/sql-database/sql-database-automatic-tuning-enable&quot; rel=&quot;noopener&quot;&gt;Azure SQL Server Automatic Tuning settings&lt;/a&gt; and what I’ll show in my script below.&lt;/p&gt;&lt;h2 id=&quot;the-script&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/configure-azure-sql-server-automatic-tuning-with-powershell/#the-script&quot; class=&quot;heading-anchor&quot;&gt;The Script&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The following script can be executed in PowerShell (of course) and requires a number of parameters to be passed to it:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;SubscriptionId&lt;/strong&gt; - the subscription Id of the Azure subscription that contains the Azure SQL Server.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ResourceGroupName&lt;/strong&gt; - The name of the resource group containing SQL Server or database.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ServerName&lt;/strong&gt; - The name of the Azure SQL Server to set the automatic tuning options on.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;DatabaseName&lt;/strong&gt; &lt;strong&gt;-&lt;/strong&gt; The name of the Azure SQL Database to set the automatic tuning options on. If you pass this parameter then the automatic tuning settings are applied to the Azure SQL Database, not the server.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Mode&lt;/strong&gt; - This defines where the settings for the automatic tuning are obtained from. Inherit is only valid if the &lt;strong&gt;DatabaseName&lt;/strong&gt; is specified.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;CreateIndex&lt;/strong&gt; &lt;strong&gt;-&lt;/strong&gt; Enable automatic tuning for creating an index.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;DropIndex&lt;/strong&gt; &lt;strong&gt;-&lt;/strong&gt; Enable automatic tuning for dropping an index.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ForceLastGoodPlan -&lt;/strong&gt; Enable automatic tuning for forcing last good plan.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Requirements:&lt;/strong&gt; You need to have the &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/azure/install-azurerm-ps&quot; rel=&quot;noopener&quot;&gt;installed&lt;/a&gt; the &lt;strong&gt;AzureRM.Profile PowerShell&lt;/strong&gt; module (part of the AzureRM PowerShell Modules) to use this script. The script also requires you to have logged into your Azure Subscription using &lt;strong&gt;Add-AzureRmAccount&lt;/strong&gt; (as a user or &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/azure/create-azure-service-principal-azureps&quot; rel=&quot;noopener&quot;&gt;Service Principal&lt;/a&gt;).&lt;/em&gt;&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#Requires -Modules &#39;AzureRM.Profile&#39; &lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;#
.SYNOPSIS
Configure Azure SQL Autotuning on an Azure SQL server
or database.

.DESCRIPTION
This function will retrieve a current access token from
the Azure RM PowerShell context and use it to make a direct
request to the Azure management portal endpoint for the
SQL Server or database. It will configure the Autotuning
parameters for the server.

Requires AzureRM PowerShell Modules 5.1.1 or above*.
* May work on lower versions but untested

.PARAMETER SubscriptionId
The Azure subscription Id of the subscription containing
SQL Server or database.

.PARAMETER ResourceGroupName
The name of the resource grou containing SQL Server or
database.

.PARAMETER ServerName
The name of the Azure SQL Server to set the autotuning
options on.

.PARAMETER DatabaseName
The name of the Azure SQL Database to set the autotuning
options on.

.PARAMETER Mode
This defines where the settings for the Autotuning are
obtained from.

Inherit is only valid if the DatabaseName is specified.

.PARAMETER CreateIndex
Enable autotuning for creating an index.

.PARAMETER DropIndex
Enable autotuning for dropping an index.

.PARAMETER ForceLastGoodPlan
Enable autotuning for forcing last good plan.
#&amp;gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $true)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$SubscriptionId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $true)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$ResourceGroupName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $true)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$ServerName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter()]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$DatabaseName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter()]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;ValidateSet&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Auto&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Custom&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Inherit&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$Mode&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;Auto&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter()]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;ValidateSet&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;On&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Off&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Default&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$CreateIndex&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;Default&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter()]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;ValidateSet&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;On&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Off&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Default&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$DropIndex&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;Default&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter()]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;ValidateSet&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;On&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Off&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Default&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$ForceLastGoodPlan&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;Default&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Get an access token from the Auzre RM PowerShell token cache for accessing the Azure Management Portal&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$context&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-AzureRmContext&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$cache&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TokenCache

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-not&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$cache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Use an older method of accessing the Token Cache (for old versions of AzureRM.Profile)&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$cache&lt;/span&gt; = &lt;span class=&quot;token namespace&quot;&gt;[Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache]&lt;/span&gt;::DefaultShared
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$cacheItems&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$cache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReadItems&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$cacheItem&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$cacheItems&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Where-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FilterScript &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TenantId &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Tenant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TenantId &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Select-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;First 1

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-not&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$cacheItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;Throw&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;A current access token could not be found for the tenant Id {0}.&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Tenant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TenantId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$accessToken&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$cacheItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AccessToken
    
&lt;span class=&quot;token comment&quot;&gt;# Generate the Body of the request&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$body&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    properties = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        desiredState = &lt;span class=&quot;token variable&quot;&gt;$Mode&lt;/span&gt;
        options      = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            createIndex       = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                desiredState = &lt;span class=&quot;token variable&quot;&gt;$CreateIndex&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            dropIndex         = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                desiredState = &lt;span class=&quot;token variable&quot;&gt;$DropIndex&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            forceLastGoodPlan = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                desiredState = &lt;span class=&quot;token variable&quot;&gt;$ForceLastGoodPlan&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Generate the URI to the endpoint&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$uri&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Sql/servers/{2}&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$SubscriptionId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ResourceGroupName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ServerName&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$PSBoundParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ContainsKey&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DatabaseName&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$uri&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;{0}/databases/{1}&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$uri&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$DatabaseName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Mode&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Inherit&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;Throw&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Inherit mode is only valid for a SQL database. Either use a different not or specify a database name.&#39;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$uri&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;{0}/automaticTuning/current?api-version=2017-03-01-preview&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$uri&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$bodyText&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;ConvertTo-Json&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;InputObject &lt;span class=&quot;token variable&quot;&gt;$body&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Depth 10

&lt;span class=&quot;token variable&quot;&gt;$headers&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;Authorization&#39;&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Bearer {0}&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$accessToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;Cache-Control&#39;&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;no-cache&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$invokeRestMethodParameters&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Uri         = &lt;span class=&quot;token variable&quot;&gt;$Uri&lt;/span&gt;
    Method      = &lt;span class=&quot;token string&quot;&gt;&#39;PATCH&#39;&lt;/span&gt;
    Headers     = &lt;span class=&quot;token variable&quot;&gt;$headers&lt;/span&gt;
    ContentType = &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&lt;/span&gt;
    Body        = &lt;span class=&quot;token variable&quot;&gt;$bodyText&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Invoke-RestMethod&lt;/span&gt; @invokeRestMethodParameters&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;example-usage&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/configure-azure-sql-server-automatic-tuning-with-powershell/#example-usage&quot; class=&quot;heading-anchor&quot;&gt;Example Usage&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To apply custom automatic tuning to an Azure SQL Server:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;&#92;&lt;span class=&quot;token function&quot;&gt;Set-AzureRMSqlServerAutotuning&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SubscriptionId &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;Subscription Id&amp;gt;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;Resource Group name&amp;gt;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServerName &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;Azure SQL server name&amp;gt;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Mode Custom &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;CreateIndex On &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DropIndex On &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ForceLastGoodPlan Off&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/lyGbqN8BWv-650.png 650w, https://danielscottraynsford.com/img/lyGbqN8BWv-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/lyGbqN8BWv-650.webp 650w, https://danielscottraynsford.com/img/lyGbqN8BWv-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/lyGbqN8BWv-650.jpeg&quot; alt=&quot;ss_sqlserver_serverautotuning&quot; width=&quot;960&quot; height=&quot;166&quot; srcset=&quot;https://danielscottraynsford.com/img/lyGbqN8BWv-650.jpeg 650w, https://danielscottraynsford.com/img/lyGbqN8BWv-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;To apply custom automatic tuning to an Azure SQL Database:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;&#92;&lt;span class=&quot;token function&quot;&gt;Set-AzureRMSqlServerAutotuning&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SubscriptionId &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;Subscription Id&amp;gt;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;Resource Group name&amp;gt;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServerName &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;Azure SQL server name&amp;gt;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DatabaseName &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;Azure SQL database name&amp;gt;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Mode Custom &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;CreateIndex On &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DropIndex On &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ForceLastGoodPlan Off&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/lD48JQDCYy-650.png 650w, https://danielscottraynsford.com/img/lD48JQDCYy-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/lD48JQDCYy-650.webp 650w, https://danielscottraynsford.com/img/lD48JQDCYy-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/lD48JQDCYy-650.jpeg&quot; alt=&quot;ss_sqlserver_databaseautotuning&quot; width=&quot;960&quot; height=&quot;166&quot; srcset=&quot;https://danielscottraynsford.com/img/lD48JQDCYy-650.jpeg 650w, https://danielscottraynsford.com/img/lD48JQDCYy-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/configure-azure-sql-server-automatic-tuning-with-powershell/#conclusion&quot; class=&quot;heading-anchor&quot;&gt;Conclusion&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I’ve not yet encountered something in Azure that I can’t configure via the Azure REST APIs. This is because the Azure Management Portal uses the same APIs - so if it is available in the portal then you can do it via the Azure REST APIs. The biggest challenge is determining the body, header and methods available if the APIs are not yet documented.&lt;/p&gt;&lt;p&gt;If the API you need is not documented then you can raise a question in the &lt;a href=&quot;https://azure.microsoft.com/en-in/support/forums/&quot; rel=&quot;noopener&quot;&gt;Microsoft Azure Forums&lt;/a&gt; or on &lt;a href=&quot;https://stackoverflow.com/&quot; rel=&quot;noopener&quot;&gt;Stack Overflow&lt;/a&gt;. Failing that you can use the developer tools in your browser of choice to watch the API calls being made to the portal - I’ve had to resort to this many times, but documenting that process is something I’ll save for another day.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Create a Scheduled Task with unlimited Execution Time Limit in PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/create-a-scheduled-task-with-unlimited-execution-time-limit-in-powershell/" />
      <updated>2017-12-16T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/create-a-scheduled-task-with-unlimited-execution-time-limit-in-powershell/</id>
      <content type="html">
				&lt;p&gt;When creating a scheduled task in PowerShell you may wish to set the &lt;strong&gt;Execution Time Limit&lt;/strong&gt; of the task to be &lt;strong&gt;unlimited&lt;/strong&gt; (no time limit).&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/4m1JHCJN02-632.png 632w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/4m1JHCJN02-632.webp 632w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/4m1JHCJN02-632.jpeg&quot; alt=&quot;ss_scheduledtask_executiontimelimit&quot; width=&quot;632&quot; height=&quot;480&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This will prevent the task from being terminated if it is still running after a specific period of time.&lt;/p&gt;&lt;p&gt;Creating scheduled tasks using PowerShell is pretty easy using the &lt;strong&gt;*-ScheduledTask*&lt;/strong&gt; cmdlets in Windows Server 2012 and above.&lt;/p&gt;&lt;p&gt;However, after working on &lt;a href=&quot;https://github.com/PowerShell/xComputerManagement/issues/115&quot; rel=&quot;noopener&quot;&gt;this issue&lt;/a&gt; in the xScheduledTask DSC resource in the &lt;a href=&quot;https://blogs.msdn.microsoft.com/powershell/tag/dsc-resource-kit/&quot; rel=&quot;noopener&quot;&gt;Microsoft DSC Resource Kit&lt;/a&gt; I found that there are some differences in how to do this between Windows Server 2012 R2 and Windows Server 2016.&lt;/p&gt;&lt;p&gt;So in this post I’m going to show how to create a scheduled task with no Execution Time Limit that will work on both Windows Server 2012 R2 (and Windows 8/8.1) and Windows Server 2016 (and Windows 10).&lt;/p&gt;&lt;p&gt;I’ll also show the method that works only on Windows Server 2016.&lt;/p&gt;&lt;h2 id=&quot;all-versions-of-windows-server&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/create-a-scheduled-task-with-unlimited-execution-time-limit-in-powershell/#all-versions-of-windows-server&quot; class=&quot;heading-anchor&quot;&gt;All Versions of Windows Server&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To create a scheduled task with unlimited &lt;strong&gt;Execution Time Limit&lt;/strong&gt; on Windows Server 2012 R2 and Windows Server 2016.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$trigger&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-ScheduledTaskTrigger&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Once &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;At &lt;span class=&quot;token string&quot;&gt;&#39;13:00:00&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$action&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-ScheduledTaskAction&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Execute &lt;span class=&quot;token string&quot;&gt;&#39;powershell.exe&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$settingsSet&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-ScheduledTaskSettingsSet&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Set the Execution Time Limit to unlimited on all versions of Windows Server&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$settingsSet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ExecutionTimeLimit = &lt;span class=&quot;token string&quot;&gt;&#39;PT0S&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$task&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-ScheduledTask&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Trigger &lt;span class=&quot;token variable&quot;&gt;$trigger&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Action &lt;span class=&quot;token variable&quot;&gt;$action&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Settings &lt;span class=&quot;token variable&quot;&gt;$settingsSet&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Register-ScheduledTask&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TaskName &lt;span class=&quot;token string&quot;&gt;&#39;MyTask&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;InputObject &lt;span class=&quot;token variable&quot;&gt;$task&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This should also work on Windows Server 2012, but I have not confirmed this. It will NOT work on Windows Server 2008 R2. It should also work on Windows 8/8.1/10.&lt;/p&gt;&lt;h2 id=&quot;windows-server-2016-only&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/create-a-scheduled-task-with-unlimited-execution-time-limit-in-powershell/#windows-server-2016-only&quot; class=&quot;heading-anchor&quot;&gt;Windows Server 2016 Only&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To create a scheduled task with unlimited &lt;strong&gt;Execution Time Limit&lt;/strong&gt; on Windows Server 2016 only:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$trigger&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-ScheduledTaskTrigger&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Once &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;At &lt;span class=&quot;token string&quot;&gt;&#39;13:00:00&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$action&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-ScheduledTaskAction&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Execute &lt;span class=&quot;token string&quot;&gt;&#39;powershell.exe&#39;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Set the Execution Time Limit to unlimited on Windows Server 2016&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$settingsSet&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-ScheduledTaskSettingsSet&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ExecutionTimeLimit &lt;span class=&quot;token string&quot;&gt;&#39;00:00:00&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$task&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-ScheduledTask&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Trigger &lt;span class=&quot;token variable&quot;&gt;$trigger&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Action &lt;span class=&quot;token variable&quot;&gt;$action&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Settings &lt;span class=&quot;token variable&quot;&gt;$settingsSet&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Register-ScheduledTask&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TaskName &lt;span class=&quot;token string&quot;&gt;&#39;MyTask&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;InputObject &lt;span class=&quot;token variable&quot;&gt;$task&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This method is a more elegant approach and arguably how the Scheduled Task cmdlets are intended to be used. But you would only use this method if your task does not need to created on an operating system earlier than Windows Server 2016/Windows 10.&lt;/p&gt;&lt;p&gt;So, hopefully this will help anyone else out there who has struggled with this.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Auto Formatting PowerShell in Visual Studio Code</title>
      <link href="https://danielscottraynsford.com/blog/auto-formatting-powershell-in-visual-studio-code/" />
      <updated>2017-11-17T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/auto-formatting-powershell-in-visual-studio-code/</id>
      <content type="html">
				&lt;p&gt;One of the features I’m most fond of in Visual Studio Code is the &lt;strong&gt;Format Document&lt;/strong&gt; feature that is built into &lt;a href=&quot;https://code.visualstudio.com/&quot; rel=&quot;noopener&quot;&gt;Visual Studio Code&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/dzyilB8zS3-650.png 650w, https://danielscottraynsford.com/img/dzyilB8zS3-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/dzyilB8zS3-650.webp 650w, https://danielscottraynsford.com/img/dzyilB8zS3-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/dzyilB8zS3-650.jpeg&quot; alt=&quot;ss_vscode_formatdocument&quot; width=&quot;960&quot; height=&quot;197&quot; srcset=&quot;https://danielscottraynsford.com/img/dzyilB8zS3-650.jpeg 650w, https://danielscottraynsford.com/img/dzyilB8zS3-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;If you’re writing PowerShell scripts or modules then you should be using &lt;strong&gt;Visual Studio Code&lt;/strong&gt;. You should only be using &lt;strong&gt;PowerShell ISE&lt;/strong&gt; if you don’t have the ability to install &lt;strong&gt;Visual Studio Code&lt;/strong&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The &lt;strong&gt;Format Document&lt;/strong&gt; feature can be used in many different document types in Visual Studio Code to correct the layout based on your&amp;nbsp;&lt;strong&gt;user settings&lt;/strong&gt; or the&amp;nbsp;&lt;strong&gt;workspace settings&lt;/strong&gt; for the project you’re working on.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/TptSfQmLWZ-650.png 650w, https://danielscottraynsford.com/img/TptSfQmLWZ-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/TptSfQmLWZ-650.webp 650w, https://danielscottraynsford.com/img/TptSfQmLWZ-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/TptSfQmLWZ-650.jpeg&quot; alt=&quot;ss_vscode_settings&quot; width=&quot;960&quot; height=&quot;307&quot; srcset=&quot;https://danielscottraynsford.com/img/TptSfQmLWZ-650.jpeg 650w, https://danielscottraynsford.com/img/TptSfQmLWZ-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This enables me to configure &lt;strong&gt;Visual Studio Code&lt;/strong&gt; to auto format &lt;strong&gt;PowerShell&lt;/strong&gt; code the way I like it for my own projects, but still adhere to the code formatting standards of any other projects I work on without having to remember what they are for each. This saves so much time and hassle.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; If you’re contributing code to an Open Source project, the project maintainers may have included a &lt;strong&gt;.vscode&#92;settings.json&lt;/strong&gt; in the project folder. This may contain &lt;strong&gt;workspace&lt;/strong&gt; specific formatting settings that you should apply before submitting code back to the project.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;But even if you’ve don’t define and code formatting settings &lt;strong&gt;Visual Studio Code&lt;/strong&gt; will still do a great job of formatting your PowerShell code. Having nicely formatted code really is not a requirement to being awesome at writing PowerShell, but it does make it easier for not so awesome PowerShell people to read, understand and potentially maintain your work.&lt;/p&gt;&lt;h2 id=&quot;formatting-a-powershell-script&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/auto-formatting-powershell-in-visual-studio-code/#formatting-a-powershell-script&quot; class=&quot;heading-anchor&quot;&gt;Formatting a PowerShell Script&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Here are the simple instructions for auto formatting a document in Visual Studio Code:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://code.visualstudio.com/download&quot; rel=&quot;noopener&quot;&gt;Download&lt;/a&gt; and install Visual Studio Code.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Once Visual Studio Code is installed, add the &lt;strong&gt;PowerShell&lt;/strong&gt; extension:&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/lrU2ZJMcqk-650.png 650w, https://danielscottraynsford.com/img/lrU2ZJMcqk-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/lrU2ZJMcqk-650.webp 650w, https://danielscottraynsford.com/img/lrU2ZJMcqk-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/lrU2ZJMcqk-650.jpeg&quot; alt=&quot;ss_vscode_powershellextension&quot; width=&quot;960&quot; height=&quot;724&quot; srcset=&quot;https://danielscottraynsford.com/img/lrU2ZJMcqk-650.jpeg 650w, https://danielscottraynsford.com/img/lrU2ZJMcqk-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;In &lt;strong&gt;Visual Studio Code&lt;/strong&gt;, open the &lt;strong&gt;folder&lt;/strong&gt; of the &lt;strong&gt;project&lt;/strong&gt; containing the file you want to format or open an individual PowerShell file (PS1, PSD1, PSM1).&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/UMnRy93Piq-650.png 650w, https://danielscottraynsford.com/img/UMnRy93Piq-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/UMnRy93Piq-650.webp 650w, https://danielscottraynsford.com/img/UMnRy93Piq-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/UMnRy93Piq-650.jpeg&quot; alt=&quot;ss_vscode_powershellbadcodeformat&quot; width=&quot;960&quot; height=&quot;304&quot; srcset=&quot;https://danielscottraynsford.com/img/UMnRy93Piq-650.jpeg 650w, https://danielscottraynsford.com/img/UMnRy93Piq-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;&lt;strong&gt;Workspace&lt;/strong&gt; formatting settings are only used if you’ve opened the &lt;strong&gt;folder&lt;/strong&gt; rather than an individual file.&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Press **SHIFT+**&lt;strong&gt;ALT+F&lt;/strong&gt; (or press&amp;nbsp;&lt;strong&gt;F1&lt;/strong&gt; and type&amp;nbsp;&lt;strong&gt;Format&lt;/strong&gt; and select&amp;nbsp;&lt;strong&gt;Format Document&lt;/strong&gt;).&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/uZS-aey-6Z-650.png 650w, https://danielscottraynsford.com/img/uZS-aey-6Z-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/uZS-aey-6Z-650.webp 650w, https://danielscottraynsford.com/img/uZS-aey-6Z-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/uZS-aey-6Z-650.jpeg&quot; alt=&quot;ss_vscode_powershellgoodcodeformat&quot; width=&quot;960&quot; height=&quot;259&quot; srcset=&quot;https://danielscottraynsford.com/img/uZS-aey-6Z-650.jpeg 650w, https://danielscottraynsford.com/img/uZS-aey-6Z-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The code is now all well formatted, so save the document.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;It really is as easy as that.&lt;/p&gt;&lt;p&gt;Happy formatting.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Stop, Start or Restart all Web Apps in Azure using PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/stop-start-or-restart-all-web-apps-in-azure-using-powershell/" />
      <updated>2017-10-07T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/stop-start-or-restart-all-web-apps-in-azure-using-powershell/</id>
      <content type="html">
				&lt;p&gt;Here is a short (and sometimes handy) single line of PowerShell code that can be used to &lt;strong&gt;restart&lt;/strong&gt; all the Azure Web Apps in a subscription:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureRmWebApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetEnumerator&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Restart-AzureRmWebApp&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/3hYpiObqjt-650.png 650w, https://danielscottraynsford.com/img/3hYpiObqjt-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/3hYpiObqjt-650.webp 650w, https://danielscottraynsford.com/img/3hYpiObqjt-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/3hYpiObqjt-650.jpeg&quot; alt=&quot;ss_azurecloudshell_restartallwebapps&quot; width=&quot;960&quot; height=&quot;291&quot; srcset=&quot;https://danielscottraynsford.com/img/3hYpiObqjt-650.jpeg 650w, https://danielscottraynsford.com/img/3hYpiObqjt-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;Use this with care if you’re working with production systems because this _will_ restart these Web Apps without confirming first.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;This would be a handy snippet to be able to run in the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/cloud-shell/overview&quot; rel=&quot;noopener&quot;&gt;Azure Cloud Shell&lt;/a&gt;. It could also be adjusted to perform different actions on other types of resources.&lt;/p&gt;&lt;p&gt;To &lt;strong&gt;stop&lt;/strong&gt; all Web Apps in a subscription use:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureRmWebApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetEnumerator&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Stop-AzureRmWebApp&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To &lt;strong&gt;start&lt;/strong&gt; them all again:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureRmWebApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetEnumerator&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Start-AzureRmWebApp&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The key part of this command is the &lt;strong&gt;GetEnumerator()&lt;/strong&gt; method because most Azure Cmdlets don’t return an array of individual objects into the pipeline like typical PowerShell cmdlets. Instead returning a &lt;strong&gt;System.Collections.Generic.List&lt;/strong&gt; object, which requires a slight adjustment to the code. This procedure can be used for most Azure Cmdlets to allow the results to be iterated through.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/DbxGD69U2n-650.png 650w, https://danielscottraynsford.com/img/DbxGD69U2n-960.png 960w, https://danielscottraynsford.com/img/DbxGD69U2n-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/DbxGD69U2n-650.webp 650w, https://danielscottraynsford.com/img/DbxGD69U2n-960.webp 960w, https://danielscottraynsford.com/img/DbxGD69U2n-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/DbxGD69U2n-650.jpeg&quot; alt=&quot;ss_azurecloudshell_systemcollections&quot; width=&quot;1400&quot; height=&quot;139&quot; srcset=&quot;https://danielscottraynsford.com/img/DbxGD69U2n-650.jpeg 650w, https://danielscottraynsford.com/img/DbxGD69U2n-960.jpeg 960w, https://danielscottraynsford.com/img/DbxGD69U2n-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Thanks for reading.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Install Nightly Build of Azure CLI 2.0 on Windows</title>
      <link href="https://danielscottraynsford.com/blog/install-nightly-build-of-azure-cli-20-on-windows/" />
      <updated>2017-10-06T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/install-nightly-build-of-azure-cli-20-on-windows/</id>
      <content type="html">
				&lt;p&gt;The &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/azure/install-azurerm-ps?view=azurermps-4.4.0&quot; rel=&quot;noopener&quot;&gt;Azure PowerShell cmdlets&lt;/a&gt; are really first class if you’re wanting to manage Azure with PowerShell. However, they don’t always support the very latest Azure components and features. For example, at the time of writing this there is no Azure PowerShell module for managing &lt;a href=&quot;https://azure.microsoft.com/en-us/services/container-instances/&quot; rel=&quot;noopener&quot;&gt;Azure Container Instances&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The solution to this is to install the &lt;strong&gt;Nightly Build&lt;/strong&gt; of &lt;a href=&quot;https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest&quot; rel=&quot;noopener&quot;&gt;Azure CLI 2.0&lt;/a&gt;. However, on Windows it is not entirely clear the easiest way to do this. So, in this post I’ll provide a PowerShell script that will:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Install Python 3.x using &lt;a href=&quot;https://chocolatey.org/&quot; rel=&quot;noopener&quot;&gt;Chocolatey&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Use PIP (Python package manager) to install the latest nightly build packages&lt;/li&gt;&lt;li&gt;Update the Environment Path variable so that you can use Azure CLI 2.0.&lt;/li&gt;&lt;/ol&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;If you have the stable build of Azure CLI 2.0 installed using the MSI then you’ll need to configure your Environment Path variable to find the Az command that you’d like to use by default. I personally removed the stable build of Azure CLI 2.0 to make it easier.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;performing-the-install&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-nightly-build-of-azure-cli-20-on-windows/#performing-the-install&quot; class=&quot;heading-anchor&quot;&gt;Performing the Install&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Make sure you’ve got &lt;a href=&quot;https://chocolatey.org/install&quot; rel=&quot;noopener&quot;&gt;Chocolatey installed&lt;/a&gt;. If you aren’t sure what Chocolatey is, it is a package management system for Windows - not unlike Apt-Get or Yum for Linux. It is free and awesome. In this process we’ll use Chocolatey to install Python for us. If you haven’t got Chocolatey installed, see &lt;a href=&quot;https://chocolatey.org/install&quot; rel=&quot;noopener&quot;&gt;this page&lt;/a&gt; for instructions.&lt;/p&gt;&lt;p&gt;Next, download and run this PowerShell script in a PowerShell Administrator Console:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;#
    .SYNOPSIS
        Install Azure CLI 2.0 Nightly Build on Windows using Chocolatey and PowerShell
#&amp;gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-not&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Command&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name Choco &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ErrorAction SilentlyContinue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;Throw&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Chocolatey is not installed. Please install it. See https://chocolatey.org/install for instructions.&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;Write-Host&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Object &lt;span class=&quot;token string&quot;&gt;&#39;Installing Python 3 with Chocolatey...&#39;&lt;/span&gt;
&amp;amp; choco @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;install&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;python3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;-y&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;Update-SessionEnvironment&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$pyhtonScriptsPath&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Join-Path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:APPDATA &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ChildPath &lt;span class=&quot;token string&quot;&gt;&#39;Python&#92;Python36&#92;Scripts&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$currentPath&lt;/span&gt; = &lt;span class=&quot;token namespace&quot;&gt;[System.Environment]&lt;/span&gt;::GetEnvironmentVariable&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Path&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;[System.EnvironmentVariableTarget]&lt;/span&gt;::User&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;split &lt;span class=&quot;token string&quot;&gt;&#39;;&#39;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$currentPath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-notcontains&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$pyhtonScriptsPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Write-Host&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Object &lt;span class=&quot;token string&quot;&gt;&#39;Adding Python Scripts to User Environment Path...&#39;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$newPath&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$newPath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$currentPath&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$newPath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$pyhtonScriptsPath&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$newPathJoined&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$newPath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-join&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;;&#39;&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.Environment]&lt;/span&gt;::SetEnvironmentVariable&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Path&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$newPathJoined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;[System.EnvironmentVariableTarget]&lt;/span&gt;::User&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-not&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$currentPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Contains&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$pyhtonScriptsPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Write-Host&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Object &lt;span class=&quot;token string&quot;&gt;&#39;Adding Python Scripts to Current PowerShell session path...&#39;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:Path = &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:Path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;;&lt;span class=&quot;token variable&quot;&gt;$pyhtonScriptsPath&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;Write-Host&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Object &lt;span class=&quot;token string&quot;&gt;&#39;Installing nightly build of Az CLI 2.0...&#39;&lt;/span&gt;
&amp;amp; pip @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;install&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;--no-cache-dir&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;--user&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;--upgrade&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;--pre&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;azure-cli&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;--extra-index-url&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://azureclinightly.blob.core.windows.net/packages&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;Write-Host&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Object &lt;span class=&quot;token string&quot;&gt;&#39;Installation of nightly build of Az CLI 2.0 complete. Execute &quot;az&quot; to start.&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You could save the content of this script into a PS1 file and then execute it like this:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/P2Loc3zZqY-650.png 650w, https://danielscottraynsford.com/img/P2Loc3zZqY-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/P2Loc3zZqY-650.webp 650w, https://danielscottraynsford.com/img/P2Loc3zZqY-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/P2Loc3zZqY-650.jpeg&quot; alt=&quot;ss_azurecli_installnightlybuild&quot; width=&quot;960&quot; height=&quot;209&quot; srcset=&quot;https://danielscottraynsford.com/img/P2Loc3zZqY-650.jpeg 650w, https://danielscottraynsford.com/img/P2Loc3zZqY-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;It will then download and install Python, then use PIP to install the current nightly build packages. After a few minutes the installation will complete:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/H0y4BEyf-A-650.png 650w, https://danielscottraynsford.com/img/H0y4BEyf-A-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/H0y4BEyf-A-650.webp 650w, https://danielscottraynsford.com/img/H0y4BEyf-A-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/H0y4BEyf-A-650.jpeg&quot; alt=&quot;ss_azurecli_installnightlybuildcompete&quot; width=&quot;960&quot; height=&quot;714&quot; srcset=&quot;https://danielscottraynsford.com/img/H0y4BEyf-A-650.jpeg 650w, https://danielscottraynsford.com/img/H0y4BEyf-A-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;You can then run:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;az login&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To get started.&lt;/p&gt;&lt;p&gt;If you’re a bit new to Azure CLI 2.0, then another great way is to use Azure CLI Interactive:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;az login interactive&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/peXor89WVn-650.png 650w, https://danielscottraynsford.com/img/peXor89WVn-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/peXor89WVn-650.webp 650w, https://danielscottraynsford.com/img/peXor89WVn-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/peXor89WVn-650.jpeg&quot; alt=&quot;ss_azurecli_interactive&quot; width=&quot;960&quot; height=&quot;714&quot; srcset=&quot;https://danielscottraynsford.com/img/peXor89WVn-650.jpeg 650w, https://danielscottraynsford.com/img/peXor89WVn-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;If you need to update to a newer nightly build, just run the script again and it will update your packages.&lt;/p&gt;&lt;p&gt;Easy as that! Now you can experiment with all the latest automation features in Azure without needing to wait for a new version of Azure CLI 2.0 or for latest Azure PowerShell cmdlets.&lt;/p&gt;&lt;h2 id=&quot;edge-builds&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-nightly-build-of-azure-cli-20-on-windows/#edge-builds&quot; class=&quot;heading-anchor&quot;&gt;Edge Builds&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you want to install even more “bleeding edge” builds (built straight off the master branch on every merge to master) then you can make a small adjustment to the script above:&lt;/p&gt;&lt;p&gt;On &lt;a href=&quot;https://danielscottraynsford.com/blog/install-nightly-build-of-azure-cli-20-on-windows/cf9ed6c3fcadb4db6152ef1e1f18f791#file-add-azureclinightlybuildwithpython-ps1-L34&quot;&gt;line 34&lt;/a&gt; change the URL of the feed from:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://azureclinightly.blob.core.windows.net/packages&quot; rel=&quot;noopener&quot;&gt;https://azureclinightly.blob.core.windows.net/packages&lt;/a&gt;&lt;/p&gt;&lt;p&gt;To:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://azurecliprod.blob.core.windows.net/edge&quot; rel=&quot;noopener&quot;&gt;https://azurecliprod.blob.core.windows.net/edge&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Thanks for reading!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Get Azure API Management Git Credentials using PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/get-azure-api-management-git-credentials-using-powershell/" />
      <updated>2017-09-16T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/get-azure-api-management-git-credentials-using-powershell/</id>
      <content type="html">
				&lt;p&gt;One of the many great features of &lt;a href=&quot;https://azure.microsoft.com/en-us/services/api-management/&quot; rel=&quot;noopener&quot;&gt;Azure API Management&lt;/a&gt; is the fact that it has a built in &lt;strong&gt;Git repository&lt;/strong&gt; for storing the &lt;strong&gt;current configuration&lt;/strong&gt; as well as &lt;strong&gt;publishing new configurations&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/y9DdKMk7vY-650.png 650w, https://danielscottraynsford.com/img/y9DdKMk7vY-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/y9DdKMk7vY-650.webp 650w, https://danielscottraynsford.com/img/y9DdKMk7vY-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/y9DdKMk7vY-650.jpeg&quot; alt=&quot;ss_apim_gitrepository&quot; width=&quot;960&quot; height=&quot;610&quot; srcset=&quot;https://danielscottraynsford.com/img/y9DdKMk7vY-650.jpeg 650w, https://danielscottraynsford.com/img/y9DdKMk7vY-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This allows you to &lt;strong&gt;push&lt;/strong&gt; updated Azure API Management configurations to this &lt;strong&gt;internal Git repository&lt;/strong&gt; as a &lt;strong&gt;new branch&lt;/strong&gt; and then &lt;strong&gt;Deploy the configuration to API Management&lt;/strong&gt;.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The &lt;strong&gt;internal Git repository&lt;/strong&gt; in Azure API Management is &lt;em&gt;not intended&lt;/em&gt; to be used for a normal development workflow. You’ll still want to develop and store your Azure API management configuration in an external Git repository such as GitHub or TFS/VSTS and then &lt;strong&gt;copy&lt;/strong&gt; configuration updates to the &lt;strong&gt;internal Git repository&lt;/strong&gt; in Azure API Management using some sort of automated process (e.g. Continuous Integration/Continuous Delivery could be adopted for this).&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;the-internal-git-repository&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/get-azure-api-management-git-credentials-using-powershell/#the-internal-git-repository&quot; class=&quot;heading-anchor&quot;&gt;The Internal Git Repository&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To access the &lt;strong&gt;Internal Git Repository&lt;/strong&gt; requires &lt;strong&gt;short lived&lt;/strong&gt; (30 days maximum) Git credentials to be generated. This is fairly easy through the Azure API Management portal:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/q9gTgQbJjs-650.png 650w, https://danielscottraynsford.com/img/q9gTgQbJjs-898.png 898w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/q9gTgQbJjs-650.webp 650w, https://danielscottraynsford.com/img/q9gTgQbJjs-898.webp 898w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/q9gTgQbJjs-650.jpeg&quot; alt=&quot;ss_apim_gitrepositorygeneratecreds&quot; width=&quot;898&quot; height=&quot;809&quot; srcset=&quot;https://danielscottraynsford.com/img/q9gTgQbJjs-650.jpeg 650w, https://danielscottraynsford.com/img/q9gTgQbJjs-898.jpeg 898w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Unfortunately using the portal to get these credentials is a manual process and so would not be so good for an automated delivery process (e.g. CI/CD). You’d need to update these Git credentials in your CI/CD automation system every time they expired (every 30 days).&lt;/p&gt;&lt;h2 id=&quot;get-git-credentials&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/get-azure-api-management-git-credentials-using-powershell/#get-git-credentials&quot; class=&quot;heading-anchor&quot;&gt;Get Git Credentials&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;A better approach to generating the Git Credentials is to use &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/azure/install-azurerm-ps?view=azurermps-4.3.1&quot; rel=&quot;noopener&quot;&gt;Azure PowerShell&lt;/a&gt; &lt;strong&gt;API Management&lt;/strong&gt; cmdlets connected with a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-objects&quot; rel=&quot;noopener&quot;&gt;Service Principal&lt;/a&gt; to &lt;strong&gt;generate&lt;/strong&gt; the &lt;strong&gt;Git credentials&lt;/strong&gt; whenever you need them in your CI/CD pipeline.&lt;/p&gt;&lt;p&gt;This is not a completely straightforward process right now (which is unusual for the Azure PowerShell team), so I’ve created a simple PowerShell script that will take care of the nuts and bolts for you.&lt;/p&gt;&lt;h2 id=&quot;requirements&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/get-azure-api-management-git-credentials-using-powershell/#requirements&quot; class=&quot;heading-anchor&quot;&gt;Requirements&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To run this script you’ll need:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;PowerShell 5 (WMF 5.0) or greater.&lt;/li&gt;&lt;li&gt;Azure PowerShell Modules installed (make sure you’ve got the latest versions - 4.0.3 at the time of writing this).&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;You’ll also need to supply the following parameters to the script:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The Azure Subscription Id of the subscription containing the API Management instance.&lt;/li&gt;&lt;li&gt;The name of the Resource Group where the API Management instance is installed to.&lt;/li&gt;&lt;li&gt;The service name of the API Management instance.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;You can also optionally supply which of the two &lt;em&gt;internal API Management keys&lt;/em&gt;, &lt;strong&gt;primary&lt;/strong&gt; or &lt;strong&gt;secondary&lt;/strong&gt;, to use to generate the credential and also the length of time that the Git credential will be valid for (up to 30 days).&lt;/p&gt;&lt;h2 id=&quot;steps&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/get-azure-api-management-git-credentials-using-powershell/#steps&quot; class=&quot;heading-anchor&quot;&gt;Steps&lt;/a&gt;&lt;/h2&gt;&lt;h3 id=&quot;download-the-script&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/get-azure-api-management-git-credentials-using-powershell/#download-the-script&quot; class=&quot;heading-anchor&quot;&gt;Download the Script&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Download the script &lt;a href=&quot;https://danielscottraynsford.com/blog/get-azure-api-management-git-credentials-using-powershell/70ae184e1c8d22848ade6a7bc0f8255d&quot;&gt;Get-AzureRMApiManagementGitCredential.ps1&lt;/a&gt; using the PowerShell command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;iwr&lt;/span&gt; https:&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;gist&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;githubusercontent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com/PlagueHO/70ae184e1c8d22848ade6a7bc0f8255d/raw/a9ca51e690c04654dfcb934ccbc7ca9358c97f08/&lt;span class=&quot;token function&quot;&gt;Get-AzureRMApiManagementGitCredential&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;OutFile &lt;span class=&quot;token function&quot;&gt;Get-AzureRMApiManagementGitCredential&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Unblock the script using the PowerShell command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Unblock-File&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;&lt;span class=&quot;token function&quot;&gt;Get-AzureRMApiManagementGitCredential&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h3 id=&quot;using-the-script&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/get-azure-api-management-git-credentials-using-powershell/#using-the-script&quot; class=&quot;heading-anchor&quot;&gt;Using the Script&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;Use the &lt;strong&gt;Login-AzureRMAccount&lt;/strong&gt; cmdlet to authenticate to Azure. This would normally be done using a &lt;strong&gt;Service Principal&lt;/strong&gt; if using an automated process, but could be done interactively when testing.&lt;/li&gt;&lt;li&gt;Execute the script providing the &lt;strong&gt;SubscriptionId&lt;/strong&gt;, &lt;strong&gt;ResourceGroup&lt;/strong&gt; and &lt;strong&gt;ServiceName&lt;/strong&gt; parameters (and optionally the &lt;strong&gt;KeyType&lt;/strong&gt; and &lt;strong&gt;ExpiryTimespan&lt;/strong&gt;) using the following PowerShell command:&lt;/li&gt;&lt;/ol&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;&lt;span class=&quot;token function&quot;&gt;Get-AzureRMApiManagementGitCredential&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1 `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SubscriptionId &lt;span class=&quot;token string&quot;&gt;&#39;605e2ba7-056b-4982-a48b-12ff1da3c038&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroup &lt;span class=&quot;token string&quot;&gt;&#39;apimanagement-shrp-p-rgp&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServiceName &lt;span class=&quot;token string&quot;&gt;&#39;ApiManagementdsrshrpp&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;KeyType &lt;span class=&quot;token string&quot;&gt;&#39;primary&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ExpiryTimespan &lt;span class=&quot;token string&quot;&gt;&#39;4:00:00&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/F-nikwmsUK-650.png 650w, https://danielscottraynsford.com/img/F-nikwmsUK-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/F-nikwmsUK-650.webp 650w, https://danielscottraynsford.com/img/F-nikwmsUK-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/F-nikwmsUK-650.jpeg&quot; alt=&quot;ss_apim_gitrepositoryinvoke&quot; width=&quot;960&quot; height=&quot;442&quot; srcset=&quot;https://danielscottraynsford.com/img/F-nikwmsUK-650.jpeg 650w, https://danielscottraynsford.com/img/F-nikwmsUK-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;The script will return an object containing the properties &lt;strong&gt;GitUsername&lt;/strong&gt; and &lt;strong&gt;GitPassword&lt;/strong&gt; that can be provided to Git when cloning the &lt;strong&gt;internal Git repository&lt;/strong&gt;.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The &lt;strong&gt;GitPassword&lt;/strong&gt; is &lt;strong&gt;not escaped&lt;/strong&gt; so can not be directly used within a Git Clone &lt;strong&gt;URL&lt;/strong&gt; without replacing any &lt;strong&gt;/&lt;/strong&gt; or &lt;strong&gt;@&lt;/strong&gt; with &lt;strong&gt;%2F&lt;/strong&gt; and &lt;strong&gt;%40&lt;/strong&gt; respectively.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;In the example above I generated an &lt;strong&gt;internal Git Credential&lt;/strong&gt; using the &lt;strong&gt;Primary Secret Key&lt;/strong&gt; that will expire in &lt;em&gt;4 hours&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Typically you’d assign the output of this script to a variable and use the properties to generate the URL to pass into the Git Clone. For example:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/4Dp41VwYtk-650.png 650w, https://danielscottraynsford.com/img/4Dp41VwYtk-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/4Dp41VwYtk-650.webp 650w, https://danielscottraynsford.com/img/4Dp41VwYtk-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/4Dp41VwYtk-650.jpeg&quot; alt=&quot;ss_apim_gitrepositoryclone&quot; width=&quot;960&quot; height=&quot;380&quot; srcset=&quot;https://danielscottraynsford.com/img/4Dp41VwYtk-650.jpeg 650w, https://danielscottraynsford.com/img/4Dp41VwYtk-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h2 id=&quot;tips&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/get-azure-api-management-git-credentials-using-powershell/#tips&quot; class=&quot;heading-anchor&quot;&gt;Tips&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;When cloning the &lt;strong&gt;internal Git Repository&lt;/strong&gt; you’ll need the clone &lt;strong&gt;URL&lt;/strong&gt; of the repository. This is always the name of your &lt;strong&gt;Azure API Management&lt;/strong&gt; instance followed by with &lt;strong&gt;&lt;a href=&quot;http://scm.azure-api.net&quot; rel=&quot;noopener&quot;&gt;scm.azure-api.net&lt;/a&gt;&lt;/strong&gt; appended to it E.g. &lt;strong&gt;&lt;a href=&quot;https://myapimanagementinstance.scm.azure-api.net&quot; rel=&quot;noopener&quot;&gt;https://myapimanagementinstance.scm.azure-api.net&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Once you’ve uploaded a new &lt;strong&gt;Git branch&lt;/strong&gt; containing a new or updated Azure API Management configuration you’ll need to use the &lt;strong&gt;Publish-AzureRmApiManagementTenantGitConfiguration&lt;/strong&gt; cmdlet to tell &lt;strong&gt;Azure API Management&lt;/strong&gt; to publish the configuration contained in the branch. &lt;em&gt;I have not detailed this process here, but if there is interest I can cover the entire end-to-end process.&lt;/em&gt;&lt;/li&gt;&lt;li&gt;The &lt;strong&gt;Primary&lt;/strong&gt; and &lt;strong&gt;Secondary&lt;/strong&gt; &lt;strong&gt;Secret Keys&lt;/strong&gt; that are used to generate the &lt;strong&gt;internal Git Credential&lt;/strong&gt; can be &lt;strong&gt;re-generated&lt;/strong&gt; (rolled) individually if a Git credential is compromised. However, this will &lt;em&gt;invalidate&lt;/em&gt; all Git Credentials generated using that &lt;strong&gt;Secret Key&lt;/strong&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;the-script&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/get-azure-api-management-git-credentials-using-powershell/#the-script&quot; class=&quot;heading-anchor&quot;&gt;The Script&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you wish to review the script itself, here it is:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;param&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $True)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$SubscriptionId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $True)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$ResourceGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $True)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$ServiceName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter()]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;ValidateSet&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;primary&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;secondary&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$KeyType&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;primary&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter()]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[timespan]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$ExpiryTimespan&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;New-Timespan&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Hours 2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$context&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-AzureRmApiManagementContext&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$ResourceGroup&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServiceName &lt;span class=&quot;token variable&quot;&gt;$ServiceName&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; Correction thanks to @Shaun Titus
&lt;span class=&quot;token variable&quot;&gt;$expiry&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ToUniversalTime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ExpiryTimespan&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$parameters&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;keyType&quot;&lt;/span&gt;= &lt;span class=&quot;token variable&quot;&gt;$KeyType&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;expiry&quot;&lt;/span&gt;= &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;{0:yyyy-MM-ddTHH:mm:ss.000Z}&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$expiry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$resourceId&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.ApiManagement/service/{2}/users/git&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$SubscriptionId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ResourceGroup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ServiceName&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$gitUsername&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;apim&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$gitPassword&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Invoke-AzureRmResourceAction&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Action &lt;span class=&quot;token string&quot;&gt;&#39;token&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceId &lt;span class=&quot;token variable&quot;&gt;$resourceId&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Parameters &lt;span class=&quot;token variable&quot;&gt;$parameters&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ApiVersion &lt;span class=&quot;token string&quot;&gt;&#39;2016-10-10&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value

&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    GitUsername = &lt;span class=&quot;token variable&quot;&gt;$gitUsername&lt;/span&gt;
    GitPassword = &lt;span class=&quot;token variable&quot;&gt;$gitPassword&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So, hopefully that will be enough information to get anyone else started on building a CI/CD pipeline for deploying &lt;strong&gt;Azure API Management&lt;/strong&gt; configurations.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Sonatype Nexus Containers with Persistent Storage in Azure Container Instances</title>
      <link href="https://danielscottraynsford.com/blog/sonatype-nexus-containers-with-persistent-storage-in-azure-container-instances/" />
      <updated>2017-08-06T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/sonatype-nexus-containers-with-persistent-storage-in-azure-container-instances/</id>
      <content type="html">
				&lt;p&gt;On the &lt;a href=&quot;https://dscottraynsford.wordpress.com/2017/08/05/persistent-storage-in-azure-container-instances/&quot; rel=&quot;noopener&quot;&gt;back of yesterdays post&lt;/a&gt; on running Azure Container Instance containers with persistent storage, I thought I’d try a couple of other containers with &lt;a href=&quot;https://dscottraynsford.wordpress.com/2017/08/05/persistent-storage-in-azure-container-instances/&quot; rel=&quot;noopener&quot;&gt;my script&lt;/a&gt;.&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;I don’t actually plan on running any of these apps, I just wanted to test out the process and my scripts to identify any problems.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;I tried:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://hub.docker.com/r/sonatype/nexus/&quot; rel=&quot;noopener&quot;&gt;Sonatype Nexus 2&lt;/a&gt; - sonatype/nexus:oss&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://hub.docker.com/r/sonatype/nexus3/&quot; rel=&quot;noopener&quot;&gt;Sonatype Nexus 3&lt;/a&gt; - sonatype/nexus3:latest&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://hub.docker.com/r/jenkins/jenkins/&quot; rel=&quot;noopener&quot;&gt;Jenkins&lt;/a&gt; - jenkins/jenkins&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;And here are the results of my tests:&lt;/p&gt;&lt;h2 id=&quot;sonatype-nexus-2&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/sonatype-nexus-containers-with-persistent-storage-in-azure-container-instances/#sonatype-nexus-2&quot; class=&quot;heading-anchor&quot;&gt;Sonatype Nexus 2&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Works perfectly and the container starts up quickly (under 10 seconds):&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/YxoOnpCJ3w-650.png 650w, https://danielscottraynsford.com/img/YxoOnpCJ3w-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/YxoOnpCJ3w-650.webp 650w, https://danielscottraynsford.com/img/YxoOnpCJ3w-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/YxoOnpCJ3w-650.jpeg&quot; alt=&quot;ss_aci_sonatypenexus2&quot; width=&quot;960&quot; height=&quot;657&quot; srcset=&quot;https://danielscottraynsford.com/img/YxoOnpCJ3w-650.jpeg 650w, https://danielscottraynsford.com/img/YxoOnpCJ3w-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;I passed the following parameters to the script:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;&lt;span class=&quot;token function&quot;&gt;Install-AzureContainerInstancePersistStorage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1 `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServicePrincipalUsername &lt;span class=&quot;token string&quot;&gt;&#39;ce6fca5e-a22d-44b2-a75a-f3b20fcd1b16&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServicePrincipalPassword &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConvertTo-SecureString&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;String &lt;span class=&quot;token string&quot;&gt;&#39;JUJfenwe89hwNNF723ibw2YBybf238ybflA=&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AsPlainText &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TenancyId &lt;span class=&quot;token string&quot;&gt;&#39;8871b1ba-7d3d-45f3-8ee0-bb60c0e4733e&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SubscriptionName &lt;span class=&quot;token string&quot;&gt;&#39;Visual Studio Enterprise&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AppCode &lt;span class=&quot;token string&quot;&gt;&#39;nexus&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;UniqueCode &lt;span class=&quot;token string&quot;&gt;&#39;mine&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ContainerImage &lt;span class=&quot;token string&quot;&gt;&#39;sonatype/nexus:oss&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ContainerPort &lt;span class=&quot;token string&quot;&gt;&#39;8081&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VolumeName &lt;span class=&quot;token string&quot;&gt;&#39;nexus&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MountPoint &lt;span class=&quot;token string&quot;&gt;&#39;/sonatype-work/&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note: The Nexus 2 server is only accessible on the path /nexus/.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;sonatype-nexus-3&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/sonatype-nexus-containers-with-persistent-storage-in-azure-container-instances/#sonatype-nexus-3&quot; class=&quot;heading-anchor&quot;&gt;Sonatype Nexus 3&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Works perfectly but after takes at least a minute to be accessible after the container starts. But this is normal behavior for Nexus 3.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/6gSAzL95ss-650.png 650w, https://danielscottraynsford.com/img/6gSAzL95ss-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/6gSAzL95ss-650.webp 650w, https://danielscottraynsford.com/img/6gSAzL95ss-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/6gSAzL95ss-650.jpeg&quot; alt=&quot;ss_aci_sonatypenexus3&quot; width=&quot;960&quot; height=&quot;657&quot; srcset=&quot;https://danielscottraynsford.com/img/6gSAzL95ss-650.jpeg 650w, https://danielscottraynsford.com/img/6gSAzL95ss-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;I passed the following parameters to the script:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;&lt;span class=&quot;token function&quot;&gt;Install-AzureContainerInstancePersistStorage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1 `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServicePrincipalUsername &lt;span class=&quot;token string&quot;&gt;&#39;ce6fca5e-a22d-44b2-a75a-f3b20fcd1b16&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServicePrincipalPassword &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConvertTo-SecureString&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;String &lt;span class=&quot;token string&quot;&gt;&#39;JUJfenwe89hwNNF723ibw2YBybf238ybflA=&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AsPlainText &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TenancyId &lt;span class=&quot;token string&quot;&gt;&#39;8871b1ba-7d3d-45f3-8ee0-bb60c0e4733e&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SubscriptionName &lt;span class=&quot;token string&quot;&gt;&#39;Visual Studio Enterprise&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AppCode &lt;span class=&quot;token string&quot;&gt;&#39;nexus3&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;UniqueCode &lt;span class=&quot;token string&quot;&gt;&#39;mine&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ContainerImage &lt;span class=&quot;token string&quot;&gt;&#39;sonatype/nexus3:latest&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ContainerPort &lt;span class=&quot;token string&quot;&gt;&#39;8081&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VolumeName &lt;span class=&quot;token string&quot;&gt;&#39;nexus3&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MountPoint &lt;span class=&quot;token string&quot;&gt;&#39;/nexus-data/&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;jenkins&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/sonatype-nexus-containers-with-persistent-storage-in-azure-container-instances/#jenkins&quot; class=&quot;heading-anchor&quot;&gt;Jenkins&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Unfortunately Jenkins does not work with a persistent storage volume from an Azure Share. It seems to be trying to set the timestamp of the file that will contain the InitialAdminPassword, which is failing:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/FjKfhI65R4-650.png 650w, https://danielscottraynsford.com/img/FjKfhI65R4-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/FjKfhI65R4-650.webp 650w, https://danielscottraynsford.com/img/FjKfhI65R4-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/FjKfhI65R4-650.jpeg&quot; alt=&quot;ss_aci_jenkins&quot; width=&quot;960&quot; height=&quot;657&quot; srcset=&quot;https://danielscottraynsford.com/img/FjKfhI65R4-650.jpeg 650w, https://danielscottraynsford.com/img/FjKfhI65R4-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;I passed the following parameters to the script:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;&lt;span class=&quot;token function&quot;&gt;Install-AzureContainerInstancePersistStorage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1 `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServicePrincipalUsername &lt;span class=&quot;token string&quot;&gt;&#39;ce6fca5e-a22d-44b2-a75a-f3b20fcd1b16&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServicePrincipalPassword &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConvertTo-SecureString&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;String &lt;span class=&quot;token string&quot;&gt;&#39;JUJfenwe89hwNNF723ibw2YBybf238ybflA=&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AsPlainText &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TenancyId &lt;span class=&quot;token string&quot;&gt;&#39;8871b1ba-7d3d-45f3-8ee0-bb60c0e4733e&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SubscriptionName &lt;span class=&quot;token string&quot;&gt;&#39;Visual Studio Enterprise&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AppCode &lt;span class=&quot;token string&quot;&gt;&#39;jenkinshome&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;UniqueCode &lt;span class=&quot;token string&quot;&gt;&#39;dsr&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ContainerImage &lt;span class=&quot;token string&quot;&gt;&#39;jenkins/jenkins:lts&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ContainerPort &lt;span class=&quot;token string&quot;&gt;&#39;8080&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VolumeName &lt;span class=&quot;token string&quot;&gt;&#39;jenkinshome&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MountPoint &lt;span class=&quot;token string&quot;&gt;&#39;/var/jenkins_home/&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So, this is still a little bit hit and miss, but in general Azure Container Instances look like a very promising way to run different types of services in containers without a lot of overhead. With a bit of automation, this could turn out to be a cost effective way to quickly and easily run some common services.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Persistent Storage in Azure Container Instances</title>
      <link href="https://danielscottraynsford.com/blog/persistent-storage-in-azure-container-instances/" />
      <updated>2017-08-05T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/persistent-storage-in-azure-container-instances/</id>
      <content type="html">
				&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update 2018-04-26:&lt;/strong&gt; At some point Microsoft made a change to the requirements of the ARM template creating the Azure Container Instance. It now requires the Ports to be specified within the container as well as we the container group. I have &lt;strong&gt;improved the ARM template&lt;/strong&gt; to meet the current requirements.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update 2017-08-06:&lt;/strong&gt; I have &lt;strong&gt;improved the script&lt;/strong&gt; so that it is idempotent (can be run more than once and will only create anything that is missing). The Azure Container Instance resource group can be deleted once you’ve finished with the container and then recreated again with this same script when you next need it. The storage will be preserved in the separate storage account resource group. The script can now be run with the&amp;nbsp;&lt;strong&gt;-verbose&lt;/strong&gt; parameter and will produce much better progress information.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://azure.microsoft.com/en-us/services/container-instances/&quot; rel=&quot;noopener&quot;&gt;Azure Container Instances&lt;/a&gt; &lt;strong&gt;(ACI)&lt;/strong&gt; is a new resource type in &lt;strong&gt;Azure&lt;/strong&gt; that allows you to quickly and easily create containers without the complexity or overhead of &lt;a href=&quot;https://azure.microsoft.com/en-us/services/service-fabric/&quot; rel=&quot;noopener&quot;&gt;Azure Service Fabric&lt;/a&gt;, &lt;a href=&quot;https://azure.microsoft.com/en-us/services/container-service/&quot; rel=&quot;noopener&quot;&gt;Azure Container Services&lt;/a&gt; or provisioning a Windows Server 2016 VM.&lt;/p&gt;&lt;p&gt;It allows you to &lt;em&gt;quickly&lt;/em&gt; create containers that are &lt;em&gt;billed by the second&lt;/em&gt; from container images stored in &lt;strong&gt;Docker Hub&lt;/strong&gt; or your own &lt;a href=&quot;https://azure.microsoft.com/en-us/services/container-registry/&quot; rel=&quot;noopener&quot;&gt;Azure Container Registry&lt;/a&gt; &lt;strong&gt;(ACR)&lt;/strong&gt;. Even though this feature is still in preview, it is &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/container-instances/&quot; rel=&quot;noopener&quot;&gt;very easy to get up and running&lt;/a&gt; with it.&lt;/p&gt;&lt;p&gt;But this post &lt;em&gt;isn’t&lt;/em&gt; about creating basic container instances, it is about running container instances where some of the &lt;strong&gt;storage must persist&lt;/strong&gt;. This is a basic function of a container host, but if you don’t have access to the host storage then things get more difficult. That said, &lt;strong&gt;Azure Container Instances&lt;/strong&gt; do support &lt;em&gt;mounting Azure File Shares&lt;/em&gt; into the container as volumes. It is fairly easy to do, but requires quite a number of steps.&lt;/p&gt;&lt;p&gt;There is some provided &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/container-instances/container-instances-mounting-azure-files-volume&quot; rel=&quot;noopener&quot;&gt;documentation for persisting storage in a container instance&lt;/a&gt;, but it is quite a manual process and the &lt;strong&gt;example ARM templates are currently broken&lt;/strong&gt;: there are some typos and missing properties. So this post aims to make the whole thing a lot &lt;em&gt;simpler&lt;/em&gt; and &lt;em&gt;automatable&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;So in this post, I’m going to &lt;strong&gt;share a PowerShell function&lt;/strong&gt; and Azure Resource Manager (ARM) template that will allow you to easily provision an &lt;strong&gt;Azure Container Instance&lt;/strong&gt; with an &lt;strong&gt;Azure File Share&lt;/strong&gt; mounted. The process defaults to installing a &lt;a href=&quot;https://www.gocd.org/&quot; rel=&quot;noopener&quot;&gt;GoCD Server&lt;/a&gt; container (version 17.8.0 if you’re interested), but you could use it to &lt;strong&gt;install any other Linux Container that needs persistent storage&lt;/strong&gt;. The script is parameterized so other containers and mount points can be specified - e.g. it should be fairly easy to use this for other servers like &lt;a href=&quot;https://www.sonatype.com/download-oss-sonatype&quot; rel=&quot;noopener&quot;&gt;Sonatype Nexus&lt;/a&gt; or &lt;a href=&quot;https://jenkins.io/&quot; rel=&quot;noopener&quot;&gt;Jenkins Server&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Update 2017-08-06: I documented my findings trying out these other servers in &lt;a href=&quot;https://dscottraynsford.wordpress.com/2017/08/06/sonatype-nexus-containers-with-persistent-storage-in-azure-container-instances/&quot; rel=&quot;noopener&quot;&gt;my following blog post&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;requirements&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/persistent-storage-in-azure-container-instances/#requirements&quot; class=&quot;heading-anchor&quot;&gt;Requirements&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To perform this process you will need the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;PowerShell 5.0+ (PowerShell 4.0 may work, but I haven’t tested it).&lt;/li&gt;&lt;li&gt;The &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/azure/install-azurerm-ps?view=azurermps-4.2.0&quot; rel=&quot;noopener&quot;&gt;Azure PowerShell module installed&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Created an Application Service Principal - see below.&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;azure-service-principal&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/persistent-storage-in-azure-container-instances/#azure-service-principal&quot; class=&quot;heading-anchor&quot;&gt;Azure Service Principal&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Before you start this process you will need to have created an Application Service Principal in Azure that will be used to perform the deployment. Follow &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal#create-an-azure-active-directory-application&quot; rel=&quot;noopener&quot;&gt;the instructions on this page&lt;/a&gt; to create an application and then get the Service Principal from it.&lt;/p&gt;&lt;p&gt;You will need to record these values as they will be provided to the script later on:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Application Id&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Application Key&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Tenant Id&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Subscription Name&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;the-process&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/persistent-storage-in-azure-container-instances/#the-process&quot; class=&quot;heading-anchor&quot;&gt;The Process&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The process will perform the following tasks:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The &lt;strong&gt;Service Principal&lt;/strong&gt; is used to login to Azure to perform the deployment.&lt;/li&gt;&lt;li&gt;An &lt;strong&gt;Azure Resource Group&lt;/strong&gt; is created to contain a &lt;strong&gt;Azure Storage Account&lt;/strong&gt; and &lt;strong&gt;Azure Key Vault&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;An &lt;strong&gt;Azure Storage Account&lt;/strong&gt; is created and an &lt;strong&gt;Azure File Share&lt;/strong&gt; is created in it.&lt;/li&gt;&lt;li&gt;An &lt;strong&gt;Azure Key Vault&lt;/strong&gt; is created to store the &lt;strong&gt;Storage Account Key&lt;/strong&gt; and make it accessible to the &lt;strong&gt;Azure Container Instance&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;The &lt;strong&gt;Service Principal&lt;/strong&gt; is granted permission to the &lt;strong&gt;Azure Key Vault&lt;/strong&gt; to read and write secrets.&lt;/li&gt;&lt;li&gt;The key to the &lt;strong&gt;Storage Account Key&lt;/strong&gt; is added as a secret to the &lt;strong&gt;Azure Key Vault&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;The parameters are set in an ARM Template parameter file.&lt;/li&gt;&lt;li&gt;An &lt;strong&gt;Azure Resource Group&lt;/strong&gt; is created to contain the &lt;strong&gt;Azure Container Instance&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;the-script&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/persistent-storage-in-azure-container-instances/#the-script&quot; class=&quot;heading-anchor&quot;&gt;The Script&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This is the content of the script:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;[CmdletBinding()]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;param&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $True)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ServicePrincipalUsername&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $True)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[SecureString]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ServicePrincipalPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $True)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$TenancyId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $True)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$SubscriptionName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$AppCode&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;gocd&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# just a short code to identify this app&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$UniqueCode&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;dsr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# a short unique code to ensure that resources are unique&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ContainerImage&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;gocd/gocd-server:v17.8.0&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# the container image name and version to deploy&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ContainerPort&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;8153&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# The port to expose on the container&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$VolumeName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;gocd&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# The name of the volume to mount&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$MountPoint&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;/godata/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# The mount point&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Int]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$CPU&lt;/span&gt; = 1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# The number of CPUs to assign to the instance&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$MemoryInGB&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;1.5&#39;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# The amount of memory to assign to the instance&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$supportRGName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;{0}{1}rg&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$UniqueCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$AppCode&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$storageAccountName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;{0}{1}storage&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$UniqueCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$AppCode&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$storageShareName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;{0}{1}share&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$UniqueCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$AppCode&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$keyvaultName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;{0}{1}akv&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$UniqueCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$AppCode&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$keyvaultStorageSecretName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;{0}key&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$storageAccountName&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$aciRGName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;{0}{1}acirg&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$UniqueCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$AppCode&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$aciName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;{0}{1}aci&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$UniqueCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$AppCode&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$location&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;eastus&#39;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Login to Azure using Service Principal&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Connecting to Azure Subscription &quot;{0}&quot; using Service Principal account &quot;{1}&quot;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$SubscriptionName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ServicePrincipalUsername&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$servicePrincipalCredential&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TypeName &lt;span class=&quot;token string&quot;&gt;&#39;System.Management.Automation.PSCredential&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ArgumentList &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ServicePrincipalUsername&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ServicePrincipalPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Add-AzureRmAccount&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TenantId &lt;span class=&quot;token variable&quot;&gt;$TenancyId&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SubscriptionName &lt;span class=&quot;token variable&quot;&gt;$SubscriptionName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServicePrincipal &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Credential &lt;span class=&quot;token variable&quot;&gt;$servicePrincipalCredential&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Create resource group for Key Vault and Storage Account&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-not&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureRmResourceGroup&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$supportRGName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ErrorAction SilentlyContinue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Creating Resource Group &quot;{0}&quot; for Storage Account and Key Vault&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$supportRGName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-AzureRmResourceGroup&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$supportRGName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Location &lt;span class=&quot;token variable&quot;&gt;$location&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Create Key Vault&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-not&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureRmKeyVault&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$supportRGName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ErrorAction SilentlyContinue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Creating Key Vault &quot;{0}&quot; in Resource Group &quot;{1}&quot;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$supportRGName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-AzureRmKeyVault&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$supportRGName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Location &lt;span class=&quot;token variable&quot;&gt;$location&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;EnabledForTemplateDeployment &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;EnabledForDeployment
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Setting Key Vault &quot;{0}&quot; access policy to enable Service Principal &quot;{1}&quot; to Get,List and Set secrets&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ServicePrincipalUsername&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Set-AzureRmKeyVaultAccessPolicy&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$supportRGName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServicePrincipalName &lt;span class=&quot;token variable&quot;&gt;$ServicePrincipalUsername&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PermissionsToSecrets get&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Getting Key Vault &quot;{0}&quot; Id&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$keyvaultNameId&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureRmKeyVault&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ResourceId

&lt;span class=&quot;token comment&quot;&gt;# Create Storage Account&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-not&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureRmStorageAccount&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$supportRGName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$storageAccountName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ErrorAction SilentlyContinue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Creating Storage Account &quot;{0}&quot; in Resource Group &quot;{1}&quot;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$storageAccountName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$supportRGName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-AzureRmStorageAccount&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$supportRGName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$storageAccountName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SkuName Standard_LRS &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Location &lt;span class=&quot;token variable&quot;&gt;$location&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Getting Storage Account &quot;{0}&quot; key&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$storageAccountName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$storageAccountKey&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-AzureRmStorageAccountKey&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$supportRGName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$storageAccountName&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$storageConnectionString&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$storageAccountName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$storageAccountKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value
&lt;span class=&quot;token variable&quot;&gt;$storageContext&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-AzureStorageContext&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ConnectionString &lt;span class=&quot;token variable&quot;&gt;$storageConnectionString&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-not&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureStorageShare&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$storageShareName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Context &lt;span class=&quot;token variable&quot;&gt;$storageContext&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ErrorAction SilentlyContinue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Creating Azure Storage Share &quot;{0}&quot; in Storage Account {1}&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$storageShareName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$storageAccountName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-AzureStorageShare&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$storageShareName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Context &lt;span class=&quot;token variable&quot;&gt;$storageContext&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Add the Storage Key to the Key Vault&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Adding Storage Account &quot;{0}&quot; key to Key Vault &quot;{1}&quot;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$storageAccountName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$keyvaultName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Set-AzureKeyVaultSecret&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyvaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$keyvaultStorageSecretName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SecretValue &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConvertTo-SecureString&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;String &lt;span class=&quot;token variable&quot;&gt;$storageAccountKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AsPlainText &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Create Azure Container Intstance&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-not&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureRmResourceGroup&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$aciRGName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ErrorAction SilentlyContinue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Creating Resource Group &quot;{0}&quot; for Container Group&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$aciRGName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-AzureRmResourceGroup&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$aciRGName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Location &lt;span class=&quot;token variable&quot;&gt;$location&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Generate the azure deployment parameters&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$azureDeployParametersPath&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Join-Path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$PSScriptRoot&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ChildPath &lt;span class=&quot;token string&quot;&gt;&#39;aci-azuredeploy.parameters.json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$azureDeployPath&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Join-Path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$PSScriptRoot&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ChildPath &lt;span class=&quot;token string&quot;&gt;&#39;aci-azuredeploy.json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$azureDeployParameters&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;ConvertFrom-Json&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;InputObject &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Content&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$azureDeployParametersPath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Raw&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$azureDeployParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;containername&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value = &lt;span class=&quot;token variable&quot;&gt;$aciName&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$azureDeployParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;containerimage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value = &lt;span class=&quot;token variable&quot;&gt;$ContainerImage&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$azureDeployParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cpu&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value = &lt;span class=&quot;token variable&quot;&gt;$CPU&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$azureDeployParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;memoryingb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value = &lt;span class=&quot;token variable&quot;&gt;$MemoryInGB&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$azureDeployParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;containerport&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value = &lt;span class=&quot;token variable&quot;&gt;$ContainerPort&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$azureDeployParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sharename&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value = &lt;span class=&quot;token variable&quot;&gt;$storageShareName&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$azureDeployParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;storageaccountname&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value = &lt;span class=&quot;token variable&quot;&gt;$storageAccountName&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$azureDeployParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;storageaccountkey&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reference&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyVault&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id = &lt;span class=&quot;token variable&quot;&gt;$keyvaultNameId&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$azureDeployParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;storageaccountkey&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reference&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secretName = &lt;span class=&quot;token variable&quot;&gt;$keyvaultStorageSecretName&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$azureDeployParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;volumename&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value = &lt;span class=&quot;token variable&quot;&gt;$VolumeName&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$azureDeployParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mountpoint&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value = &lt;span class=&quot;token variable&quot;&gt;$MountPoint&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Set-Content&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$azureDeployParametersPath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Value &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConvertTo-Json&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;InputObject &lt;span class=&quot;token variable&quot;&gt;$azureDeployParameters&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Depth 6&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force

&lt;span class=&quot;token variable&quot;&gt;$deploymentName&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$azureDeployPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;BaseName &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;-&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ToUniversalTime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ToString&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;MMdd-HHmm&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Deploying Container Group &quot;{0}&quot; to Resource Group &quot;{1}&quot;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$aciName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$aciRGName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-AzureRmResourceGroupDeployment&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$deploymentName&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$aciRGName&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TemplateFile &lt;span class=&quot;token variable&quot;&gt;$azureDeployPath&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TemplateParameterFile &lt;span class=&quot;token variable&quot;&gt;$azureDeployParametersPath&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ErrorVariable errorMessages

&lt;span class=&quot;token comment&quot;&gt;# Get the container info and display it&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$subscriptionId&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureRmSubscription&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SubscriptionName &lt;span class=&quot;token variable&quot;&gt;$SubscriptionName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Id
&lt;span class=&quot;token variable&quot;&gt;$resourceId&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.ContainerInstance/containerGroups/{2}&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$subscriptionId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$aciRGName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$aciName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$containerState&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;Unknown&#39;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$containerState&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Running&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Waiting for container to enter running state&#39;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$containerResource&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-AzureRmResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceId &lt;span class=&quot;token variable&quot;&gt;$resourceId&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$containerState&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$containerResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Properties&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state
    &lt;span class=&quot;token function&quot;&gt;Start-Sleep&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Seconds 2
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Container is running on http://{0}:{1}&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$containerResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Properties&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ipAddress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ip&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$containerResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Properties&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ipAddress&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ports&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The script requires a four parameters to be provided:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;ServicePrincipalUsername&lt;/strong&gt; - the &lt;strong&gt;Application Id&lt;/strong&gt; obtained when creating the &lt;strong&gt;Service Principal&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ServicePrincipalPassword&lt;/strong&gt; - the &lt;strong&gt;Application Key&lt;/strong&gt; we got (or set) when creating the &lt;strong&gt;Service Principal&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;TenancyId&lt;/strong&gt; - The &lt;strong&gt;Tenancy Id&lt;/strong&gt; we got during the &lt;strong&gt;Service Principal&lt;/strong&gt; creation process.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;SubscriptionName&lt;/strong&gt; - the name of the subscription to install the ACI and other resources into.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;There are also some other optional parameters that can be provided that allow the container image that is used, the &lt;em&gt;TCP port the container&lt;/em&gt; listens on and &lt;em&gt;mount point&lt;/em&gt; for the &lt;strong&gt;Auzre File Share&lt;/strong&gt;. If you don’t provide these parameters will be used which will create a GoCD Server.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;AppCode&lt;/strong&gt; - A short code to identify this application. It gets added to the resource names and resource group names. Defaults to &lt;strong&gt;‘gocd’&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;UniqueCode&lt;/strong&gt; - this string is just used to ensure that globally unique names for the resources can be created. Defaults to ‘&lt;strong&gt;zzz&lt;/strong&gt;’.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ContainerImage&lt;/strong&gt; - this is the name and version of the container image to be deployed to the ACI. Defaults to ‘&lt;strong&gt;gocd/gocd-server:v17.8.0&lt;/strong&gt;’.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;CPU&lt;/strong&gt; - The number of cores to assign to the container instance. Defaults to &lt;strong&gt;1&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;MemoryInGB&lt;/strong&gt; - The amount of memory (in GB) to assign to the container instance. Defaults to &lt;strong&gt;1.5&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ContainerPort&lt;/strong&gt; - The port that the container listens on. Go CD Server defaults to 8153.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;VolumeName&lt;/strong&gt; - this is a volume name that is used to represent the volume in the ARM template. It can really be set to anything. Defaults to ‘&lt;strong&gt;gocd&lt;/strong&gt;’.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;MountPoint&lt;/strong&gt; - this is the folder in the Container that the &lt;strong&gt;Azure File Share&lt;/strong&gt; is mounted to. Defaults to ‘&lt;strong&gt;/godata/&lt;/strong&gt;’.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;arm-template-files&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/persistent-storage-in-azure-container-instances/#arm-template-files&quot; class=&quot;heading-anchor&quot;&gt;ARM Template Files&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;There are two other files that are required for this process:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;ARM template&lt;/strong&gt; - the ARM template file that will be used to install the ACI.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ARM template parameters&lt;/strong&gt; - this file will be used to pass in the settings to the ARM Template.&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;arm-template&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/persistent-storage-in-azure-container-instances/#arm-template&quot; class=&quot;heading-anchor&quot;&gt;ARM Template&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This file is called &lt;strong&gt;aci-azuredeploy.json&lt;/strong&gt; and should be downloaded to the same folder as the script above.&lt;/p&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;contentVersion&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;parameters&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;containername&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;containerimage&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;cpu&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;int&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;memoryingb&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;containerport&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;sharename&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;storageaccountname&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;storageaccountkey&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;securestring&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;volumename&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;mountpoint&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;resources&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;containername&#39;)]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Microsoft.ContainerInstance/containerGroups&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;apiVersion&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2018-04-01&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;location&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[resourceGroup().location]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;containers&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;containername&#39;)]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;image&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;containerimage&#39;)]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;ports&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;port&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;containerport&#39;)]&quot;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;resources&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;requests&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token property&quot;&gt;&quot;cpu&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;cpu&#39;)]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token property&quot;&gt;&quot;memoryInGb&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;memoryingb&#39;)]&quot;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;volumeMounts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;volumename&#39;)]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;mountPath&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;mountpoint&#39;)]&quot;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;osType&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Linux&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;ipAddress&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Public&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;ports&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;protocol&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tcp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;port&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;containerport&#39;)]&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;volumes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;volumename&#39;)]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;azureFile&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;shareName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;sharename&#39;)]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;storageAccountName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;storageaccountname&#39;)]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;storageAccountKey&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[parameters(&#39;storageaccountkey&#39;)]&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;arm-template-parameters&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/persistent-storage-in-azure-container-instances/#arm-template-parameters&quot; class=&quot;heading-anchor&quot;&gt;ARM Template Parameters&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This file is called&amp;nbsp;&lt;strong&gt;aci-azuredeploy.parameters.json&lt;/strong&gt; and should be downloaded to the same folder as the script above.&lt;/p&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;contentVersion&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;parameters&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;containername&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;containerimage&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;cpu&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;memoryingb&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.5&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;containerport&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;sharename&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;storageaccountname&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;storageaccountkey&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;reference&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;keyVault&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token property&quot;&gt;&quot;secretName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;volumename&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;mountpoint&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;steps&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/persistent-storage-in-azure-container-instances/#steps&quot; class=&quot;heading-anchor&quot;&gt;Steps&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To use the script the following steps need to be followed:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Download the three files above (the script and the two ARM template files) and put them into the same folder:&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/0ud3sddNJ--643.png 643w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/0ud3sddNJ--643.webp 643w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/0ud3sddNJ--643.jpeg&quot; alt=&quot;ss_aci_filesrequires&quot; width=&quot;643&quot; height=&quot;93&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Open a &lt;strong&gt;PowerShell&lt;/strong&gt; window.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Change directory to the folder you place the files into by executing:&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;CD &lt;code&gt;&amp;lt;folder location&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Execute the script like this (passing in the variables):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;&lt;span class=&quot;token function&quot;&gt;Install-AzureContainerInstancePersistStorage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1 `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServicePrincipalUsername &lt;span class=&quot;token string&quot;&gt;&#39;ce6fca5e-a22d-44b2-a75a-f3b20fcd1b16&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServicePrincipalPassword &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConvertTo-SecureString&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;String &lt;span class=&quot;token string&quot;&gt;&#39;JUJfenwe89hwNNF723ibw2YBybf238ybflA=&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AsPlainText &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TenancyId &lt;span class=&quot;token string&quot;&gt;&#39;8871b1ba-7d3d-45f3-8ee0-bb60c0e4733e&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SubscriptionName &lt;span class=&quot;token string&quot;&gt;&#39;Visual Studio Enterprise&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AppCode &lt;span class=&quot;token string&quot;&gt;&#39;gocd&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;UniqueCode &lt;span class=&quot;token string&quot;&gt;&#39;mine&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ContainerImage &lt;span class=&quot;token string&quot;&gt;&#39;gocd/gocd-server:v17.8.0&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ContainerPort &lt;span class=&quot;token string&quot;&gt;&#39;8153&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VolumeName &lt;span class=&quot;token string&quot;&gt;&#39;gocd&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MountPoint &lt;span class=&quot;token string&quot;&gt;&#39;/godata/&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/XLy_or8DA7-650.png 650w, https://danielscottraynsford.com/img/XLy_or8DA7-960.png 960w, https://danielscottraynsford.com/img/XLy_or8DA7-1319.png 1319w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/XLy_or8DA7-650.webp 650w, https://danielscottraynsford.com/img/XLy_or8DA7-960.webp 960w, https://danielscottraynsford.com/img/XLy_or8DA7-1319.webp 1319w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/XLy_or8DA7-650.jpeg&quot; alt=&quot;ss_aci_executingscript&quot; width=&quot;1319&quot; height=&quot;118&quot; srcset=&quot;https://danielscottraynsford.com/img/XLy_or8DA7-650.jpeg 650w, https://danielscottraynsford.com/img/XLy_or8DA7-960.jpeg 960w, https://danielscottraynsford.com/img/XLy_or8DA7-1319.jpeg 1319w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The process will then begin and make take a few minutes to complete:&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/g7Mm-hXoj3-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/g7Mm-hXoj3-650.gif&quot; alt=&quot;ss_aci_creategocd&quot; width=&quot;650&quot; height=&quot;415&quot;&gt;&lt;/picture&gt;&lt;strong&gt;Note:&lt;/strong&gt; I’ve changed the keys to this service principal and deleted this storage account, so I using these Service Principal or Storage Account keys won’t work!&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Once completed you will be able to log in to the Azure Portal and find the newly created Resource Groups:&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/Yiar_BTkte-650.png 650w, https://danielscottraynsford.com/img/Yiar_BTkte-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Yiar_BTkte-650.webp 650w, https://danielscottraynsford.com/img/Yiar_BTkte-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Yiar_BTkte-650.jpeg&quot; alt=&quot;ss_aci_resourcegroup&quot; width=&quot;960&quot; height=&quot;687&quot; srcset=&quot;https://danielscottraynsford.com/img/Yiar_BTkte-650.jpeg 650w, https://danielscottraynsford.com/img/Yiar_BTkte-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Open the resource group &lt;strong&gt;*gocdacirg&lt;/strong&gt; and then select the container group &lt;strong&gt;*gocdaci&lt;/strong&gt;**😗*&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/qzu2lSxxlk-650.png 650w, https://danielscottraynsford.com/img/qzu2lSxxlk-960.png 960w, https://danielscottraynsford.com/img/qzu2lSxxlk-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/qzu2lSxxlk-650.webp 650w, https://danielscottraynsford.com/img/qzu2lSxxlk-960.webp 960w, https://danielscottraynsford.com/img/qzu2lSxxlk-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/qzu2lSxxlk-650.jpeg&quot; alt=&quot;ss_aci_getcontainerip&quot; width=&quot;1400&quot; height=&quot;655&quot; srcset=&quot;https://danielscottraynsford.com/img/qzu2lSxxlk-650.jpeg 650w, https://danielscottraynsford.com/img/qzu2lSxxlk-960.jpeg 960w, https://danielscottraynsford.com/img/qzu2lSxxlk-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The IP Address of the container is displayed. You can copy this and paste it into a browser window along with the port the container exposed. In the case of Go CD it is 8153:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/NtYoisvDlj-650.png 650w, https://danielscottraynsford.com/img/NtYoisvDlj-960.png 960w, https://danielscottraynsford.com/img/NtYoisvDlj-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/NtYoisvDlj-650.webp 650w, https://danielscottraynsford.com/img/NtYoisvDlj-960.webp 960w, https://danielscottraynsford.com/img/NtYoisvDlj-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/NtYoisvDlj-650.jpeg&quot; alt=&quot;ss_aci_runninggocdserver&quot; width=&quot;1400&quot; height=&quot;720&quot; srcset=&quot;https://danielscottraynsford.com/img/NtYoisvDlj-650.jpeg 650w, https://danielscottraynsford.com/img/NtYoisvDlj-960.jpeg 960w, https://danielscottraynsford.com/img/NtYoisvDlj-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The process is now completed.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The Azure Container Instance can now be &lt;strong&gt;deleted&lt;/strong&gt; and &lt;strong&gt;recreated&lt;/strong&gt; at will, to reduce cost or simply upgrade to a new version. The &lt;strong&gt;Azure File Share&lt;/strong&gt; will persist the data stored by the container into the mounted volume:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/KgvEVKYBty-650.png 650w, https://danielscottraynsford.com/img/KgvEVKYBty-960.png 960w, https://danielscottraynsford.com/img/KgvEVKYBty-1293.png 1293w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/KgvEVKYBty-650.webp 650w, https://danielscottraynsford.com/img/KgvEVKYBty-960.webp 960w, https://danielscottraynsford.com/img/KgvEVKYBty-1293.webp 1293w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/KgvEVKYBty-650.jpeg&quot; alt=&quot;ss_aci_storageexplorerfileshare&quot; width=&quot;1293&quot; height=&quot;761&quot; srcset=&quot;https://danielscottraynsford.com/img/KgvEVKYBty-650.jpeg 650w, https://danielscottraynsford.com/img/KgvEVKYBty-960.jpeg 960w, https://danielscottraynsford.com/img/KgvEVKYBty-1293.jpeg 1293w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Hopefully this process will help you implement persisted storage containers in Azure Container Instances more easily and quickly.&lt;/p&gt;&lt;p&gt;Thanks for reading!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Publish an Azure RM Web App using a Service Principal in PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/publish-an-azure-rm-web-app-using-a-service-principal-in-powershell/" />
      <updated>2017-07-12T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/publish-an-azure-rm-web-app-using-a-service-principal-in-powershell/</id>
      <content type="html">
				&lt;h2 id=&quot;introduction&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/publish-an-azure-rm-web-app-using-a-service-principal-in-powershell/#introduction&quot; class=&quot;heading-anchor&quot;&gt;Introduction&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Deploying an &lt;a href=&quot;https://azure.microsoft.com/en-us/services/app-service/web/&quot; rel=&quot;noopener&quot;&gt;Azure Web App&lt;/a&gt; is almost &lt;em&gt;stupidly simple.&lt;/em&gt; If I were to list the methods and tools I’d still be typing next week. The problem with many of these tools and process is that they do a whole lot of magic under the hood which makes the process difficult to manage in &lt;strong&gt;source control&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;I’m a big believer that all code (including deployment code) should be in the &lt;strong&gt;application source repository&lt;/strong&gt; so it can be run by &lt;strong&gt;any tool&lt;/strong&gt; or &lt;strong&gt;release pipeline&lt;/strong&gt; - including &lt;strong&gt;manually&lt;/strong&gt; by development teams. This ensures that whatever deployment process is used, it is the same no matter who or what runs it - and we end up &lt;strong&gt;continuously testing&lt;/strong&gt; the deployment code and process.&lt;/p&gt;&lt;p&gt;So I decided to go and find out how to deploy an &lt;a href=&quot;https://azure.microsoft.com/en-us/services/app-service/web/&quot; rel=&quot;noopener&quot;&gt;Azure Web App&lt;/a&gt; using &lt;strong&gt;PowerShell&lt;/strong&gt; using an &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-objects&quot; rel=&quot;noopener&quot;&gt;Service Principal&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&quot;where-is-publish-azurermwebsiteproject&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/publish-an-azure-rm-web-app-using-a-service-principal-in-powershell/#where-is-publish-azurermwebsiteproject&quot; class=&quot;heading-anchor&quot;&gt;Where is Publish-AzureRMWebsiteProject?&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;If you look through the &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/azure/overview?view=azurermps-4.1.0&quot; rel=&quot;noopener&quot;&gt;Azure PowerShell&lt;/a&gt; cmdlets you’ll find a &lt;strong&gt;service manager&lt;/strong&gt; one called &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/azure/publish-azurewebsiteproject?view=azuresmps-4.0.0&quot; rel=&quot;noopener&quot;&gt;Publish-AzureWebsiteProject&lt;/a&gt;. This cmdlet &lt;em&gt;looks like&lt;/em&gt; it should do the trick, but it isn’t suitable because it &lt;em&gt;requires authentication&lt;/em&gt; by a &lt;strong&gt;user account&lt;/strong&gt; &lt;strong&gt;instead of a service principal&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Only &lt;strong&gt;service principal&lt;/strong&gt; accounts can be authenticated using automation. Therefore using &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/azure/publish-azurewebsiteproject?view=azuresmps-4.0.0&quot; rel=&quot;noopener&quot;&gt;Publish-AzureWebsiteProject&lt;/a&gt; would only work if a development team member was able to &lt;strong&gt;interactively login&lt;/strong&gt;- which would prevent the same process being used for &lt;strong&gt;automation&lt;/strong&gt; or our &lt;strong&gt;continuous delivery&lt;/strong&gt; pipeline. The newer Azure Resource Manager cmdlets (*-AzureRM*) all support a login using a &lt;strong&gt;service principal&lt;/strong&gt;, but the problem is that there is no &lt;strong&gt;Publish-AzureRMWebsiteProject&lt;/strong&gt; cmdlet.&lt;/p&gt;&lt;p&gt;So, to work around this limitation I determined I had to use &lt;a href=&quot;https://www.iis.net/downloads/microsoft/web-deploy&quot; rel=&quot;noopener&quot;&gt;Web Deploy/MSDeploy&lt;/a&gt;. The purpose of this post is to &lt;strong&gt;share the PowerShell function/code&lt;/strong&gt; and process I used to do this. This will work with and without &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/app-service-web/web-sites-staged-publishing&quot; rel=&quot;noopener&quot;&gt;Web App deployment slots&lt;/a&gt;.&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;In my case our teams put all deployment code into a &lt;a href=&quot;https://github.com/psake/psake&quot; rel=&quot;noopener&quot;&gt;PowerShell PSake&lt;/a&gt; task in the &lt;em&gt;application source code repository&lt;/em&gt; to make it trivial for anyone to run the deployment. The &lt;strong&gt;continuous delivery pipeline&lt;/strong&gt; was also able to call the exact same task to perform the deployment. There is no requirement to use&amp;nbsp;&lt;a href=&quot;https://github.com/psake/psake&quot; rel=&quot;noopener&quot;&gt;PowerShell PSake&lt;/a&gt; - just a simple PowerShell script will do.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;the-code&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/publish-an-azure-rm-web-app-using-a-service-principal-in-powershell/#the-code&quot; class=&quot;heading-anchor&quot;&gt;The Code&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;So, I’ll start by just pasting the function that does performs the task:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;[CmdletBinding()]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $True)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[pscredential]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$Credential&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $True)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$TenantId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $True)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$SubscriptionId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $True)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$WebAppPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $True)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$ResourceGroupName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory = $True)]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$WebAppServiceName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter()]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$SlotName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token namespace&quot;&gt;[Parameter()]&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$MSDeployPath&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$env&lt;/span&gt;:ProgramFiles&#92;IIS&#92;Microsoft Web Deploy V3&#92;msdeploy.exe&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-not&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Test-Path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$MSDeployPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;Throw&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MSDeploy.exe not found at &#39;&lt;span class=&quot;token variable&quot;&gt;$MSDeployPath&lt;/span&gt;&#39;. Please install MSDeploy or specify the path to MSDeploy.exe on this system.&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Connect to Azure using SP&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$connectParameters&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Credential     = &lt;span class=&quot;token variable&quot;&gt;$Credential&lt;/span&gt;
    TenantId       = &lt;span class=&quot;token variable&quot;&gt;$TenantId&lt;/span&gt;
    SubscriptionId = &lt;span class=&quot;token variable&quot;&gt;$SubscriptionId&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Connecting to Azure.&#39;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Add-AzureRmAccount&lt;/span&gt; @connectParameters &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServicePrincipal
  
&lt;span class=&quot;token comment&quot;&gt;# If a slot name is passed ensure all cmdlets use it&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt;::IsNullOrEmpty&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$SlotName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$slotParameters&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$slotParameters&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Slot = &lt;span class=&quot;token variable&quot;&gt;$SlotName&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Get the Publishing profile from Azure&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$publishProfilePath&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Join-Path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:Temp &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ChildPath &lt;span class=&quot;token string&quot;&gt;&#39;publishprofile.xml&#39;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Getting publishing profile for web app&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-AzureRmWebAppSlotPublishingProfile&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;OutputFile &lt;span class=&quot;token variable&quot;&gt;$publishProfilePath&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Format WebDeploy `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$ResourceGroupName&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$WebAppServiceName&lt;/span&gt; `
    @slotParameters

&lt;span class=&quot;token comment&quot;&gt;# Stop the web app slot to make sure deployment is possible.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Stopping web app.&#39;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Stop-AzureRmWebAppSlot&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$ResourceGroupName&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$WebAppServiceName&lt;/span&gt; `
    @slotParameters

&lt;span class=&quot;token comment&quot;&gt;# Deploy the web site&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$source&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&quot;-source:contentPath=&lt;span class=&quot;token variable&quot;&gt;$WebAppPath&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$dest&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&quot;-dest:contentPath=d:&#92;home&#92;site&#92;wwwroot&#92;,publishSettings=&lt;span class=&quot;token variable&quot;&gt;$publishProfilePath&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Publising web app content.&#39;&lt;/span&gt;
&amp;amp; &lt;span class=&quot;token variable&quot;&gt;$MSDeployPath&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;-verb:sync&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$source&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Start the web app back up&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Starting web app.&#39;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Start-AzureRmWebAppSlot&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$ResourceGroupName&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$WebAppServiceName&lt;/span&gt; `
    @slotParameters

&lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Waiting for web app to start up...&#39;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$webappSlot&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-AzureRmWebAppSlot&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$ResourceGroupName&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$WebAppServiceName&lt;/span&gt; `
    @slotParameters

&lt;span class=&quot;token comment&quot;&gt;# Wait for the site to report that it has started (optional)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$webappSlot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Running&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Start-Sleep&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Seconds 1

    &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Waiting for web app to start up...&#39;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$webappSlot&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-AzureRmWebAppSlot&lt;/span&gt; `
        &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$ResourceGroupName&lt;/span&gt; `
        &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$WebAppServiceName&lt;/span&gt; `
        @slotParameters
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Web app deployment complete.&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Just save this file as &lt;strong&gt;Publish-AzureRMWebappProject.ps1&lt;/strong&gt; and you’re ready to start publishing (almost).&lt;/p&gt;&lt;p&gt;Before you can use this function you’ll need to get a few things sorted:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Create a &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-authenticate-service-principal#create-service-principal-with-password&quot; rel=&quot;noopener&quot;&gt;Service Principal with a password&lt;/a&gt; to use to deploy the web app using the instructions on &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-authenticate-service-principal#create-service-principal-with-password&quot; rel=&quot;noopener&quot;&gt;this page&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Make sure you have got the latest version of the Azure PowerShell Modules installed (I used v4.0.0). See &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/azure/install-azurerm-ps?view=azurermps-4.1.0&quot; rel=&quot;noopener&quot;&gt;this page&lt;/a&gt; for instructions.&lt;/li&gt;&lt;li&gt;Make sure you’ve got MSDeploy.exe installed on your computer - see &lt;a href=&quot;https://www.iis.net/downloads/microsoft/web-deploy&quot; rel=&quot;noopener&quot;&gt;this page&lt;/a&gt; for instructions. &lt;em&gt;You can pass the &lt;strong&gt;path&lt;/strong&gt; to MSDeploy.exe into the Publish-AzureRMWebappProject.ps1 using the &lt;strong&gt;MSDeployPath&lt;/strong&gt; parameter.&lt;/em&gt;&lt;/li&gt;&lt;li&gt;Gather the following things (there are many ways of doing that - but I’ll leave it up to you to figure out what works for you):&lt;ol&gt;&lt;li&gt;the &lt;strong&gt;Subscription Id&lt;/strong&gt; of the subscription you’ll be deploying to.&lt;/li&gt;&lt;li&gt;the &lt;strong&gt;Tenant Id&lt;/strong&gt; of the Azure Active Directory containing your Service Principal.&lt;/li&gt;&lt;li&gt;the &lt;strong&gt;Application Id&lt;/strong&gt; that was displayed to you when you created the &lt;strong&gt;Service Principal&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;the &lt;strong&gt;Password&lt;/strong&gt; you assigned when you created the &lt;strong&gt;Service Principal&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Once you have got all this information you can call the script above like this:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$SubscriptionId&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;3a54931f-5351-4ec4-9cf8-518e03257eff&#39;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Not real&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$TenantId&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;eef4615a-8a57-4519-99ea-e2a8bad20f82&#39;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Not real&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$Password&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;MyP@ssword99&#39;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Not real&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$Username&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;a3716a34-ae63-4ab8-8fb7-1e5f15ec3975&#39;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Not real&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$passwordSecure&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;ConvertTo-SecureString&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;String &lt;span class=&quot;token variable&quot;&gt;$Password&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AsPlainText &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force
&lt;span class=&quot;token variable&quot;&gt;$Credential&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TypeName PSCredential &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Username&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$passwordSecure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;&lt;span class=&quot;token function&quot;&gt;Publish-AzureRMWebappProject&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Credential &lt;span class=&quot;token variable&quot;&gt;$Credential&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SubscriptionId &lt;span class=&quot;token variable&quot;&gt;$SubscriptionId&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TenantId &lt;span class=&quot;token variable&quot;&gt;$TenantId&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;WebAppPath &lt;span class=&quot;token string&quot;&gt;&#39;C:&#92;Users&#92;Dan&#92;Source&#92;MyAwesomeWebApp&#92;debug&#92;netcoreapp1.1&#92;publish&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token string&quot;&gt;&#39;MyAwesomeWebApp&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;WebAppServiceName &lt;span class=&quot;token string&quot;&gt;&#39;WebApp&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SlotName &lt;span class=&quot;token string&quot;&gt;&#39;offline&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;You’ll need to make sure to replace the variables $SubscriptionId, $TenantId, $Password and $Username with the values for &lt;strong&gt;your Azure Subscription&lt;/strong&gt;, &lt;strong&gt;Tenancy&lt;/strong&gt; and &lt;strong&gt;Service Principal&lt;/strong&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;When everything is done correctly this is what happens when you run it (with -Verbose enabled):&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/LQLNB7Q49m-650.png 650w, https://danielscottraynsford.com/img/LQLNB7Q49m-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/LQLNB7Q49m-650.webp 650w, https://danielscottraynsford.com/img/LQLNB7Q49m-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/LQLNB7Q49m-650.jpeg&quot; alt=&quot;ss_webappdeploy_publishazurermwebappproject&quot; width=&quot;960&quot; height=&quot;449&quot; srcset=&quot;https://danielscottraynsford.com/img/LQLNB7Q49m-650.jpeg 650w, https://danielscottraynsford.com/img/LQLNB7Q49m-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;In the case above I was installing to a deployment staging slot called &lt;strong&gt;offline&lt;/strong&gt;, so the new version of my website wouldn’t have been visible in my &lt;strong&gt;production&lt;/strong&gt; slot until I called the &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/module/azurerm.websites/switch-azurermwebappslot?view=azurermps-4.1.0&quot; rel=&quot;noopener&quot;&gt;Swap-AzureRmWebAppSlot&lt;/a&gt; cmdlet to swap the &lt;strong&gt;offline&lt;/strong&gt; slot with my &lt;strong&gt;production&lt;/strong&gt; slot.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;All in all, this is fairly robust and allows our &lt;strong&gt;development teams&lt;/strong&gt; and our &lt;strong&gt;automation&lt;/strong&gt; and &lt;strong&gt;continuous delivery pipeline&lt;/strong&gt; to all use the exact same deployment code which reduces deployment failures.&lt;/p&gt;&lt;p&gt;If you’re interested in more details about the code/process, please feel free to ask questions.&lt;/p&gt;&lt;p&gt;Thanks for reading.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Change the Friendly Name of a Cert with PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/change-the-friendly-name-of-a-cert-with-powershell/" />
      <updated>2017-06-09T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/change-the-friendly-name-of-a-cert-with-powershell/</id>
      <content type="html">
				&lt;p&gt;While working on adding a new feature in the certificate request DSC resource, I came across this handy little trick: You can change the Friendly Name of a certificate using PowerShell.&lt;/p&gt;&lt;p&gt;All you need to do is identify the certificate using Get-ChildItem and then assign the new FriendlyName to it.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path Cert:&#92;LocalMachine&#92;My&#92;97CB2928C7AC163A750BF16CF1D2CF1A3DDAAA8E&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;FriendlyName = &lt;span class=&quot;token string&quot;&gt;&#39;New Cert Name&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/ExVJvsvsUI-650.png 650w, https://danielscottraynsford.com/img/ExVJvsvsUI-960.png 960w, https://danielscottraynsford.com/img/ExVJvsvsUI-1221.png 1221w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ExVJvsvsUI-650.webp 650w, https://danielscottraynsford.com/img/ExVJvsvsUI-960.webp 960w, https://danielscottraynsford.com/img/ExVJvsvsUI-1221.webp 1221w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ExVJvsvsUI-650.jpeg&quot; alt=&quot;ss_cert_changefriendlyname&quot; width=&quot;1221&quot; height=&quot;164&quot; srcset=&quot;https://danielscottraynsford.com/img/ExVJvsvsUI-650.jpeg 650w, https://danielscottraynsford.com/img/ExVJvsvsUI-960.jpeg 960w, https://danielscottraynsford.com/img/ExVJvsvsUI-1221.jpeg 1221w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/Ikxm5LLI_4-650.png 650w, https://danielscottraynsford.com/img/Ikxm5LLI_4-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Ikxm5LLI_4-650.webp 650w, https://danielscottraynsford.com/img/Ikxm5LLI_4-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Ikxm5LLI_4-650.jpeg&quot; alt=&quot;ss_cert_changefriendlynamecertlm&quot; width=&quot;960&quot; height=&quot;116&quot; srcset=&quot;https://danielscottraynsford.com/img/Ikxm5LLI_4-650.jpeg 650w, https://danielscottraynsford.com/img/Ikxm5LLI_4-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Sometimes PowerShell still surprises me at how easy it can make things. I didn’t need to search help or the internet - just typed it in and it worked!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Using Azure Key Vault with PowerShell - Part 1</title>
      <link href="https://danielscottraynsford.com/blog/using-azure-key-vault-with-powershell-part-1/" />
      <updated>2017-04-17T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/using-azure-key-vault-with-powershell-part-1/</id>
      <content type="html">
				&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/key-vault/&quot; rel=&quot;noopener&quot;&gt;Azure Key Vault&lt;/a&gt; is used to safeguard and manage &lt;strong&gt;cryptographic keys&lt;/strong&gt;, &lt;strong&gt;certificates&lt;/strong&gt; and &lt;strong&gt;secrets&lt;/strong&gt; used by cloud applications and services (you can still consume these on-premise though). This allows other application, services or users in an Azure subscription to store and retrieve these &lt;strong&gt;cryptographic keys&lt;/strong&gt;,&amp;nbsp;&lt;strong&gt;certificates&lt;/strong&gt; and &lt;strong&gt;secrets&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Once &lt;strong&gt;cryptographic keys&lt;/strong&gt;, &lt;strong&gt;certificates&lt;/strong&gt; and &lt;strong&gt;secrets&lt;/strong&gt; have been stored in a &lt;strong&gt;Azure Key Vault&lt;/strong&gt; access policies can be configured to provide access to them by other users or applications.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Azure Key Vault&lt;/strong&gt; also stores all past versions of a &lt;strong&gt;cryptographic key&lt;/strong&gt;, &lt;strong&gt;certificate&lt;/strong&gt; or &lt;strong&gt;secret&lt;/strong&gt; when they are updated. So this allows easily rolling back if anything breaks.&lt;/p&gt;&lt;p&gt;This post is going to show how:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Set up an Azure Key Vault using the &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/azure/overview&quot; rel=&quot;noopener&quot;&gt;PowerShell Azure Module&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Set &lt;strong&gt;administration access policies&lt;/strong&gt; on the &lt;strong&gt;Azure Key Vault&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Grant other &lt;em&gt;users&lt;/em&gt; or &lt;em&gt;applications&lt;/em&gt; &lt;strong&gt;access&lt;/strong&gt; to &lt;strong&gt;cryptographic keys&lt;/strong&gt;,&amp;nbsp;&lt;strong&gt;certificates&lt;/strong&gt; or &lt;strong&gt;secrets&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Add&lt;/strong&gt;, &lt;strong&gt;retrieve&lt;/strong&gt; and &lt;strong&gt;remove&lt;/strong&gt; a &lt;strong&gt;cryptographic key&lt;/strong&gt; from the &lt;strong&gt;Azure Key Vault&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Add&lt;/strong&gt;, &lt;strong&gt;retrieve&lt;/strong&gt; and &lt;strong&gt;remove&lt;/strong&gt; a &lt;strong&gt;secret&lt;/strong&gt; from the &lt;strong&gt;Azure Key Vault&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;requirements&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/using-azure-key-vault-with-powershell-part-1/#requirements&quot; class=&quot;heading-anchor&quot;&gt;Requirements&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Before getting started there is a few things that will be needed:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;An &lt;strong&gt;Azure account&lt;/strong&gt;. I’m sure you’ve already got one, but if not &lt;a href=&quot;https://azure.microsoft.com/en-us/free/&quot; rel=&quot;noopener&quot;&gt;create a free one here&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;The &lt;strong&gt;Azure PowerShell module&lt;/strong&gt; needs to be installed. &lt;a href=&quot;https://docs.microsoft.com/en-us/powershell/azure/install-azurerm-ps&quot; rel=&quot;noopener&quot;&gt;Click here&lt;/a&gt; for instructions on how install it.&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;install-the-key-vault&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/using-azure-key-vault-with-powershell-part-1/#install-the-key-vault&quot; class=&quot;heading-anchor&quot;&gt;Install the Key Vault&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The first task is to customize and install the Azure Key Vault using the following &lt;strong&gt;PowerShell script&lt;/strong&gt;.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# The name of the Azure subscription to install the Key Vault into&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$subscriptionName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;MySubscription&#39;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# The resource group that will contain the Key Vault to create to contain the Key Vault&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$resourceGroupName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;MyKeyVaultRG&#39;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# The name of the Key Vault to install&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;MyKeyVault&#39;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# The Azure data center to install the Key Vault to&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$location&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;southcentralus&#39;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# These are the Azure AD users that will have admin permissions to the Key Vault&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$keyVaultAdminUsers&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Joe Boggs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Jenny Biggs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Login to Azure&lt;/span&gt;
Login-AzureRMAccount

&lt;span class=&quot;token comment&quot;&gt;# Select the appropriate subscription&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Select-AzureRmSubscription&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SubscriptionName &lt;span class=&quot;token variable&quot;&gt;$subscriptionName&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Make the Key Vault provider is available&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Register-AzureRmResourceProvider&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ProviderNamespace Microsoft&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;KeyVault

&lt;span class=&quot;token comment&quot;&gt;# Create the Resource Group&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;New-AzureRmResourceGroup&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$resourceGroupName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Location &lt;span class=&quot;token variable&quot;&gt;$location&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Create the Key Vault (enabling it for Disk Encryption, Deployment and Template Deployment)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;New-AzureRmKeyVault&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$resourceGroupName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Location &lt;span class=&quot;token variable&quot;&gt;$location&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;EnabledForDiskEncryption &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;EnabledForDeployment &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;EnabledForTemplateDeployment

&lt;span class=&quot;token comment&quot;&gt;# Add the Administrator policies to the Key Vault&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$keyVaultAdminUser&lt;/span&gt; in &lt;span class=&quot;token variable&quot;&gt;$keyVaultAdminUsers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$UserObjectId&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureRmADUser&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SearchString &lt;span class=&quot;token variable&quot;&gt;$keyVaultAdminUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Id
    &lt;span class=&quot;token function&quot;&gt;Set-AzureRmKeyVaultAccessPolicy&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$resourceGroupName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ObjectId &lt;span class=&quot;token variable&quot;&gt;$UserObjectId&lt;/span&gt; `
        &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PermissionsToKeys all &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PermissionsToSecrets all &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PermissionsToCertificates all
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But first, the variables in the &lt;strong&gt;PowerShell script&lt;/strong&gt; need to be customized to suit. The variables in the &lt;strong&gt;PowerShell script&lt;/strong&gt; that needs to be set are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;$subscriptionName&lt;/strong&gt; - the name of the Azure subscription to install the Key Vault into.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;$resourceGroupName -&lt;/strong&gt; the name of the Resource Group to create to contain the Key Vault.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;$keyVaultName&lt;/strong&gt; - the name of the Key Vault to create.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;$location&lt;/strong&gt; - the Azure data center to install the Key Vault to (use &lt;strong&gt;Get-AzureRMLocation&lt;/strong&gt; to get a list of available Azure data centers).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;$keyVaultAdminUsers&lt;/strong&gt; - an array of users that will be given administrator (full control over &lt;strong&gt;cryptographic keys&lt;/strong&gt;, &lt;strong&gt;certificates&lt;/strong&gt; and &lt;strong&gt;secrets&lt;/strong&gt;). The user names specified must match the full name of users found in the &lt;strong&gt;Azure AD&lt;/strong&gt; assigned to the Azure tenancy.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/hI46NjQMWN-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/hI46NjQMWN-650.gif&quot; alt=&quot;ss_akv_create&quot; width=&quot;650&quot; height=&quot;346&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;It will take about 30 seconds for the Azure Key Vault to be installed. It will then show up in the Azure Subscription:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/axhOC4CDkB-650.png 650w, https://danielscottraynsford.com/img/axhOC4CDkB-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/axhOC4CDkB-650.webp 650w, https://danielscottraynsford.com/img/axhOC4CDkB-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/axhOC4CDkB-650.jpeg&quot; alt=&quot;ss_akv_createcompleteportal&quot; width=&quot;960&quot; height=&quot;547&quot; srcset=&quot;https://danielscottraynsford.com/img/axhOC4CDkB-650.jpeg 650w, https://danielscottraynsford.com/img/axhOC4CDkB-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h2 id=&quot;assigning-permissions&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/using-azure-key-vault-with-powershell-part-1/#assigning-permissions&quot; class=&quot;heading-anchor&quot;&gt;Assigning Permissions&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Once the &lt;strong&gt;Azure Key Vault&lt;/strong&gt; is setup and an administrator or two have been assigned, other access policies will usually need to be assigned to &lt;strong&gt;users&lt;/strong&gt; and/or &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-objects&quot; rel=&quot;noopener&quot;&gt;application or service principal&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;To create an &lt;strong&gt;access policy&lt;/strong&gt; to allow a &lt;strong&gt;user&lt;/strong&gt; to &lt;em&gt;get&lt;/em&gt; and &lt;em&gt;list&lt;/em&gt; &lt;strong&gt;cryptographic key****s&lt;/strong&gt;, &lt;strong&gt;certificates&lt;/strong&gt; and&amp;nbsp;&lt;strong&gt;secrets&lt;/strong&gt; if you know the &lt;strong&gt;User Principal Name&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Set-AzureRmKeyVaultAccessPolicy&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$resourceGroupName&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;UserPrincipalName &lt;span class=&quot;token string&quot;&gt;&#39;Joe.Boggs@contoso.com&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PermissionsToCertificates list&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;get `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PermissionsToKeys list&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;get `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PermissionsToSecrets list&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;get&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;Tthe above code assumes you still have the variables set from the ‘Install the Key Vault’ section.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;If you only have the &lt;strong&gt;full name&lt;/strong&gt; of the &lt;strong&gt;user&lt;/strong&gt; then you’ll need to look up the &lt;strong&gt;Object Id&lt;/strong&gt; for the user in the &lt;strong&gt;Azure AD&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$userObjectId&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureRmADUser&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SearchString &lt;span class=&quot;token string&quot;&gt;&#39;Joe Bloggs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Id
&lt;span class=&quot;token function&quot;&gt;Set-AzureRmKeyVaultAccessPolicy&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$resourceGroupName&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ObjectId &lt;span class=&quot;token variable&quot;&gt;$userObjectId&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PermissionsToCertificates list&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;get `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PermissionsToKeys list&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;get `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PermissionsToSecrets list&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;get&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;The above code assumes you still have the variables set from the ‘Install the Key Vault’ section.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;To create an &lt;strong&gt;access policy&lt;/strong&gt; to allow a &lt;strong&gt;service principal&lt;/strong&gt; or&amp;nbsp;&lt;strong&gt;application&lt;/strong&gt; to &lt;em&gt;get&lt;/em&gt; and &lt;em&gt;list&lt;/em&gt; &lt;strong&gt;cryptographic key****s&lt;/strong&gt; if you know the &lt;strong&gt;Application Id&lt;/strong&gt; (a GUID):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Set-AzureRmKeyVaultAccessPolicy&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$resourceGroupName&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServicePrincipalName &lt;span class=&quot;token string&quot;&gt;&#39;e9b1bc3c-4769-4a98-9014-b315fd2adf53&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PermissionsToCertificates list&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;get `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PermissionsToKeys list&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;get `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PermissionsToSecrets list&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;get&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;The above code assumes you still have the variables set from the ‘Install the Key Vault’ section.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Changing the values of the &lt;strong&gt;PermissionsToKeys&lt;/strong&gt;**,** &lt;strong&gt;PermissionsToCertificates&lt;/strong&gt; and &lt;strong&gt;PermissionsToSecrets&lt;/strong&gt; parameters in the cmdlets above allow different permissions to be set for each policy.&lt;/p&gt;&lt;p&gt;The available permissions for &lt;strong&gt;certificates&lt;/strong&gt;, &lt;strong&gt;keys&lt;/strong&gt; and &lt;strong&gt;secrets&lt;/strong&gt; are:&lt;/p&gt;&lt;p&gt;[gallery ids=“5624,5625” type=“rectangular”]&lt;/p&gt;&lt;p&gt;An &lt;strong&gt;access policy&lt;/strong&gt; can be removed from &lt;strong&gt;users&lt;/strong&gt; or &lt;strong&gt;service principals&lt;/strong&gt; using the&amp;nbsp;&lt;strong&gt;Remove-AzureRmKeyVaultAccessPolicy&lt;/strong&gt; cmdet:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Remove-AzureRmKeyVaultAccessPolicy&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceGroupName &lt;span class=&quot;token variable&quot;&gt;$resourceGroupName&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;UserPrincipalName &lt;span class=&quot;token string&quot;&gt;&#39;Joe.Boggs@contoso.com&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;The above code assumes you still have the variables set from the ‘Install the Key Vault’ section.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;working-with-secrets&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/using-azure-key-vault-with-powershell-part-1/#working-with-secrets&quot; class=&quot;heading-anchor&quot;&gt;Working with Secrets&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Secrets&lt;/strong&gt; can be &lt;em&gt;created&lt;/em&gt;, &lt;em&gt;updated, retrieved&lt;/em&gt; and &lt;em&gt;deleted&lt;/em&gt; by users or applications that have been assigned with the &lt;strong&gt;appropriate policy&lt;/strong&gt;.&lt;/p&gt;&lt;h3 id=&quot;creating/updating-secrets&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/using-azure-key-vault-with-powershell-part-1/#creating/updating-secrets&quot; class=&quot;heading-anchor&quot;&gt;Creating/Updating Secrets&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;To create a new secret, use the &lt;strong&gt;Set-AzureKeyVaultSecret&lt;/strong&gt; cmdlet:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Set-AzureKeyVaultSecret&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&#39;MyAdminPassword&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SecretValue &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConvertTo-SecureString&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;String &lt;span class=&quot;token string&quot;&gt;&#39;P@ssword!1&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AsPlainText &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;The above code assumes you still have the variables set from the ‘Install the Key Vault’ section.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;This will create a secret called &lt;strong&gt;MyAdminPassword&lt;/strong&gt; with the value &lt;strong&gt;P@ssword!1&lt;/strong&gt; in the &lt;strong&gt;Azure Key Vault&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;The secret can be updated to a new value using the same cmdlet:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Set-AzureKeyVaultSecret&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&#39;MyAdminPassword&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SecretValue &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConvertTo-SecureString&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;String &lt;span class=&quot;token string&quot;&gt;&#39;Sup3rS3cr3tP4ss!&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AsPlainText &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Additional parameters&lt;/strong&gt; can also be assigned to each version of a secret to control how it can be used:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;ContentType&lt;/strong&gt; - the type of content the secret contains (e.g. ‘txt’)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;NotBefore&lt;/strong&gt; - the date that the secret is valid after.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Expires&lt;/strong&gt; - the date the secret is valid until.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Disable&lt;/strong&gt; - marks the secret as disabled.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Tag&lt;/strong&gt; - assigns tags to the secret.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;For example:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Set-AzureKeyVaultSecret&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&#39;MyAdminPassword&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SecretValue &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConvertTo-SecureString&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;String &lt;span class=&quot;token string&quot;&gt;&#39;Sup3rS3cr3tP4ss!&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AsPlainText &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ContentType &lt;span class=&quot;token string&quot;&gt;&#39;txt&#39;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;NotBefore &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ToUniversalTime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Expires &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AddYears&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ToUniversalTime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Disable:&lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Tags @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Risk&#39;&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;High&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/042P0DH7y8-650.png 650w, https://danielscottraynsford.com/img/042P0DH7y8-960.png 960w, https://danielscottraynsford.com/img/042P0DH7y8-1264.png 1264w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/042P0DH7y8-650.webp 650w, https://danielscottraynsford.com/img/042P0DH7y8-960.webp 960w, https://danielscottraynsford.com/img/042P0DH7y8-1264.webp 1264w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/042P0DH7y8-650.jpeg&quot; alt=&quot;ss_akv_secretupdatewithparameters&quot; width=&quot;1264&quot; height=&quot;388&quot; srcset=&quot;https://danielscottraynsford.com/img/042P0DH7y8-650.jpeg 650w, https://danielscottraynsford.com/img/042P0DH7y8-960.jpeg 960w, https://danielscottraynsford.com/img/042P0DH7y8-1264.jpeg 1264w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h3 id=&quot;retrieving-secrets&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/using-azure-key-vault-with-powershell-part-1/#retrieving-secrets&quot; class=&quot;heading-anchor&quot;&gt;Retrieving Secrets&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;To retrieve the &lt;strong&gt;latest (current) version&lt;/strong&gt; of a secret, use the &lt;strong&gt;Get-AzureKeyVaultSecret&lt;/strong&gt; cmdlet:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$secretText&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureKeyVaultSecret&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&#39;MyAdminPassword&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SecretValue&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will assign the stored secret to the variable &lt;strong&gt;$secretText&lt;/strong&gt; as a &lt;strong&gt;SecureString&lt;/strong&gt;. This can then be passed to any other cmdlets that require a &lt;strong&gt;SecureString&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;To list &lt;strong&gt;all&lt;/strong&gt; the versions of a secret, add the &lt;strong&gt;IncludeVersions&lt;/strong&gt; parameter:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureKeyVaultSecret&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&#39;MyAdminPassword&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;IncludeVersions&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/6mBw6yJmaG-650.png 650w, https://danielscottraynsford.com/img/6mBw6yJmaG-960.png 960w, https://danielscottraynsford.com/img/6mBw6yJmaG-1264.png 1264w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/6mBw6yJmaG-650.webp 650w, https://danielscottraynsford.com/img/6mBw6yJmaG-960.webp 960w, https://danielscottraynsford.com/img/6mBw6yJmaG-1264.webp 1264w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/6mBw6yJmaG-650.jpeg&quot; alt=&quot;ss_akv_secretallhistory&quot; width=&quot;1264&quot; height=&quot;469&quot; srcset=&quot;https://danielscottraynsford.com/img/6mBw6yJmaG-650.jpeg 650w, https://danielscottraynsford.com/img/6mBw6yJmaG-960.jpeg 960w, https://danielscottraynsford.com/img/6mBw6yJmaG-1264.jpeg 1264w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;To retrieve a &lt;strong&gt;specific version&lt;/strong&gt; of a secret, use the &lt;strong&gt;Get-AzureKeyVaultSecret&lt;/strong&gt; cmdlet with the &lt;strong&gt;Version&lt;/strong&gt; parameter specified:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$secretText&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-AzureKeyVaultSecret&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&#39;MyAdminPassword&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Version &lt;span class=&quot;token string&quot;&gt;&#39;02218af0521749b084bb08bd13184efb&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;removing-secrets&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/using-azure-key-vault-with-powershell-part-1/#removing-secrets&quot; class=&quot;heading-anchor&quot;&gt;Removing Secrets&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Finally, to remove a secret use the &lt;strong&gt;Remove-AzureKeyVaultSecret&lt;/strong&gt; cmdlet:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Remove-AzureKeyVaultSecret&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VaultName &lt;span class=&quot;token variable&quot;&gt;$keyVaultName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&#39;MyAdminPassword&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That pretty much covers managing and using &lt;strong&gt;secrets&lt;/strong&gt; in &lt;strong&gt;Azure Key Vault&lt;/strong&gt; using &lt;strong&gt;PowerShell&lt;/strong&gt;**.**&lt;/p&gt;&lt;h2 id=&quot;cryptographic-keys-and-certificates&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/using-azure-key-vault-with-powershell-part-1/#cryptographic-keys-and-certificates&quot; class=&quot;heading-anchor&quot;&gt;Cryptographic keys and Certificates&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In the next part of this series I’ll cover using &lt;strong&gt;Azure Key Vault&lt;/strong&gt; to use and manage &lt;strong&gt;cryptographic keys&lt;/strong&gt; and &lt;strong&gt;certificates&lt;/strong&gt;. Thanks for sticking with me this far.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Downloading GitHub .GitIgnore templates with PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/downloading-github-gitignore-templates-with-powershell/" />
      <updated>2017-02-24T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/downloading-github-gitignore-templates-with-powershell/</id>
      <content type="html">
				&lt;p&gt;This will be a relatively short post today to get be back into the blogging rhythm. Most of my time has been spent of late working on the DSC Resource Kit adding &lt;a href=&quot;https://codecov.io/gh/PlagueHO/xNetworking/&quot; rel=&quot;noopener&quot;&gt;code coverage reporting&lt;/a&gt; and new xCertificate features.&lt;/p&gt;&lt;p&gt;So, today’s post shows how you can use some simple PowerShell code to pull down the list of &lt;a href=&quot;https://github.com/github/gitignore&quot; rel=&quot;noopener&quot;&gt;.gitIgnore templates from GitHub&lt;/a&gt; and then retrieve the one I wanted. There are lots of different ways I could have done this, but I decided to use the &lt;a href=&quot;https://developer.github.com/v3/gitignore/&quot; rel=&quot;noopener&quot;&gt;GitHub REST API&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;First up, lets get the list of available &lt;em&gt;.gitIgnore templates:&lt;/em&gt;&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$templateList&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Invoke-WebRequest&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;URI &lt;span class=&quot;token string&quot;&gt;&#39;https://api.github.com/gitignore/templates&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;UseBasicParsing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Content &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;ConvertFrom-JSON&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will get the list of &lt;em&gt;.GitIgnore templates&lt;/em&gt; to an array variable called &lt;strong&gt;$templateList&lt;/strong&gt;. I could then display the list to a user:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/1swtzjx-ZB-650.png 650w, https://danielscottraynsford.com/img/1swtzjx-ZB-960.png 960w, https://danielscottraynsford.com/img/1swtzjx-ZB-1287.png 1287w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/1swtzjx-ZB-650.webp 650w, https://danielscottraynsford.com/img/1swtzjx-ZB-960.webp 960w, https://danielscottraynsford.com/img/1swtzjx-ZB-1287.webp 1287w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/1swtzjx-ZB-650.jpeg&quot; alt=&quot;ss_ghgi_getgitignoretemplates&quot; width=&quot;1287&quot; height=&quot;506&quot; srcset=&quot;https://danielscottraynsford.com/img/1swtzjx-ZB-650.jpeg 650w, https://danielscottraynsford.com/img/1swtzjx-ZB-960.jpeg 960w, https://danielscottraynsford.com/img/1swtzjx-ZB-1287.jpeg 1287w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Now, all I need to do is to download the named &lt;em&gt;.gitIgnore Template&lt;/em&gt; to a folder:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Invoke-WebRequest&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;URI &lt;span class=&quot;token string&quot;&gt;&#39;https://api.github.com/gitignore/templates/VisualStudio&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;UseBasicParsing &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;Select-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ExpandProperty Content &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;ConvertFrom-JSON&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;Select-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ExpandProperty Source &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;Out-File&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FilePath &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gitignore&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will download the &lt;strong&gt;VisualStudio&lt;/strong&gt; &lt;em&gt;.giIgnore&lt;/em&gt; template and save it with the filename &lt;strong&gt;.gitignore&lt;/strong&gt; to the current folder.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/p-wVzgEdm7-650.png 650w, https://danielscottraynsford.com/img/p-wVzgEdm7-960.png 960w, https://danielscottraynsford.com/img/p-wVzgEdm7-1287.png 1287w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/p-wVzgEdm7-650.webp 650w, https://danielscottraynsford.com/img/p-wVzgEdm7-960.webp 960w, https://danielscottraynsford.com/img/p-wVzgEdm7-1287.webp 1287w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/p-wVzgEdm7-650.jpeg&quot; alt=&quot;ss_ghgi_getgitignorefile&quot; width=&quot;1287&quot; height=&quot;506&quot; srcset=&quot;https://danielscottraynsford.com/img/p-wVzgEdm7-650.jpeg 650w, https://danielscottraynsford.com/img/p-wVzgEdm7-960.jpeg 960w, https://danielscottraynsford.com/img/p-wVzgEdm7-1287.jpeg 1287w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;I could have specified a different &lt;em&gt;.gitIgnore template&lt;/em&gt; by changing the &lt;strong&gt;VisualStudio&lt;/strong&gt; in the URL to another template that appears in the &lt;strong&gt;$templateList&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;You might have noticed that I included the &lt;strong&gt;-UseBasicParsing&lt;/strong&gt; parameter in the &lt;strong&gt;Invoke-WebRequest&lt;/strong&gt; call. This is to ensure the cmdlet works on machines that don’t have Internet Explorer installed - e.g. Nano Server or Linux/OSX. I haven’t tried this on PowerShell running on Linux or OSX, but I can’t see any reason why it wouldn’t work on those OS’s.&lt;/p&gt;&lt;p&gt;The next steps for this code might be to get these included as some new cmdlets in &lt;a href=&quot;https://twitter.com/pcgeek86&quot; rel=&quot;noopener&quot;&gt;Trevor Sullivan’s&lt;/a&gt; &lt;a href=&quot;https://github.com/pcgeek86/PSGitHub&quot; rel=&quot;noopener&quot;&gt;PSGitHub PowerShell Module&lt;/a&gt;. You can download his module from the &lt;a href=&quot;https://www.powershellgallery.com/packages/PSGitHub&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt; if you’re not familiar with it.&lt;/p&gt;&lt;p&gt;Thanks for reading.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Using PFX Files in PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/using-pfx-files-in-powershell/" />
      <updated>2017-01-10T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/using-pfx-files-in-powershell/</id>
      <content type="html">
				&lt;p&gt;One of the things I’ve been working on lately is adding a new resource to the &lt;a href=&quot;https://github.com/PowerShell/xCertificate&quot; rel=&quot;noopener&quot;&gt;xCertificate DSC Resource module&lt;/a&gt; for exporting an certificate with (or without) the private key from the Windows Certificate Store as a .CER or .PFX file. The very insightful (and fellow DSC Resource maintainer) &lt;a href=&quot;https://twitter.com/johanljunggren&quot; rel=&quot;noopener&quot;&gt;@JohanLjunggren&lt;/a&gt; has been giving some really great direction on this new resource.&lt;/p&gt;&lt;p&gt;One of these suggested features was to be able to identify if the certificate chain within a PFX file is different to the chain in the Windows Certificate Store. This is because a PFX file can contain not just a single certificate but the entire trust chain required by the certificate being exported.&lt;/p&gt;&lt;p&gt;Therefore what we would need to do is be able to step through the certificates in the PFX and examine each one. It turns out this is pretty simple using the .NET Class:&lt;/p&gt;&lt;p&gt;&lt;code&gt;System.Security.Cryptography.X509Certificates.X509Certificate2Collection&lt;/code&gt;&lt;/p&gt;&lt;p&gt;So, to read the PFX in to a variable called $PFX all we need to do is this:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$PFXPath&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;d:&#92;Cert.pfx&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$PFXPassword&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;pass&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$PFX&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TypeName &lt;span class=&quot;token string&quot;&gt;&#39;System.Security.Cryptography.X509Certificates.X509Certificate2Collection&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$PFX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Import&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$PFXPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$PFXPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;[System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]&lt;/span&gt;::PersistKeySet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The $PFXPath variable is set to the path to the PFX file we’re going to read in. The $PFXPassword is a string (not SecureString) containing the password used to protect the PFX file when it was exported.&lt;/p&gt;&lt;p&gt;We now have all the certificates loaded into an array in the $PFX variable and work with them like any other array:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/NCyUfK5VYn-650.png 650w, https://danielscottraynsford.com/img/NCyUfK5VYn-960.png 960w, https://danielscottraynsford.com/img/NCyUfK5VYn-1286.png 1286w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/NCyUfK5VYn-650.webp 650w, https://danielscottraynsford.com/img/NCyUfK5VYn-960.webp 960w, https://danielscottraynsford.com/img/NCyUfK5VYn-1286.webp 1286w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/NCyUfK5VYn-650.jpeg&quot; alt=&quot;ss_readpfx_loadingthepfx&quot; width=&quot;1286&quot; height=&quot;196&quot; srcset=&quot;https://danielscottraynsford.com/img/NCyUfK5VYn-650.jpeg 650w, https://danielscottraynsford.com/img/NCyUfK5VYn-960.jpeg 960w, https://danielscottraynsford.com/img/NCyUfK5VYn-1286.jpeg 1286w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Now, that we have the #PFX array, we can identify the thumbprint of the certificate that was actually exported (as opposed to the certificates in the trust chain) by looking at the last array item:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$PFX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$PFX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Count-1&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fl&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I’m piping the output Format-List so we can see the entire x509 certificate details.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/Cvc9xJ506T-650.png 650w, https://danielscottraynsford.com/img/Cvc9xJ506T-960.png 960w, https://danielscottraynsford.com/img/Cvc9xJ506T-1286.png 1286w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Cvc9xJ506T-650.webp 650w, https://danielscottraynsford.com/img/Cvc9xJ506T-960.webp 960w, https://danielscottraynsford.com/img/Cvc9xJ506T-1286.webp 1286w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Cvc9xJ506T-650.jpeg&quot; alt=&quot;ss_readpfx_showissuedcertificate&quot; width=&quot;1286&quot; height=&quot;439&quot; srcset=&quot;https://danielscottraynsford.com/img/Cvc9xJ506T-650.jpeg 650w, https://danielscottraynsford.com/img/Cvc9xJ506T-960.jpeg 960w, https://danielscottraynsford.com/img/Cvc9xJ506T-1286.jpeg 1286w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;In the case of the DSC Resource we’ll compare the certificate thumbprint of the last certificate in the PFX with the thumbprint that of the certificate in the Windows Certificate Store that we’re wanting to export. If they’re different we will then perform another export using the Export-PFXCertificate cmdlet.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Protip: You can actually verify the certificate and the entire trust chain is valid and not expired by calling the verify method on the last certificate:&lt;/em&gt;&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Cert&lt;/span&gt; in &lt;span class=&quot;token variable&quot;&gt;$PFX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Cert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Subject&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; is valid: &lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Cert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Verify&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;)&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/FfHfaRST6N-650.png 650w, https://danielscottraynsford.com/img/FfHfaRST6N-926.png 926w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/FfHfaRST6N-650.webp 650w, https://danielscottraynsford.com/img/FfHfaRST6N-926.webp 926w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/FfHfaRST6N-650.jpeg&quot; alt=&quot;ss_readpfx_validateissuedcertificate&quot; width=&quot;926&quot; height=&quot;82&quot; srcset=&quot;https://danielscottraynsford.com/img/FfHfaRST6N-650.jpeg 650w, https://danielscottraynsford.com/img/FfHfaRST6N-926.jpeg 926w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;In the case above, the certificate I exported was actually invalid (it had expired):&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/WWfoPFv4Ge-405.png 405w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/WWfoPFv4Ge-405.webp 405w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/WWfoPFv4Ge-405.jpeg&quot; alt=&quot;ss_readpfx_expiredcertificate&quot; width=&quot;405&quot; height=&quot;515&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;So we could easily use the Validate method to test the certificates validity before we import them into the Windows Certificate Store. But beware, the Validate method will check that the certificate chain is trusted. To be trusted the entire chain must have been imported into the Windows Certificate Store in the appropriate stores (e.g. Trusted Root CA/Intermedicate CA stores).&lt;/em&gt;&lt;/p&gt;&lt;p&gt;So, finally this gives us the code required to implement the &lt;strong&gt;xCertificateExport&lt;/strong&gt; Resource in the DSC Resource Kit. We can now perform a comparison of the certificates a PFX file to ensure that they are the same as the certificates that have already been exported.&lt;/p&gt;&lt;p&gt;This information is not something that you might use every day, but hopefully it’s information that someone might find useful. So thank you for taking the time to read this.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Test Website SSL Certificates Continuously with PowerShell and Pester</title>
      <link href="https://danielscottraynsford.com/blog/test-website-ssl-certificates-continuously-with-powershell-and-pester/" />
      <updated>2016-12-23T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/test-website-ssl-certificates-continuously-with-powershell-and-pester/</id>
      <content type="html">
				&lt;p&gt;One of the most &lt;strong&gt;common problems&lt;/strong&gt; that our teams deal with is ensuring that &lt;strong&gt;SSL certificates&lt;/strong&gt; are &lt;strong&gt;working correctly&lt;/strong&gt;. We’ve all had that urgent call in telling us that the web site is down or some key API or authentication function is offline - only to find out it was caused by an expired certificate.&lt;/p&gt;&lt;p&gt;An easy way of preventing this situation would have been to set up a task that &lt;strong&gt;continuously tests&lt;/strong&gt; your &lt;strong&gt;SSL endpoints&lt;/strong&gt; (internal and external web apps and sites, REST API’s etc.) and warns us if:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The certificate is about to expire (with x days).&lt;/li&gt;&lt;li&gt;The SSL endpoint is using safe SSL protocols (e.g. TLS 1.2).&lt;/li&gt;&lt;li&gt;The certificate is using SHA256.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This seemed like a good task for Pester (or &lt;a href=&quot;https://dscottraynsford.wordpress.com/2016/10/23/continuously-testing-your-infrastructure-with-ovf-and-microsoft-operations-management-suite/&quot; rel=&quot;noopener&quot;&gt;Operation Validation Framework&lt;/a&gt;). So, after a bit of digging around I found &lt;a href=&quot;http://blog.whatsupduck.net/2014/10/checking-ssl-and-tls-versions-with-powershell.html&quot; rel=&quot;noopener&quot;&gt;this awesome blog post&lt;/a&gt; from &lt;a href=&quot;https://twitter.com/gpduck&quot; rel=&quot;noopener&quot;&gt;Chris Duck&lt;/a&gt; showing how to retrieve the certificate and SSL protocol information from an &lt;strong&gt;SSL endpoint&lt;/strong&gt; using &lt;strong&gt;PowerShell&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Chris’ post contained this PowerShell cmdlet:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;#
    .DESCRIPTION
    Outputs the SSL protocols that the client is able to successfully use to connect to a server.

    .PARAMETER ComputerName
    The name of the remote computer to connect to.

    .PARAMETER Port
    The remote port to connect to. The default is 443.

    .EXAMPLE
    Test-SslProtocol -ComputerName &quot;www.google.com&quot;

    ComputerName       : www.google.com
    Port               : 443
    KeyLength          : 2048
    SignatureAlgorithm : rsa-sha1
    Ssl2               : False
    Ssl3               : True
    Tls                : True
    Tls11              : True
    Tls12              : True

    .NOTES
    Copyright 2014 Chris Duck
    http://blog.whatsupduck.net

    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
#&amp;gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Test-SslProtocol&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true)]&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$ComputerName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;token namespace&quot;&gt;[Parameter(ValueFromPipelineByPropertyName=$true)]&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[int]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Port&lt;/span&gt; = 443
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;begin&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$ProtocolNames&lt;/span&gt; = &lt;span class=&quot;token namespace&quot;&gt;[System.Security.Authentication.SslProtocols]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Get-Member&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Static &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MemberType Property &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Where-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Filter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name &lt;span class=&quot;token operator&quot;&gt;-notin&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Default&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;None&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Foreach-Object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt; = &lt;span class=&quot;token namespace&quot;&gt;[Ordered]&lt;/span&gt;@&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Add&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ComputerName&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ComputerName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Add&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Port&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Port&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Add&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;KeyLength&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Add&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;SignatureAlgorithm&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token variable&quot;&gt;$ProtocolNames&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$ProtocolName&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$Socket&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Net&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Sockets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Socket&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; `
                &lt;span class=&quot;token namespace&quot;&gt;[System.Net.Sockets.SocketType]&lt;/span&gt;::Stream&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token namespace&quot;&gt;[System.Net.Sockets.ProtocolType]&lt;/span&gt;::Tcp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$Socket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ComputerName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Port&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$NetStream&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Net&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Sockets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NetworkStream&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Socket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$SslStream&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Net&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Security&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SslStream&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$NetStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$SslStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AuthenticateAsClient&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ComputerName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ProtocolName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$RemoteCertificate&lt;/span&gt; = &lt;span class=&quot;token namespace&quot;&gt;[System.Security.Cryptography.X509Certificates.X509Certificate2]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$SslStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RemoteCertificate
                &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;KeyLength&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$RemoteCertificate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PublicKey&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;KeySize
                &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;SignatureAlgorithm&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$RemoteCertificate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SignatureAlgorithm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;FriendlyName
                &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Certificate&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$RemoteCertificate&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Add&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ProtocolName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Add&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ProtocolName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$SslStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Close&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[PSCustomObject]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# function Test-SslProtocol&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So that was the hard part done, all I needed was to add this function to some &lt;a href=&quot;https://blogs.technet.microsoft.com/heyscriptingguy/2015/12/14/what-is-pester-and-why-should-i-care/&quot; rel=&quot;noopener&quot;&gt;Pester&lt;/a&gt; tests.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you are running these tests on an operating system older than Windows 10 or Windows Server 2016 then you will need to install the PowerShell Pester module by running this command in an Administrator PowerShell console:&lt;/p&gt;&lt;p&gt;Install-Module -Name Pester&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;So after a little bit of tinkering I ended up with a set of tests that I combined into the same file as Chris’ function from earlier. I called the file &lt;strong&gt;SSL.tests.ps1&lt;/strong&gt;. I used the file extension &lt;strong&gt;.tests.ps1&lt;/strong&gt; because that is the file extension &lt;strong&gt;Pester&lt;/strong&gt; looks for when it runs.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The tests are located at the bottom of the file below the Test-SslProtocol function.&lt;/p&gt;&lt;/blockquote&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;#
    .DESCRIPTION
    Outputs the SSL protocols that the client is able to successfully use to connect to a server.

    .PARAMETER ComputerName
    The name of the remote computer to connect to.

    .PARAMETER Port
    The remote port to connect to. The default is 443.

    .EXAMPLE
    Test-SslProtocol -ComputerName &quot;www.google.com&quot;

    ComputerName       : www.google.com
    Port               : 443
    KeyLength          : 2048
    SignatureAlgorithm : rsa-sha1
    Ssl2               : False
    Ssl3               : True
    Tls                : True
    Tls11              : True
    Tls12              : True

    .NOTES
    Copyright 2014 Chris Duck
    http://blog.whatsupduck.net

    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
#&amp;gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Test-SslProtocol&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true)]&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$ComputerName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;token namespace&quot;&gt;[Parameter(ValueFromPipelineByPropertyName=$true)]&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[int]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Port&lt;/span&gt; = 443
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;begin&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$ProtocolNames&lt;/span&gt; = &lt;span class=&quot;token namespace&quot;&gt;[System.Security.Authentication.SslProtocols]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Get-Member&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Static &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MemberType Property &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Where-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Filter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name &lt;span class=&quot;token operator&quot;&gt;-notin&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Default&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;None&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Foreach-Object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;process&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt; = &lt;span class=&quot;token namespace&quot;&gt;[Ordered]&lt;/span&gt;@&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Add&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ComputerName&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ComputerName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Add&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Port&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Port&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Add&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;KeyLength&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Add&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;SignatureAlgorithm&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token variable&quot;&gt;$ProtocolNames&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$ProtocolName&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$Socket&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Net&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Sockets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Socket&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; `
                &lt;span class=&quot;token namespace&quot;&gt;[System.Net.Sockets.SocketType]&lt;/span&gt;::Stream&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token namespace&quot;&gt;[System.Net.Sockets.ProtocolType]&lt;/span&gt;::Tcp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$Socket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ComputerName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Port&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$NetStream&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Net&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Sockets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NetworkStream&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Socket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$SslStream&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Net&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Security&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SslStream&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$NetStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$SslStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AuthenticateAsClient&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ComputerName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ProtocolName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$RemoteCertificate&lt;/span&gt; = &lt;span class=&quot;token namespace&quot;&gt;[System.Security.Cryptography.X509Certificates.X509Certificate2]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$SslStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RemoteCertificate
                &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;KeyLength&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$RemoteCertificate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PublicKey&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;KeySize
                &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;SignatureAlgorithm&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$RemoteCertificate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SignatureAlgorithm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;FriendlyName
                &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Certificate&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$RemoteCertificate&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Add&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ProtocolName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Add&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ProtocolName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$SslStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Close&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[PSCustomObject]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ProtocolStatus&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# function Test-SslProtocol&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# List of Web sites that we want to check the SSL on&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$WebSitesToTest&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;www.google.com&#39;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;www.bing.com&#39;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;www.yahoo.com&#39;&lt;/span&gt;    
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Number of days out to warn about certificate expiration&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$WarningThreshold&lt;/span&gt; = 14

Describe &lt;span class=&quot;token string&quot;&gt;&#39;SSL endpoints&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$WebSite&lt;/span&gt; in &lt;span class=&quot;token variable&quot;&gt;$WebSitesToTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        Context &lt;span class=&quot;token variable&quot;&gt;$WebSite&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$script&lt;/span&gt;:SSLResult = &lt;span class=&quot;token function&quot;&gt;Test-SslProtocol&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ComputerName &lt;span class=&quot;token variable&quot;&gt;$WebSite&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Port 443
            
            It &lt;span class=&quot;token string&quot;&gt;&#39;Should have Signature Algorithm of sha256RSA&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$script&lt;/span&gt;:SSLResult&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SignatureAlgorithm &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Be &lt;span class=&quot;token string&quot;&gt;&#39;sha256RSA&#39;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            
            It &lt;span class=&quot;token string&quot;&gt;&#39;Should support TLS1.2&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$script&lt;/span&gt;:SSLResult&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TLS12 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;BeTrue
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            
            It &lt;span class=&quot;token string&quot;&gt;&quot;Should not going to expire in &lt;span class=&quot;token variable&quot;&gt;$WarningThreshold&lt;/span&gt; days&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$script&lt;/span&gt;:SSLResult&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Certificate&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NotAfter &lt;span class=&quot;token operator&quot;&gt;-gt&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AddDays&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$WarningThreshold&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;BeTrue
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So, now to test these SSL endpoints all I need to do is run in a PowerShell console with the current folder set to the folder containing my &lt;strong&gt;SSL.tests.ps1&lt;/strong&gt; file:&lt;/p&gt;&lt;p&gt;cd C:&#92;SSLTests&#92;&lt;br&gt;Invoke-Pester&lt;/p&gt;&lt;p&gt;This is the result:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/ZxvsqFxP1o-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ZxvsqFxP1o-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ZxvsqFxP1o-650.jpeg&quot; alt=&quot;ss_testssl_pesteroutput&quot; width=&quot;650&quot; height=&quot;378&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This shows that all the SSL endpoint certificates being used by &lt;a href=&quot;http://google.com&quot; rel=&quot;noopener&quot;&gt;google.com&lt;/a&gt;, &lt;a href=&quot;http://bing.com&quot; rel=&quot;noopener&quot;&gt;bing.com&lt;/a&gt; and &lt;a href=&quot;http://yahoo.com&quot; rel=&quot;noopener&quot;&gt;yahoo.com&lt;/a&gt; are all valid SHA-256 certificates and aren’t going to expire in 14 days.&lt;/p&gt;&lt;p&gt;All I would then need to do is put this in a task to run every hour or so and perform some task when the tests fail:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;cd c:&#92;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Invoke-Pester&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PassThru&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;FailedCount &lt;span class=&quot;token operator&quot;&gt;-gt&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;Write-Host&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;An SSL Endpoint test failed. Notify someone here!&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point you will still need to use some mechanism to notify someone when they fail. One method could be to write an event into the &lt;strong&gt;Windows Event Log&lt;/strong&gt; and then use &lt;strong&gt;Microsoft Operations Management Suite&lt;/strong&gt; (or &lt;strong&gt;SCOM&lt;/strong&gt;) to monitor for this event and send an e-mail or other alert to the appropriate administrators.&lt;/p&gt;&lt;p&gt;&lt;em&gt;For an example showing how to use OMS to monitor custom events created by failed &lt;em&gt;Pester and OVF&lt;/em&gt; tests, see my previous article &lt;a href=&quot;https://dscottraynsford.wordpress.com/2016/10/23/continuously-testing-your-infrastructure-with-ovf-and-microsoft-operations-management-suite/&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;potential-improvements&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/test-website-ssl-certificates-continuously-with-powershell-and-pester/#potential-improvements&quot; class=&quot;heading-anchor&quot;&gt;Potential Improvements&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;There are a number of ways you could go about improving this process, which our teams have in fact implemented. If you’re considering implementing this process then you might want to also consider them:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Put the &lt;strong&gt;Test-SSLProtocol&lt;/strong&gt; cmdlet into a PowerShell Module that you can share easily throughout your organization.&lt;/li&gt;&lt;li&gt;Put your tests into &lt;strong&gt;source control&lt;/strong&gt; and have the task &lt;strong&gt;clone the tests&lt;/strong&gt; directly from source control every time they are run - this allows tests to be stored centrally and can be change tracked.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Parameterize&lt;/strong&gt; the tests so that you don’t have to hard code the endpoints to test in the script file. Parameters can be passed into Pester tests &lt;a href=&quot;http://wahlnetwork.com/2016/07/28/using-the-script-param-to-pass-parameters-into-pester-tests/&quot; rel=&quot;noopener&quot;&gt;fairly easily&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Use something like &lt;a href=&quot;https://jenkins.io/&quot; rel=&quot;noopener&quot;&gt;Jenkins&lt;/a&gt;, &lt;a href=&quot;https://technet.microsoft.com/en-us/library/hh205987(v=sc.12).aspx&quot; rel=&quot;noopener&quot;&gt;SCOM&lt;/a&gt; or &lt;a href=&quot;https://www.splunk.com/&quot; rel=&quot;noopener&quot;&gt;Splunk&lt;/a&gt; to run the tests continuously.&lt;/li&gt;&lt;li&gt;Run the tests in an &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/automation/automation-intro&quot; rel=&quot;noopener&quot;&gt;Azure Automation&lt;/a&gt; account in the Cloud.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Really, the options for implementing this methodology are nearly limitless. You can engineer a solution that will work for you and your teams, using whatever tools are at your disposal.&lt;/p&gt;&lt;p&gt;At the end of the day, the goal here should be:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Reduce the risk that your internal or external applications or websites are using bad certificates.&lt;/li&gt;&lt;li&gt;Reduce the risk that an application or website will be deployed without a valid certificate (write infrastructure tests before you deploy your infrastructure - &lt;a href=&quot;https://en.wikipedia.org/wiki/Test-driven_development&quot; rel=&quot;noopener&quot;&gt;TDD&lt;/a&gt; for operations).&lt;/li&gt;&lt;li&gt;Reduce the risk you’ll get woken up in the middle of the night with an expired certificate.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;So, in this holiday season, I hope this post helps you ensure your certificates won’t expire in the next two weeks and you won’t get called into fix a certificate problem when you should be lying on a beach in the sun (in the southern hemisphere anyway).&lt;/p&gt;&lt;p&gt;Have a good one!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Continuously Testing your Infrastructure with OVF and Microsoft Operations Management Suite</title>
      <link href="https://danielscottraynsford.com/blog/continuously-testing-your-infrastructure-with-ovf-and-microsoft-operations-management-suite/" />
      <updated>2016-10-22T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/continuously-testing-your-infrastructure-with-ovf-and-microsoft-operations-management-suite/</id>
      <content type="html">
				&lt;h2 id=&quot;introduction&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/continuously-testing-your-infrastructure-with-ovf-and-microsoft-operations-management-suite/#introduction&quot; class=&quot;heading-anchor&quot;&gt;Introduction&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;One of the cool new features in Windows Server 2016 is &lt;a href=&quot;https://github.com/PowerShell/Operation-Validation-Framework&quot; rel=&quot;noopener&quot;&gt;Operation Validation Framework&lt;/a&gt;. &lt;strong&gt;Operation Validation Framework (OVF)&lt;/strong&gt; is an (open source) PowerShell module that contains:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;A set of tools for executing validation of the operation of a system. It provides a way to organize and execute Pester tests which are written to validate operation (rather than limited feature tests)&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;One of the things I’ve been using &lt;strong&gt;OVF&lt;/strong&gt; for is to &lt;strong&gt;continuously test&lt;/strong&gt; parts of my infrastructure. Any time a failure occurs an &lt;strong&gt;error&lt;/strong&gt; event is written to the &lt;strong&gt;Event Log&lt;/strong&gt;. I then have &lt;strong&gt;Microsoft Operations Management Suite&lt;/strong&gt; set up to monitor my &lt;strong&gt;Event Log&lt;/strong&gt; for any &lt;strong&gt;errors&lt;/strong&gt; and then alert me (by e-mail) if they occur.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: OVF tests are just &lt;a href=&quot;https://github.com/pester/Pester&quot; rel=&quot;noopener&quot;&gt;Pester&lt;/a&gt; tests, so if you’ve ever written a Pester test then you’ll have no trouble at all with OVF. If you haven’t written a Pester test before, &lt;a href=&quot;http://www.powershellmagazine.com/2014/03/12/get-started-with-pester-powershell-unit-testing-framework/&quot; rel=&quot;noopener&quot;&gt;here is a great introduction&lt;/a&gt;. If you’re going to just learn one thing about PowerShell this year, I’d definitely suggest Pester.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;The great thing about &lt;strong&gt;OVF&lt;/strong&gt; is that it can test &lt;em&gt;&lt;strong&gt;anything&lt;/strong&gt;&lt;/em&gt; that PowerShell can get information about - which is practically anything. For some great examples showing how to use OVF to test your infrastructure, see &lt;a href=&quot;https://pshirwin.wordpress.com/2016/04/08/active-directory-operations-test/&quot; rel=&quot;noopener&quot;&gt;this article&lt;/a&gt; or &lt;a href=&quot;https://pshirwin.wordpress.com/2015/11/06/pester-script-to-test-dns-configuration/&quot; rel=&quot;noopener&quot;&gt;this article&lt;/a&gt; by the insightful &lt;a href=&quot;https://twitter.com/IrwinStrachan&quot; rel=&quot;noopener&quot;&gt;Irwin Strachan&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;In this article I’m going to show you how to set up a basic set of OVF tests to &lt;strong&gt;continuously test&lt;/strong&gt; some DNS components on a server, write any failures to the &lt;strong&gt;Event Log&lt;/strong&gt; and then configure &lt;strong&gt;Microsoft OMS&lt;/strong&gt; to monitor the Event Log for &lt;strong&gt;OVF failures&lt;/strong&gt; and alert you if and something breaks.&lt;/p&gt;&lt;p&gt;I am calling my tests &lt;strong&gt;ValidateDNS&lt;/strong&gt; which is reflected in the files and &lt;strong&gt;Event Log&lt;/strong&gt; &lt;strong&gt;Source&lt;/strong&gt; for the events that are created, but you can call these tests what ever you like. I’m also going to create my tests and related files as a &lt;strong&gt;PowerShell Module&lt;/strong&gt; with the same name (&lt;strong&gt;ValidateDNS&lt;/strong&gt;). You don’t have to do this - you could make a much simpler process, but for me it made sense because I could then publish my set of tests to my own &lt;a href=&quot;https://github.com/PowerShell/PSPrivateGallery&quot; rel=&quot;noopener&quot;&gt;private PowerShell Gallery&lt;/a&gt; or to a &lt;a href=&quot;https://www.sonatype.com/nexus-repository-oss&quot; rel=&quot;noopener&quot;&gt;Sonatype Nexus OSS&lt;/a&gt; server.&lt;/p&gt;&lt;p&gt;I am also going to assume you’ve got an &lt;a href=&quot;https://www.microsoft.com/en-us/cloud-platform/operations-management-suite&quot; rel=&quot;noopener&quot;&gt;Microsoft Operations Management Suite&lt;/a&gt; account all setup. If you don’t have an OMS account, you can &lt;a href=&quot;https://www.microsoft.com/en-us/cloud-platform/operations-management-suite-trial&quot; rel=&quot;noopener&quot;&gt;get a free trial one here&lt;/a&gt;. You should also have the &lt;strong&gt;OMS Windows Agent&lt;/strong&gt; installed onto the machine that will execute your OVF tests.&lt;/p&gt;&lt;h2 id=&quot;lets-do-this&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/continuously-testing-your-infrastructure-with-ovf-and-microsoft-operations-management-suite/#lets-do-this&quot; class=&quot;heading-anchor&quot;&gt;Let’s Do This!&lt;/a&gt;&lt;/h2&gt;&lt;h3 id=&quot;step-1-installing-the-operationvalidation-module&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/continuously-testing-your-infrastructure-with-ovf-and-microsoft-operations-management-suite/#step-1-installing-the-operationvalidation-module&quot; class=&quot;heading-anchor&quot;&gt;Step 1 - Installing the OperationValidation Module&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The &lt;strong&gt;OperationValidation&lt;/strong&gt; PowerShell module comes in-box with &lt;strong&gt;Windows Server 2016&lt;/strong&gt; (and &lt;strong&gt;Windows 10 AE&lt;/strong&gt;), but must be downloaded for earlier operating systems.&lt;/p&gt;&lt;p&gt;To download and install the &lt;strong&gt;OperationValidation&lt;/strong&gt; PowerShell module on earlier operating systems enter the following cmdlet in an Administrator PowerShell console:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name OperationValidation&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/U4fL3Rgyy2-650.png 650w, https://danielscottraynsford.com/img/U4fL3Rgyy2-877.png 877w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/U4fL3Rgyy2-650.webp 650w, https://danielscottraynsford.com/img/U4fL3Rgyy2-877.webp 877w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/U4fL3Rgyy2-650.jpeg&quot; alt=&quot;ss_ovfoms_installoperationvalidation&quot; width=&quot;877&quot; height=&quot;61&quot; srcset=&quot;https://danielscottraynsford.com/img/U4fL3Rgyy2-650.jpeg 650w, https://danielscottraynsford.com/img/U4fL3Rgyy2-877.jpeg 877w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This will download the module from the &lt;a href=&quot;https://www.powershellgallery.com/packages/OperationValidation&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: to download modules from the PowerShell Gallery you’ll either need &lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=50395&quot; rel=&quot;noopener&quot;&gt;WMF 5.0&lt;/a&gt; or &lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=51451&quot; rel=&quot;noopener&quot;&gt;PowerShell PackageManagement&lt;/a&gt; installed. I strongly recommend the former option if possible.&lt;/em&gt;&lt;/p&gt;&lt;h3 id=&quot;step-2-create-ovf-test-files&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/continuously-testing-your-infrastructure-with-ovf-and-microsoft-operations-management-suite/#step-2-create-ovf-test-files&quot; class=&quot;heading-anchor&quot;&gt;Step 2 - Create OVF Test Files&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The next step is to create the OVF tests in a file. OVF work best if they are contained in a specific folder structure in the &lt;strong&gt;PowerShell Modules&lt;/strong&gt; folder.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: you can create the the OVF tests in a different location if that works for you, but it requires specifying the &lt;strong&gt;-testFilePath&lt;/strong&gt; parameter when calling &lt;strong&gt;Invoke-OperationValidation&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Create a folder in your PowerShell Modules folder (c:&#92;program files&#92;WindowsPowerShell&#92;Modules) with the name of your tests. I used &lt;strong&gt;ValidateDNS&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;In the &lt;strong&gt;ValidateDNS&lt;/strong&gt; folder create the following folder structure:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Diagnostics&#92;&lt;ul&gt;&lt;li&gt;Simple_&#92;_&lt;/li&gt;&lt;li&gt;Comprehensive_&#92;_&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/96GNiJFbPk-202.png 202w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/96GNiJFbPk-202.webp 202w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/96GNiJFbPk-202.jpeg&quot; alt=&quot;ss_ovfoms_folderstructure&quot; width=&quot;202&quot; height=&quot;129&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;In the &lt;strong&gt;Simple&lt;/strong&gt; folder create a file called &lt;strong&gt;ValidateDNS.Simple.Tests.ps1&lt;/strong&gt; with the contents:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Describe &lt;span class=&quot;token string&quot;&gt;&#39;DNS&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    It &lt;span class=&quot;token string&quot;&gt;&#39;Should be running&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Service&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name DNS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Status &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be Running
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        
    &lt;span class=&quot;token variable&quot;&gt;$Forwarders&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-DnsServerForwarder&lt;/span&gt;
        
    It &lt;span class=&quot;token string&quot;&gt;&#39;First forwarder should be 8.8.8.8&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Forwarders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;IPAddress&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token string&quot;&gt;&#39;8.8.8.8&#39;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    It &lt;span class=&quot;token string&quot;&gt;&#39;Second forwarder should be 4.4.4.4&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Forwarders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;IPAddress&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token string&quot;&gt;&#39;4.4.4.4&#39;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    It &lt;span class=&quot;token string&quot;&gt;&#39;Should resolve microsoft.com&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Resolve-DnsName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Server LocalHost &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name microsoft&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Not &lt;span class=&quot;token keyword&quot;&gt;Throw&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Edit the tests and create any that are validate the things you want to test for.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The OVF tests above just check some basic settings of a DNS Server and so would normally be run on a Windows DNS Server. As noted above, you could write tests for almost anything, including validating things on other systems. I intentionally have setup one of the tests to fail for demonstration purposes (a gold star for anyone who can tell which test will fail).&lt;/p&gt;&lt;p&gt;&lt;em&gt;In a future article I’ll cover how to test components on remote machines so you can use a single central node to perform all your OVF testing.&lt;/em&gt;&lt;/p&gt;&lt;h3 id=&quot;step-3-create-a-module-for-running-the-tests&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/continuously-testing-your-infrastructure-with-ovf-and-microsoft-operations-management-suite/#step-3-create-a-module-for-running-the-tests&quot; class=&quot;heading-anchor&quot;&gt;Step 3 - Create a Module for Running the Tests&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Although we could just run the tests as is, the output will just end up in the console, which is not what we want here. We want any failed tests to be put into the &lt;strong&gt;Application Event Log&lt;/strong&gt;**.**&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Create a file called &lt;strong&gt;ValidateDNS.psm1&lt;/strong&gt; in the &lt;strong&gt;ValidateDNS&lt;/strong&gt; folder created earlier.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Add the following code to this &lt;strong&gt;ValidateDNS.psm1&lt;/strong&gt; file:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Invoke-ValidateDNS&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[cmdletbinding()]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$EventSource&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;ValidateDNS&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;token namespace&quot;&gt;[Int32]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$EventId&lt;/span&gt; = 10000&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;ValidateSet&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Simple&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Comprehensive&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$TestType&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;Simple&#39;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# Edit these settings&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# Add the Event Source if it doesn&#39;t exist&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-not&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;[system.diagnostics.eventlog]&lt;/span&gt;::SourceExists&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$EventSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[system.diagnostics.EventLog]&lt;/span&gt;::CreateEventSource&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$EventSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Application&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# if&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# Execute the tests&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$FailedTests&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Invoke-OperationValidation&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ModuleName ValidateDNS &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TestType &lt;span class=&quot;token variable&quot;&gt;$TestType&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Where-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Property Result &lt;span class=&quot;token operator&quot;&gt;-EQ&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Value &lt;span class=&quot;token string&quot;&gt;&#39;Failed&#39;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# Add the Failed tests to the Event Log&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$FailedTest&lt;/span&gt; in &lt;span class=&quot;token variable&quot;&gt;$FailedTests&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Write-EventLog&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;LogName Application `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Source &lt;span class=&quot;token variable&quot;&gt;$EventSource&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;EntryType Error `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token variable&quot;&gt;$FailedTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;EventId &lt;span class=&quot;token variable&quot;&gt;$EventId&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Category 0
        &lt;span class=&quot;token variable&quot;&gt;$EventId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# foreach&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Save the &lt;strong&gt;ValidateDNS.psm1&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The above file is a PowerShell Module will make available a single cmdlet called &lt;strong&gt;Invoke-ValidateDNS&lt;/strong&gt;. We can now just run &lt;strong&gt;Invoke-ValidateDNS&lt;/strong&gt; in a PowerShell console and the following tasks will be performed:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;create a new Event Source for the &lt;strong&gt;Applications Event Log&lt;/strong&gt; that we can use in OMS to identify any errors thrown by our tests.&lt;/li&gt;&lt;li&gt;Execute the OVF tests in &lt;strong&gt;ValidateDNS.Simple.Tests.ps1&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Add &lt;strong&gt;Error&lt;/strong&gt; entries to the &lt;strong&gt;Applications Event Log&lt;/strong&gt; for &lt;em&gt;each failed test&lt;/em&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;step-4-schedule-the-script&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/continuously-testing-your-infrastructure-with-ovf-and-microsoft-operations-management-suite/#step-4-schedule-the-script&quot; class=&quot;heading-anchor&quot;&gt;Step 4 - Schedule the Script&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This step we will create a Scheduled Task to run the cmdlet we created in Step 3. You could use the &lt;strong&gt;Task Scheduler UI&lt;/strong&gt; to do this, but this is a PowerShell blog after all, so here is a script you can run that will create the scheduled task:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$Cred&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-Credential&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;User to run task as&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$Action&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-ScheduledTaskAction&lt;/span&gt; `
    –Execute &lt;span class=&quot;token string&quot;&gt;&#39;PowerShell.exe&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Argument &lt;span class=&quot;token string&quot;&gt;&#39;-WindowStyle Hidden -Command &quot;Import-Module ValidateDNS; Invoke-ValidateDNS;&quot;&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$Trigger&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-ScheduledTaskTrigger&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Once `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;At &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Date&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Hour 0 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Minute 0 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Second 0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;RepetitionInterval &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;New-TimeSpan&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Minutes 60&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;RepetitionDuration &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;[System.TimeSpan]&lt;/span&gt;::MaxValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$Task&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-ScheduledTask&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Description &lt;span class=&quot;token string&quot;&gt;&#39;Validate DNS&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Action &lt;span class=&quot;token variable&quot;&gt;$Action&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Trigger &lt;span class=&quot;token variable&quot;&gt;$Trigger&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Register-ScheduledTask&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TaskName &lt;span class=&quot;token string&quot;&gt;&#39;Validate DNS&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;InputObject &lt;span class=&quot;token variable&quot;&gt;$Task&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;User &lt;span class=&quot;token variable&quot;&gt;$Cred&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UserName `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Password &lt;span class=&quot;token variable&quot;&gt;$Cred&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetNetworkCredential&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Password&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/aUWHKav-jL-650.png 650w, https://danielscottraynsford.com/img/aUWHKav-jL-877.png 877w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/aUWHKav-jL-650.webp 650w, https://danielscottraynsford.com/img/aUWHKav-jL-877.webp 877w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/aUWHKav-jL-650.jpeg&quot; alt=&quot;ss_ovfoms_scheduletask&quot; width=&quot;877&quot; height=&quot;314&quot; srcset=&quot;https://danielscottraynsford.com/img/aUWHKav-jL-650.jpeg 650w, https://danielscottraynsford.com/img/aUWHKav-jL-877.jpeg 877w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;You will be prompted for the account details to run the task under, so enter &lt;strong&gt;valid credentials&lt;/strong&gt; for this machine &lt;em&gt;that give the task the correct access to run the tests&lt;/em&gt;. E.g. if the tests need Local Administrator access to the machine to run correctly, then ensure the account assigned is a Local Administrator.&lt;/p&gt;&lt;p&gt;This will run the script every 60 minutes. You could adjust it easily to run more or less frequently if you want to. This is what the Task Scheduler UI will show:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/xmXv2OkAnF-650.png 650w, https://danielscottraynsford.com/img/xmXv2OkAnF-960.png 960w, https://danielscottraynsford.com/img/xmXv2OkAnF-1218.png 1218w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/xmXv2OkAnF-650.webp 650w, https://danielscottraynsford.com/img/xmXv2OkAnF-960.webp 960w, https://danielscottraynsford.com/img/xmXv2OkAnF-1218.webp 1218w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/xmXv2OkAnF-650.jpeg&quot; alt=&quot;ss_ovfoms_scheduletaskui.png&quot; width=&quot;1218&quot; height=&quot;651&quot; srcset=&quot;https://danielscottraynsford.com/img/xmXv2OkAnF-650.jpeg 650w, https://danielscottraynsford.com/img/xmXv2OkAnF-960.jpeg 960w, https://danielscottraynsford.com/img/xmXv2OkAnF-1218.jpeg 1218w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Every time the tests run and a test failure occurs the Application Event Log will show:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/mINMEFBJr3-650.png 650w, https://danielscottraynsford.com/img/mINMEFBJr3-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/mINMEFBJr3-650.webp 650w, https://danielscottraynsford.com/img/mINMEFBJr3-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/mINMEFBJr3-650.jpeg&quot; alt=&quot;ss_ovfoms_errorevent&quot; width=&quot;960&quot; height=&quot;591&quot; srcset=&quot;https://danielscottraynsford.com/img/mINMEFBJr3-650.jpeg 650w, https://danielscottraynsford.com/img/mINMEFBJr3-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Now that we have any test failures appearing in the Event Log, we can move onto Microsoft Operations Management Suite.&lt;/p&gt;&lt;h2 id=&quot;step-5-create-a-log-search-and-alert&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/continuously-testing-your-infrastructure-with-ovf-and-microsoft-operations-management-suite/#step-5-create-a-log-search-and-alert&quot; class=&quot;heading-anchor&quot;&gt;Step 5 - Create a Log Search and Alert&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As noted earlier, I’m assuming you have already set up the computer running your OVF tests to your OMS account as a data source:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/xe2goobrxp-650.png 650w, https://danielscottraynsford.com/img/xe2goobrxp-960.png 960w, https://danielscottraynsford.com/img/xe2goobrxp-1353.png 1353w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/xe2goobrxp-650.webp 650w, https://danielscottraynsford.com/img/xe2goobrxp-960.webp 960w, https://danielscottraynsford.com/img/xe2goobrxp-1353.webp 1353w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/xe2goobrxp-650.jpeg&quot; alt=&quot;ss_ovfoms_omsagent&quot; width=&quot;1353&quot; height=&quot;933&quot; srcset=&quot;https://danielscottraynsford.com/img/xe2goobrxp-650.jpeg 650w, https://danielscottraynsford.com/img/xe2goobrxp-960.jpeg 960w, https://danielscottraynsford.com/img/xe2goobrxp-1353.jpeg 1353w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;What we need to do now is create and save a new Log Search that will select our OVF test failures. To do this:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;In OMS, click the &lt;strong&gt;Log Search&lt;/strong&gt; button.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;In the &lt;strong&gt;Search&lt;/strong&gt; box enter (adjust the Source= if you used a different name for your tests in earlier tests):&lt;/p&gt;&lt;p&gt;(Type=Event) (EventLevelName=error) (Source=ValidateDNS)&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/9UHuqwm7pf-650.png 650w, https://danielscottraynsford.com/img/9UHuqwm7pf-960.png 960w, https://danielscottraynsford.com/img/9UHuqwm7pf-1351.png 1351w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/9UHuqwm7pf-650.webp 650w, https://danielscottraynsford.com/img/9UHuqwm7pf-960.webp 960w, https://danielscottraynsford.com/img/9UHuqwm7pf-1351.webp 1351w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/9UHuqwm7pf-650.jpeg&quot; alt=&quot;ss_ovfoms_omslogsearch&quot; width=&quot;1351&quot; height=&quot;900&quot; srcset=&quot;https://danielscottraynsford.com/img/9UHuqwm7pf-650.jpeg 650w, https://danielscottraynsford.com/img/9UHuqwm7pf-960.jpeg 960w, https://danielscottraynsford.com/img/9UHuqwm7pf-1351.jpeg 1351w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;You will now be shown all the events on &lt;strong&gt;all&lt;/strong&gt; &lt;strong&gt;computers&lt;/strong&gt; matching these criteria:&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/p89NEg4gSv-650.png 650w, https://danielscottraynsford.com/img/p89NEg4gSv-960.png 960w, https://danielscottraynsford.com/img/p89NEg4gSv-1351.png 1351w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/p89NEg4gSv-650.webp 650w, https://danielscottraynsford.com/img/p89NEg4gSv-960.webp 960w, https://danielscottraynsford.com/img/p89NEg4gSv-1351.webp 1351w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/p89NEg4gSv-650.jpeg&quot; alt=&quot;ss_ovfoms_omslogsearchevents&quot; width=&quot;1351&quot; height=&quot;900&quot; srcset=&quot;https://danielscottraynsford.com/img/p89NEg4gSv-650.jpeg 650w, https://danielscottraynsford.com/img/p89NEg4gSv-960.jpeg 960w, https://danielscottraynsford.com/img/p89NEg4gSv-1351.jpeg 1351w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;em&gt;From here you could further refine your search if you want, for example, I could have added additional filters on Computer or EventId. But for me this is all I needed.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt; to save the &lt;strong&gt;Log Search&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;In the &lt;strong&gt;Name&lt;/strong&gt; enter ‘Validate DNS Events’ and in the &lt;strong&gt;Category&lt;/strong&gt; enter ‘OVF’:&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/skJxmKduOo-407.png 407w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/skJxmKduOo-407.webp 407w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/skJxmKduOo-407.jpeg&quot; alt=&quot;ss_ovfoms_omslogsearchsave&quot; width=&quot;407&quot; height=&quot;336&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;You can actually enter whatever works for you here.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click the &lt;strong&gt;Alert&lt;/strong&gt; button to add a new &lt;strong&gt;Alert Rule&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Configure the Alert Rule as follows (customizing to suit you):&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/3gajheG4cY-650.png 650w, https://danielscottraynsford.com/img/3gajheG4cY-960.png 960w, https://danielscottraynsford.com/img/3gajheG4cY-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/3gajheG4cY-650.webp 650w, https://danielscottraynsford.com/img/3gajheG4cY-960.webp 960w, https://danielscottraynsford.com/img/3gajheG4cY-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/3gajheG4cY-650.jpeg&quot; alt=&quot;ss_ovfoms_omslogsearchalert&quot; width=&quot;1400&quot; height=&quot;753&quot; srcset=&quot;https://danielscottraynsford.com/img/3gajheG4cY-650.jpeg 650w, https://danielscottraynsford.com/img/3gajheG4cY-960.jpeg 960w, https://danielscottraynsford.com/img/3gajheG4cY-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt; to save the Alert.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;You’re now done!&lt;/p&gt;&lt;p&gt;The DNS Admins will now receive an e-mail whenever any of the DNS validation tests fail:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/_Nnmg9-E40-650.png 650w, https://danielscottraynsford.com/img/_Nnmg9-E40-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/_Nnmg9-E40-650.webp 650w, https://danielscottraynsford.com/img/_Nnmg9-E40-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/_Nnmg9-E40-650.jpeg&quot; alt=&quot;ss_ovfoms_omserroremail&quot; width=&quot;960&quot; height=&quot;620&quot; srcset=&quot;https://danielscottraynsford.com/img/_Nnmg9-E40-650.jpeg 650w, https://danielscottraynsford.com/img/_Nnmg9-E40-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;If you look down in the &lt;strong&gt;ParamaterXML&lt;/strong&gt; section you can even see the test that failed. So the DNS Admins can dive straight to the root of the problem.&lt;/p&gt;&lt;p&gt;How cool is that? Now we can feel more confident that problems will be noticed by our technical teams when they happen rather than waiting for an end user to complain.&lt;/p&gt;&lt;p&gt;Of course the tests above are fairly basic. They are just meant as an example of what sort of things can be done. Some of our teams have put together far more comprehensive sets of tests that validate things like ADFS tokens and SSL certificate validity.&lt;/p&gt;&lt;h2 id=&quot;final-thoughts&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/continuously-testing-your-infrastructure-with-ovf-and-microsoft-operations-management-suite/#final-thoughts&quot; class=&quot;heading-anchor&quot;&gt;Final Thoughts&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;There are a few things worth pointing about the process above:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;I chose to use &lt;strong&gt;OVF&lt;/strong&gt; to execute the tests but I could have just as easily used plain old &lt;strong&gt;Pester&lt;/strong&gt;. If it makes more sense to you to use Pester, go right ahead.&lt;/li&gt;&lt;li&gt;I used &lt;strong&gt;Microsoft OMS&lt;/strong&gt; to centrally monitor the events, but I could just of easily used Microsoft System Center Operations Manager (SCOM). There are many other alternatives as well. I chose OMS though because of the slick UI and &lt;a href=&quot;https://www.microsoft.com/en-us/cloud-platform/operations-management-suite-mobile-apps&quot; rel=&quot;noopener&quot;&gt;mobile apps&lt;/a&gt;. Use what works for you.&lt;/li&gt;&lt;li&gt;This guide is intended to show what sort of thing can be done. It isn’t intended to tell you what you must do or how you must do it. If there is something else that works better for you, then use it!&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Although I’ve focused on the technology and methods here, if you take away one thing from this article I’d like it to be this:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Continuous Testing&lt;/strong&gt; of your infrastructure is something that is &lt;strong&gt;really easy&lt;/strong&gt; to implement and &lt;strong&gt;has so many benefits&lt;/strong&gt;. It will allow &lt;strong&gt;you&lt;/strong&gt; and &lt;strong&gt;your stakeholders&lt;/strong&gt; to feel &lt;strong&gt;more confident&lt;/strong&gt; that &lt;strong&gt;problems are&lt;/strong&gt; &lt;strong&gt;not going unnoticed&lt;/strong&gt; and &lt;strong&gt;allow them to sleep better&lt;/strong&gt;. It will also ensure that &lt;strong&gt;when things do go wrong&lt;/strong&gt; (and they always do) that the &lt;strong&gt;first people to notice are the people who can do something about it&lt;/strong&gt;!&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Happy infrastructure testing!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Install Docker on Windows Server 2016 using DSC</title>
      <link href="https://danielscottraynsford.com/blog/install-docker-on-windows-server-2016-using-dsc/" />
      <updated>2016-10-15T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/install-docker-on-windows-server-2016-using-dsc/</id>
      <content type="html">
				&lt;p&gt;Windows Server 2016 is now GA and it contains some pretty exciting stuff. Chief among them for me is support for containers by way of &lt;a href=&quot;http://www.docker.com/&quot; rel=&quot;noopener&quot;&gt;Docker&lt;/a&gt;. So, one of the first things I did was start installing Windows Server 2016 VM’s (Server Core and Nano Server naturally) and installing Docker on them so I could begin experimenting with Docker Swarms and other cool stuff.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Edit: If you’re looking for a DSC configuration for setting up Docker on a Windows 10 Anniversary Edition machine, see the Windows 10 AE section below.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;At first I started using the &lt;a href=&quot;https://blog.docker.com/2016/09/build-your-first-docker-windows-server-container&quot; rel=&quot;noopener&quot;&gt;standard manual instructions&lt;/a&gt; provided by Docker, but this doesn’t really suit any kind of automation or infrastructure as code methodology. This of course was a good job for &lt;a href=&quot;https://msdn.microsoft.com/en-us/powershell/dsc/overview&quot; rel=&quot;noopener&quot;&gt;PowerShell Desired State Configuration (DSC)&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;So, what I did was put together a basic DSC config that I could load into a DSC Pull Server and build out lots of Docker nodes quickly and easily. This worked really nicely for me to build out lots of Windows Server 2016 Container hosts in very short order:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/TJd1w5-I_j-650.png 650w, https://danielscottraynsford.com/img/TJd1w5-I_j-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/TJd1w5-I_j-650.webp 650w, https://danielscottraynsford.com/img/TJd1w5-I_j-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/TJd1w5-I_j-650.jpeg&quot; alt=&quot;ss_dockerdsc_installing&quot; width=&quot;960&quot; height=&quot;501&quot; srcset=&quot;https://danielscottraynsford.com/img/TJd1w5-I_j-650.jpeg 650w, https://danielscottraynsford.com/img/TJd1w5-I_j-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;If you don’t have a DSC Pull server or you just want a simple script that you can use to quickly configure a Windows Server 2016 (Core or Core with GUI only) then read on.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note: This script and process is really just an example of how you can configure Docker Container hosts with DSC. In a real production environment you would probably want to use a DSC Pull Server.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;&lt;h2 id=&quot;get-it-done&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-docker-on-windows-server-2016-using-dsc/#get-it-done&quot; class=&quot;heading-anchor&quot;&gt;Get it Done&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Edit: After a suggestion from Michael Friis (&lt;a href=&quot;https://twitter.com/friism&quot; rel=&quot;noopener&quot;&gt;@friism&lt;/a&gt;) I have uploaded the script to the &lt;a href=&quot;https://www.powershellgallery.com/packages/Install-DockerOnWS2016UsingDSC&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt; and provided a simplified method of installation. The steps could be simplified even further into a single line, but I’ve kept them separate to show the process.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;&lt;h3 id=&quot;using-powershell-gallery&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-docker-on-windows-server-2016-using-dsc/#using-powershell-gallery&quot; class=&quot;heading-anchor&quot;&gt;Using PowerShell Gallery&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;On a &lt;strong&gt;Windows Server 2016 Server Core&lt;/strong&gt; or &lt;strong&gt;Windows Server 2016 Server Core with GUI&lt;/strong&gt; server:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Log on as a user with &lt;strong&gt;Local Administrator&lt;/strong&gt; privileges.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Start an &lt;strong&gt;Administrator PowerShell&lt;/strong&gt; console - if you’re using Server Core just enter &lt;strong&gt;PowerShell&lt;/strong&gt; at the command prompt:&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/n1zX9wXu8a-570.png 570w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/n1zX9wXu8a-570.webp 570w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/n1zX9wXu8a-570.jpeg&quot; alt=&quot;ss_dockerdsc_console&quot; width=&quot;570&quot; height=&quot;101&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Install the &lt;strong&gt;Install-DockerOnWS2016UsingDSC.ps1&lt;/strong&gt; script from the PowerShell Gallery using this command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Install-Script&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name Install-DockerOnWs2016UsingDSC&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;You may be asked to confirm installation of these modules, answer yes to any confirmations.&lt;/em&gt;&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/ogXQdZtUst-650.png 650w, https://danielscottraynsford.com/img/ogXQdZtUst-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ogXQdZtUst-650.webp 650w, https://danielscottraynsford.com/img/ogXQdZtUst-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ogXQdZtUst-650.jpeg&quot; alt=&quot;ss_dockerdsc_consolegetscript&quot; width=&quot;960&quot; height=&quot;218&quot; srcset=&quot;https://danielscottraynsford.com/img/ogXQdZtUst-650.jpeg 650w, https://danielscottraynsford.com/img/ogXQdZtUst-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Run the &lt;strong&gt;Install-DockerOnWS2016UsingDSC.ps1&lt;/strong&gt; script using:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Install-DockerOnWs2016UsingDSC&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/__4rksNF2d-650.png 650w, https://danielscottraynsford.com/img/__4rksNF2d-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/__4rksNF2d-650.webp 650w, https://danielscottraynsford.com/img/__4rksNF2d-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/__4rksNF2d-650.jpeg&quot; alt=&quot;ss_dockerdsc_consolerunscriptfromgallery&quot; width=&quot;960&quot; height=&quot;501&quot; srcset=&quot;https://danielscottraynsford.com/img/__4rksNF2d-650.jpeg 650w, https://danielscottraynsford.com/img/__4rksNF2d-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The script will run and reboot the server once. Not long after the reboot the Docker service will start up and you can get working with containers:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/ovpAGxjX8W-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ovpAGxjX8W-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ovpAGxjX8W-650.jpeg&quot; alt=&quot;ss_dockerdsc_consoledockerdetails&quot; width=&quot;650&quot; height=&quot;322&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;You’re now ready to start working with Containers.&lt;/p&gt;&lt;h3 id=&quot;the-older-method-without-powershell-gallery&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-docker-on-windows-server-2016-using-dsc/#the-older-method-without-powershell-gallery&quot; class=&quot;heading-anchor&quot;&gt;The Older Method (without PowerShell Gallery)&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;On a &lt;strong&gt;Windows Server 2016 Server Core&lt;/strong&gt; or &lt;strong&gt;Windows Server 2016 Server Core with GUI&lt;/strong&gt; server:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Log on as a user with &lt;strong&gt;Local Administrator&lt;/strong&gt; privileges.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Start an &lt;strong&gt;Administrator PowerShell&lt;/strong&gt; console - if you’re using Server Core just enter &lt;strong&gt;PowerShell&lt;/strong&gt; at the command prompt:&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/n1zX9wXu8a-570.png 570w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/n1zX9wXu8a-570.webp 570w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/n1zX9wXu8a-570.jpeg&quot; alt=&quot;ss_dockerdsc_console&quot; width=&quot;570&quot; height=&quot;101&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install the DSC Resources&lt;/strong&gt; required for the DSC configuration by executing these commands:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name xPSDesiredStateConfiguration
&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name xPendingReboot&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;You may be asked to confirm installation of these modules, answer yes to any confirmations.&lt;/em&gt;&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/WBPOYiN8aD-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/WBPOYiN8aD-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/WBPOYiN8aD-650.jpeg&quot; alt=&quot;ss_dockerdsc_consoleinstallresources&quot; width=&quot;650&quot; height=&quot;308&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Docker installation DSC script&lt;/strong&gt; by executing this command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Invoke-WebRequest&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Uri &lt;span class=&quot;token string&quot;&gt;&#39;https://gist.githubusercontent.com/PlagueHO/d9595cae1788f436b97bd4c90d50d72e/raw/1146baa2b1e0c8b3869004074b4c97bf71ce9c3c/Install-DockerOnWS2016ByDSC.ps1&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;OutFile &lt;span class=&quot;token string&quot;&gt;&#39;Install-DockerOnWS2016ByDSC.ps1&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/AAm_PY2TJH-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/AAm_PY2TJH-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/AAm_PY2TJH-650.jpeg&quot; alt=&quot;ss_dockerdsc_consoledownloadscript&quot; width=&quot;650&quot; height=&quot;59&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Run the Docker installation DSC script&lt;/strong&gt; by executing this command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;Install-DockerOnWS2016ByDSC&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/amEl8udt1Y-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/amEl8udt1Y-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/amEl8udt1Y-650.jpeg&quot; alt=&quot;ss_dockerdsc_consolerunscript&quot; width=&quot;650&quot; height=&quot;402&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The script will run and reboot the server once. Not long after the reboot the Docker service will start up and you can get working with containers:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/ovpAGxjX8W-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ovpAGxjX8W-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ovpAGxjX8W-650.jpeg&quot; alt=&quot;ss_dockerdsc_consoledockerdetails&quot; width=&quot;650&quot; height=&quot;322&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;You’re now ready to start working with Containers.&lt;/p&gt;&lt;h2 id=&quot;what-the-script-does&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-docker-on-windows-server-2016-using-dsc/#what-the-script-does&quot; class=&quot;heading-anchor&quot;&gt;What the Script Does&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In case you’re interested in what the script actually contains, here are the components:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Configuration ContainerHostDsc -&lt;/strong&gt; the &lt;a href=&quot;https://msdn.microsoft.com/en-us/powershell/dsc/configurations&quot; rel=&quot;noopener&quot;&gt;DSC configuration&lt;/a&gt; that configures the node as a Docker Container host.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Configuration ConfigureLCM&lt;/strong&gt; - the &lt;a href=&quot;https://msdn.microsoft.com/en-us/powershell/dsc/metaconfig&quot; rel=&quot;noopener&quot;&gt;LCM meta configuration&lt;/a&gt; that sets &lt;strong&gt;Push Mode&lt;/strong&gt;, allows the LCM to reboot the node if required and configures &lt;strong&gt;ApplyAndAutoCorrect&lt;/strong&gt; mode.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ConfigData&lt;/strong&gt; - a &lt;a href=&quot;https://msdn.microsoft.com/en-us/powershell/dsc/configdata&quot; rel=&quot;noopener&quot;&gt;ConfigData object&lt;/a&gt; that contains the list of node names to apply this DSC Configuration to - in this case LocalHost.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ConfigureLCM&lt;/strong&gt; - the call to the &lt;strong&gt;Configuration ConfigureLCM&lt;/strong&gt; to &lt;a href=&quot;https://msdn.microsoft.com/en-us/powershell/dsc/metaconfig&quot; rel=&quot;noopener&quot;&gt;compile the LCM meta configuration MOF file&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Set-DscLocalConfigurationManager&lt;/strong&gt; - this applies the compiled LCM meta configuration MOF file to LocalHost to configure the LCM.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ContainerHostDsc&lt;/strong&gt; - the call to the &lt;strong&gt;Configuration ContainerHostDsc&lt;/strong&gt; to compile the DSC MOF file.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Start-DSCConfiguration&lt;/strong&gt; - this command starts the LCM applying the DSC MOF file produces by the &lt;strong&gt;ContainerHostDsc&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The complete script can be found &lt;a href=&quot;https://danielscottraynsford.com/blog/install-docker-on-windows-server-2016-using-dsc/d9595cae1788f436b97bd4c90d50d72e&quot;&gt;here&lt;/a&gt;. Feel free to use this code in anyway that makes sense to you.&lt;/p&gt;&lt;h2 id=&quot;what-about-windows-10-ae&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-docker-on-windows-server-2016-using-dsc/#what-about-windows-10-ae&quot; class=&quot;heading-anchor&quot;&gt;What About Windows 10 AE?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you’re looking for a DSC configuration that does the same thing for &lt;em&gt;Windows 10 Anniversary edition&lt;/em&gt;, &lt;strong&gt;Ben Gelens&lt;/strong&gt; (&lt;a href=&quot;https://twitter.com/bgelens&quot; rel=&quot;noopener&quot;&gt;@bgelens&lt;/a&gt;) has written an awesome DSC config that will do the trick. Check it out &lt;a href=&quot;https://gist.github.com/bgelens/152fdc075b6ffcf639da775958076c6a&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Happy containering!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Easily Create a Hyper-V Windows Server 2016 AD &amp;amp; Nano Server Lab</title>
      <link href="https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/" />
      <updated>2016-10-04T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/</id>
      <content type="html">
				&lt;h2 id=&quot;introduction&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#introduction&quot; class=&quot;heading-anchor&quot;&gt;Introduction&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;One of the PowerShell Modules I’ve been working on for the last year is called &lt;a href=&quot;https://github.com/PlagueHO/LabBuilder&quot; rel=&quot;noopener&quot;&gt;LabBuilder&lt;/a&gt;.The goal of this module is:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;To automatically build a multiple machine Hyper-V Lab environment from an XML configuration file and other optional installation scripts.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;What this essentially does is allow you to easily build Lab environments using a specification file. All you need to do is provide the Hyper-V environment and the Operating System disk ISO files that will be used to build the lab. This is great for getting a Lab environment spun up for testing or training purposes.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Building a new Lab can take a little while, depending on the number of VM’s in the Lab as well as the number of different Operating Systems used. For example, a Lab with 10 VMs could take an hour or two to spin up, depending on your hardware.&lt;/p&gt;&lt;p&gt;The LabBuilder module comes with a set of sample Labs that you can build “as is” or modify for your own purpose. There are samples for simple one or two machine Labs as well as more complex scenarios such as failover clusters and two tier PKI environments. Plus, if you’re feeling adventurous you can easily create your own LabBuilder configurations from scratch or by modifying an existing LabBuilder configuration.&lt;/p&gt;&lt;p&gt;In this article I’ll show how to use a configuration sample that will build a lab containing the following servers:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;1 x Windows Server 2016 RTM Domain Controller (with DNS)&lt;/li&gt;&lt;li&gt;1 x Windows Server 2016 RTM DHCP Server&lt;/li&gt;&lt;li&gt;1 x Windows Server 2016 RTM Certificate Authority Server&lt;/li&gt;&lt;li&gt;1 x Windows Server 2016 RTM Edge Node (Routing and Remote Access server)&lt;/li&gt;&lt;li&gt;8 x Windows Server 2016 RTM Nano Servers (not yet automatically Domain Joined - but I’m working on it).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This is a great environment for experimenting with both Windows Server 2016 as well as Nano Server.&lt;/p&gt;&lt;p&gt;So, lets get started.&lt;/p&gt;&lt;h2 id=&quot;requirements&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#requirements&quot; class=&quot;heading-anchor&quot;&gt;Requirements&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To follow along with this guide your Lab host (the machine that will host your Lab) will need to have the following:&lt;/p&gt;&lt;h3 id=&quot;be-running-windows-server-2012-r2-windows-server-2016-or-windows-10&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#be-running-windows-server-2012-r2-windows-server-2016-or-windows-10&quot; class=&quot;heading-anchor&quot;&gt;Be running Windows Server 2012 R2, Windows Server 2016 or Windows 10&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I strongly recommend using &lt;em&gt;Windows 10 Anniversary Edition&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;If you are using Windows Server 2012 R2 you will need to &lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=50395&quot; rel=&quot;noopener&quot;&gt;install WMF 5.0 or above&lt;/a&gt;. Although WMF 4.0 should work, I haven’t tested it.&lt;/p&gt;&lt;h3 id=&quot;have-enough-ram-disk-and-cpu-available-for-your-lab&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#have-enough-ram-disk-and-cpu-available-for-your-lab&quot; class=&quot;heading-anchor&quot;&gt;&lt;strong&gt;Have enough RAM, Disk and CPU available for your Lab&lt;/strong&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Running a lot of VMs at once can be fairly taxing on your hardware. For most Sample Lab I’d recommend at least a quad core CPU, 16 GB RAM and a fast SSD with at least 10 GB per VM free (although for Nano Server VMs only 800MB is required).&lt;/p&gt;&lt;p&gt;&lt;strong&gt;The amount of disk used is minimized by using &lt;em&gt;differencing disks&lt;/em&gt;, but Labs can still get pretty big.&lt;/strong&gt;&lt;/p&gt;&lt;h3 id=&quot;hyper-v-enabled&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#hyper-v-enabled&quot; class=&quot;heading-anchor&quot;&gt;Hyper-V Enabled&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;If you’re using Windows 10, see &lt;a href=&quot;https://msdn.microsoft.com/en-us/virtualization/hyperv_on_windows/quick_start/walkthrough_install&quot; rel=&quot;noopener&quot;&gt;this guide&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;If you’re using Windows Server 2012 R2 or Windows Server 2016, you probably already know how to do this, so I won’t cover this here.&lt;/em&gt;&lt;/p&gt;&lt;h3 id=&quot;copies-of-any-windows-install-media-that-is-used-by-the-lab&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#copies-of-any-windows-install-media-that-is-used-by-the-lab&quot; class=&quot;heading-anchor&quot;&gt;Copies of any Windows install media that is used by the Lab&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;In our case this is just a copy of the Windows Server 2016 Evaluation ISO. You can download this ISO from &lt;a href=&quot;https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; for free.&lt;/p&gt;&lt;p&gt;&lt;em&gt;You can use non-evaluation ISOs instead if you have access to them, but at the time of writing this the Windows Server 2016 non-evaluation ISO wasn’t yet available on my MSDN subscription.&lt;/em&gt;&lt;/p&gt;&lt;h3 id=&quot;an-internet-connection&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#an-internet-connection&quot; class=&quot;heading-anchor&quot;&gt;An Internet Connection&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Most Labs use DSC to configure each VM once it has been provisioned, so the ability to download any required &lt;a href=&quot;https://www.powershellgallery.com/items?q=DSC&amp;amp;x=0&amp;amp;y=0&quot; rel=&quot;noopener&quot;&gt;DSC Resources from the PowerShell Gallery&lt;/a&gt; is required. Some sample Labs also download MSI packages and other installers that will be deployed to the Lab Virtual Machines during installation - for example RSAT is often installed onto Windows 10 Lab machines automatically.&lt;/p&gt;&lt;h2 id=&quot;the-process&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#the-process&quot; class=&quot;heading-anchor&quot;&gt;The Process&lt;/a&gt;&lt;/h2&gt;&lt;h3 id=&quot;step-1-install-the-module&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#step-1-install-the-module&quot; class=&quot;heading-anchor&quot;&gt;Step 1 - Install the Module&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The first thing you’ll need to do is install the LabBuilder Module. Execute this PowerShell command at an Administrator PowerShell prompt:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name LabBuilder&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/dv71sXObbs-650.png 650w, https://danielscottraynsford.com/img/dv71sXObbs-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/dv71sXObbs-650.webp 650w, https://danielscottraynsford.com/img/dv71sXObbs-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/dv71sXObbs-650.jpeg&quot; alt=&quot;ss_labbuilder_installmodule&quot; width=&quot;960&quot; height=&quot;83&quot; srcset=&quot;https://danielscottraynsford.com/img/dv71sXObbs-650.jpeg 650w, https://danielscottraynsford.com/img/dv71sXObbs-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Note: If you have an older version of LabBuilder&lt;/strong&gt; &lt;strong&gt;installed&lt;/strong&gt;**, I’d recommend you update it to at least 0.8.3.1081 because this was the version I was using to write this guide.**&lt;/p&gt;&lt;h3 id=&quot;step-2-create-the-isos-and-vhds-folders&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#step-2-create-the-isos-and-vhds-folders&quot; class=&quot;heading-anchor&quot;&gt;Step 2 - Create the ISOs and VHDs Folders&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Most labs are built using Windows Install media contained in ISO files. These are converted to VHD files that are then used by one or more Labs. We need a location to store these files.&lt;/p&gt;&lt;p&gt;By default all sample Labs expect these folders to be D:&#92;ISOs and D:&#92;VHDs. If you don’t have a D: Drive on your computer, you’ll need to adjust the LabBuilder configuration file in Step 4.&lt;/p&gt;&lt;p&gt;Execute the following PowerShell commands at an Administrator PowerShell prompt:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;New-Item&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&#39;d:&#92;ISOs&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ItemType Directory
&lt;span class=&quot;token function&quot;&gt;New-Item&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&#39;d:&#92;VHDs&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ItemType Directory&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/Dg-mHOXTv1-650.png 650w, https://danielscottraynsford.com/img/Dg-mHOXTv1-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Dg-mHOXTv1-650.webp 650w, https://danielscottraynsford.com/img/Dg-mHOXTv1-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Dg-mHOXTv1-650.jpeg&quot; alt=&quot;ss_labbuilder_createisosandvhdsfolders&quot; width=&quot;960&quot; height=&quot;305&quot; srcset=&quot;https://danielscottraynsford.com/img/Dg-mHOXTv1-650.jpeg 650w, https://danielscottraynsford.com/img/Dg-mHOXTv1-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h3 id=&quot;step-3-create-a-folder-to-contain-the-lab&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#step-3-create-a-folder-to-contain-the-lab&quot; class=&quot;heading-anchor&quot;&gt;Step 3 - Create a Folder to Contain the Lab&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;When building a Lab with LabBuilder it will create all VMs, VHDs and other related files in a single folder.&lt;/p&gt;&lt;p&gt;For all sample LabBuilder configurations, this folder defaults to a folder in C:&#92;vm. For the sample Lab we’re building in this guide it will install the Lab into c:&#92;vm&#92;NANOTEST.COM. This can be changed by editing the configuration in Step 4.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Note: Make sure you have enough space on your chosen drive to store the Lab. 10GB per VM is a good rough guide to the amount of space required (although it usually works out as a lot less because of the use of differencing disks).&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Execute the following PowerShell commands at an Administrator PowerShell prompt:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;New-Item&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&#39;c:&#92;VM&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ItemType Directory&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;step-4-customize-the-sample-lab-file&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#step-4-customize-the-sample-lab-file&quot; class=&quot;heading-anchor&quot;&gt;Step 4 - Customize the Sample Lab file&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;We’re going to build the Lab using the sample Lab found in the &lt;strong&gt;samples&lt;/strong&gt; folder in the &lt;strong&gt;LabBuilder&lt;/strong&gt; module folder. The sample we’re using is called &lt;strong&gt;Sample_WS2016_NanoDomain.xml&lt;/strong&gt;. I’d suggest editing this file in an editor like &lt;a href=&quot;https://notepad-plus-plus.org/download/v7.html&quot; rel=&quot;noopener&quot;&gt;Notepad++&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;If you changed the paths in &lt;strong&gt;Step 2&lt;/strong&gt; or &lt;strong&gt;Step 3&lt;/strong&gt; then you’ll need to change the paths shown in this screenshot:&lt;/p&gt;&lt;h3 id&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/&quot; class=&quot;heading-anchor&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/jhUe5ihtYL-650.png 650w, https://danielscottraynsford.com/img/jhUe5ihtYL-960.png 960w, https://danielscottraynsford.com/img/jhUe5ihtYL-1296.png 1296w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/jhUe5ihtYL-650.webp 650w, https://danielscottraynsford.com/img/jhUe5ihtYL-960.webp 960w, https://danielscottraynsford.com/img/jhUe5ihtYL-1296.webp 1296w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/jhUe5ihtYL-650.jpeg&quot; alt=&quot;ss_labbuilder_nanodomainconfig&quot; width=&quot;1296&quot; height=&quot;654&quot; srcset=&quot;https://danielscottraynsford.com/img/jhUe5ihtYL-650.jpeg 650w, https://danielscottraynsford.com/img/jhUe5ihtYL-960.jpeg 960w, https://danielscottraynsford.com/img/jhUe5ihtYL-1296.jpeg 1296w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;You may also change other items in the &lt;strong&gt;Settings&lt;/strong&gt; section, but be aware that some changes (such as changing the domain name) will also need to be changed elsewhere in the file.&lt;/p&gt;&lt;p&gt;If you already have an &lt;strong&gt;External Switch&lt;/strong&gt; configured in &lt;strong&gt;Hyper-V&lt;/strong&gt; that you’d like to use for this Lab to communicate externally, then you should set the name of the switch here:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/A4LL7OLiWX-650.png 650w, https://danielscottraynsford.com/img/A4LL7OLiWX-960.png 960w, https://danielscottraynsford.com/img/A4LL7OLiWX-1294.png 1294w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/A4LL7OLiWX-650.webp 650w, https://danielscottraynsford.com/img/A4LL7OLiWX-960.webp 960w, https://danielscottraynsford.com/img/A4LL7OLiWX-1294.webp 1294w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/A4LL7OLiWX-650.jpeg&quot; alt=&quot;ss_labbuilder_nanodomainconfigexternalswitch&quot; width=&quot;1294&quot; height=&quot;652&quot; srcset=&quot;https://danielscottraynsford.com/img/A4LL7OLiWX-650.jpeg 650w, https://danielscottraynsford.com/img/A4LL7OLiWX-960.jpeg 960w, https://danielscottraynsford.com/img/A4LL7OLiWX-1294.jpeg 1294w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;If you don’t already have an &lt;strong&gt;External Switch&lt;/strong&gt; defined in &lt;strong&gt;Hyper-V&lt;/strong&gt; then one called &lt;strong&gt;General Purpose External&lt;/strong&gt; will be created for you. It will use the first &lt;strong&gt;Network Adapter&lt;/strong&gt; (physical or team) that is not already assigned to an External Switch. You can control this behavior in the LabBuilder configuration file but it is beyond the scope of this guide.&lt;/p&gt;&lt;p&gt;Save the &lt;strong&gt;Sample_WS2016_NanoDomain.xml&lt;/strong&gt; once you’ve finished changing it.&lt;/p&gt;&lt;h3 id=&quot;step-5-copy-the-windows-media-isos&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#step-5-copy-the-windows-media-isos&quot; class=&quot;heading-anchor&quot;&gt;Step 5 - Copy the Windows Media ISOs&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Now that the ISOs folder is ready, you will need to copy the Windows Install media ISO files into it. In this case we need to copy in the ISO for Windows Server 2016 (an evaluation copy can be downloaded from &lt;a href=&quot;https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;The ISO file must be name:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;14393.0.160715-1616.RS1_RELEASE_SERVER_EVAL_X64FRE_EN-US.ISO&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;If it is named anything else then you will either need to rename it or go back to &lt;strong&gt;Step 4&lt;/strong&gt; and adjust the sample Lab configuration file.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/TSNTCKrbvA-650.png 650w, https://danielscottraynsford.com/img/TSNTCKrbvA-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/TSNTCKrbvA-650.webp 650w, https://danielscottraynsford.com/img/TSNTCKrbvA-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/TSNTCKrbvA-650.jpeg&quot; alt=&quot;ss_labbuilder_isofoldercontents&quot; width=&quot;960&quot; height=&quot;568&quot; srcset=&quot;https://danielscottraynsford.com/img/TSNTCKrbvA-650.jpeg 650w, https://danielscottraynsford.com/img/TSNTCKrbvA-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h3 id=&quot;step-6-build-the-lab&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#step-6-build-the-lab&quot; class=&quot;heading-anchor&quot;&gt;Step 6 - Build the Lab&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;We’re now ready to build the lab from the sample configuration.&lt;/p&gt;&lt;p&gt;Execute the following PowerShell commands at an Administrator PowerShell prompt:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$ConfigPath&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Join-Path&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Split-Path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name LabBuilder &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ListAvailable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Parent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ChildPath &lt;span class=&quot;token string&quot;&gt;&#39;Samples&#92;Sample_WS2016_NanoDomain.xml&#39;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Install-Lab&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ConfigPath &lt;span class=&quot;token variable&quot;&gt;$ConfigPath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will begin the task of building out your Lab. The commands just determine the location of your LabBuilder sample file and then call the &lt;strong&gt;Install-Lab&lt;/strong&gt; cmdlet. I could have specified the path to the sample file manually, and you can if you prefer.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/NEKRjFFKYr-650.png 650w, https://danielscottraynsford.com/img/NEKRjFFKYr-877.png 877w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/NEKRjFFKYr-650.webp 650w, https://danielscottraynsford.com/img/NEKRjFFKYr-877.webp 877w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/NEKRjFFKYr-650.jpeg&quot; alt=&quot;ss_labbuilder_installlabbuilding&quot; width=&quot;877&quot; height=&quot;643&quot; srcset=&quot;https://danielscottraynsford.com/img/NEKRjFFKYr-650.jpeg 650w, https://danielscottraynsford.com/img/NEKRjFFKYr-877.jpeg 877w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;So sit back and grab a tea or coffee (or beer), because this will take a little while.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Note: The individual virtual machines are configured using PowerShell DSC after they are first started up. This means that it might actually take some time for things like domain joins and other post configuration tasks to complete. So if you find a Lab VM hasn’t yet joined the domain, it is most likely that the DSC configuration is still being applied.&lt;/strong&gt;&lt;/p&gt;&lt;h2 id=&quot;using-the-lab&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#using-the-lab&quot; class=&quot;heading-anchor&quot;&gt;Using the Lab&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Once you’ve built the Lab, you can log into the VMs like any other Hyper-V VM. Just double click the Virtual Machine and enter your login details:&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/HRU0URFDLk-646.png 646w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/HRU0URFDLk-646.webp 646w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/HRU0URFDLk-646.jpeg&quot; alt=&quot;ss_labbuilder_installlab_hypervvms&quot; width=&quot;646&quot; height=&quot;60&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/a4Ztc9ba6i-650.png 650w, https://danielscottraynsford.com/img/a4Ztc9ba6i-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/a4Ztc9ba6i-650.webp 650w, https://danielscottraynsford.com/img/a4Ztc9ba6i-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/a4Ztc9ba6i-650.jpeg&quot; alt=&quot;ss_labbuilder_installlab_domainlogin&quot; width=&quot;960&quot; height=&quot;818&quot; srcset=&quot;https://danielscottraynsford.com/img/a4Ztc9ba6i-650.jpeg 650w, https://danielscottraynsford.com/img/a4Ztc9ba6i-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;For the sample Lab the Domain &lt;strong&gt;Administrator&lt;/strong&gt; account password is configured as &lt;strong&gt;P@ssword!1&lt;/strong&gt;. This is set in the Lab Sample configuration and you can change it if you like.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Note: Nano Server is not designed to have an interactive GUI. You interact with Nano Server via&lt;/strong&gt; &lt;strong&gt;PowerShell Remoting. You’ll want to have a basic knowledge of PowerShell and PowerShell Remoting before attempting to administer Nano Servers&lt;/strong&gt;**.**&lt;/p&gt;&lt;h2 id=&quot;shutting-down-the-lab&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#shutting-down-the-lab&quot; class=&quot;heading-anchor&quot;&gt;Shutting Down the Lab&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Once the Lab has been completely built, you can shut it down with the &lt;strong&gt;Stop-Lab&lt;/strong&gt; command. You need to pass the path to the Lab Configuration file to shut it down:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$ConfigPath&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Join-Path&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Split-Path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name LabBuilder &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ListAvailable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Parent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ChildPath &lt;span class=&quot;token string&quot;&gt;&#39;Samples&#92;Sample_WS2016_NanoDomain.xml&#39;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Stop-Lab&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ConfigPath &lt;span class=&quot;token variable&quot;&gt;$ConfigPath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The Virtual Machines in the Lab will be shut down in an order defined in the Lab Configuration file. This will ensure that the VMs are shut down in the correct order (e.g. shut down the domain controllers last).&lt;/p&gt;&lt;h2 id=&quot;starting-the-lab-up&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#starting-the-lab-up&quot; class=&quot;heading-anchor&quot;&gt;Starting the Lab Up&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you need to start up a previously created Lab, use the &lt;strong&gt;Start-Lab&lt;/strong&gt; command. You will again need to provide the path to the Lab Configuration file of the Lab you want to shut down:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$ConfigPath&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Join-Path&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Split-Path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name LabBuilder &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ListAvailable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Parent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ChildPath &lt;span class=&quot;token string&quot;&gt;&#39;Samples&#92;Sample_WS2016_NanoDomain.xml&#39;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Start-Lab&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ConfigPath &lt;span class=&quot;token variable&quot;&gt;$ConfigPath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The Virtual Machines in the Lab will be started up in an order defined in the Lab Configuration file. This will ensure that the VMs are started up in the correct order.&lt;/p&gt;&lt;h2 id=&quot;uninstalling-the-lab&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#uninstalling-the-lab&quot; class=&quot;heading-anchor&quot;&gt;Uninstalling the Lab&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you want to completely remove a Lab, use the &lt;strong&gt;Uninstall-Lab&lt;/strong&gt; command. You will again need to provide the path to the Lab Configuration file of the Lab you want to unisntall:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$ConfigPath&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Join-Path&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Split-Path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name LabBuilder &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ListAvailable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Parent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ChildPath &lt;span class=&quot;token string&quot;&gt;&#39;Samples&#92;Sample_WS2016_NanoDomain.xml&#39;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Uninstall-Lab&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ConfigPath &lt;span class=&quot;token variable&quot;&gt;$ConfigPath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Note: You will be asked to confirm the removals&lt;/strong&gt;.&lt;/p&gt;&lt;h2 id=&quot;wrapping-up&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/easily-create-a-hyper-v-windows-server-2016-ad-andamp;-nano-server-lab/#wrapping-up&quot; class=&quot;heading-anchor&quot;&gt;Wrapping Up&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This article has hopefully given you a basic understanding of how to use LabBuilder to stand up a Hyper-V Lab in relatively short order and without a lot of commands and clicks. This project is still in Beta and so there may be bugs as well as some incomplete features. If you want to raise an issue with this project (or even submit a PR), head on over to the &lt;a href=&quot;https://github.com/PlagueHO/LabBuilder&quot; rel=&quot;noopener&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Export a Base-64 x.509 Cert using PowerShell on Windows 7</title>
      <link href="https://danielscottraynsford.com/blog/export-a-base-64-x509-cert-using-powershell-on-windows-7/" />
      <updated>2016-09-10T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/export-a-base-64-x509-cert-using-powershell-on-windows-7/</id>
      <content type="html">
				&lt;p&gt;Exporting a Base-64 Encoded x.509 certificate using PowerShell is trivial if you have the &lt;a href=&quot;https://technet.microsoft.com/en-us/library/hh848628.aspx&quot; rel=&quot;noopener&quot;&gt;Export-Certificate&lt;/a&gt; cmdlet available. However, many of the nodes I work with are Windows 7 which unfortunately doesn’t include these cmdlets. Therefore I needed an alternate method of exporting these Base-64 encoded x.509 certificates from these nodes.&lt;/p&gt;&lt;p&gt;So I came up with this little snippet of code:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$certificate&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;path Cert:&#92;CurrentUser&#92;My&#92;D675AE3AE9F7B56348C17EE527F261CFCEA0FD13
&lt;span class=&quot;token variable&quot;&gt;$base64certificate&lt;/span&gt; = @&lt;span class=&quot;token string&quot;&gt;&quot;
-----BEGIN CERTIFICATE-----
&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;[Convert]&lt;/span&gt;::ToBase64String&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$certificate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Export&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Cert&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;, [System.Base64FormattingOptions]::InsertLineBreaks)))
-----END CERTIFICATE-----
&quot;&lt;/span&gt;@
&lt;span class=&quot;token function&quot;&gt;Set-Content&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:USERPROFILE&#92;Documents&#92;MyCert.cer&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Value &lt;span class=&quot;token variable&quot;&gt;$base64certificate&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Hope someone finds it useful.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Tips for HQRM DSC Resources</title>
      <link href="https://danielscottraynsford.com/blog/tips-for-hqrm-dsc-resources/" />
      <updated>2016-08-14T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/tips-for-hqrm-dsc-resources/</id>
      <content type="html">
				&lt;p&gt;I’ve spent a fair amount of time recently working on getting some of my DSC Resources (&lt;a href=&quot;https://github.com/PlagueHO/SystemLocaleDsc&quot; rel=&quot;noopener&quot;&gt;SystemLocaleDsc&lt;/a&gt;, &lt;a href=&quot;https://github.com/PlagueHO/WSManDsc&quot; rel=&quot;noopener&quot;&gt;WSManDsc&lt;/a&gt;, &lt;a href=&quot;https://github.com/PlagueHO/iSCSIDsc&quot; rel=&quot;noopener&quot;&gt;iSCSIDsc&lt;/a&gt; and &lt;a href=&quot;https://github.com/PlagueHO/FSRMDsc&quot; rel=&quot;noopener&quot;&gt;FSRMDsc&lt;/a&gt;) accepted into the &lt;strong&gt;&lt;a href=&quot;https://github.com/PowerShell/DscResources&quot; rel=&quot;noopener&quot;&gt;Microsoft DSC Community Resource Kit&lt;/a&gt;&lt;/strong&gt;. Some are nearly there (SystemLocaleDsc and WSManDsc), whereas others have a way to go yet.&lt;/p&gt;&lt;p&gt;I’ve had one resource already accepted (&lt;a href=&quot;https://github.com/PowerShell/xDFS&quot; rel=&quot;noopener&quot;&gt;xDFS&lt;/a&gt;) into the &lt;strong&gt;DSC Community Resource kit&lt;/strong&gt;, but this was before the &lt;strong&gt;High Quality Resource Module&lt;/strong&gt; (HQRM) guidelines became available. The &lt;a href=&quot;https://github.com/PowerShell/DscResources/blob/master/HighQualityModuleGuidelines.md&quot; rel=&quot;noopener&quot;&gt;HQRM guidelines&lt;/a&gt; are a set of standards that DSC modules &lt;strong&gt;must meet&lt;/strong&gt; and &lt;strong&gt;maintain&lt;/strong&gt; to be considered a &lt;strong&gt;High Quality Resource Module&lt;/strong&gt;. Once they meet these requirements they may be eligible to have the&amp;nbsp;&lt;strong&gt;‘x’&lt;/strong&gt; moniker removed with ‘&lt;strong&gt;Dsc&lt;/strong&gt;’ being added to the name.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;More information:&lt;/strong&gt; If you want to read a bit more about the &lt;strong&gt;HQRM&lt;/strong&gt; standards, you can find the HQRM Guidelines &lt;a href=&quot;https://github.com/PowerShell/DscResources/blob/master/HighQualityModuleGuidelines.md&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Any modules being submitted for inclusion into the &lt;strong&gt;DSC Community Resource kit&lt;/strong&gt; will be expected to meet the &lt;strong&gt;HQRM&lt;/strong&gt; standards. The process of acceptance requires three reviewers from the Microsoft DSC team to review the module.&lt;/p&gt;&lt;p&gt;I thought it might be helpful to anyone else who might want to submit a &lt;strong&gt;DSC Resource&lt;/strong&gt; into the &lt;strong&gt;DSC Community Resource kit&lt;/strong&gt; to get a list of issues the reviewers found with my submissions. This might allow you to fix up your modules before the review process - which will help the reviewers out (they hate having to be critical of your code as much as you do). This enables the submission process to go much faster as well.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;More information:&lt;/strong&gt; If you want to read more about the submission process, you can find the documentation &lt;a href=&quot;https://github.com/PowerShell/DscResources/blob/master/NewResourceModuleSubmissions.md&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;I’ll keep this post updated with any new issues the reviewers pick up. Feel free to ask for clarifications on the issues.&lt;/p&gt;&lt;p&gt;So here is my list of what I have done wrong (so far):&lt;/p&gt;&lt;h3 id=&quot;missing-get-help-documentation&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/tips-for-hqrm-dsc-resources/#missing-get-help-documentation&quot; class=&quot;heading-anchor&quot;&gt;Missing Get-Help Documentation&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Every function (public or private) within the DSC resource module must contain a standard help block containing at least a &lt;strong&gt;.SYNOPSIS&lt;/strong&gt; and &lt;strong&gt;.PARAMETER&lt;/strong&gt; block:&lt;/p&gt;&lt;p&gt;This will get rejected:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/gjrFNIu4j8-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/gjrFNIu4j8-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/gjrFNIu4j8-650.jpeg&quot; alt=&quot;ss_hqrmreview_gethelpbad&quot; width=&quot;650&quot; height=&quot;272&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This is good:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/K5FRBS5-cX-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/K5FRBS5-cX-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/K5FRBS5-cX-650.jpeg&quot; alt=&quot;ss_hqrmreview_gethelpgood&quot; width=&quot;650&quot; height=&quot;498&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h3 id=&quot;examples-missing-explanation&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/tips-for-hqrm-dsc-resources/#examples-missing-explanation&quot; class=&quot;heading-anchor&quot;&gt;Examples Missing Explanation&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;All examples in the &lt;strong&gt;Examples&lt;/strong&gt; folder and the &lt;strong&gt;&lt;a href=&quot;http://Readme.md&quot; rel=&quot;noopener&quot;&gt;Readme.md&lt;/a&gt;&lt;/strong&gt; must contain an explanation of what the example will do.&lt;/p&gt;&lt;p&gt;This is bad:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/YoeI0IuojF-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/YoeI0IuojF-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/YoeI0IuojF-650.jpeg&quot; alt=&quot;ss_hqrmreview_exampledescriptionbad&quot; width=&quot;650&quot; height=&quot;271&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This is good:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/qFxq7QW4eT-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/qFxq7QW4eT-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/qFxq7QW4eT-650.jpeg&quot; alt=&quot;ss_hqrmreview_exampledescriptiongood&quot; width=&quot;650&quot; height=&quot;303&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h3 id=&quot;old-or-incorrect-unit/integration-test-headers&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/tips-for-hqrm-dsc-resources/#old-or-incorrect-unit/integration-test-headers&quot; class=&quot;heading-anchor&quot;&gt;Old or Incorrect Unit/Integration Test Headers&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;There is a standard method of unit and integration testing&amp;nbsp;&lt;strong&gt;DSC Resources&lt;/strong&gt;. Your DSC resources should use these methods where ever possible. Any tests should therefore be based on the latest &lt;a href=&quot;https://github.com/PowerShell/DscResources/blob/master/Tests.Template/unit_template.ps1&quot; rel=&quot;noopener&quot;&gt;unit test templates&lt;/a&gt; and &lt;a href=&quot;https://github.com/PowerShell/DscResources/blob/master/Tests.Template/integration_template.ps1&quot; rel=&quot;noopener&quot;&gt;integration test templates&lt;/a&gt;. You should therefore ensure your tests are based on the latest practices and contain the latest header.&lt;/p&gt;&lt;p&gt;&lt;em&gt;This is probably the hardest thing to get right if you’re not paying close attention to the current DSC community best practices around testing. So feel free to ask me for help.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This is bad:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/igET_YnkeT-650.webp 650w, https://danielscottraynsford.com/img/igET_YnkeT-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/igET_YnkeT-650.png&quot; alt=&quot;ss_hqrmreview_testheaderbad&quot; width=&quot;960&quot; height=&quot;308&quot; srcset=&quot;https://danielscottraynsford.com/img/igET_YnkeT-650.png 650w, https://danielscottraynsford.com/img/igET_YnkeT-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This is good:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/mf5qn1461o-650.png 650w, https://danielscottraynsford.com/img/mf5qn1461o-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/mf5qn1461o-650.webp 650w, https://danielscottraynsford.com/img/mf5qn1461o-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/mf5qn1461o-650.jpeg&quot; alt=&quot;ss_hqrmreview_testheadergood&quot; width=&quot;960&quot; height=&quot;307&quot; srcset=&quot;https://danielscottraynsford.com/img/mf5qn1461o-650.jpeg 650w, https://danielscottraynsford.com/img/mf5qn1461o-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h3 id=&quot;incorrect-capitalization-of-local-variables&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/tips-for-hqrm-dsc-resources/#incorrect-capitalization-of-local-variables&quot; class=&quot;heading-anchor&quot;&gt;Incorrect Capitalization of Local Variables&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Local variables must start with a lower case letter. I needed to correct this on several occasions.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: this is for local variables. Parameter names should start with Uppercase.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This is bad:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/ZrdWO0n_Lo-365.png 365w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ZrdWO0n_Lo-365.webp 365w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ZrdWO0n_Lo-365.jpeg&quot; alt=&quot;ss_hqrmreview_localparameterbad&quot; width=&quot;365&quot; height=&quot;52&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This is good:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/nOTDN5EeCa-371.png 371w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/nOTDN5EeCa-371.webp 371w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/nOTDN5EeCa-371.jpeg&quot; alt=&quot;ss_hqrmreview_localparametergood&quot; width=&quot;371&quot; height=&quot;58&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h3 id=&quot;spaces-around-=-in-localization-files&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/tips-for-hqrm-dsc-resources/#spaces-around-=-in-localization-files&quot; class=&quot;heading-anchor&quot;&gt;Spaces around = in Localization Files&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;In any localization files you should make sure there is a space on either side of the = sign. This greatly improves message readability.&lt;/p&gt;&lt;p&gt;This is bad:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/pQHPu3TCja-650.png 650w, https://danielscottraynsford.com/img/pQHPu3TCja-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/pQHPu3TCja-650.webp 650w, https://danielscottraynsford.com/img/pQHPu3TCja-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/pQHPu3TCja-650.jpeg&quot; alt=&quot;ss_hqrmreview_localizationbad.png&quot; width=&quot;960&quot; height=&quot;147&quot; srcset=&quot;https://danielscottraynsford.com/img/pQHPu3TCja-650.jpeg 650w, https://danielscottraynsford.com/img/pQHPu3TCja-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This is good:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/WCce25PuHl-650.png 650w, https://danielscottraynsford.com/img/WCce25PuHl-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/WCce25PuHl-650.webp 650w, https://danielscottraynsford.com/img/WCce25PuHl-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/WCce25PuHl-650.jpeg&quot; alt=&quot;ss_hqrmreview_localizationgood&quot; width=&quot;960&quot; height=&quot;142&quot; srcset=&quot;https://danielscottraynsford.com/img/WCce25PuHl-650.jpeg 650w, https://danielscottraynsford.com/img/WCce25PuHl-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h3 id=&quot;missing-code-of-conduct-in-readmemd&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/tips-for-hqrm-dsc-resources/#missing-code-of-conduct-in-readmemd&quot; class=&quot;heading-anchor&quot;&gt;Missing code of Conduct in &lt;/a&gt;&lt;a href=&quot;http://Readme.md&quot; rel=&quot;noopener&quot;&gt;Readme.md&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;All modules that are part of the DSC Resource Kit must contain this message in the &lt;a href=&quot;http://Readme.md&quot; rel=&quot;noopener&quot;&gt;Readme.md&lt;/a&gt;:&lt;/p&gt;&lt;p&gt;This project has adopted the &lt;a href=&quot;https://opensource.microsoft.com/codeofconduct/&quot; rel=&quot;noopener&quot;&gt;Microsoft Open Source Code of Conduct&lt;/a&gt;.&lt;br&gt;For more information see the &lt;a href=&quot;https://opensource.microsoft.com/codeofconduct/faq/&quot; rel=&quot;noopener&quot;&gt;Code of Conduct FAQ&lt;/a&gt; or contact &lt;a href=&quot;mailto:opencode@microsoft.com&quot;&gt;opencode@microsoft.com&lt;/a&gt; with any additional questions or comments.&lt;/p&gt;&lt;p&gt;This is bad:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/MCkXiAuMJq-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/MCkXiAuMJq-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/MCkXiAuMJq-650.jpeg&quot; alt=&quot;ss_hqrmreview_codeofconductbad&quot; width=&quot;650&quot; height=&quot;219&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This is good:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/KnO0xCnX-c-650.png 650w, https://danielscottraynsford.com/img/KnO0xCnX-c-960.png 960w, https://danielscottraynsford.com/img/KnO0xCnX-c-1281.png 1281w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/KnO0xCnX-c-650.webp 650w, https://danielscottraynsford.com/img/KnO0xCnX-c-960.webp 960w, https://danielscottraynsford.com/img/KnO0xCnX-c-1281.webp 1281w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/KnO0xCnX-c-650.jpeg&quot; alt=&quot;ss_hqrmreview_codeofconductgood&quot; width=&quot;1281&quot; height=&quot;273&quot; srcset=&quot;https://danielscottraynsford.com/img/KnO0xCnX-c-650.jpeg 650w, https://danielscottraynsford.com/img/KnO0xCnX-c-960.jpeg 960w, https://danielscottraynsford.com/img/KnO0xCnX-c-1281.jpeg 1281w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h3 id=&quot;missing-localization-file-indent&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/tips-for-hqrm-dsc-resources/#missing-localization-file-indent&quot; class=&quot;heading-anchor&quot;&gt;Missing Localization file indent&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;All strings in localization files should be indented.&lt;/p&gt;&lt;p&gt;This is bad:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/RPAqaJhxz3-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/RPAqaJhxz3-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/RPAqaJhxz3-650.jpeg&quot; alt=&quot;ss_hqrmreview_localizationdatabad&quot; width=&quot;650&quot; height=&quot;292&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This is good:&lt;/p&gt;&lt;h3 id&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/tips-for-hqrm-dsc-resources/&quot; class=&quot;heading-anchor&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/Xi0Q9xlgmj-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Xi0Q9xlgmj-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Xi0Q9xlgmj-650.jpeg&quot; alt=&quot;ss_hqrmreview_localizationdatagood&quot; width=&quot;650&quot; height=&quot;287&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/h3&gt;&lt;h3 id=&quot;final-words&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/tips-for-hqrm-dsc-resources/#final-words&quot; class=&quot;heading-anchor&quot;&gt;Final Words&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;There were some other issues raised which I will also document, however I am still in discussion with the DSC team over the best methods to use to solve the issues (specifically the use of &lt;strong&gt;InModuleScope&lt;/strong&gt; in unit tests).&lt;/p&gt;&lt;p&gt;The main thing you can do to help speed this process up and reduce the load on the reviewers however is to implement all the &lt;a href=&quot;https://github.com/PowerShell/DscResources/blob/master/BestPractices.md&quot; rel=&quot;noopener&quot;&gt;best practices&lt;/a&gt; and &lt;a href=&quot;https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md&quot; rel=&quot;noopener&quot;&gt;guidelines&lt;/a&gt; listed.&lt;/p&gt;&lt;p&gt;I hope this helps someone out there.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Failed to Start Docker Service on Windows 10 AE</title>
      <link href="https://danielscottraynsford.com/blog/failed-to-start-docker-service-on-windows-10-ae/" />
      <updated>2016-08-04T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/failed-to-start-docker-service-on-windows-10-ae/</id>
      <content type="html">
				&lt;p&gt;So, pretty much the first thing I did when the Windows 10 Anniversary Edition was installed onto my primary development machine was to installer the &lt;strong&gt;Windows Container Service&lt;/strong&gt; and Docker on it.&lt;/p&gt;&lt;p&gt;I used the &lt;a href=&quot;https://msdn.microsoft.com/en-us/virtualization/windowscontainers/quick_start/quick_start_windows_10&quot; rel=&quot;noopener&quot;&gt;Windows Containers on Windows 10 Quick start guide&lt;/a&gt; to perform the installation. This is the same method I’d been using on my secondary development machine (running Insider Preview builds) since it was first available in build 14372.&lt;/p&gt;&lt;p&gt;Note: The Windows Containers on Windows 10 Quick Start guide doesn’t mention the Anniversary Edition specifically, but the method still works.&lt;/p&gt;&lt;p&gt;Unfortunately though, this time it didn’t work. When I attempted to start the Docker Service I received the error:&lt;/p&gt;&lt;p&gt;start-service : Failed to start service ‘Docker Engine (docker)’.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/trKlPYRjZJ-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/trKlPYRjZJ-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/trKlPYRjZJ-650.jpeg&quot; alt=&quot;ss_docker_startserviceerror&quot; width=&quot;650&quot; height=&quot;144&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;So, after a bit of digging around I found the following error in the &lt;strong&gt;Windows Event Log&lt;/strong&gt; in the &lt;strong&gt;Application&lt;/strong&gt; logs:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/6ouPL08u7W-650.png 650w, https://danielscottraynsford.com/img/6ouPL08u7W-960.png 960w, https://danielscottraynsford.com/img/6ouPL08u7W-1253.png 1253w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/6ouPL08u7W-650.webp 650w, https://danielscottraynsford.com/img/6ouPL08u7W-960.webp 960w, https://danielscottraynsford.com/img/6ouPL08u7W-1253.webp 1253w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/6ouPL08u7W-650.jpeg&quot; alt=&quot;ss_docker_startserviceerror_eventlog&quot; width=&quot;1253&quot; height=&quot;711&quot; srcset=&quot;https://danielscottraynsford.com/img/6ouPL08u7W-650.jpeg 650w, https://danielscottraynsford.com/img/6ouPL08u7W-960.jpeg 960w, https://danielscottraynsford.com/img/6ouPL08u7W-1253.jpeg 1253w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Basically what this was telling me was that the Docker Daemon couldn’t create the new virtual network adapter that it needed - because it already existed. So a quick run of &lt;strong&gt;Get-NetAdapter&lt;/strong&gt; and I found that the docker adapter “vEthernet (HNS Internal)” already existed:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/6ouPL08u7W-650.png 650w, https://danielscottraynsford.com/img/6ouPL08u7W-960.png 960w, https://danielscottraynsford.com/img/6ouPL08u7W-1253.png 1253w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/6ouPL08u7W-650.webp 650w, https://danielscottraynsford.com/img/6ouPL08u7W-960.webp 960w, https://danielscottraynsford.com/img/6ouPL08u7W-1253.webp 1253w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/6ouPL08u7W-650.jpeg&quot; alt=&quot;ss_docker_startserviceerror_eventlog&quot; width=&quot;1253&quot; height=&quot;711&quot; srcset=&quot;https://danielscottraynsford.com/img/6ouPL08u7W-650.jpeg 650w, https://danielscottraynsford.com/img/6ouPL08u7W-960.jpeg 960w, https://danielscottraynsford.com/img/6ouPL08u7W-1253.jpeg 1253w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;So what I needed to do was &lt;strong&gt;uninstall&lt;/strong&gt; this adapter so that the &lt;strong&gt;Docker Service&lt;/strong&gt; could recreate it. I’m not actually aware of a &lt;strong&gt;command line&lt;/strong&gt; method of doing (except for using &lt;a href=&quot;https://chocolatey.org/packages/devcon.portable&quot; rel=&quot;noopener&quot;&gt;DevCon&lt;/a&gt;) so I had to resort to using &lt;strong&gt;Device Manager&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/-vVw_FH9Wa-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/-vVw_FH9Wa-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/-vVw_FH9Wa-650.jpeg&quot; alt=&quot;ss_docker_startservice_uninstalldevice&quot; width=&quot;650&quot; height=&quot;476&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;You’ll need to use the output of the &lt;strong&gt;Get-NetAdapter&lt;/strong&gt; to find he right adapter &lt;strong&gt;uninstall&lt;/strong&gt;. Once it has been uninstalled you should be able to start the service again:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/V9RvdG7wJ7-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/V9RvdG7wJ7-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/V9RvdG7wJ7-650.jpeg&quot; alt=&quot;ss_docker_startservice_dockerstarts&quot; width=&quot;650&quot; height=&quot;93&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This time the service should start successfully. A quick call to &lt;strong&gt;docker ps&lt;/strong&gt; shows that the container service is indeed working. So now I can get onto the process pulling down the &lt;strong&gt;base container images&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Hopefully if anyone else runs into this problem in Windows 10 AE this will help them resolve it.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Allow PowerShell to Traverse a Secure Proxy</title>
      <link href="https://danielscottraynsford.com/blog/allow-powershell-to-traverse-a-secure-proxy/" />
      <updated>2016-06-24T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/allow-powershell-to-traverse-a-secure-proxy/</id>
      <content type="html">
				&lt;p&gt;One of the first things I like to do when setting up my development machine in a new environment is to update PowerShell help with the &lt;strong&gt;update-help&lt;/strong&gt; cmdlet. After that, I will then go and download a slew of modules from the &lt;a href=&quot;http://www.powershellgallery.com/&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;However, recently I needed to set up my development machine on an environment that is behind an internet proxy that requires authentication. This meant that a lot of PowerShell cmdlets can’t be used because they don’t have support for traversing a proxy - or at least, not one that requires authentication. Take the aforementioned &lt;strong&gt;update-help&lt;/strong&gt; and &lt;strong&gt;install-module&lt;/strong&gt; cmdlets - I just couldn’t do with out these.&lt;/p&gt;&lt;p&gt;So I set about trying to find a way around this. So after lots of googling and trial and error (and also getting my Active Directory account locked out on more than one occasion) I came up with a solution.&lt;/p&gt;&lt;p&gt;Basically it requires using the &lt;strong&gt;NETSH&lt;/strong&gt; command to configure the proxy settings and then configure the web client with my proxy credentials (which were AD integrated):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$ProxyAddress&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;http://myproxy.contoso.com&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$ProxyCredentials&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-Credential&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &amp;amp; netsh @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;winhttp&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;set&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;proxy&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ProxyAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$webclient&lt;/span&gt;=&lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Net&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WebClient
&lt;span class=&quot;token variable&quot;&gt;$webclient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Proxy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Credentials = &lt;span class=&quot;token variable&quot;&gt;$ProxyCredentials&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The code I needed to traverse the proxy could then be executed. Once it has completed the task using the proxy I would then reset it back to the default state (using settings from internet explorer):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &amp;amp; netsh @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;winhttp&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;import&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;proxy&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;source=ie&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After using this a bit I thought it would be great to turn it into a function that I could just call, passing a script block that I wanted to be able to traverse the proxy with. So I came up with this:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Use-Proxy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory)]&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[ScriptBlock]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ScriptBlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ProxyAddress&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;http://myproxy.contoso.com&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;token namespace&quot;&gt;[PSCredential]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ProxyCredentials&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# If proxy creendials weren&#39;t passed in then see if global proxy creds&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# are set. Global Proxy credentials can save some typing by setting&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# them in the PS session.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-not&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$PSBoundParameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ContainsKey&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;ProxyCredentials&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Global&lt;/span&gt;:ProxyCredentials&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$ProxyCredentials&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$Global&lt;/span&gt;:ProxyCredentials
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$ProxyCredentials&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-Credential&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&quot;Enter proxy credentials&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# if&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# if&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# Configure the proxy&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &amp;amp; netsh @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;winhttp&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;set&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;proxy&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ProxyAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$webclient&lt;/span&gt;=&lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Net&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WebClient
        &lt;span class=&quot;token variable&quot;&gt;$webclient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Proxy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Credentials = &lt;span class=&quot;token variable&quot;&gt;$ProxyCredentials&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Call the actual code&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Invoke-Command&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ScriptBlock &lt;span class=&quot;token variable&quot;&gt;$ScriptBlock&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;Throw&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Reset the proxy&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt; = &amp;amp; netsh @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;winhttp&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;import&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;proxy&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;source=ie&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# try&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Function Use-Proxy&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To use this script, simply save it as a PS1 file (e.g. Use-Proxy.ps1), customizing the default proxy URL if you like and then dot source the file. Once it has been dot sourced it can be called, optionally passing the URL of the proxy server and credentials to use to authenticate to it:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; c:&#92;Scripts&#92;&lt;span class=&quot;token function&quot;&gt;Use-Proxy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1
&lt;span class=&quot;token function&quot;&gt;Use-Proxy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;Update-Help&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name Pester
  &lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name PSScriptAnalyzer
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you don’t pass any credentials, you will be prompted to enter them. I also added some code into this function so that you can specify a global variable containing the credentials to use to traverse the proxy. This can save on lots of typing, but might be frowned upon by your security team.&lt;/p&gt;&lt;p&gt;Finally, I also added the proxy reset code into the &lt;strong&gt;finally&lt;/strong&gt; block of a &lt;strong&gt;try&lt;/strong&gt;…&lt;strong&gt;catch&lt;/strong&gt; to ensure that if the code in the script block throws an error the proxy will be reset. In my case I also loaded this function into a PowerShell module that can be distributed to other team members.&lt;/p&gt;&lt;p&gt;Happy proxy traversing!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>VisualStudio and ISE Steroids Vertical Guides</title>
      <link href="https://danielscottraynsford.com/blog/visualstudio-and-ise-steroids-vertical-guides/" />
      <updated>2016-05-20T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/visualstudio-and-ise-steroids-vertical-guides/</id>
      <content type="html">
				&lt;p&gt;To make it easier for &lt;em&gt;reviewers&lt;/em&gt; and &lt;em&gt;other programmers&lt;/em&gt; to read PowerShell code it is recommended that lines of PowerShell code don’t exceed 100 characters. If you run past this limit you should usually split the line using a &lt;strong&gt;backtick&lt;/strong&gt; (`). I also find that this limit prompts me to rethink my code logic if the line gets too long. Besides, no one wants to have to scroll several pages horizontally to be able to read your whole line of code.&lt;/p&gt;&lt;p&gt;However, one of the most common issues I run into when reviewing (or writing my own) PowerShell DSC resources is going over the 100 character limit. For your own projects you’re most welcome to use any styles that you want (although I’d still recommend this one for the reasons above). However, if you’re going to commit this code to the PowerShell DSC community resources you’ll need to ensure your code &lt;a href=&quot;https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md#each-line-should-have-less-than-100-characters&quot; rel=&quot;noopener&quot;&gt;meets this requirement&lt;/a&gt;, otherwise reviewers will likely ask you change it.&lt;/p&gt;&lt;p&gt;But there is an easy way to help identify this problem if you’re using Visual Studio Code or PowerShell ISE Steroids: &lt;em&gt;Setup a vertical guide/ruler at 100 characters&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/VvhPiggCNa-650.png 650w, https://danielscottraynsford.com/img/VvhPiggCNa-960.png 960w, https://danielscottraynsford.com/img/VvhPiggCNa-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/VvhPiggCNa-650.webp 650w, https://danielscottraynsford.com/img/VvhPiggCNa-960.webp 960w, https://danielscottraynsford.com/img/VvhPiggCNa-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/VvhPiggCNa-650.jpeg&quot; alt=&quot;ss_verticalruler_visualstudiocode&quot; width=&quot;1400&quot; height=&quot;711&quot; srcset=&quot;https://danielscottraynsford.com/img/VvhPiggCNa-650.jpeg 650w, https://danielscottraynsford.com/img/VvhPiggCNa-960.jpeg 960w, https://danielscottraynsford.com/img/VvhPiggCNa-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;br&gt;A nice easy to see vertical ruler/guide tells us when our lines get too long… oh dear this line is too long.&lt;/p&gt;&lt;h2 id=&quot;to-set-up-a-vertical-ruler-in-visual-studio-code&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/visualstudio-and-ise-steroids-vertical-guides/#to-set-up-a-vertical-ruler-in-visual-studio-code&quot; class=&quot;heading-anchor&quot;&gt;To Set up a Vertical Ruler in Visual Studio Code&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Select &lt;strong&gt;User Settings&lt;/strong&gt; (you can use &lt;strong&gt;Workspace Settings&lt;/strong&gt; if you want) from &lt;strong&gt;Preferences&lt;/strong&gt; in the &lt;strong&gt;File&lt;/strong&gt; menu:&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/3bMqP7I-ea-386.png 386w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/3bMqP7I-ea-386.webp 386w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/3bMqP7I-ea-386.jpeg&quot; alt=&quot;ss_verticalruler_visualstudiocodemenu&quot; width=&quot;386&quot; height=&quot;449&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Your user &lt;strong&gt;settings.json&lt;/strong&gt; file will load on the right, with the &lt;strong&gt;Default Settings&lt;/strong&gt; on the left:&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/DX836yNLfz-650.png 650w, https://danielscottraynsford.com/img/DX836yNLfz-960.png 960w, https://danielscottraynsford.com/img/DX836yNLfz-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/DX836yNLfz-650.webp 650w, https://danielscottraynsford.com/img/DX836yNLfz-960.webp 960w, https://danielscottraynsford.com/img/DX836yNLfz-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/DX836yNLfz-650.jpeg&quot; alt=&quot;ss_verticalruler_visualstudiocodesettings.png&quot; width=&quot;1400&quot; height=&quot;737&quot; srcset=&quot;https://danielscottraynsford.com/img/DX836yNLfz-650.jpeg 650w, https://danielscottraynsford.com/img/DX836yNLfz-960.jpeg 960w, https://danielscottraynsford.com/img/DX836yNLfz-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Add the following line to your settings.json file in between the braces:&lt;/p&gt;&lt;p&gt;“editor.rulers”: [100]&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/kN3Kcijw9B-437.png 437w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/kN3Kcijw9B-437.webp 437w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/kN3Kcijw9B-437.jpeg&quot; alt=&quot;ss_verticalruler_visualstudiocodesettingsrulers&quot; width=&quot;437&quot; height=&quot;116&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Save the file and restart &lt;strong&gt;Visual Studio Code&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;to-set-up-a-vertical-ruler-in-ise-steroids&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/visualstudio-and-ise-steroids-vertical-guides/#to-set-up-a-vertical-ruler-in-ise-steroids&quot; class=&quot;heading-anchor&quot;&gt;To Set up a Vertical Ruler in ISE Steroids&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Select &lt;strong&gt;Show&lt;/strong&gt; &lt;strong&gt;Secondary Toolbar&lt;/strong&gt; from the &lt;strong&gt;View&lt;/strong&gt; menu.&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/omsiiom9Oj-474.png 474w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/omsiiom9Oj-474.webp 474w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/omsiiom9Oj-474.jpeg&quot; alt=&quot;ss_verticalruler_isesteroidsmenu&quot; width=&quot;474&quot; height=&quot;261&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click the &lt;strong&gt;Vertical Guides&lt;/strong&gt; button and select &lt;strong&gt;Global Guide…&lt;/strong&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/zsYS5SSnQB-278.png 278w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/zsYS5SSnQB-278.webp 278w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/zsYS5SSnQB-278.jpeg&quot; alt=&quot;ss_verticalruler_isesteroidsverticalguidebutton&quot; width=&quot;278&quot; height=&quot;217&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Enter &lt;strong&gt;100&lt;/strong&gt; in the input box and press enter.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/iOZQRJd6vI-650.png 650w, https://danielscottraynsford.com/img/iOZQRJd6vI-960.png 960w, https://danielscottraynsford.com/img/iOZQRJd6vI-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/iOZQRJd6vI-650.webp 650w, https://danielscottraynsford.com/img/iOZQRJd6vI-960.webp 960w, https://danielscottraynsford.com/img/iOZQRJd6vI-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/iOZQRJd6vI-650.jpeg&quot; alt=&quot;ss_verticalruler_isesteroidsruleradded&quot; width=&quot;1400&quot; height=&quot;873&quot; srcset=&quot;https://danielscottraynsford.com/img/iOZQRJd6vI-650.jpeg 650w, https://danielscottraynsford.com/img/iOZQRJd6vI-960.jpeg 960w, https://danielscottraynsford.com/img/iOZQRJd6vI-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;The vertical guide is added - better fix that long line!&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;That is all there is to using vertical guides. Having them setup if you’re planning on committing code to a community project might save you some extra commits and help the community reviewers merge your code faster.&lt;/p&gt;&lt;p&gt;Happy coding!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>DSC Resource Kit - Anniversary Release</title>
      <link href="https://danielscottraynsford.com/blog/dsc-resource-kit-anniversary-release/" />
      <updated>2016-05-19T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/dsc-resource-kit-anniversary-release/</id>
      <content type="html">
				&lt;p&gt;The latest DSC Resource Kit (all your favorite DSC Resources in one handy pack) is &lt;a href=&quot;https://blogs.msdn.microsoft.com/powershell/2016/05/18/dsc-resource-kit-anniversary-release/&quot; rel=&quot;noopener&quot;&gt;available now&lt;/a&gt;. It is one mighty release with all sorts of awesomeness included! I strongly recommend picking it up if you’re doing DSC automation, as it has something for everyone.&lt;/p&gt;&lt;p&gt;Happy automating!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Bulk Updating Nano Servers using PowerShell and CIM</title>
      <link href="https://danielscottraynsford.com/blog/bulk-updating-nano-servers-using-powershell-and-cim/" />
      <updated>2016-05-14T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/bulk-updating-nano-servers-using-powershell-and-cim/</id>
      <content type="html">
				&lt;h2 id=&quot;introduction&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/bulk-updating-nano-servers-using-powershell-and-cim/#introduction&quot; class=&quot;heading-anchor&quot;&gt;Introduction&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Feel free to skip this introduction and jump down to the &lt;a href=&quot;https://danielscottraynsford.com/blog/bulk-updating-nano-servers-using-powershell-and-cim/#updating-via-cim&quot;&gt;Updating via CIM&lt;/a&gt; section to see the actual update process.&lt;/p&gt;&lt;p&gt;Yesterday, I decided to connect my Windows Server 2016 TP5 Active Directory lab to the new &lt;a href=&quot;https://blogs.technet.microsoft.com/nanoserver/2016/02/09/introducing-server-management-tools/&quot; rel=&quot;noopener&quot;&gt;Azure Server Management Tools&lt;/a&gt;. This was mainly to look at the experience with managing &lt;strong&gt;Nano Server&lt;/strong&gt; using these new Azure-based tools.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/lbiTMrcX1F-650.png 650w, https://danielscottraynsford.com/img/lbiTMrcX1F-960.png 960w, https://danielscottraynsford.com/img/lbiTMrcX1F-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/lbiTMrcX1F-650.webp 650w, https://danielscottraynsford.com/img/lbiTMrcX1F-960.webp 960w, https://danielscottraynsford.com/img/lbiTMrcX1F-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/lbiTMrcX1F-650.jpeg&quot; alt=&quot;ss_azuresmt_nanoserverupdate&quot; width=&quot;1400&quot; height=&quot;700&quot; srcset=&quot;https://danielscottraynsford.com/img/lbiTMrcX1F-650.jpeg 650w, https://danielscottraynsford.com/img/lbiTMrcX1F-960.jpeg 960w, https://danielscottraynsford.com/img/lbiTMrcX1F-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;The interface is very slick, as you’d expect from anything managed within the &lt;em&gt;new Azure Portal&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;I did run into a minor glitch when installing updates, but the team over at &lt;strong&gt;Azure&lt;/strong&gt; (major thanks &lt;a href=&quot;https://twitter.com/brendanpower&quot; rel=&quot;noopener&quot;&gt;@BrendanPower&lt;/a&gt;) had the problem identified and solved within 24 hours (and it was rolled out while I was writing this post). Also remember, this is an &lt;strong&gt;Azure Preview Feature&lt;/strong&gt; updating a &lt;strong&gt;Windows Server 2016 Technical Preview 5&lt;/strong&gt; operating system, so you’d expect a few glitches. I did a lot of experimenting with these new features, and this was the only thing I ran into. I’m still extremely impressed with the responsiveness of the team over there in Redmond.&lt;/p&gt;&lt;p&gt;&lt;em&gt;In fact, while I was writing this very post, I was contacted to let me know the fix had gone into production, and it worked perfectly:&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/kKTW5z2vTH-650.png 650w, https://danielscottraynsford.com/img/kKTW5z2vTH-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/kKTW5z2vTH-650.webp 650w, https://danielscottraynsford.com/img/kKTW5z2vTH-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/kKTW5z2vTH-650.jpeg&quot; alt=&quot;ss_azuresmt_nanoserverupdatecomplete&quot; width=&quot;960&quot; height=&quot;562&quot; srcset=&quot;https://danielscottraynsford.com/img/kKTW5z2vTH-650.jpeg 650w, https://danielscottraynsford.com/img/kKTW5z2vTH-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Anyway, this post isn’t about how to configure an &lt;strong&gt;Azure Server Management Tools Gateway&lt;/strong&gt; (it is extremely easy, but if anyone would be interested in a video, let me know and I’ll make one). It is about updating &lt;strong&gt;Nano Servers&lt;/strong&gt; using &lt;strong&gt;CIM&lt;/strong&gt; and, by extension, &lt;strong&gt;updating lots of servers in one go&lt;/strong&gt;.&lt;/p&gt;&lt;hr&gt;&lt;h2 id=&quot;updating-via-cim&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/bulk-updating-nano-servers-using-powershell-and-cim/#updating-via-cim&quot; class=&quot;heading-anchor&quot;&gt;Updating via CIM&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In Windows Nano Server TP4, Microsoft included CIM cmdlets. This enabled us to use the &lt;strong&gt;root/Microsoft/Windows/WindowsUpdate CIM Namespace&lt;/strong&gt; to use Windows Update to install updates on a Nano Server. After a bit of searching around, I found &lt;a href=&quot;https://blogs.technet.microsoft.com/nanoserver/2016/01/16/updating-nano-server-using-windows-update-or-windows-server-update-service/&quot; rel=&quot;noopener&quot;&gt;this blog post&lt;/a&gt; covering the process.&lt;/p&gt;&lt;p&gt;However, I have lots of Nano Servers, and updating them one at a time would be a real pain. So I decided to write a short PowerShell snippet to update all of them at once. This snippet should actually work with any Windows Server 2016 TP4 (or greater) version.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Get-AvailableUpdates&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[CmdletBinding()]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[PSCredential]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Credential&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Credential&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;Credentials to use to update Servers&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;token namespace&quot;&gt;[Parameter(Mandatory)]&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[ValidateNotNullOrEmpty()]&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[String[]]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Servers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;token namespace&quot;&gt;[Switch]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Install&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        
        &lt;span class=&quot;token namespace&quot;&gt;[Switch]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Restart&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$servers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ForEach-Object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$session&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-CimSession&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ComputerName &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Credential &lt;span class=&quot;token variable&quot;&gt;$credential&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$instance&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-CimInstance&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Namespace root/Microsoft/Windows/WindowsUpdate `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ClassName MSFT_WUOperationsSession `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;CimSession &lt;span class=&quot;token variable&quot;&gt;$session&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Due to a bug in CIM on Nano Server (TP4 and TP5), an error is returned when&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# there are no available updates.&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# We use ErrorAction SilentlyContinue to ignore this (DON&#39;T do this in a production script!!!!)&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$scanResults&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Instance&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Invoke-CimMethod&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MethodName ScanForUpdates `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Arguments @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;SearchCriteria=&lt;span class=&quot;token string&quot;&gt;&quot;IsInstalled=0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;OnlineScan=&lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;CimSession &lt;span class=&quot;token variable&quot;&gt;$session&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ErrorAction SilentlyContinue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$scanResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt; has &lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$scanResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; updates to be installed:&quot;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Install&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$installResult&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$Instance&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Invoke-CimMethod&lt;/span&gt; `
                    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MethodName ApplyApplicableUpdates `
                    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;CimSession &lt;span class=&quot;token variable&quot;&gt;$Session&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$installResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReturnValue &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token string&quot;&gt;&#39;Updates were installed successfully:&#39;&lt;/span&gt;
                    &lt;span class=&quot;token variable&quot;&gt;$scanResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Updates
                    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Restart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token string&quot;&gt;&quot;Restarting &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&quot;&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;Invoke-Command&lt;/span&gt; `
                            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ComputerName &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt; `
                            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Credential &lt;span class=&quot;token variable&quot;&gt;$credential&lt;/span&gt; `
                            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ScriptBlock &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Restart-Computer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token string&quot;&gt;&#39;You may need to reboot this server for update installation to complete.&#39;&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token string&quot;&gt;&#39;An error occurred installing updates:&#39;&lt;/span&gt;
                    &lt;span class=&quot;token variable&quot;&gt;$installResult&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&#39;Set -Install flag to install updates&#39;&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$scanResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Updates
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt; has no updates to be installed.&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Remove-CimSession&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;CimSession &lt;span class=&quot;token variable&quot;&gt;$session&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This snippet contains a simple function that takes three parameters:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Credential&lt;/strong&gt; - This is the credentials to use to connect to the Nano Server and update it. If not passed, you will be presented with a login dialog asking for them. It is assumed that all servers being updated use the same credentials.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Servers&lt;/strong&gt; - This is the array of server names to apply updates to.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Install&lt;/strong&gt; - Setting this optional switch will cause the updates to be installed. If it is not set, you will just be told for each server the list of updates that are required.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Restart&lt;/strong&gt; - This optional switch will cause the servers to automatically restart after updates are installed (even if they don’t technically need a restart). Only set this switch if the &lt;strong&gt;Install&lt;/strong&gt; switch is set.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;For example, calling the function with:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-AvailableUpdates&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Servers &lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO4&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO5&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO6&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO7&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO8&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Would show the update status of the eight listed Nano Servers and show any updates available to be installed:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/rway3PT8us-650.png 650w, https://danielscottraynsford.com/img/rway3PT8us-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/rway3PT8us-650.webp 650w, https://danielscottraynsford.com/img/rway3PT8us-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/rway3PT8us-650.jpeg&quot; alt=&quot;ss_updatenano_updatesavailable&quot; width=&quot;960&quot; height=&quot;662&quot; srcset=&quot;https://danielscottraynsford.com/img/rway3PT8us-650.jpeg 650w, https://danielscottraynsford.com/img/rway3PT8us-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;However, adding an &lt;strong&gt;-Install&lt;/strong&gt; parameter to the call:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-AvailableUpdates&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Servers &lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO4&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO5&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO6&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO7&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO8&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Install&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Will cause all available updates to be installed onto the listed Nano Servers:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/zFwG2Cvapw-650.png 650w, https://danielscottraynsford.com/img/zFwG2Cvapw-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/zFwG2Cvapw-650.webp 650w, https://danielscottraynsford.com/img/zFwG2Cvapw-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/zFwG2Cvapw-650.jpeg&quot; alt=&quot;ss_updatenano_updatesinstalled&quot; width=&quot;960&quot; height=&quot;662&quot; srcset=&quot;https://danielscottraynsford.com/img/zFwG2Cvapw-650.jpeg 650w, https://danielscottraynsford.com/img/zFwG2Cvapw-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;You may still need to restart these servers after the updates are installed. If you run the function again without restarting the servers first, you will be told the updates still need to be installed. If you want the servers to automatically be restarted, add a &lt;strong&gt;-Restart&lt;/strong&gt; parameter:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-AvailableUpdates&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Servers &lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO4&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO5&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO6&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO7&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA-NANO8&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Install `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Restart&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Like this:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/WfoIeofu_B-650.png 650w, https://danielscottraynsford.com/img/WfoIeofu_B-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/WfoIeofu_B-650.webp 650w, https://danielscottraynsford.com/img/WfoIeofu_B-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/WfoIeofu_B-650.jpeg&quot; alt=&quot;ss_updatenano_updatesinstalledrestart&quot; width=&quot;960&quot; height=&quot;662&quot; srcset=&quot;https://danielscottraynsford.com/img/WfoIeofu_B-650.jpeg 650w, https://danielscottraynsford.com/img/WfoIeofu_B-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This is even more useful when you consider that you can update standard (Core and GUI) Windows Server 2016 installations like this as well. This might also work on earlier versions of Windows Server (2012, 2012 R2) as well, but I don’t have time to try this out.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Edit: 21 May 2016 - I tested this on Windows Server 2012 R2, but it does not work because the required CIM classes are not available on that version of the Operating System.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: I really haven’t put much work into error checking or reporting on this process, so if you run into any errors (servers not online, bad credentials, etc.), then they might not be handled elegantly. This code is really an example of what can be easily done.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This function would be a definite candidate for a &lt;strong&gt;PowerShell Workflow&lt;/strong&gt;—allowing complete parallelization of the process.&lt;/p&gt;&lt;p&gt;I hope this is useful, and have a great weekend!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Using a Windows Virtual NAT with a Hyper-V Lab</title>
      <link href="https://danielscottraynsford.com/blog/using-a-windows-virtual-nat-with-a-hyper-v-lab/" />
      <updated>2016-05-11T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/using-a-windows-virtual-nat-with-a-hyper-v-lab/</id>
      <content type="html">
				&lt;p&gt;One of the new features introduced into Windows in build 10586 and above was the new &lt;strong&gt;NAT&lt;/strong&gt; &lt;strong&gt;Virtual Switch&lt;/strong&gt;. This feature was primarily introduced to ease the introduction of the &lt;a href=&quot;https://msdn.microsoft.com/en-us/virtualization/windowscontainers/about/about_overview&quot; rel=&quot;noopener&quot;&gt;Windows Containers&lt;/a&gt; in the upcoming release of &lt;strong&gt;Windows Server 2016&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;In more recent builds of Windows (build 14295 and above) the &lt;strong&gt;NAT Virtual Switch&lt;/strong&gt; has been removed in favor of a new &lt;strong&gt;Virtual NAT&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;Device&lt;/strong&gt; that exists separate from the &lt;strong&gt;Hyper-V Virtual Switch&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;This new &lt;strong&gt;Virtual NAT Device&lt;/strong&gt; is more inline with Microsoft’s &lt;strong&gt;Software Defined Networking&lt;/strong&gt; approach. It also allows us to create multiple Hyper-V Lab environments where each Lab is completely isolated from any others but still be connected to the Internet by way of the &lt;strong&gt;Virtual NAT Device&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Previously, to give all the machines in a Lab internet access we would have had to use:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;An External Switch&lt;/strong&gt; - Connect all machines to an &lt;strong&gt;External Virtual Switch&lt;/strong&gt; that was connected to the internet via one of the &lt;strong&gt;Hyper-V Host’s&lt;/strong&gt; network adapters.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A Guest NAT&lt;/strong&gt; - Install a NAT onto one of the Guest Virtual Machines in the Lab. For example, install Windows Server 2012 R2 with the Remote Access role and configure a NAT. This would still require at least this node in the Lab to be connected to the internet via an &lt;strong&gt;External Virtual Switch&lt;/strong&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Each of these approaches had some drawbacks:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Each Lab was not completely isolated from the other labs.&lt;/li&gt;&lt;li&gt;An entire guest might need to be provisioned to provide internet access to the other machines in the Lab.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;But using the &lt;strong&gt;Virtual NAT device&lt;/strong&gt; allows us to configure Labs with complete network isolation but still being connected to the internet without the use of a &lt;strong&gt;guest NAT&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_virtualnat_diagram3.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/YsuAsEFLFO-650.png 650w, https://danielscottraynsford.com/img/YsuAsEFLFO-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/YsuAsEFLFO-650.webp 650w, https://danielscottraynsford.com/img/YsuAsEFLFO-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/YsuAsEFLFO-650.jpeg&quot; alt=&quot;ss_virtualnat_diagram&quot; width=&quot;960&quot; height=&quot;555&quot; srcset=&quot;https://danielscottraynsford.com/img/YsuAsEFLFO-650.jpeg 650w, https://danielscottraynsford.com/img/YsuAsEFLFO-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;So, to configure a pair of Labs like in the diagram above all we need is to execute a few &lt;strong&gt;PowerShell Cmdlets&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: Make sure your Hyper-V host is at least &lt;strong&gt;build 14295&lt;/strong&gt; (Windows 10 build 14295 or Windows Server 2016 TP5). Otherwise these cmdlets will fail.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;If you want some more detail on setting up a Virtual NAT, see &lt;a href=&quot;https://msdn.microsoft.com/en-us/virtualization/hyperv_on_windows/user_guide/setup_nat_network&quot; rel=&quot;noopener&quot;&gt;Set up a NAT Network&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;configure-hyper-v-lab-with-nat&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/using-a-windows-virtual-nat-with-a-hyper-v-lab/#configure-hyper-v-lab-with-nat&quot; class=&quot;heading-anchor&quot;&gt;Configure Hyper-V Lab with NAT&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To configure a Hyper-V Lab with NAT, perform the following steps, executing any PowerShell cmdlets in an &lt;strong&gt;Administrator PowerShell console&lt;/strong&gt;.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Create a &lt;strong&gt;Hyper-V Internal Virtual Switch&lt;/strong&gt; on your &lt;strong&gt;Host&lt;/strong&gt;: [sourcecode language=“powershell”] New-VMSwitch -Name Lab1 -SwitchType Internal [/sourcecode] This will also create a &lt;strong&gt;Virtual Network Adapter&lt;/strong&gt; connected to the host.&lt;/li&gt;&lt;li&gt;Assign the gateway IP address of the NAT to the &lt;strong&gt;Virtual Network Adapter&lt;/strong&gt;: [sourcecode language=“powershell”] # Get the MAC Address of the VM Adapter bound to the virtual switch $MacAddress = (Get-VMNetworkAdapter -ManagementOS -SwitchName Lab1).MacAddress # Use the MAC Address of the Virtual Adapter to look up the Adapter in the Net Adapter list $Adapter = Get-NetAdapter | Where-Object { (($_.MacAddress -replace ‘-’,‘’) -eq $MacAddress) } New-NetIPAddress –IPAddress 192.168.140.1 -PrefixLength 24 -InterfaceIndex $Adapter.ifIndex [/sourcecode]&lt;/li&gt;&lt;li&gt;Create the &lt;strong&gt;Virtual NAT device&lt;/strong&gt;: [sourcecode language=“powershell”] New-NetNat –Name Lab1NAT –InternalIPInterfaceAddressPrefix 192.168.140.0/24 [/sourcecode]&lt;/li&gt;&lt;li&gt;Configure the network settings on each &lt;strong&gt;guest virtual network adapter&lt;/strong&gt; assigned to the &lt;strong&gt;virtual switch&lt;/strong&gt; in the &lt;em&gt;192.168.140.0/24&lt;/em&gt; &lt;strong&gt;subnet&lt;/strong&gt; and configure the &lt;strong&gt;default gateway&lt;/strong&gt; to be &lt;em&gt;192.168.140.1&lt;/em&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;That’s it - all machines in the Lab should have access to the internet and be completely isolated as well. Naturally I have updated the &lt;a href=&quot;https://github.com/PlagueHO/LabBuilder&quot; rel=&quot;noopener&quot;&gt;LabBuilder&lt;/a&gt; system to support this new functionality as well.&lt;/p&gt;&lt;p&gt;I hope this was useful and happy NATing.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>cDFS is dead, long live xDFS</title>
      <link href="https://danielscottraynsford.com/blog/cdfs-is-dead-long-live-xdfs/" />
      <updated>2016-05-11T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/cdfs-is-dead-long-live-xdfs/</id>
      <content type="html">
				&lt;p&gt;The &lt;a href=&quot;https://github.com/PowerShell/xDFS&quot; rel=&quot;noopener&quot;&gt;xDFS DSC resource module&lt;/a&gt; has been officially released to the &lt;a href=&quot;https://www.powershellgallery.com/packages/xdfs&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt; thanks to the awesome review efforts of the Microsoft PowerShell Team. The &lt;strong&gt;cDFS DSC Resource&lt;/strong&gt; has now been &lt;strong&gt;unlisted&lt;/strong&gt; from the PowerShell Gallery. So now is the time to update any DSC configuration scripts to use xDFS.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/FrwOpwtSKp-650.png 650w, https://danielscottraynsford.com/img/FrwOpwtSKp-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/FrwOpwtSKp-650.webp 650w, https://danielscottraynsford.com/img/FrwOpwtSKp-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/FrwOpwtSKp-650.jpeg&quot; alt=&quot;ss_xdfs_releasepsgallery&quot; width=&quot;960&quot; height=&quot;460&quot; srcset=&quot;https://danielscottraynsford.com/img/FrwOpwtSKp-650.jpeg 650w, https://danielscottraynsford.com/img/FrwOpwtSKp-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Important: There were some minor changes to xDFS when it was converted from cDFS. For information on what you’ll need to change to convert to xDFS see my &lt;a href=&quot;https://dscottraynsford.wordpress.com/2016/05/06/cdfs-moving-to-the-powershell-team/&quot; rel=&quot;noopener&quot;&gt;earlier post&lt;/a&gt;.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>cDFS moving to the PowerShell Team</title>
      <link href="https://danielscottraynsford.com/blog/cdfs-moving-to-the-powershell-team/" />
      <updated>2016-05-06T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/cdfs-moving-to-the-powershell-team/</id>
      <content type="html">
				&lt;p&gt;Just a Friday afternoon heads up - if you’re using the &lt;a href=&quot;https://www.powershellgallery.com/packages/cDFS/2.1.0.238&quot; rel=&quot;noopener&quot;&gt;cDFS DSC Resource&lt;/a&gt; I created to manage Windows Server Distributed File System (Replication and Namespaces), it has now been accepted into the PowerShell Community resources and will be under the control of the PowerShell Team.&lt;/p&gt;&lt;p&gt;This means that the &lt;a href=&quot;https://github.com/PlagueHO/xDFS&quot; rel=&quot;noopener&quot;&gt;GitHub source code repository&lt;/a&gt; will be moving over to the &lt;a href=&quot;https://github.com/PowerShell&quot; rel=&quot;noopener&quot;&gt;PowerShell organization&lt;/a&gt; in the next few days. This also means that any future releases of this resource module won’t be provided by me as &lt;strong&gt;cDFS&lt;/strong&gt;, but will be released by the PowerShell team as &lt;strong&gt;xDFS&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;So I recommend that when this happens you switch over to using the &lt;strong&gt;xDFS&lt;/strong&gt; resource. I will put another post up here when the change over officially occurs. The first official release version under the new &lt;strong&gt;xDFS&lt;/strong&gt; name will be &lt;strong&gt;3.0.0.x&lt;/strong&gt;. I won’t make any further changes or bug fixes to the &lt;strong&gt;cDFS&lt;/strong&gt; resources.&lt;/p&gt;&lt;p&gt;It is also worth noting that as part of this move some minor changes were made to the DSC Resource modules. These are &lt;strong&gt;breaking changes&lt;/strong&gt; and you will most likely need to update any DSC Configurations depending on this, but you would have to do this anyway because of the name change.&lt;/p&gt;&lt;p&gt;The changes are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Resource &lt;strong&gt;xDFSRepGroup&lt;/strong&gt; renamed to &lt;strong&gt;xDFSReplicationGroup&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Resource &lt;strong&gt;xDFSRepGroupConnection&lt;/strong&gt; renamed to &lt;strong&gt;xDFSReplicationGroupConnection&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Resource &lt;strong&gt;xDFSRepGroupFolder&lt;/strong&gt; renamed to &lt;strong&gt;xDFSReplicationGroupFolder&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Resource &lt;strong&gt;xDFSRepGroupMembership&lt;/strong&gt; renamed to &lt;strong&gt;xDFSReplicationGroupMembership&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;xDFSReplicationGroupConnection:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;Changed &lt;strong&gt;DisableConnection&lt;/strong&gt; parameter to &lt;strong&gt;EnsureEnabled&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Changed &lt;strong&gt;DisableRDC&lt;/strong&gt; parameter to &lt;strong&gt;EnsureRDCEnabled&lt;/strong&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;These changes should only require minor changes to your configuration scripts to implement.&lt;/p&gt;&lt;p&gt;Thanks for reading and have a great Friday~&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Nano Server Packages in Windows Server 2016 TP5</title>
      <link href="https://danielscottraynsford.com/blog/nano-server-packages-in-windows-server-2016-tp5/" />
      <updated>2016-04-28T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/nano-server-packages-in-windows-server-2016-tp5/</id>
      <content type="html">
				&lt;p&gt;Happy TP5 day!&lt;/p&gt;&lt;p&gt;Here is just a quick snapshot of the package list for Nano Server in Windows Server 2016 TP5:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/Rqz9PO6pTQ-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Rqz9PO6pTQ-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Rqz9PO6pTQ-650.jpeg&quot; alt=&quot;ss_nanotp5_packagelist&quot; width=&quot;650&quot; height=&quot;274&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Right off the bat I notice a few new ones are included:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;BootFromWim&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;SecureStartup&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ShieldedVM&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I can guess at the purpose of these packages, but will be interested to learn a bit more about them. That is all I have time for as I now need to go and update the LabBuilder sample projects to use this new ISO.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Install a VMWare ESXi 6.0 Hypervisor in a Hyper-V VM</title>
      <link href="https://danielscottraynsford.com/blog/install-a-vmware-esxi-60-hypervisor-in-a-hyper-v-vm/" />
      <updated>2016-04-22T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/install-a-vmware-esxi-60-hypervisor-in-a-hyper-v-vm/</id>
      <content type="html">
				&lt;h2 id=&quot;update-19th-february-2018&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-a-vmware-esxi-60-hypervisor-in-a-hyper-v-vm/#update-19th-february-2018&quot; class=&quot;heading-anchor&quot;&gt;&lt;strong&gt;Update 19th February 2018:&lt;/strong&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This article has had a lot more attention than I ever expected! This has brought to light several issues with the process as well as changes made to Hyper-V and ESXi that break the process. You could wade through all 256 comments and assemble the corrections yourself, but user @Gambit has helpfully done this for me! So I’ve included the summary here:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;@Tom Watson:&lt;/strong&gt; Adding a line to &lt;code&gt;/etc/vmware/config&lt;/code&gt; with &lt;code&gt;vmx.allowNested = &quot;TRUE&quot;&lt;/code&gt;. Since all VMs will be running nested (whole point of article), this is a must if you want them to start! The alternative is adding this line to every VMX file for each guest. Tom’s solution was much more elegant.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;@burnett437:&lt;/strong&gt; Configure ESXi Management vSwitch to “accept” “promiscuous mode”. Apparently, this was a known issue even in nested ESXi 5.5. While the host network will operate for a while without a problem, the “promiscuous mode” policy will eventually be tripped, and you won’t be able to talk to the host at random times. Now I think this has to do with the nature of nested virtual switches (a VMWare vSwitch inside of a Hyper-V Virtual Switch). When this happens, I found you could “down/up” the management &lt;code&gt;vnicX&lt;/code&gt; to get it back, but just set the setting to not worry about it.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;@me:&lt;/strong&gt; Network threat detection doesn’t like nested virtual switches either. My Symantec Endpoint Client would occasionally block traffic coming from my local NIC (vSphere Client/vSphere Converter) to this nested Host (Windows Firewall did NOT seem to care), but SEP occasionally tripped because of all “suspicious” addressing to the nested host.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;@me:&lt;/strong&gt; Use vSphere converter for guest images coming from other VMWare products….duh. ESXi doesn’t like split VHDKs; the guest won’t boot. This is a beginner mistake, but this article is for Hyper-V admins that may not know the subtle nuances between VMWare products.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;@RichMD:&lt;/strong&gt; I like the idea of hardware pass-through on the NIC. I may tinker with this because it may resolve several issues, not just the ESXi v6.5 NIC blacklist issue. Theoretically, passing the physical NIC directly to the nested ESXi Host could/should resolve the “promiscuous mode,” “Network Threat Protection,” and even the “Half Duplex Legacy Adapter Requirement” (very slow network performance) problems.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;the-original-article-starts-here&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-a-vmware-esxi-60-hypervisor-in-a-hyper-v-vm/#the-original-article-starts-here&quot; class=&quot;heading-anchor&quot;&gt;The Original Article Starts Here&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Recently I’ve been playing around with the new Hyper-V Nested Virtualization feature within Windows 10 (build 10565 and greater) and Windows Server 2016. It is pretty cool to be able to create virtualized lab environments containing Hyper-V clusters. But what if we want a lab that contains VMWare ESXi Hypervisors running on a Hyper-V host? I couldn’t find the process documented anywhere and couldn’t even confirm if it should be possible. But after asking a lot of annoying questions—thanks &lt;a href=&quot;https://nz.linkedin.com/in/adam-burns-b8307664&quot; rel=&quot;noopener&quot;&gt;Adam Burns&lt;/a&gt;—Googling, and hair-pulling, I managed to get it going:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/W3XyEnbS2Y-650.png 650w, https://danielscottraynsford.com/img/W3XyEnbS2Y-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/W3XyEnbS2Y-650.webp 650w, https://danielscottraynsford.com/img/W3XyEnbS2Y-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/W3XyEnbS2Y-650.jpeg&quot; alt=&quot;ss_vmwareinhv_proof&quot; width=&quot;960&quot; height=&quot;818&quot; srcset=&quot;https://danielscottraynsford.com/img/W3XyEnbS2Y-650.jpeg 650w, https://danielscottraynsford.com/img/W3XyEnbS2Y-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;So this seems like a good topic for a blog post.&lt;/p&gt;&lt;h2 id=&quot;what-youll-need&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-a-vmware-esxi-60-hypervisor-in-a-hyper-v-vm/#what-youll-need&quot; class=&quot;heading-anchor&quot;&gt;What You’ll Need&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;You are going to need a few things to get this working:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A Hyper-V host&lt;/strong&gt; running on Windows 10 (build 10565 or greater) or Windows Server 2016 TP4.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Enable-NestedVM.ps1&lt;/strong&gt; - A PowerShell script for enabling Nested Virtualization in a Hyper-V VM. Click &lt;a href=&quot;https://github.com/Microsoft/Virtualization-Documentation/blob/master/hyperv-tools/Nested/Enable-NestedVm.ps1&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; to get the file from the Microsoft team on GitHub.&lt;/li&gt;&lt;li&gt;A &lt;strong&gt;VMWare account&lt;/strong&gt; - just sign up for one &lt;a href=&quot;https://my.vmware.com/web/vmware/registration&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; if you don’t already have one.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;VMWare PowerShell CLI&lt;/strong&gt; installed - I used the 6.3 release 1 that I downloaded from &lt;a href=&quot;https://my.vmware.com/group/vmware/get-download?downloadGroup=PCLI630R1&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ESXi-Customizer-PS.ps1&lt;/strong&gt; - A PowerShell script for injecting network drivers into an ESXi 5.x/6.x ISO. I downloaded it from &lt;a href=&quot;http://www.v-front.de/p/esxi-customizer-ps.html#download&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I suggest you download all of the above items to a working folder—I called mine &lt;code&gt;D:&#92;ESX-In-Hyper-V&lt;/code&gt;, so these instructions will reflect that, but you can call your folder whatever you like.&lt;/p&gt;&lt;p&gt;You should end up with a folder containing these files:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/NsAPzmnG2l-229.png 229w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/NsAPzmnG2l-229.webp 229w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/NsAPzmnG2l-229.jpeg&quot; alt=&quot;ss_vmwareinhv_neededfiles&quot; width=&quot;229&quot; height=&quot;65&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;And before you ask: No, you don’t need a VMWare ESXi 6.0 ISO—this will get downloaded and produced for us.&lt;/p&gt;&lt;h2 id=&quot;the-process&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-a-vmware-esxi-60-hypervisor-in-a-hyper-v-vm/#the-process&quot; class=&quot;heading-anchor&quot;&gt;The Process&lt;/a&gt;&lt;/h2&gt;&lt;h3 id=&quot;part-1-prepare-an-esxi-60-iso-with-network-drivers&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-a-vmware-esxi-60-hypervisor-in-a-hyper-v-vm/#part-1-prepare-an-esxi-60-iso-with-network-drivers&quot; class=&quot;heading-anchor&quot;&gt;Part 1 - Prepare an ESXi 6.0 ISO with Network Drivers&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The biggest problem I ran into when trying to install ESXi onto Hyper-V was that the ESXi kernel doesn’t come with drivers for the &lt;em&gt;Microsoft Virtual Network Adapter&lt;/em&gt; or the &lt;em&gt;Microsoft Legacy Network Adapter&lt;/em&gt; (emulates a DECchip 21140). So you’ll need to &lt;strong&gt;inject&lt;/strong&gt; these drivers into the &lt;strong&gt;VMWare ESXi 6.0&lt;/strong&gt; ISO. Luckily, there is a script available and the appropriate drivers DECchip 21140 (called “net-tulip” for some reason) that makes this process a breeze:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Install &lt;strong&gt;VMWare PowerCLI&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Open a &lt;strong&gt;PowerShell&lt;/strong&gt; console.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Enter the following commands:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;CD D:&#92;ESX-In-Hyper-V&#92;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;ESXi-Customizer-&lt;span class=&quot;token function&quot;&gt;PS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;v2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;4&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;v60 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;vft &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;load net-tulip&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;After a few minutes, the VMWare ESXi 6.0 ISO will be downloaded, and the “net-tulip” drivers merged with it:&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/9tvrvEWMBI-650.png 650w, https://danielscottraynsford.com/img/9tvrvEWMBI-859.png 859w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/9tvrvEWMBI-650.webp 650w, https://danielscottraynsford.com/img/9tvrvEWMBI-859.webp 859w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/9tvrvEWMBI-650.jpeg&quot; alt=&quot;ss_vmwareinhv_createesxiiso&quot; width=&quot;859&quot; height=&quot;634&quot; srcset=&quot;https://danielscottraynsford.com/img/9tvrvEWMBI-650.jpeg 650w, https://danielscottraynsford.com/img/9tvrvEWMBI-859.jpeg 859w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;The ISO will now be available in the &lt;code&gt;D:&#92;ESX-In-Hyper-V&lt;/code&gt; folder:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/fnUZUXAkjD-293.png 293w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/fnUZUXAkjD-293.webp 293w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/fnUZUXAkjD-293.jpeg&quot; alt=&quot;ss_vmwareinhv_neededfilesandiso&quot; width=&quot;293&quot; height=&quot;92&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h3 id=&quot;part-2-create-the-hyper-v-vm&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-a-vmware-esxi-60-hypervisor-in-a-hyper-v-vm/#part-2-create-the-hyper-v-vm&quot; class=&quot;heading-anchor&quot;&gt;Part 2 - Create the Hyper-V VM&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;In &lt;strong&gt;Hyper-V Manager&lt;/strong&gt;, create a new Virtual Machine:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/i9xn3Rh9wD-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/i9xn3Rh9wD-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/i9xn3Rh9wD-650.jpeg&quot; alt=&quot;ss_vmwareinhv_newvmpath&quot; width=&quot;650&quot; height=&quot;492&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Select &lt;strong&gt;Generation 1&lt;/strong&gt; and click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Set the &lt;strong&gt;Startup Memory&lt;/strong&gt; to at least &lt;strong&gt;4096MB&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Uncheck &lt;strong&gt;Use Dynamic Memory for this Virtual Machine:&lt;/strong&gt;&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/9IIT0GK7IL-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/9IIT0GK7IL-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/9IIT0GK7IL-650.jpeg&quot; alt=&quot;ss_vmwareinhv_newvmmemory&quot; width=&quot;650&quot; height=&quot;492&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Don’t bother to &lt;strong&gt;Configure Networking&lt;/strong&gt; on the next step—just click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Select &lt;strong&gt;Create a new virtual hard disk&lt;/strong&gt; and set the &lt;strong&gt;Size&lt;/strong&gt; to &lt;strong&gt;10GB&lt;/strong&gt; (this is just going to be the boot disk for the ESXi Hypervisor):&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/-V2XZcGsPt-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/-V2XZcGsPt-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/-V2XZcGsPt-650.jpeg&quot; alt=&quot;ss_vmwareinhv_newvmdisk&quot; width=&quot;650&quot; height=&quot;492&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Select &lt;strong&gt;Install an operating system from a bootable CD/DVD-ROM&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Select &lt;strong&gt;Image file (.iso)&lt;/strong&gt; and browse to the &lt;strong&gt;ISO&lt;/strong&gt; created in &lt;strong&gt;Part 1&lt;/strong&gt;:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/qnhlQlQihF-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/qnhlQlQihF-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/qnhlQlQihF-650.jpeg&quot; alt=&quot;ss_vmwareinhv_newvminstallation&quot; width=&quot;650&quot; height=&quot;492&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;, then click &lt;strong&gt;Finish&lt;/strong&gt; to create the Virtual Machine:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/rnlATcMFcO-339.png 339w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/rnlATcMFcO-339.webp 339w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/rnlATcMFcO-339.jpeg&quot; alt=&quot;ss_vmwareinhv_newvm&quot; width=&quot;339&quot; height=&quot;19&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Right-click the new &lt;strong&gt;Virtual Machine&lt;/strong&gt; and select &lt;strong&gt;Settings.&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Select the &lt;strong&gt;Processor&lt;/strong&gt; node and increase the &lt;strong&gt;Number of Virtual Processors&lt;/strong&gt; to &lt;strong&gt;at least 2&lt;/strong&gt;:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/IeV5G2h2Y_-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/IeV5G2h2Y_-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/IeV5G2h2Y_-650.jpeg&quot; alt=&quot;ss_vmwareinhv_vmsettings_processor&quot; width=&quot;650&quot; height=&quot;618&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Select the existing &lt;strong&gt;Network Adapter&lt;/strong&gt; node and click &lt;strong&gt;Remove:&lt;/strong&gt;&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/MsheqtXAZy-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/MsheqtXAZy-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/MsheqtXAZy-650.jpeg&quot; alt=&quot;ss_vmwareinhv_vmsettings_removenetwork&quot; width=&quot;650&quot; height=&quot;618&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Select the &lt;strong&gt;Add Hardware&lt;/strong&gt; node and select &lt;strong&gt;Legacy Network Adapter&lt;/strong&gt;:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/GGqd18u7Ut-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/GGqd18u7Ut-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/GGqd18u7Ut-650.jpeg&quot; alt=&quot;ss_vmwareinhv_vmsettings_addnetwork&quot; width=&quot;650&quot; height=&quot;618&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Add&lt;/strong&gt;:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/hiamJzrveS-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/hiamJzrveS-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/hiamJzrveS-650.jpeg&quot; alt=&quot;ss_vmwareinhv_vmsettings_addlegacy&quot; width=&quot;650&quot; height=&quot;618&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Select a &lt;strong&gt;Virtual Switch&lt;/strong&gt; to connect the &lt;strong&gt;ESXi Host&lt;/strong&gt; to.&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The rest of the process involves enabling nested virtualization, booting the ESXi VM, and configuring the ESXi boot options. If you’d like me to continue with those steps, let me know!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Get an Array of Localized Hyper-V Integration Service Names</title>
      <link href="https://danielscottraynsford.com/blog/get-an-array-of-localized-hyper-v-integration-service-names/" />
      <updated>2016-04-19T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/get-an-array-of-localized-hyper-v-integration-service-names/</id>
      <content type="html">
				&lt;p&gt;Today’s PowerShell snippet is used to get a list of Localized captions for the available Integration Services available on a Hyper-V host. I needed this because &lt;a href=&quot;https://github.com/PlagueHO/LabBuilder&quot; rel=&quot;noopener&quot;&gt;LabBuilder&lt;/a&gt; allows the individual Integration Services to be enabled or disabled per Lab Virtual Machine.&lt;/p&gt;&lt;p&gt;It does this using the Integration Service names configured in the configuration XML file. The problem of course is localization - something I often overlook. If you need to enable/disable an Integration Service on a VM, you need to know the name of it. The name of course is a localized string, so you need to know what the possible values are on the current machine culture.&lt;/p&gt;&lt;p&gt;So, after a lot of digging around in the WMI/CIM I managed to locate the various classes I need and converted them into a simple function:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; GetIntegrationServiceNames &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[CmdLetBinding()]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;param&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$Captions&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$Classes&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;Msvm_VssComponentSettingData&#39;&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;Msvm_ShutdownComponentSettingData&#39;&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;Msvm_TimeSyncComponentSettingData&#39;&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;Msvm_HeartbeatComponentSettingData&#39;&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;Msvm_GuestServiceInterfaceComponentSettingData&#39;&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;Msvm_KvpExchangeComponentSettingData&#39;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Class&lt;/span&gt; in &lt;span class=&quot;token variable&quot;&gt;$Classes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Captions&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-CimInstance&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Class&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Class&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Namespace Root&#92;Virtualization&#92;V2 `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Property Caption &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Select-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;First 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Caption
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# foreach&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# This Integration Service is registered in CIM but are not exposed in Hyper-V&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# &#39;Msvm_RdvComponentSettingData&#39;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Captions&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# GetIntegrationServiceNames&lt;/span&gt;

GetIntegrationServiceNames&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The output of the function looks like this for English US:&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;VSS
Shutdown
Time Synchronization
Heartbeat
Guest Service Interface
Key-Value Pair Exchange&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Hopefully someone will find it handy.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Install Jenkins using DSC – Part 2</title>
      <link href="https://danielscottraynsford.com/blog/install-jenkins-using-dsc-part-2/" />
      <updated>2016-04-18T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/install-jenkins-using-dsc-part-2/</id>
      <content type="html">
				&lt;p&gt;In my &lt;a href=&quot;https://dscottraynsford.wordpress.com/2016/04/14/install-jenkins-on-windows-server-core-part-1/&quot; rel=&quot;noopener&quot;&gt;previous post&lt;/a&gt;, I showed how to create a PowerShell script that would install a Jenkins CI Master server onto a Windows Server Core installation. The obvious next step for such a script was to convert it into a DSC configuration file.&lt;/p&gt;&lt;p&gt;In this post, I’m assuming WMF 5.0 is installed onto the server that will be converted into a Jenkins Master. You could manage this without WMF 5.0, but you’d need to manually install the DSC Resource modules that the configuration will use.&lt;/p&gt;&lt;p&gt;Once again, the full DSC Configuration script can be found at the end of the post.&lt;/p&gt;&lt;h2 id=&quot;requirements&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-jenkins-using-dsc-part-2/#requirements&quot; class=&quot;heading-anchor&quot;&gt;Requirements&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;You’ll need:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A physical or virtual machine running Windows Server 2012 R2 Core (or Full)—it should be a completely clean install with WMF 5.0 installed on it.&lt;/li&gt;&lt;li&gt;An administrator login to the server.&lt;/li&gt;&lt;li&gt;An internet connection to the server.&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;resource-modules&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-jenkins-using-dsc-part-2/#resource-modules&quot; class=&quot;heading-anchor&quot;&gt;Resource Modules&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This DSC Configuration requires the use of three DSC Resources:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;cChoco&lt;/strong&gt; - This community resource is used to install Chocolatey and Jenkins.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;xNetworking&lt;/strong&gt; - This resource is used to configure the networking on the server if required.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;PSDesiredStateConfiguration&lt;/strong&gt; - This resource comes with PowerShell by default and is used to provide the &lt;strong&gt;Script&lt;/strong&gt; resource.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The easiest way to install these resource modules is by executing these commands on the Jenkins server:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Make sure the DSC Resource modules are downloaded&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name cChoco &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force
&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name xNetworking &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, if you’re using a Pull server or compiling the DSC MOF on a development machine (rather than the Jenkins node), you would need to use other methods of ensuring the modules are available.&lt;/p&gt;&lt;h2 id=&quot;the-configuration-components&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-jenkins-using-dsc-part-2/#the-configuration-components&quot; class=&quot;heading-anchor&quot;&gt;The Configuration Components&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The DSC Configuration needs to do the following things:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Configure Networking&lt;/strong&gt; (optional)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Install .NET 3.5 Framework&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Install Chocolatey&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Install JDK 8&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Install Jenkins&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Configure Jenkins Port&lt;/strong&gt; (optional)&lt;/li&gt;&lt;/ol&gt;&lt;h3 id=&quot;configure-networking&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-jenkins-using-dsc-part-2/#configure-networking&quot; class=&quot;heading-anchor&quot;&gt;Configure Networking&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I like to use the &lt;strong&gt;xNetworking&lt;/strong&gt; DSC resource to configure the IPv4 and IPv6 settings on the network adapter to have a static configuration. However, you won’t need to do this if you’re using DHCP or manual configuration. Note, in my case, my adapter was called “Ethernet”.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;xIPAddress IPv4_1 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    InterfaceAlias = &lt;span class=&quot;token string&quot;&gt;&#39;Ethernet&#39;&lt;/span&gt;
    AddressFamily  = &lt;span class=&quot;token string&quot;&gt;&#39;IPv4&#39;&lt;/span&gt;
    IPAddress      = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.128.20&#39;&lt;/span&gt;
    SubnetMask     = &lt;span class=&quot;token string&quot;&gt;&#39;24&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
xDefaultGatewayAddress IPv4G_1 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    InterfaceAlias = &lt;span class=&quot;token string&quot;&gt;&#39;Ethernet&#39;&lt;/span&gt;
    AddressFamily  = &lt;span class=&quot;token string&quot;&gt;&#39;IPv4&#39;&lt;/span&gt;
    Address        = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.128.19&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
xDnsServerAddress IPv4D_1 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    InterfaceAlias = &lt;span class=&quot;token string&quot;&gt;&#39;Ethernet&#39;&lt;/span&gt;
    AddressFamily  = &lt;span class=&quot;token string&quot;&gt;&#39;IPv4&#39;&lt;/span&gt;
    Address        = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.128.10&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
xIPAddress IPv6_1 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    InterfaceAlias = &lt;span class=&quot;token string&quot;&gt;&#39;Ethernet&#39;&lt;/span&gt;
    AddressFamily  = &lt;span class=&quot;token string&quot;&gt;&#39;IPv6&#39;&lt;/span&gt;
    IPAddress      = &lt;span class=&quot;token string&quot;&gt;&#39;fd53:ccc5:895a:bc00::14&#39;&lt;/span&gt;
    SubnetMask     = &lt;span class=&quot;token string&quot;&gt;&#39;64&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
xDefaultGatewayAddress IPv6G_1 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    InterfaceAlias = &lt;span class=&quot;token string&quot;&gt;&#39;Ethernet&#39;&lt;/span&gt;
    AddressFamily  = &lt;span class=&quot;token string&quot;&gt;&#39;IPv6&#39;&lt;/span&gt;
    Address        = &lt;span class=&quot;token string&quot;&gt;&#39;fd53:ccc5:895a:bc00::13&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
xDnsServerAddress IPv6D_1 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    InterfaceAlias = &lt;span class=&quot;token string&quot;&gt;&#39;Ethernet&#39;&lt;/span&gt;
    AddressFamily  = &lt;span class=&quot;token string&quot;&gt;&#39;IPv6&#39;&lt;/span&gt;
    Address        = &lt;span class=&quot;token string&quot;&gt;&#39;fd53:ccc5:895a:bc00::a&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;install-net-35-framework&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-jenkins-using-dsc-part-2/#install-net-35-framework&quot; class=&quot;heading-anchor&quot;&gt;Install .NET 3.5 Framework&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Jenkins requires the .NET 3.5 Framework, so I’m going to use the WindowsFeature DSC Resource to install it:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;WindowsFeature NetFrameworkCore &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Ensure = &lt;span class=&quot;token string&quot;&gt;&quot;Present&quot;&lt;/span&gt;
    Name   = &lt;span class=&quot;token string&quot;&gt;&quot;NET-Framework-Core&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;install-chocolatey&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-jenkins-using-dsc-part-2/#install-chocolatey&quot; class=&quot;heading-anchor&quot;&gt;Install Chocolatey&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Next up, I’m going to use the &lt;strong&gt;cChocoInstaller&lt;/strong&gt; resource in the &lt;strong&gt;cChoco&lt;/strong&gt; resource module (available on PowerShell Gallery &lt;a href=&quot;https://www.powershellgallery.com/packages/cChoco&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;) to install the &lt;strong&gt;Chocolatey&lt;/strong&gt; package manager:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;cChocoInstaller installChoco &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    InstallDir = &lt;span class=&quot;token string&quot;&gt;&quot;c:&#92;choco&quot;&lt;/span&gt;
    DependsOn  = &lt;span class=&quot;token string&quot;&gt;&quot;[WindowsFeature]NetFrameworkCore&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;install-jdk-8-and-jenkins&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-jenkins-using-dsc-part-2/#install-jdk-8-and-jenkins&quot; class=&quot;heading-anchor&quot;&gt;Install JDK 8 and Jenkins&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The &lt;strong&gt;cChocoPackageInstaller&lt;/strong&gt; resource module is then used to install JDK 8 and Jenkins:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Install JDK8&lt;/span&gt;
cChocoPackageInstaller installJdk8 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Name      = &lt;span class=&quot;token string&quot;&gt;&quot;jdk8&quot;&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[cChocoInstaller]installChoco&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Install Jenkins&lt;/span&gt;
cChocoPackageInstaller installJenkins &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Name      = &lt;span class=&quot;token string&quot;&gt;&quot;Jenkins&quot;&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[cChocoInstaller]installChoco&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;configure-jenkins-port&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-jenkins-using-dsc-part-2/#configure-jenkins-port&quot; class=&quot;heading-anchor&quot;&gt;Configure Jenkins Port&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The last step of the configuration is optional. By default, Jenkins is configured to listen on port 8080; however, I want to change it to 80. So this next part uses the &lt;strong&gt;Script&lt;/strong&gt; resource to change the &lt;code&gt;--httpPort&lt;/code&gt; setting in the &lt;strong&gt;Jenkins.xml&lt;/strong&gt; file. I use &lt;strong&gt;Regex&lt;/strong&gt; to do this:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Script SetJenkinsPort &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    SetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose &lt;span class=&quot;token string&quot;&gt;&quot;Setting Jenkins Port to &lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:JenkinsPort&quot;&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-Content&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;${ENV:ProgramFiles(x86)}&#92;Jenkins&#92;Jenkins.xml&quot;&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$NewConfig&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-replace&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;--httpPort=[0-9]*&#92;s&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;--httpPort=&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:JenkinsPort &quot;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Set-Content&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;${ENV:ProgramFiles(x86)}&#92;Jenkins&#92;Jenkins.xml&quot;&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Value &lt;span class=&quot;token variable&quot;&gt;$NewConfig&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose &lt;span class=&quot;token string&quot;&gt;&quot;Restarting Jenkins&quot;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Restart-Service&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name Jenkins
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    GetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-Content&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;${ENV:ProgramFiles(x86)}&#92;Jenkins&#92;Jenkins.xml&quot;&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Matches&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;[regex]&lt;/span&gt;::matches&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--httpPort=([0-9]*)&#92;s&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;IgnoreCase&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$CurrentPort&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$Matches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Groups&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value
        &lt;span class=&quot;token keyword&quot;&gt;Return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&#39;JenkinsPort&#39;&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$CurrentPort&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    TestScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
        &lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-Content&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;${ENV:ProgramFiles(x86)}&#92;Jenkins&#92;Jenkins.xml&quot;&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Matches&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;[regex]&lt;/span&gt;::matches&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--httpPort=([0-9]*)&#92;s&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;IgnoreCase&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$CurrentPort&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$Matches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Groups&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value
        
        &lt;span class=&quot;token keyword&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:JenkinsPort &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$CurrentPort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;# Jenkins port must be changed&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$False&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Jenkins is already on correct port&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$True&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[cChocoPackageInstaller]installJenkins&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;create-the-mof&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-jenkins-using-dsc-part-2/#create-the-mof&quot; class=&quot;heading-anchor&quot;&gt;Create the MOF&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The final thing to do is download the cChoco and xNetworking DSC Resources, create the MOF, and then ask the LCM to apply it:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$ConfigData&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    AllNodes = 
    @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            NodeName = &lt;span class=&quot;token string&quot;&gt;&quot;LocalHost&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

JENKINS_CI &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;JenkinsPort 80 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ConfigurationData &lt;span class=&quot;token variable&quot;&gt;$ConfigData&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;Start-DscConfiguration&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;JENKINS_CI &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Wait &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;the-complete-dsc-configuration&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-jenkins-using-dsc-part-2/#the-complete-dsc-configuration&quot; class=&quot;heading-anchor&quot;&gt;The Complete DSC Configuration&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Here is the complete DSC Configuration file. You just need to copy it to the server and run it. It will compile the configuration into a MOF and tell the LCM to apply it. Just remember to ensure required DSC Resource modules are installed.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Configuration JENKINS_CI &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$JenkinsPort&lt;/span&gt; = 8080
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ModuleName &lt;span class=&quot;token string&quot;&gt;&#39;PSDesiredStateConfiguration&#39;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ModuleName &lt;span class=&quot;token string&quot;&gt;&#39;cChoco&#39;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ModuleName &lt;span class=&quot;token string&quot;&gt;&#39;xNetworking&#39;&lt;/span&gt;

    Node &lt;span class=&quot;token variable&quot;&gt;$AllNodes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NodeName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Configure networking (optional)&lt;/span&gt;
        xIPAddress IPv4_1 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            InterfaceAlias = &lt;span class=&quot;token string&quot;&gt;&#39;Ethernet&#39;&lt;/span&gt;
            AddressFamily  = &lt;span class=&quot;token string&quot;&gt;&#39;IPv4&#39;&lt;/span&gt;
            IPAddress      = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.128.20&#39;&lt;/span&gt;
            SubnetMask     = &lt;span class=&quot;token string&quot;&gt;&#39;24&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        xDefaultGatewayAddress IPv4G_1 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            InterfaceAlias = &lt;span class=&quot;token string&quot;&gt;&#39;Ethernet&#39;&lt;/span&gt;
            AddressFamily  = &lt;span class=&quot;token string&quot;&gt;&#39;IPv4&#39;&lt;/span&gt;
            Address        = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.128.19&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        xDnsServerAddress IPv4D_1 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            InterfaceAlias = &lt;span class=&quot;token string&quot;&gt;&#39;Ethernet&#39;&lt;/span&gt;
            AddressFamily  = &lt;span class=&quot;token string&quot;&gt;&#39;IPv4&#39;&lt;/span&gt;
            Address        = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.128.10&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        xIPAddress IPv6_1 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            InterfaceAlias = &lt;span class=&quot;token string&quot;&gt;&#39;Ethernet&#39;&lt;/span&gt;
            AddressFamily  = &lt;span class=&quot;token string&quot;&gt;&#39;IPv6&#39;&lt;/span&gt;
            IPAddress      = &lt;span class=&quot;token string&quot;&gt;&#39;fd53:ccc5:895a:bc00::14&#39;&lt;/span&gt;
            SubnetMask     = &lt;span class=&quot;token string&quot;&gt;&#39;64&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        xDefaultGatewayAddress IPv6G_1 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            InterfaceAlias = &lt;span class=&quot;token string&quot;&gt;&#39;Ethernet&#39;&lt;/span&gt;
            AddressFamily  = &lt;span class=&quot;token string&quot;&gt;&#39;IPv6&#39;&lt;/span&gt;
            Address        = &lt;span class=&quot;token string&quot;&gt;&#39;fd53:ccc5:895a:bc00::13&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        xDnsServerAddress IPv6D_1 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            InterfaceAlias = &lt;span class=&quot;token string&quot;&gt;&#39;Ethernet&#39;&lt;/span&gt;
            AddressFamily  = &lt;span class=&quot;token string&quot;&gt;&#39;IPv6&#39;&lt;/span&gt;
            Address        = &lt;span class=&quot;token string&quot;&gt;&#39;fd53:ccc5:895a:bc00::a&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        
        &lt;span class=&quot;token comment&quot;&gt;# Install .NET 3.5&lt;/span&gt;
        WindowsFeature NetFrameworkCore &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Ensure = &lt;span class=&quot;token string&quot;&gt;&quot;Present&quot;&lt;/span&gt;
            Name   = &lt;span class=&quot;token string&quot;&gt;&quot;NET-Framework-Core&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;# Install Chocolatey&lt;/span&gt;
        cChocoInstaller installChoco &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            InstallDir = &lt;span class=&quot;token string&quot;&gt;&quot;c:&#92;choco&quot;&lt;/span&gt;
            DependsOn  = &lt;span class=&quot;token string&quot;&gt;&quot;[WindowsFeature]NetFrameworkCore&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;# Install JDK8&lt;/span&gt;
        cChocoPackageInstaller installJdk8 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Name      = &lt;span class=&quot;token string&quot;&gt;&quot;jdk8&quot;&lt;/span&gt;
            DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[cChocoInstaller]installChoco&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;# Install Jenkins&lt;/span&gt;
        cChocoPackageInstaller installJenkins &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Name      = &lt;span class=&quot;token string&quot;&gt;&quot;Jenkins&quot;&lt;/span&gt;
            DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[cChocoInstaller]installChoco&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;# Set the Jenkins Port&lt;/span&gt;
        Script SetJenkinsPort &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            SetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose &lt;span class=&quot;token string&quot;&gt;&quot;Setting Jenkins Port to &lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:JenkinsPort&quot;&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-Content&lt;/span&gt; `
                    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;${ENV:ProgramFiles(x86)}&#92;Jenkins&#92;Jenkins.xml&quot;&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$NewConfig&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt; `
                    &lt;span class=&quot;token operator&quot;&gt;-replace&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;--httpPort=[0-9]*&#92;s&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;--httpPort=&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:JenkinsPort &quot;&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;Set-Content&lt;/span&gt; `
                    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;${ENV:ProgramFiles(x86)}&#92;Jenkins&#92;Jenkins.xml&quot;&lt;/span&gt; `
                    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Value &lt;span class=&quot;token variable&quot;&gt;$NewConfig&lt;/span&gt; `
                    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force
                &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose &lt;span class=&quot;token string&quot;&gt;&quot;Restarting Jenkins&quot;&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;Restart-Service&lt;/span&gt; `
                    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name Jenkins
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            GetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-Content&lt;/span&gt; `
                    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;${ENV:ProgramFiles(x86)}&#92;Jenkins&#92;Jenkins.xml&quot;&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$Matches&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;[regex]&lt;/span&gt;::matches&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--httpPort=([0-9]*)&#92;s&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;IgnoreCase&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$CurrentPort&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$Matches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Groups&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value
                &lt;span class=&quot;token keyword&quot;&gt;Return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token string&quot;&gt;&#39;JenkinsPort&#39;&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$CurrentPort&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            TestScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
                &lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-Content&lt;/span&gt; `
                    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;${ENV:ProgramFiles(x86)}&#92;Jenkins&#92;Jenkins.xml&quot;&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$Matches&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;[regex]&lt;/span&gt;::matches&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--httpPort=([0-9]*)&#92;s&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;IgnoreCase&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$CurrentPort&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$Matches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Groups&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value
                
                &lt;span class=&quot;token keyword&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:JenkinsPort &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$CurrentPort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token comment&quot;&gt;# Jenkins port must be changed&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$False&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;# Jenkins is already on correct port&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$True&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[cChocoPackageInstaller]installJenkins&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$ConfigData&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    AllNodes = 
    @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            NodeName = &lt;span class=&quot;token string&quot;&gt;&quot;LocalHost&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

JENKINS_CI &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;JenkinsPort 80 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ConfigurationData &lt;span class=&quot;token variable&quot;&gt;$ConfigData&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;Start-DscConfiguration&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;JENKINS_CI &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Wait &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Within five to ten minutes, the Jenkins server will be configured and ready to go.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Install Jenkins on Windows Server Core - Part 1</title>
      <link href="https://danielscottraynsford.com/blog/install-jenkins-on-windows-server-core-part-1/" />
      <updated>2016-04-14T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/install-jenkins-on-windows-server-core-part-1/</id>
      <content type="html">
				&lt;p&gt;I’ll admit it—I love Windows Server Core and I use it whenever possible. I think everyone should try and do the same. However, I know not everyone is a PowerShell expert or has any desire to be one.&lt;/p&gt;&lt;p&gt;So for this blog post, I’m going to show how I created a simple script that will install &lt;a href=&quot;https://jenkins.io/&quot; rel=&quot;noopener&quot;&gt;Jenkins CI&lt;/a&gt; on a &lt;strong&gt;Windows Server Core&lt;/strong&gt; system to be a &lt;strong&gt;Jenkins Master server&lt;/strong&gt;. Feel free to just skip down to the end and use the completed script if you want to. I did this on a &lt;strong&gt;Windows Server 2012 R2 Core&lt;/strong&gt; system, but this would probably work on &lt;strong&gt;Windows Server 2016 TP4&lt;/strong&gt; (the currently available version). You could, of course, use this on a &lt;strong&gt;Windows Server Full&lt;/strong&gt; system as well.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: Installing a Windows Server Core as a &lt;strong&gt;Jenkins Slave server&lt;/strong&gt; is a similar process, but there is no need to install the Jenkins Server software or service. I won’t cover the process of installing a Windows Jenkins Slave in this post.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This is part one of a two-part post. In part two, I’ll convert the process over to a &lt;strong&gt;DSC configuration&lt;/strong&gt; that can be applied to one or more nodes to make the process even easier and ensure your Jenkins servers maintain their state.&lt;/p&gt;&lt;h2 id=&quot;requirements&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-jenkins-on-windows-server-core-part-1/#requirements&quot; class=&quot;heading-anchor&quot;&gt;Requirements&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;You’ll need:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A physical or virtual machine running Windows Server 2012 R2 Core (or Full)—it should be a completely clean install with nothing already installed.&lt;/li&gt;&lt;li&gt;An administrator login to the server.&lt;/li&gt;&lt;li&gt;An internet connection to the server.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;the-script&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-jenkins-on-windows-server-core-part-1/#the-script&quot; class=&quot;heading-anchor&quot;&gt;The Script&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The first thing I like to do is get all the variables into one place so I can easily see what options I might want to set. In this case, the only thing I care about is setting a static IP Address for the server and also the port Jenkins will be assigned to:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Configure the settings to use to set up this Jenkins Executor&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$Port&lt;/span&gt; = 80
&lt;span class=&quot;token variable&quot;&gt;$IPAddress&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.1.96&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$SubnetPrefixLength&lt;/span&gt; = 24
&lt;span class=&quot;token variable&quot;&gt;$DNSServers&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;192.168.1.1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$DefaultGateway&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.1.1&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The next thing I need to do is ensure the &lt;strong&gt;.NET Framework v3.5&lt;/strong&gt; is installed (required by Jenkins on Windows):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Install .NET Framework 3.5&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Install-WindowsFeature&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name NET-Framework-Core&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For this installation, I’m actually going to let the &lt;a href=&quot;https://chocolatey.org/&quot; rel=&quot;noopener&quot;&gt;Chocolatey&lt;/a&gt; package manager do most of the heavy lifting of actually downloading and installing the Jenkins bits. So I need to install &lt;strong&gt;Chocolatey&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Install Chocolatey&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;iex&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new-object&lt;/span&gt; net&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;webclient&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DownloadString&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://chocolatey.org/install.ps1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next up, I use &lt;strong&gt;Chocolatey&lt;/strong&gt; to install both the &lt;strong&gt;Oracle JDK 8&lt;/strong&gt; and the &lt;strong&gt;Jenkins&lt;/strong&gt; bits. These will be downloaded off the internet, so it may take a little while depending on your connection. The &lt;code&gt;-y&lt;/code&gt; parameter forces the install to occur without prompting:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Install JDK 8&lt;/span&gt;
choco install jdk8 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;y

&lt;span class=&quot;token comment&quot;&gt;# Install Jenkins using Chocolatey&lt;/span&gt;
choco install Jenkins &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;y&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What I’m going to do next is configure the port Jenkins should run on. This is done by changing the &lt;code&gt;--httpPort&lt;/code&gt; setting in the &lt;code&gt;C:&#92;Program Files (x86)&#92;Jenkins&#92;Jenkins.xml&lt;/code&gt; file. I’ll use a simple &lt;strong&gt;RegEx&lt;/strong&gt; to do this. Also, because the &lt;strong&gt;Jenkins Service&lt;/strong&gt; is already running at this point, I’ll need to restart it before the changed setting will be read:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Set the port Jenkins uses&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-Content&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;${ENV:ProgramFiles(x86)}&#92;Jenkins&#92;Jenkins.xml&quot;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$NewConfig&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-replace&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;--httpPort=[0-9]*&#92;s&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;--httpPort=&lt;span class=&quot;token variable&quot;&gt;$Port&lt;/span&gt; &quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Set-Content&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;${ENV:ProgramFiles(x86)}&#92;Jenkins&#92;Jenkins.xml&quot;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Value &lt;span class=&quot;token variable&quot;&gt;$NewConfig&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force
&lt;span class=&quot;token function&quot;&gt;Restart-Service&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name Jenkins&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The Chocolatey Jenkins package automatically configures a firewall rule named “Jenkins” that allows inbound traffic to the &lt;code&gt;java.exe&lt;/code&gt; application. This means that external machines will be able to connect to this Jenkins server. You may want to change this by removing the “Jenkins” firewall rule and replacing it with something more specific to your needs; however, I didn’t do this in my script.&lt;/p&gt;&lt;p&gt;The final section is optional—it just configures the network connection on the machine to use a static IP address. You could omit this section completely if you were using DHCP or some other method of configuring the network connection:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Set a static IP Address - optional&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;New-NetIPAddress&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;IPAddress &lt;span class=&quot;token variable&quot;&gt;$IPAddress&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;InterfaceAlias Ethernet `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DefaultGateway &lt;span class=&quot;token variable&quot;&gt;$DefaultGateway&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AddressFamily IPv4 `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PrefixLength &lt;span class=&quot;token variable&quot;&gt;$SubnetPrefixLength&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Set-DnsClientServerAddress&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;InterfaceAlias Ethernet `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Addresses &lt;span class=&quot;token variable&quot;&gt;$DNSServers&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That’s all there is to it.&lt;/p&gt;&lt;h2 id=&quot;the-complete-script&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-jenkins-on-windows-server-core-part-1/#the-complete-script&quot; class=&quot;heading-anchor&quot;&gt;The Complete Script&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Here is the complete script. You can just fire up PowerShell on the Core server and copy/paste this directly into the PowerShell console, or use some other method of running it:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Configure the settings to use to set up this Jenkins Executor&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$Port&lt;/span&gt; = 80
&lt;span class=&quot;token variable&quot;&gt;$IPAddress&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.1.96&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$SubnetPrefixLength&lt;/span&gt; = 24
&lt;span class=&quot;token variable&quot;&gt;$DNSServers&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;192.168.1.1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$DefaultGateway&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.1.1&#39;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Install .NET Framework 3.5&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Install-WindowsFeature&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name NET-Framework-Core

&lt;span class=&quot;token comment&quot;&gt;# Install Chocolatey&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;iex&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new-object&lt;/span&gt; net&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;webclient&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DownloadString&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://chocolatey.org/install.ps1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Install JDK 8&lt;/span&gt;
choco install jdk8 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;y

&lt;span class=&quot;token comment&quot;&gt;# Install Jenkins using Chocolatey&lt;/span&gt;
choco install Jenkins &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;y

&lt;span class=&quot;token comment&quot;&gt;# Set the port Jenkins uses&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-Content&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;${ENV:ProgramFiles(x86)}&#92;Jenkins&#92;Jenkins.xml&quot;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$NewConfig&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-replace&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;--httpPort=[0-9]*&#92;s&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;--httpPort=&lt;span class=&quot;token variable&quot;&gt;$Port&lt;/span&gt; &quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Set-Content&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;${ENV:ProgramFiles(x86)}&#92;Jenkins&#92;Jenkins.xml&quot;&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Value &lt;span class=&quot;token variable&quot;&gt;$NewConfig&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force
&lt;span class=&quot;token function&quot;&gt;Restart-Service&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name Jenkins

&lt;span class=&quot;token comment&quot;&gt;# Set a static IP Address - optional&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;New-NetIPAddress&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;IPAddress &lt;span class=&quot;token variable&quot;&gt;$IPAddress&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;InterfaceAlias Ethernet `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DefaultGateway &lt;span class=&quot;token variable&quot;&gt;$DefaultGateway&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AddressFamily IPv4 `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PrefixLength &lt;span class=&quot;token variable&quot;&gt;$SubnetPrefixLength&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Set-DnsClientServerAddress&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;InterfaceAlias Ethernet `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Addresses &lt;span class=&quot;token variable&quot;&gt;$DNSServers&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Tomorrow I’ll improve on this process by converting it into a &lt;strong&gt;DSC configuration&lt;/strong&gt;, which will ensure the Jenkins Server maintains its state and makes provisioning them even easier.&lt;/p&gt;&lt;p&gt;Thanks for reading.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Which Physical Network Adapters are bound to Virtual Switches?</title>
      <link href="https://danielscottraynsford.com/blog/which-physical-network-adapters-are-bound-to-virtual-switches/" />
      <updated>2016-04-08T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/which-physical-network-adapters-are-bound-to-virtual-switches/</id>
      <content type="html">
				&lt;p&gt;Today’s post has quite a long title for what is going to be a fairly short post. While making some improvements to &lt;a href=&quot;https://github.com/PlagueHO/LabBuilder&quot; rel=&quot;noopener&quot;&gt;LabBuilder&lt;/a&gt;, I had a need to find out which physical network adapters on a host are bound to Hyper-V Virtual Switches. This is because a single physical adapter can only be bound to a single External Virtual Switch.&lt;/p&gt;&lt;p&gt;So I wrote a few lines of PowerShell that would do the trick:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$MacAddress&lt;/span&gt; = `
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-VMNetworkAdapter&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ManagementOS `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-VMSwitch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; ? &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
      &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SwitchType &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;External&#39;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MacAddress

&lt;span class=&quot;token function&quot;&gt;Get-NetAdapter&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Physical &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; ? &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MacAddress &lt;span class=&quot;token operator&quot;&gt;-replace&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;-&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-in&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$MacAddress&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first piece gets a list of MAC addresses for all Virtual Network Adapters that are configured for use by the host OS (managementOS) on External Switches.&lt;/p&gt;&lt;p&gt;The second piece then gets the list of Physical network adapters that match the MAC addresses from the first line. I had to use a -&lt;strong&gt;Replace&lt;/strong&gt; to get rid of the dashes in the Physical network adapter MAC address so that I could compare it with the MAC Address in the Virtual Network Adapters. It would be nice if the MAC address format was standard across all modules, but it is a pretty minor complaint.&lt;/p&gt;&lt;p&gt;So as you can see, PowerShell makes this unbelievably easy. This piece of code allows me to ensure that when LabBuilder is creating a new External Switch it doesn’t use a physical adapter that has already been used.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Nano Server - CIM or WMI?</title>
      <link href="https://danielscottraynsford.com/blog/nano-server-cim-or-wmi/" />
      <updated>2016-04-05T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/nano-server-cim-or-wmi/</id>
      <content type="html">
				&lt;p&gt;Just a quick Nano server tip for this morning. As of &lt;strong&gt;Windows Server 2016 TP4&lt;/strong&gt;, &lt;strong&gt;Nano Server&lt;/strong&gt; only contains the &lt;strong&gt;CIM&lt;/strong&gt; cmdlets. It does &lt;em&gt;not&lt;/em&gt; contain the &lt;strong&gt;WMI&lt;/strong&gt; cmdlets:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/manJYzPx2y-650.png 650w, https://danielscottraynsford.com/img/manJYzPx2y-859.png 859w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/manJYzPx2y-650.webp 650w, https://danielscottraynsford.com/img/manJYzPx2y-859.webp 859w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/manJYzPx2y-650.jpeg&quot; alt=&quot;ss_nano_availablemodulescim&quot; width=&quot;859&quot; height=&quot;477&quot; srcset=&quot;https://danielscottraynsford.com/img/manJYzPx2y-650.jpeg 650w, https://danielscottraynsford.com/img/manJYzPx2y-859.jpeg 859w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This is a good thing as it means that we can still perform some tasks that are not covered by the &lt;strong&gt;PowerShell modules&lt;/strong&gt; that are provided with Nano Server.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Configuring iSCSI and iSNS with DSC</title>
      <link href="https://danielscottraynsford.com/blog/configuring-iscsi-and-isns-with-dsc/" />
      <updated>2016-04-04T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/configuring-iscsi-and-isns-with-dsc/</id>
      <content type="html">
				&lt;p&gt;Several months back I created a &lt;a href=&quot;https://www.powershellgallery.com/packages/ciSCSI&quot; rel=&quot;noopener&quot;&gt;DSC Resource&lt;/a&gt; for configuring &lt;strong&gt;iSCSI Server Targets&lt;/strong&gt; (including Virtual Disks) as well as &lt;strong&gt;iSCSI Initiators&lt;/strong&gt; using &lt;strong&gt;Desired State Configuration (DSC)&lt;/strong&gt;. I created this for several reasons:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;I needed a way for &lt;a href=&quot;https://github.com/PlagueHO/LabBuilder&quot; rel=&quot;noopener&quot;&gt;LabBuilder&lt;/a&gt; to automatically build Scale-Out File Servers (with CSVs).&lt;/li&gt;&lt;li&gt;I needed something to use as an example in my &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/12/14/creating-professional-dsc-resources-part-1/&quot; rel=&quot;noopener&quot;&gt;Creating Professional DSC Resources&lt;/a&gt; series.&lt;/li&gt;&lt;li&gt;No one else had already created one.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;This weekend I decided to add &lt;strong&gt;iSNS Server&lt;/strong&gt; support to the resource—for both the &lt;strong&gt;ciSCSIServerTarget&lt;/strong&gt; and &lt;strong&gt;ciSCSIInitiator&lt;/strong&gt; resources. So with that feature added, I thought it might be a good opportunity for me to write a quick blog post on how to use these &lt;strong&gt;DSC Resources&lt;/strong&gt;.&lt;/p&gt;&lt;h3 id=&quot;installing-the-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/configuring-iscsi-and-isns-with-dsc/#installing-the-resource&quot; class=&quot;heading-anchor&quot;&gt;Installing the Resource&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;You can find the new &lt;strong&gt;ciSCSI&lt;/strong&gt; resource in the &lt;a href=&quot;https://www.powershellgallery.com/packages/ciSCSI&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;For those of you using &lt;strong&gt;Windows Management Framework 5.0&lt;/strong&gt; (or have the &lt;strong&gt;PowerShellGet&lt;/strong&gt; module installed), you can just use the command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name ciSCSI&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you don’t have &lt;strong&gt;Windows Management Framework 5.0&lt;/strong&gt; (and don’t have the &lt;strong&gt;PowerShellGet&lt;/strong&gt; module installed), you will need to download and install the resource from the &lt;a href=&quot;https://github.com/PlagueHO/ciSCSI/tree/master&quot; rel=&quot;noopener&quot;&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&quot;using-the-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/configuring-iscsi-and-isns-with-dsc/#using-the-resource&quot; class=&quot;heading-anchor&quot;&gt;Using the Resource&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;If you’d rather just jump right into the resource documentation and examples, you can find it &lt;a href=&quot;https://github.com/PlagueHO/ciSCSI/blob/master/README.md&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;. Otherwise, read on and I’ll cover this resource to configure both an &lt;strong&gt;iSCSI Server Target&lt;/strong&gt; and an &lt;strong&gt;iSCSI Initiator&lt;/strong&gt;. I’ll also show how to register iSCSI Server Targets and Initiators with an &lt;strong&gt;iSNS Server&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Important:&lt;/strong&gt; Although the &lt;strong&gt;ciSCSI DSC Resource&lt;/strong&gt; will work on &lt;strong&gt;Windows Management Framework 4.0&lt;/strong&gt;, these examples require the use of the &lt;strong&gt;WaitForAny&lt;/strong&gt; DSC Resource, which is only available in &lt;strong&gt;Windows Management Framework 5.0&lt;/strong&gt;. This resource is used to ensure that the &lt;strong&gt;iSCSI Server Target&lt;/strong&gt; has been created before trying to connect any &lt;strong&gt;iSCSI Initiators&lt;/strong&gt; to it. The resource could be omitted, but errors will be reported by the LCM on the iSCSI Initiator computers if the iSCSI Server Target is not available before the iSCSI Initiator DSC MOF is applied.&lt;/em&gt;&lt;/p&gt;&lt;h4 id=&quot;the-example-environment&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/configuring-iscsi-and-isns-with-dsc/#the-example-environment&quot; class=&quot;heading-anchor&quot;&gt;The Example Environment&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;In this example, the DSC Configurations that are being created will refer to the following servers:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://FS1.CONTOSO.COM&quot; rel=&quot;noopener&quot;&gt;FS1.CONTOSO.COM&lt;/a&gt;&lt;/strong&gt; - This is the file server that will contain the &lt;strong&gt;iSCSI Virtual Disks&lt;/strong&gt; and &lt;strong&gt;iSCSI Server Target&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://CLUS1.CONTOSO.COM&quot; rel=&quot;noopener&quot;&gt;CLUS1.CONTOSO.COM&lt;/a&gt;, &lt;a href=&quot;http://CLUS2.CONTOSO.COM&quot; rel=&quot;noopener&quot;&gt;CLUS2.CONTOSO.COM&lt;/a&gt;, &lt;a href=&quot;http://CLUS3.CONTOSO.COM&quot; rel=&quot;noopener&quot;&gt;CLUS3.CONTOSO.COM&lt;/a&gt;&lt;/strong&gt; - These are the Windows Server 2012 R2 (or Windows Server 2016) Cluster Server nodes that will be connecting to the &lt;strong&gt;iSCSI Server Target&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://ISNS1.CONTOSO.COM&quot; rel=&quot;noopener&quot;&gt;ISNS1.CONTOSO.COM&lt;/a&gt;&lt;/strong&gt; - This is a server with the &lt;strong&gt;iSNS Server&lt;/strong&gt; Windows Feature installed on it. The &lt;strong&gt;iSNS default domain&lt;/strong&gt; has been configured on this server already.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The DSC configurations that will be created will create four 128GB dynamic &lt;strong&gt;iSCSI Virtual Disks&lt;/strong&gt; on the &lt;code&gt;D:&#92;&lt;/code&gt; drive of &lt;strong&gt;&lt;a href=&quot;http://FS1.CONTOSO.COM&quot; rel=&quot;noopener&quot;&gt;FS1.CONTOSO.COM&lt;/a&gt;&lt;/strong&gt;. An &lt;strong&gt;iSCSI Server Target&lt;/strong&gt; called &lt;strong&gt;FS1-Server-Target&lt;/strong&gt; will be created, and the four &lt;strong&gt;iSCSI Virtual Disks&lt;/strong&gt; will be attached to it.&lt;/p&gt;&lt;h4 id=&quot;configuring-the-iscsi-server-target&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/configuring-iscsi-and-isns-with-dsc/#configuring-the-iscsi-server-target&quot; class=&quot;heading-anchor&quot;&gt;Configuring the iSCSI Server Target&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;A DSC configuration that creates an &lt;strong&gt;iSCSI Server Target&lt;/strong&gt; requires the following steps to be performed in the DSC Resource:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Install the &lt;strong&gt;iSCSI Target Server&lt;/strong&gt; Windows Feature (&lt;code&gt;FS-iSCSITarget-Server&lt;/code&gt;).&lt;/li&gt;&lt;li&gt;Initialize any physical disks that will be used to store the &lt;strong&gt;iSCSI Virtual Disks&lt;/strong&gt; (optional).&lt;/li&gt;&lt;li&gt;Create the &lt;strong&gt;iSCSI Virtual Disks&lt;/strong&gt; that will be used by the &lt;strong&gt;iSCSI Server Target&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Create the &lt;strong&gt;iSCSI Server Target&lt;/strong&gt; and optionally register it with an &lt;strong&gt;iSNS Server&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Here is the DSC Configuration:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Configuration ISCSI_SERVER_TARGET &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ModuleName &lt;span class=&quot;token string&quot;&gt;&#39;PSDesiredStateConfiguration&#39;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ModuleName xStorage
    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ModuleName ciSCSI

    Node &lt;span class=&quot;token string&quot;&gt;&#39;FS1&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Step 1 - Install the iSCSI Target Server Windows Feature&lt;/span&gt;
        WindowsFeature ISCSITargetServerInstall &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Ensure = &lt;span class=&quot;token string&quot;&gt;&quot;Present&quot;&lt;/span&gt;
            Name = &lt;span class=&quot;token string&quot;&gt;&quot;FS-iSCSITarget-Server&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;# Step 2 - Initialize any physical disks that will be used to store the iSCSI Virtual Disks&lt;/span&gt;
        xWaitforDisk Disk2 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            DiskNumber = 1
            RetryIntervalSec = 60
            RetryCount = 60
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        xDisk DVolume &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            DiskNumber = 1
            DriveLetter = &lt;span class=&quot;token string&quot;&gt;&#39;D&#39;&lt;/span&gt;
            DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[xWaitforDisk]Disk2&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        File VirtualDisksFolder &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Ensure = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
            DestinationPath = &lt;span class=&quot;token string&quot;&gt;&#39;D:&#92;iSCSIVirtualDisks&#39;&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Type&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;Directory&#39;&lt;/span&gt;
            DependsOn = &lt;span class=&quot;token string&quot;&gt;&#39;[xDisk]DVolume&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;# Step 3 - Create the iSCSI Virtual Disks&lt;/span&gt;
        ciSCSIVirtualDisk VDisk1 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Ensure = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
            Path = &lt;span class=&quot;token string&quot;&gt;&#39;D:&#92;iSCSIVirtualDisks&#92;VDisk1.vhdx&#39;&lt;/span&gt;
            DiskType = &lt;span class=&quot;token string&quot;&gt;&#39;Dynamic&#39;&lt;/span&gt;
            SizeBytes = 128GB
            DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[File]VirtualDisksFolder&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        ciSCSIVirtualDisk VDisk2 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Ensure = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
            Path = &lt;span class=&quot;token string&quot;&gt;&#39;D:&#92;iSCSIVirtualDisks&#92;VDisk2.vhdx&#39;&lt;/span&gt;
            DiskType = &lt;span class=&quot;token string&quot;&gt;&#39;Dynamic&#39;&lt;/span&gt;
            SizeBytes = 128GB
            DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[File]VirtualDisksFolder&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        ciSCSIVirtualDisk VDisk3 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Ensure = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
            Path = &lt;span class=&quot;token string&quot;&gt;&#39;D:&#92;iSCSIVirtualDisks&#92;VDisk3.vhdx&#39;&lt;/span&gt;
            DiskType = &lt;span class=&quot;token string&quot;&gt;&#39;Dynamic&#39;&lt;/span&gt;
            SizeBytes = 128GB
            DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[File]VirtualDisksFolder&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        ciSCSIVirtualDisk VDisk4 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Ensure = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
            Path = &lt;span class=&quot;token string&quot;&gt;&#39;D:&#92;iSCSIVirtualDisks&#92;VDisk4.vhdx&#39;&lt;/span&gt;
            DiskType = &lt;span class=&quot;token string&quot;&gt;&#39;Dynamic&#39;&lt;/span&gt;
            SizeBytes = 128GB
            DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[File]VirtualDisksFolder&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;# Step 4 - Create the iSCSI Server Target and optionally register it with an iSNS Server&lt;/span&gt;
        ciSCSIServerTarget iSCSIServerTarget &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Ensure = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
            TargetName = &lt;span class=&quot;token string&quot;&gt;&#39;FS1-Server-Target&#39;&lt;/span&gt;
            InitiatorIds = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&#39;Iqn:iqn.1991-05.com.microsoft:CLUS1.CONTOSO.COM&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&#39;Iqn:iqn.1991-05.com.microsoft:CLUS2.CONTOSO.COM&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&#39;Iqn:iqn.1991-05.com.microsoft:CLUS3.CONTOSO.COM&#39;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            Paths = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&#39;D:&#92;iSCSIVirtualDisks&#92;VDisk1.vhdx&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&#39;D:&#92;iSCSIVirtualDisks&#92;VDisk2.vhdx&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&#39;D:&#92;iSCSIVirtualDisks&#92;VDisk3.vhdx&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&#39;D:&#92;iSCSIVirtualDisks&#92;VDisk4.vhdx&#39;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            iSNSServer = &lt;span class=&quot;token string&quot;&gt;&#39;isns1.contoso.com&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Important:&lt;/strong&gt; Note that the &lt;strong&gt;TargetName&lt;/strong&gt; is set to ‘FS1-Server-Target’, which will automatically configure the &lt;strong&gt;Target IQN&lt;/strong&gt; to ‘iqn.1991-05.com.microsoft:FS1-FS1-Server-Target-Target’. This is because the &lt;strong&gt;Microsoft iSCSI Server Target&lt;/strong&gt; cmdlets automatically name the &lt;strong&gt;Server Target&lt;/strong&gt; for you using the following format:&lt;/em&gt;&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;iqn.1991-05.com.microsoft:$($ComputerName)-$($ServerTarget)-Target&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;This is very important to remember because the &lt;strong&gt;iSCSI Initiators&lt;/strong&gt; use this string to identify the&amp;nbsp;&lt;strong&gt;Server Target&lt;/strong&gt; to connect to.&lt;/em&gt;&lt;/p&gt;&lt;h4 id=&quot;configuring-the-iscsi-initiator&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/configuring-iscsi-and-isns-with-dsc/#configuring-the-iscsi-initiator&quot; class=&quot;heading-anchor&quot;&gt;Configuring the iSCSI Initiator&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;A DSC configuration for each of the iSCSI Initiators that will connect to the iSCSI Server Target requires the following steps to be performed in the DSC Resource:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Start the &lt;strong&gt;Microsoft iSCSI Initiator Service&lt;/strong&gt; service (MSiSCSI).&lt;/li&gt;&lt;li&gt;Use the &lt;strong&gt;WaitForAny&lt;/strong&gt; WMF 5.0 DSC Resource to wait for the &lt;strong&gt;iSCSI Server Target&lt;/strong&gt; to be created (optional).&lt;/li&gt;&lt;li&gt;Connect the &lt;strong&gt;iSCSI Initiator&lt;/strong&gt; to the &lt;strong&gt;iSCSI Server Target&lt;/strong&gt; and optionally register it with an &lt;strong&gt;iSNS Server&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Here is the DSC Configuration for &lt;strong&gt;&lt;a href=&quot;http://CLUS1.CONTOSO.COM&quot; rel=&quot;noopener&quot;&gt;CLUS1.CONTOSO.COM&lt;/a&gt;&lt;/strong&gt; (the configuration for the other nodes would be similar except with different InitiatorPortalAddress values):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Configuration ISCSI_INITIATOR &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ModuleName PSDesiredStateConfiguration
    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ModuleName xPSDesiredStateConfiguration
    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ModuleName ciSCSI

    Node &lt;span class=&quot;token string&quot;&gt;&#39;CLUS1&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Step 1 - Start the Microsoft iSCSI Initiator Service service (MSiSCSI)&lt;/span&gt;
        Service iSCSIService &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Name = &lt;span class=&quot;token string&quot;&gt;&#39;MSiSCSI&#39;&lt;/span&gt;
            StartupType = &lt;span class=&quot;token string&quot;&gt;&#39;Automatic&#39;&lt;/span&gt;
            State = &lt;span class=&quot;token string&quot;&gt;&#39;Running&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;# Step 2 - Use the WaitForAny WMF 5.0 DSC Resource to wait for the iSCSI Server Target to be created (optional).&lt;/span&gt;
        WaitForAny WaitForiSCSIServerTarget &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            ResourceName = &lt;span class=&quot;token string&quot;&gt;&quot;[ciSCSIServerTarget]ClusterServerTarget&quot;&lt;/span&gt;
            NodeName = &lt;span class=&quot;token string&quot;&gt;&#39;FS1&#39;&lt;/span&gt;
            RetryIntervalSec = 30
            RetryCount = 30
            DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[Service]iSCSIService&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;# Step 3 - Connect the iSCSI Initiator to the iSCSI Server Target and optionally register it with an iSNS Server&lt;/span&gt;
        ciSCSIInitiator iSCSIInitiator &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Ensure = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
            NodeAddress = &lt;span class=&quot;token string&quot;&gt;&#39;iqn.1991-05.com.microsoft:fs1-fs1-server-target-target&#39;&lt;/span&gt;
            TargetPortalAddress = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.129.10&#39;&lt;/span&gt;
            InitiatorPortalAddress = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.129.24&#39;&lt;/span&gt;
            IsPersistent = &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;
            iSNSServer = &lt;span class=&quot;token string&quot;&gt;&#39;isns1.contoso.com&#39;&lt;/span&gt;
            DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[WaitForAny]WaitForiSCSIServerTarget&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# End of ciSCSITarget Resource&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Important:&lt;/strong&gt; We need to make sure the&amp;nbsp;&lt;strong&gt;NodeAddress&lt;/strong&gt; is set to the the &lt;strong&gt;Target IQN&lt;/strong&gt; from the &lt;strong&gt;iSCSI Server Target&lt;/strong&gt; - in this case ‘iqn.1991-05.com.microsoft:FS1-FS1-Server-Target-Target’.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;It is also recommended that you use IP Addresses for the &lt;strong&gt;TargetPortalAddress&lt;/strong&gt; and &lt;strong&gt;InitiatorPortalAddress&lt;/strong&gt; parameters rather than server names, as this will force the iSCSI traffic to use the appropriate network adapter.&lt;/p&gt;&lt;h3 id=&quot;isns-server-configuration&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/configuring-iscsi-and-isns-with-dsc/#isns-server-configuration&quot; class=&quot;heading-anchor&quot;&gt;iSNS Server Configuration&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;There are a few things to keep in mind when you have your iSCSI DSC Configurations registering with an iSNS Server:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The Default Domain on the &lt;strong&gt;iSNS Server&lt;/strong&gt; should have been created.&lt;/li&gt;&lt;li&gt;If the &lt;strong&gt;iSNS Server&lt;/strong&gt; is not available or contactable by the &lt;strong&gt;iSCSI Server Target&lt;/strong&gt; or &lt;strong&gt;Initiator&lt;/strong&gt; when the &lt;strong&gt;DSC Configuration&lt;/strong&gt; is applied the DSC configuration will not throw an error, but the iSNS Server Address will not be set. However, next time the DSC configuration is applied by the LCM it will try again (and again the next time etc).&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Using &lt;strong&gt;iSNS Server&lt;/strong&gt; is completely optional and is mostly used in larger environments with more than twenty &lt;strong&gt;iSCSI Server Targets&lt;/strong&gt; and where the Initiators will be connected to the &lt;strong&gt;iSCSI Server Targets&lt;/strong&gt; manually or where DSC can’t be used on the &lt;strong&gt;iSCSI Server Targets&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;That is all there is to using this resource to configure a Windows Server 2012 iSCSI SAN using DSC.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: I have submitted this DSC Resource to be included in the &lt;a href=&quot;https://github.com/PowerShell/DscResources&quot; rel=&quot;noopener&quot;&gt;Microsoft Community DSC Resources project&lt;/a&gt;. If it is accepted then the name of the DSC Resource will change from &lt;strong&gt;ciSCSI&lt;/strong&gt; to &lt;strong&gt;iSCSI&lt;/strong&gt;. The resource hasn’t yet been reviewed and I’m not aware of an ETA for it. The old ‘c’ and ‘x’ nomenclature used by DSC Resources is being phased out.&lt;/em&gt;&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Configure iSNS Server in an iSCSI Initiator with PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/configure-isns-server-in-an-iscsi-initiator-with-powershell/" />
      <updated>2016-03-27T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/configure-isns-server-in-an-iscsi-initiator-with-powershell/</id>
      <content type="html">
				&lt;p&gt;This will just be a very quick post today.&lt;/p&gt;&lt;p&gt;Configuring an &lt;strong&gt;iSCSI Initiator&lt;/strong&gt; to use an &lt;strong&gt;iSNS Server&lt;/strong&gt; is fairly easy using the iSCSI configuration utility:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/UQRfpbm-GQ-495.png 495w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/UQRfpbm-GQ-495.webp 495w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/UQRfpbm-GQ-495.jpeg&quot; alt=&quot;ss_isns_guiinitiatorconfig&quot; width=&quot;495&quot; height=&quot;698&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;But lets face it, that’s no fun and it really doesn’t fit well when when we have to configure 10’s or 100’s of initiators or we’re using&amp;nbsp;&lt;strong&gt;Server Core&lt;/strong&gt;. Not to mention that this is something we’d really want to do with &lt;strong&gt;Desired State Configuration&lt;/strong&gt;. Besides, this is a PowerShell blog.&lt;/p&gt;&lt;p&gt;It is &lt;a href=&quot;https://blogs.technet.microsoft.com/filecab/2012/06/08/iscsi-target-cmdlet-reference/&quot; rel=&quot;noopener&quot;&gt;easy enough&lt;/a&gt; to configure &lt;strong&gt;iSCSI Targets&lt;/strong&gt; to register with an &lt;strong&gt;iSNS Server&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Set-WmiInstance&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Namespace root&#92;wmi &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Class&lt;/span&gt; WT_iSNSServer –Arguments @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;ServerName=&lt;span class=&quot;token string&quot;&gt;&quot;ISNSSERVER.CONTOSO.COM&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But unfortunately I couldn’t find any documentation on how to do this on an &lt;strong&gt;iSCSI Initiator&lt;/strong&gt;. But after a little bit of digging around WMI I found the appropriate class:&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;MSiSCSIInitiator_iSNSServerClass&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;So, to add the &lt;strong&gt;iSNS Server&lt;/strong&gt; to the &lt;strong&gt;iSCSI Initiator&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Set-WmiInstance&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Namespace root&#92;wmi `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Class&lt;/span&gt; MSiSCSIInitiator_iSNSServerClass `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Arguments @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;iSNSServerAddress=&lt;span class=&quot;token string&quot;&gt;&quot;ISNSSERVER.CONTOSO.COM&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice that the WMI Class &lt;strong&gt;argument&lt;/strong&gt; &lt;strong&gt;name&lt;/strong&gt; for the setting the &lt;strong&gt;iSNS Server&lt;/strong&gt; name in an &lt;strong&gt;iSCSI Initiator&lt;/strong&gt; is different (iSNSServerAddress) compared to setting it for an &lt;strong&gt;iSCSI Target&lt;/strong&gt; (ServerName).&lt;/p&gt;&lt;p&gt;To list the currently set iSNS Servers:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-WmiObject&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Class&lt;/span&gt; MSiSCSIInitiator_iSNSServerClass `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Namespace root&#92;wmi&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/TM1AWWtCxD-650.png 650w, https://danielscottraynsford.com/img/TM1AWWtCxD-877.png 877w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/TM1AWWtCxD-650.webp 650w, https://danielscottraynsford.com/img/TM1AWWtCxD-877.webp 877w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/TM1AWWtCxD-650.jpeg&quot; alt=&quot;ss_isns_getlistservers&quot; width=&quot;877&quot; height=&quot;219&quot; srcset=&quot;https://danielscottraynsford.com/img/TM1AWWtCxD-650.jpeg 650w, https://danielscottraynsford.com/img/TM1AWWtCxD-877.jpeg 877w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;And if you need to remove an &lt;strong&gt;iSNS Server&lt;/strong&gt; from the &lt;strong&gt;iSCSI Initiator&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-WmiObject&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Class&lt;/span&gt; MSiSCSIInitiator_iSNSServerClass `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Namespace root&#92;wmi `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Filter&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;iSNSServerAddress=&#39;ISNSSERVER.CONTOSO.COM&#39;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Remove-WmiObject&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pretty easy right?&lt;/p&gt;&lt;p&gt;In a few weeks I plan to integrate iSNS registration with my &lt;a href=&quot;https://github.com/PlagueHO/ciSCSI&quot; rel=&quot;noopener&quot;&gt;iSCSI DSC Resources&lt;/a&gt; (also available on &lt;a href=&quot;https://www.powershellgallery.com/packages/ciSCSI/1.0.0.14&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt;) so that the whole process of registering iSCSI Initiators and Targets with iSNS is just a little bit easier.&lt;/p&gt;&lt;p&gt;Thanks for reading.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Creating a Cloneable Class in WMF 5.0</title>
      <link href="https://danielscottraynsford.com/blog/creating-a-cloneable-class-in-wmf-50/" />
      <updated>2016-03-11T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/creating-a-cloneable-class-in-wmf-50/</id>
      <content type="html">
				&lt;h2 id=&quot;introduction&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-a-cloneable-class-in-wmf-50/#introduction&quot; class=&quot;heading-anchor&quot;&gt;Introduction&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;You might have noticed that instances of certain types of classes are created, a method called &lt;strong&gt;Clone&lt;/strong&gt; is available that will create a (&lt;a href=&quot;http://stackoverflow.com/questions/184710/what-is-the-difference-between-a-deep-copy-and-a-shallow-copy&quot; rel=&quot;noopener&quot;&gt;shallow&lt;/a&gt;) copy of the object. A PowerShell &lt;strong&gt;Hashtable&lt;/strong&gt; object is a classic example of a class that supports the &lt;strong&gt;Clone&lt;/strong&gt; method:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$Car&lt;/span&gt; = &lt;span class=&quot;token namespace&quot;&gt;[Hashtable]&lt;/span&gt;::New&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$Car&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Add&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Make&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Lamborghini&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$Car&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Add&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Model&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Aventador&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$NewCar&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$Car&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Clone&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is nothing new to developers, but for most Ops people it might be something they’re not that familiar with. But if you are an Ops person who is implementing more advanced PowerShell modules or scripts in WMF 5.0 that require custom classes, then this might be something you need to do.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: you can do this in WMF 3.0 and 4.0 but it requires reflection and a lot more code, so isn’t something I’m going to cover here.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;For this post, I’m assuming you have a basic knowledge of how to create classes in WMF 5.0. If you aren’t familiar with creating classes, take a look at &lt;a href=&quot;https://blogs.technet.microsoft.com/heyscriptingguy/2015/09/01/powershell-5-create-simple-class/&quot; rel=&quot;noopener&quot;&gt;this series&lt;/a&gt; for a very good primer.&lt;/p&gt;&lt;p&gt;If you just go and create a new class in PowerShell and try to call the clone method, an error with be thrown:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/q21t8auuR2-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/q21t8auuR2-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/q21t8auuR2-650.jpeg&quot; alt=&quot;ss_cloneable_nolonemethoderror&quot; width=&quot;650&quot; height=&quot;167&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This is because by default a new class that is defined in PowerShell is based off the &lt;strong&gt;System.Object&lt;/strong&gt; class which does not implement the &lt;strong&gt;ICloneable&lt;/strong&gt; interface. The &lt;strong&gt;ICloneable&lt;/strong&gt; interface is what gives us the &lt;strong&gt;Clone&lt;/strong&gt; method on an object. So we need to tell PowerShell that we want our new class to implement the &lt;strong&gt;ICloneable&lt;/strong&gt; interface.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: You don’t really need to know what an interface is to use it, but if you do want a better understanding of it, &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms173156.aspx&quot; rel=&quot;noopener&quot;&gt;this&lt;/a&gt; is a good place to start. Details on the ICloneable Interface can be found &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.icloneable%28v=vs.110%29.aspx&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;implementing-an-interface&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-a-cloneable-class-in-wmf-50/#implementing-an-interface&quot; class=&quot;heading-anchor&quot;&gt;Implementing an Interface&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Creating a class that implements the &lt;strong&gt;ICloneable&lt;/strong&gt; interface just requires that we add the name of the interface to implement after a colon following the class name:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; Car:ICloneable &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Make&lt;/span&gt;
  &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Model&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, if we try to define this class as is we’ll get an error:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/KMW9ySJCCR-650.png 650w, https://danielscottraynsford.com/img/KMW9ySJCCR-960.png 960w, https://danielscottraynsford.com/img/KMW9ySJCCR-1314.png 1314w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/KMW9ySJCCR-650.webp 650w, https://danielscottraynsford.com/img/KMW9ySJCCR-960.webp 960w, https://danielscottraynsford.com/img/KMW9ySJCCR-1314.webp 1314w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/KMW9ySJCCR-650.jpeg&quot; alt=&quot;ss_cloneable_classclonemethodnotimplementederror&quot; width=&quot;1314&quot; height=&quot;202&quot; srcset=&quot;https://danielscottraynsford.com/img/KMW9ySJCCR-650.jpeg 650w, https://danielscottraynsford.com/img/KMW9ySJCCR-960.jpeg 960w, https://danielscottraynsford.com/img/KMW9ySJCCR-1314.jpeg 1314w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;The problem is that we’ve told PowerShell that the &lt;strong&gt;Car&lt;/strong&gt; class implements &lt;strong&gt;ICloneable&lt;/strong&gt; and should therefore have a &lt;strong&gt;Clone&lt;/strong&gt; method, but we haven’t actually created the &lt;strong&gt;Clone&lt;/strong&gt; method in the class.&lt;/p&gt;&lt;p&gt;To do this we need to add the method to our class definition:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; Car:ICloneable &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token namespace&quot;&gt;[Object]&lt;/span&gt; Clone &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token variable&quot;&gt;$NewCar&lt;/span&gt; = &lt;span class=&quot;token namespace&quot;&gt;[Car]&lt;/span&gt;::New&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Property&lt;/span&gt; in &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Get-Member&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MemberType Property&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token variable&quot;&gt;$NewCar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Property&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Property&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# foreach&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$NewCar&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Clone&lt;/span&gt;
  &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Make&lt;/span&gt;
  &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Model&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above code first creates a new Car object, then gets a list of the &lt;strong&gt;properties&lt;/strong&gt; on the existing (&lt;strong&gt;$This&lt;/strong&gt;) object and uses a &lt;strong&gt;foreach&lt;/strong&gt; loop to copy the content of each property to the new Car object (&lt;strong&gt;$NewCar&lt;/strong&gt;). The &lt;strong&gt;$NewCar&lt;/strong&gt; object is returned to the calling code.&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;This performs a &lt;a href=&quot;http://stackoverflow.com/questions/184710/what-is-the-difference-between-a-deep-copy-and-a-shallow-copy&quot; rel=&quot;noopener&quot;&gt;shallow copy&lt;/a&gt; of the object. If you want to perform a &lt;strong&gt;deep copy&lt;/strong&gt; then you’ll need to implement code based on the child objects within the existing object._&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;You’ve now created an class that implements an interface. The .NET framework provides hundreds (if not thousands) of interfaces that you potentially could implement on your PowerShell classes. Of course, you could have cloned the Car object without implementing the &lt;strong&gt;ICloneable&lt;/strong&gt; interface at all, but this post is intended to be a general introduction to implementing interfaces in WMF 5.0 as well as the &lt;strong&gt;ICloneable&lt;/strong&gt; interface.&lt;/p&gt;&lt;p&gt;Thanks for reading.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>PowerShell V5 New Feature: Protect/Unprotect-CmsMessage</title>
      <link href="https://danielscottraynsford.com/blog/powershell-v5-new-feature-protect/unprotect-cmsmessage/" />
      <updated>2016-03-10T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/powershell-v5-new-feature-protect/unprotect-cmsmessage/</id>
      <content type="html">
				&lt;p&gt;This interesting article gives some background details on some of the problems I ran into after upgrading my DSC dev machine to WMF 5.0 10586. This is because in WMF5.0 the DSC credential encryption mechanism was converted to use Protect/Unprotect-CMSMessage. It clears up a lot of things for me and is a worthwhile read if you’re using DSC credential encryption on WMF5.0.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Building a Enum that Supports Bit Fields in PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/building-a-enum-that-supports-bit-fields-in-powershell/" />
      <updated>2016-03-09T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/building-a-enum-that-supports-bit-fields-in-powershell/</id>
      <content type="html">
				&lt;p&gt;I found this post extremely useful - definitely worth a read.&lt;br&gt;&lt;a href=&quot;http://blogs.technet.com/b/heyscriptingguy/archive/2016/03/08/building-a-enum-that-supports-bit-fields-in-powershell.aspx&quot; rel=&quot;noopener&quot;&gt;Building a Enum that Supports Bit Fields in PowerShell&lt;/a&gt;&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Windows Management Framework 5.0 (WMF) RTM re-published</title>
      <link href="https://danielscottraynsford.com/blog/windows-management-framework-50-wmf-rtm-re-published/" />
      <updated>2016-02-25T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/windows-management-framework-50-wmf-rtm-re-published/</id>
      <content type="html">
				&lt;p&gt;After a bit of a false start the WMF 5.0 installer package has been &lt;a href=&quot;https://blogs.msdn.microsoft.com/powershell/2016/02/24/windows-management-framework-wmf-5-0-rtm-packages-has-been-republished/&quot; rel=&quot;noopener&quot;&gt;republished&lt;/a&gt;. This released fixes the &lt;a href=&quot;https://windowsserver.uservoice.com/forums/301869-powershell/suggestions/11148471-bug-wmf5-rtm-psmodulepath&quot; rel=&quot;noopener&quot;&gt;PSModulePath issue&lt;/a&gt; that popped up on the first release of this package.&lt;/p&gt;&lt;p&gt;The changes to this package are:&lt;/p&gt;&lt;h2 id=&quot;changes-in-the-republished-packages&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-management-framework-50-wmf-rtm-re-published/#changes-in-the-republished-packages&quot; class=&quot;heading-anchor&quot;&gt;Changes in the republished packages&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;The KB numbers of these packages (KB3134758, KB3134759, and KB3134760) are different than previously released WMF 5.0 RTM packages (KB3094174, KB3094175, and KB3094176).&lt;/li&gt;&lt;li&gt;These packages have fixes only for the PSModulePath issue compared to the previously released WMF 5.0 RTM packages.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Note: if you’re install the previous (broken) WMF 5.0 RTM package you’ll have to uninstall it before installing this new one.&lt;/strong&gt;&lt;/p&gt;&lt;h2 id=&quot;uninstall-previously-released-wmf-50-rtm-packages&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-management-framework-50-wmf-rtm-re-published/#uninstall-previously-released-wmf-50-rtm-packages&quot; class=&quot;heading-anchor&quot;&gt;Uninstall previously released WMF 5.0 RTM packages&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;You must uninstall previously released WMF 5.0 RTM (KB3094174, KB3094175, and KB3094176) packages.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Creating a Chocolatey Package in AppVeyor CI</title>
      <link href="https://danielscottraynsford.com/blog/creating-a-chocolatey-package-in-appveyor-ci/" />
      <updated>2016-02-21T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/creating-a-chocolatey-package-in-appveyor-ci/</id>
      <content type="html">
				&lt;h2 id=&quot;introduction&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-a-chocolatey-package-in-appveyor-ci/#introduction&quot; class=&quot;heading-anchor&quot;&gt;Introduction&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Recently I had a need to have an application published in &lt;a href=&quot;http://www.chocolatey.org/&quot; rel=&quot;noopener&quot;&gt;Chocolatey&lt;/a&gt;. If you’re not familiar with Chocolatey, it is:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;a Machine Package Manager, somewhat like apt-get, but built with Windows in mind.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The application I needed to package was actually a Microsoft tool called &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/hardware/ff544707%28v=vs.85%29.aspx&quot; rel=&quot;noopener&quot;&gt;DevCon.exe&lt;/a&gt; and is available freely from Microsoft as part of the &lt;a href=&quot;https://msdn.microsoft.com/en-us/windows/hardware/hh852365&quot; rel=&quot;noopener&quot;&gt;Windows Driver Kit&lt;/a&gt; (WDK). The WDK is a massive 2.5GB download, and I needed this one tiny (80kb) executable to be automatically installed and used as part of some Integration tests for another project using &lt;a href=&quot;http://www.appveyor.com/&quot; rel=&quot;noopener&quot;&gt;AppVeyor CI&lt;/a&gt;. Forcing AppVeyor CI to download and install a 2.5GB WDK just so some integration tests could be run was unreasonable and unworkable.&lt;/p&gt;&lt;p&gt;&lt;em&gt;If you are interested: the reason I needed DevCon.exe was to be able to automatically create Microsoft Loopback Adapters for use in testing some functions of the &lt;a href=&quot;https://github.com/PowerShell/xNetworking&quot; rel=&quot;noopener&quot;&gt;xNetworking DSC resource&lt;/a&gt;. I did this by creating a &lt;a href=&quot;https://www.powershellgallery.com/packages/LoopbackAdapter/1.0.0.16&quot; rel=&quot;noopener&quot;&gt;PowerShell LoopbackAdapter Module&lt;/a&gt; that automatically downloads and installs this Chocolatey package.&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;the-package&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-a-chocolatey-package-in-appveyor-ci/#the-package&quot; class=&quot;heading-anchor&quot;&gt;The Package&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you fancy skipping the details of the process and just want to jump in and have a look at the project, you &lt;a href=&quot;https://github.com/PlagueHO/devcon-choco-package&quot; rel=&quot;noopener&quot;&gt;can find it on GitHub&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The package is fairly simple. It just contains a few files:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;AppVeyor.yml&lt;/strong&gt; - this is the AppVeyor CI definition file that tells AppVeyor how to build this project.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://Readme.md&quot; rel=&quot;noopener&quot;&gt;Readme.md&lt;/a&gt;&lt;/strong&gt; - some details about this project.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;devcon.portable&#92;DevCon32.exe&lt;/strong&gt; - a 32-bit version of the &lt;strong&gt;DevCon.exe&lt;/strong&gt; copied straight out of the &lt;strong&gt;WDK&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;devcon.portable&#92;DevCon64.exe&lt;/strong&gt; - a 64-bit version of the &lt;strong&gt;DevCon.exe&lt;/strong&gt; copied straight out of the &lt;strong&gt;WDK&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;devcon.portable&#92;devcon.portable.nuspec&lt;/strong&gt; - this is the &lt;a href=&quot;https://docs.nuget.org/create/nuspec-reference&quot; rel=&quot;noopener&quot;&gt;nuspec package manifest&lt;/a&gt; file I created for this package.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/jB35CjwZCm-222.png 222w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/jB35CjwZCm-222.webp 222w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/jB35CjwZCm-222.jpeg&quot; alt=&quot;ss_devconchoco_filestructure&quot; width=&quot;222&quot; height=&quot;129&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;The really important files here are &lt;strong&gt;AppVeyor.yml&lt;/strong&gt; and &lt;strong&gt;devcon.portable.nuspec&lt;/strong&gt;. So I’ll cover those in a bit more detail.&lt;/p&gt;&lt;h2 id=&quot;compiling-devcon-from-source&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-a-chocolatey-package-in-appveyor-ci/#compiling-devcon-from-source&quot; class=&quot;heading-anchor&quot;&gt;Compiling DevCon from Source&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The source code for the DevCon application is actually available &lt;a href=&quot;https://github.com/Microsoft/Windows-driver-samples&quot; rel=&quot;noopener&quot;&gt;Microsoft’s own GitHub repository&lt;/a&gt;. So in my original version of this project I was actually using AppVeyor to clone this source code and then compile the application using &lt;strong&gt;MSBuild&lt;/strong&gt; as part of the packaging process. This worked really well, but the problem was that the purpose of Chocolatey is to actually install applications that can be validated as being from a trusted source.&lt;/p&gt;&lt;p&gt;What this means is that if I can’t prove that the &lt;strong&gt;DevCon32.exe&lt;/strong&gt; and &lt;strong&gt;DevCon64.exe&lt;/strong&gt; files that are in the Chocolatey package are the ones provided by Microsoft then Chocolatey (will rightly) reject the package.&lt;/p&gt;&lt;p&gt;So if AppVeyor CI compiles the DevCon every time the package is built then the bits won’t match those in the WDK. So unfortunately I had to disable this step in the process and include the &lt;strong&gt;DevCon32.exe&lt;/strong&gt; and &lt;strong&gt;DevCon64.exe&lt;/strong&gt; files copied straight out of the WDK. That way the team at &lt;strong&gt;Chocolatey&lt;/strong&gt; can use the hashes of the files to ensure that they are the same ones in the WDK.&lt;/p&gt;&lt;p&gt;I have however left the compile step in the AppVeyor CI build even though the compiled executable files are not being used anymore.&lt;/p&gt;&lt;h2 id=&quot;devconportablenuspec&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-a-chocolatey-package-in-appveyor-ci/#devconportablenuspec&quot; class=&quot;heading-anchor&quot;&gt;Devcon.Portable.Nuspec&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This is the &lt;strong&gt;Chocolatey&lt;/strong&gt; manifest file for the package. It is really just an XML file that contains the details of your package. To create it I just made a copy of the &lt;a href=&quot;https://github.com/chocolatey/chocolateytemplates/blob/master/_templates/chocolatey/__NAME__.nuspec&quot; rel=&quot;noopener&quot;&gt;template manifest file&lt;/a&gt;, named it &lt;strong&gt;devcon.portable.nuspec&lt;/strong&gt; and filled in the details.&lt;/p&gt;&lt;p&gt;Alternately, if you have &lt;strong&gt;Chocolatey&lt;/strong&gt; installed, you can run (from PowerShell or CMD):&lt;/p&gt;&lt;pre class=&quot;language-plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;choco new devcon.portable&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will create a new folder called &lt;strong&gt;devcon.portable&lt;/strong&gt; with a file &lt;strong&gt;devcon.portable&lt;/strong&gt;.&lt;strong&gt;nuspec&lt;/strong&gt; in it. You can then just customize the &lt;strong&gt;devcon.portable&lt;/strong&gt;.&lt;strong&gt;nuspec&lt;/strong&gt; with the details of the package. I also deleted the &lt;strong&gt;tools&lt;/strong&gt; folder that was created as I had no need for that.&lt;/p&gt;&lt;p&gt;In my case I ended up with this:&lt;/p&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token prolog&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Do not remove this test for UTF-8: if “Ω” doesn’t appear as greek uppercase omega letter enclosed in quotation marks, you should use an editor that supports UTF-8, not this one. --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;package&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;xmlns&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;devcon.portable&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;DevCon (Portable)&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;1.0&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;authors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Microsoft&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;authors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;owners&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Microsoft&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;owners&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;summary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Device Console (DevCon) Tool (Portable)&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;summary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
[DevCon](http://msdn.microsoft.com/en-us/library/windows/hardware/ff544707) is a command-line tool that displays detailed information about devices, and lets you search for and manipulate devices from the command line. DevCon enables, disables, installs, configures, and removes devices on the local computer and displays detailed information about devices on local and remote computers. DevCon is included in the WDK.

This pacakge includes both an x86 and an x64 version of the DevCon application:
* DevCon32.exe
* DevCon64.exe

Please use the version applicable to your Operating System.
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;projectUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;https://msdn.microsoft.com/en-us/windows/hardware/hh852365&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;projectUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;packageSourceUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;https://github.com/PlagueHO/devcon-choco-package&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;packageSourceUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;projectSourceUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;https://github.com/Microsoft/Windows-driver-samples/tree/master/setup/devcon&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;projectSourceUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;DevCon DeviceConsole WDK WindowsDriverKit&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;copyright&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Copyright (c) 2015 Microsoft&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;copyright&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;licenseUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;https://github.com/Microsoft/Windows-driver-samples/blob/master/LICENSE&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;licenseUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;requireLicenseAcceptance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;true&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;requireLicenseAcceptance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;package&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;why-portable&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-a-chocolatey-package-in-appveyor-ci/#why-portable&quot; class=&quot;heading-anchor&quot;&gt;Why “Portable”&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Most &lt;strong&gt;Chocolatey&lt;/strong&gt; packages are designed to automatically download installers from the application creator themselves and install the app silently using the installer. But in our case this is unworkable (2.5GB download). So we need to include the application executables in the package itself and we need them to end up in our %PATH% so they can be found.&lt;/p&gt;&lt;p&gt;To enable this,&amp;nbsp;&lt;strong&gt;Chocolatey&lt;/strong&gt; has implemented a feature where packages that contain the&amp;nbsp;&lt;strong&gt;.portable&lt;/strong&gt; extension will automatically have the contained executables into a special folder called the &lt;strong&gt;Chocolatey Tools Folder&lt;/strong&gt;. This folder is always in the system path.&lt;/p&gt;&lt;p&gt;For more information on .portable packages, see &lt;a href=&quot;https://github.com/chocolatey/choco/wiki/ChocolateyFAQs#portable-application--something-that-doesnt-require-a-system-install-to-use&quot; rel=&quot;noopener&quot;&gt;this page&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;appveyoryml&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-a-chocolatey-package-in-appveyor-ci/#appveyoryml&quot; class=&quot;heading-anchor&quot;&gt;AppVeyor.YML&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The &lt;strong&gt;AppVeyor.yml&lt;/strong&gt; file looks like this:&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#---------------------------------#&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#      environment configuration  #&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#---------------------------------#&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; WMF 5

&lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10.0.10586.&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;build&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Release

&lt;span class=&quot;token key atrule&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Any CPU

&lt;span class=&quot;token key atrule&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; git clone https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//github.com/Microsoft/Windows&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;driver&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;samples.git

&lt;span class=&quot;token comment&quot;&gt;#---------------------------------#&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#      build configuration        #&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#---------------------------------#&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;build_script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; msbuild &quot;Windows&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;driver&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;samples&#92;setup&#92;devcon&#92;devcon.vcxproj&quot; /p&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;Configuration=Release;Platform=Win32 /t&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;Clean;Build /verbosity&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;normal /logger&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;C:&#92;Program Files&#92;AppVeyor&#92;BuildAgent&#92;Appveyor.MSBuildLogger.dll&quot;&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; msbuild &quot;Windows&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;driver&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;samples&#92;setup&#92;devcon&#92;devcon.vcxproj&quot; /p&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;Configuration=Release;Platform=x64 /t&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;Clean;Build /verbosity&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;normal /logger&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;C:&#92;Program Files&#92;AppVeyor&#92;BuildAgent&#92;Appveyor.MSBuildLogger.dll&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;#---------------------------------#&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#      test configuration         #&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#---------------------------------#&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;#---------------------------------#&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#      deployment configuration   #&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#---------------------------------#&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;deploy_script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;ps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
      # Create Chocolately Package&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;# Because the bits need to be signed by MSFT, we are no longer using&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# the compiled version, but instead we&#39;ll use the one from the WDK.&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# The only problem is that the WDK versions aren&#39;t signed by MSFT either.&lt;/span&gt;
      
      &lt;span class=&quot;token comment&quot;&gt;# Copy-Item -Path .&#92;Windows-driver-samples&#92;setup&#92;devcon&#92;Release&#92;devcon.exe `&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;#  -Destination .&#92;devcon.portable&#92;Devcon32.exe&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# Copy-Item -Path .&#92;Windows-driver-samples&#92;setup&#92;devcon&#92;x64&#92;Release&#92;devcon.exe `&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;#  -Destination .&#92;devcon.portable&#92;Devcon64.exe&lt;/span&gt;

      Set&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;Location &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;Path .&#92;devcon.portable&#92;
      (Get&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;Content &#39;.&#92;devcon.portable.nuspec&#39; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;Raw).Replace(&quot;&amp;lt;version&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;1.0&amp;lt;/version&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&quot;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &quot;&amp;lt;version&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;$($env&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;APPVEYOR_BUILD_VERSION)&amp;lt;/version&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&quot;) &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Out&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;File &#39;.&#92;devcon.portable.nuspec&#39;
      cpack
      Push&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;AppveyorArtifact &quot;.&#92;devcon.portable.$($ENV&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;APPVEYOR_BUILD_VERSION).nupkg&quot;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The key part of this file is in the &lt;strong&gt;deploy_script&lt;/strong&gt; section:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Set-Location&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;devcon&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;portable&#92;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Content&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;.&#92;devcon.portable.nuspec&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Raw&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Replace&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;version&amp;gt;1.0&amp;lt;/version&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;version&amp;gt;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$env&lt;/span&gt;:APPVEYOR_BUILD_VERSION&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&amp;lt;/version&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Out-File&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;.&#92;devcon.portable.nuspec&#39;&lt;/span&gt;
cpack
&lt;span class=&quot;token function&quot;&gt;Push-AppveyorArtifact&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.&#92;devcon.portable.&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:APPVEYOR_BUILD_VERSION&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.nupkg&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first line changes to the folder containing the &lt;strong&gt;devcon.portable.nuspec&lt;/strong&gt; and &lt;strong&gt;devcon*.exe&lt;/strong&gt; files.&lt;/p&gt;&lt;p&gt;The second line sets the version number in the &lt;strong&gt;devcon.portable.nuspec&lt;/strong&gt; package manifest to match the current AppVeyor version number.&lt;/p&gt;&lt;p&gt;The third line creates the package using the &lt;strong&gt;cpack&lt;/strong&gt; tool.&lt;/p&gt;&lt;p&gt;The last line pushes the completed package to AppVeyor CI as an &lt;strong&gt;artifact&lt;/strong&gt; that we can then download and submit to &lt;strong&gt;Chocolatey&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/xLx5WudgYW-650.png 650w, https://danielscottraynsford.com/img/xLx5WudgYW-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/xLx5WudgYW-650.webp 650w, https://danielscottraynsford.com/img/xLx5WudgYW-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/xLx5WudgYW-650.jpeg&quot; alt=&quot;ss_devconchoco_appveyorciartifacts&quot; width=&quot;960&quot; height=&quot;309&quot; srcset=&quot;https://danielscottraynsford.com/img/xLx5WudgYW-650.jpeg 650w, https://danielscottraynsford.com/img/xLx5WudgYW-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;It is probably possible to have AppVeyor CI automatically submit the package to &lt;strong&gt;Chocolatey&lt;/strong&gt; on my behalf, but I didn’t want that in this case. But if you’re planning on doing that, you’ll want to ensure you use the AppVeyor &lt;strong&gt;Encrypt Data&lt;/strong&gt; tool to encrypt any &lt;strong&gt;Chocolatey&lt;/strong&gt; credentials that your AppVeyor.yml file might use - otherwise your &lt;strong&gt;Chocolatey&lt;/strong&gt; credentials are available for the world to see and &lt;strong&gt;abuse&lt;/strong&gt;. This would be very bad indeed!&lt;/p&gt;&lt;h3 id=&quot;the-old-build&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-a-chocolatey-package-in-appveyor-ci/#the-old-build&quot; class=&quot;heading-anchor&quot;&gt;The Old Build&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I have left in the original commands that pull the &lt;strong&gt;DevCon&lt;/strong&gt; repository from GitHub and then compile it using &lt;strong&gt;MSBuild&lt;/strong&gt;. The commands relating to this can be found in the &lt;strong&gt;build_script&lt;/strong&gt; and &lt;strong&gt;install&lt;/strong&gt; sections of the &lt;strong&gt;AppVeyor.yml&lt;/strong&gt; file.&lt;/p&gt;&lt;h2 id=&quot;final-thoughts&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-a-chocolatey-package-in-appveyor-ci/#final-thoughts&quot; class=&quot;heading-anchor&quot;&gt;Final Thoughts&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Hopefully this post might help you package up some of your own tools to use distribute with &lt;strong&gt;Chocolatey&lt;/strong&gt; for easy installation or for use with CI services such as AppVeyor.&lt;/p&gt;&lt;p&gt;If you want some more information on using &lt;strong&gt;Chocolatey&lt;/strong&gt; with PowerShell, check out &lt;a href=&quot;https://blogs.technet.microsoft.com/heyscriptingguy/2014/08/23/weekend-scripter-powershell-and-chocolatey/&quot; rel=&quot;noopener&quot;&gt;this blog post&lt;/a&gt;.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Creating Professional DSC Resources – Part 7</title>
      <link href="https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/" />
      <updated>2016-01-25T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/</id>
      <content type="html">
				&lt;p&gt;The purpose of this series of articles is to try and document a few of the lessons I learned while releasing new DSC resources as well as contributing to the existing &lt;strong&gt;Microsoft Community DSC resources&lt;/strong&gt;. These articles are not intended to tell you how to write DSC resources from a programming perspective, but to give you some ideas on what might be expected of a DSC resource you’re releasing to the public. For example, &lt;strong&gt;unit&lt;/strong&gt; and &lt;strong&gt;integration&lt;/strong&gt; tests (don’t worry if you aren’t familiar with those terms).&lt;/p&gt;&lt;p&gt;These articles are also not intended to tell you what you &lt;strong&gt;must&lt;/strong&gt; do to release your resource, but more document what will help your resource be easier to use and extend by other people. Some of these these things are obvious for people who have come from the &lt;strong&gt;development&lt;/strong&gt; community, but may be quite new to &lt;strong&gt;operations&lt;/strong&gt; people.&lt;/p&gt;&lt;p&gt;If you missed any previous articles you can find them here:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-1/&quot;&gt;Creating Professional DSC Resources – Part 1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-2/&quot;&gt;Creating Professional DSC Resources - Part 2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-3/&quot;&gt;Creating Professional DSC Resources - Part 3&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-4/&quot;&gt;Creating Professional DSC Resources - Part 4&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-5/&quot;&gt;Creating Professional DSC Resources - Part 5&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/&quot;&gt;Creating Professional DSC Resources - Part 6&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;recap&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/#recap&quot; class=&quot;heading-anchor&quot;&gt;Recap&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In the last couple of articles I covered the importance of &lt;strong&gt;automated testing&lt;/strong&gt; with &lt;strong&gt;unit testing&lt;/strong&gt; in particular. I had covered creating new &lt;strong&gt;unit tests&lt;/strong&gt; using the&amp;nbsp;&lt;strong&gt;unit&lt;/strong&gt; &lt;strong&gt;test templates&lt;/strong&gt; that are now available &lt;a href=&quot;https://github.com/PowerShell/DscResources/tree/master/Tests.Template&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;. I also covered how to complete the &lt;strong&gt;Pester Test Initialization&lt;/strong&gt; and the &lt;strong&gt;Get-TargetResource,&lt;/strong&gt; &lt;strong&gt;Set-TargetResource&lt;/strong&gt; and&amp;nbsp;&lt;strong&gt;Test-TargetResource&lt;/strong&gt; function areas of the &lt;strong&gt;unit test&lt;/strong&gt;.&lt;/p&gt;&lt;h2 id=&quot;integration-testing&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/#integration-testing&quot; class=&quot;heading-anchor&quot;&gt;Integration Testing&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Integration testing&lt;/strong&gt; is a great way of catching many errors that can’t be easily picked up by &lt;strong&gt;Unit testing&lt;/strong&gt;. It effectively tests your &lt;strong&gt;DSC Resource&lt;/strong&gt; by actually using it in a &lt;strong&gt;DSC configuration&lt;/strong&gt; file and applying it to a computer and checking the results. So this is as close to real-life testing as you can get.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Integration testing&lt;/strong&gt; of a PowerShell DSC resource should be performed after &lt;strong&gt;unit testing&lt;/strong&gt;. When a PowerShell DSC Resource is &lt;strong&gt;integration tested&lt;/strong&gt; the following process occurs:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;A DSC configuration file using the DSC resource to be integration tested is compiled into a MOF.&lt;/li&gt;&lt;li&gt;The MOF file is applied to the &lt;em&gt;test machine&lt;/em&gt;.&lt;/li&gt;&lt;li&gt;The parameters current DSC Configuration of this DSC Resource on the &lt;em&gt;test machine&lt;/em&gt; is obtained.&lt;/li&gt;&lt;li&gt;The parameters of the current DSC Configuration are compared with what was set in the DSC configuration file in step 1.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Just like &lt;strong&gt;unit testing&lt;/strong&gt; we use &lt;strong&gt;Pester&lt;/strong&gt; to test the above steps and ensure that errors don’t occur and the output is as expected.&lt;/p&gt;&lt;h2 id=&quot;sometimes-integration-tests-are-not-possible&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/#sometimes-integration-tests-are-not-possible&quot; class=&quot;heading-anchor&quot;&gt;Sometimes Integration Tests are not Possible&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Integration testing&lt;/strong&gt; is not always possible on a resource. Some resources may rely on external servers being available or they might be destructive to the machine performing the tests.&lt;/p&gt;&lt;p&gt;For example, &lt;strong&gt;integration tests&lt;/strong&gt; could not be implemented for the &lt;a href=&quot;https://github.com/PowerShell/xNetworking/tree/dev/DSCResources/MSFT_xIPAddress&quot; rel=&quot;noopener&quot;&gt;MSFT_xIPAddress&lt;/a&gt; resource in the &lt;a href=&quot;https://github.com/PowerShell/xNetworking&quot; rel=&quot;noopener&quot;&gt;xNetworking DSC Resource module&lt;/a&gt; because it would have caused the network to disconnect during testing which would have resulted in a failure of the AppVeyor CI machine running the tests.&lt;/p&gt;&lt;p&gt;But, if there is a reasonable way of implementing &lt;strong&gt;integration tests&lt;/strong&gt; for a resource in a non-destructive manor, then I’d strongly recommend it - especially as it is usually really easy.&lt;/p&gt;&lt;h2 id=&quot;dont-be-destructive&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/#dont-be-destructive&quot; class=&quot;heading-anchor&quot;&gt;Don’t Be Destructive&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Unlike &lt;strong&gt;unit testing&lt;/strong&gt;, &lt;strong&gt;integration testing&lt;/strong&gt; actually changes configuration on the machine performing the tests. If you’re using a &lt;strong&gt;continuous integration&lt;/strong&gt; service like &lt;a href=&quot;https://www.appveyor.com/&quot; rel=&quot;noopener&quot;&gt;AppVeyor&lt;/a&gt; to perform your tests then this isn’t such a problem as the test machine is “destroyed” after your tests are run.&lt;/p&gt;&lt;p&gt;However, many people also run any &lt;strong&gt;integration tests&lt;/strong&gt; on their local machines before committing code, therefore, your &lt;strong&gt;integration tests&lt;/strong&gt; should always leave the machine in the state that it was before running them. This means that any changes that will be made applying the &lt;strong&gt;integration tests&lt;/strong&gt; should be undone at the completion of your &lt;strong&gt;integration tests&lt;/strong&gt; script.&lt;/p&gt;&lt;h2 id=&quot;integration-test-files&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/#integration-test-files&quot; class=&quot;heading-anchor&quot;&gt;Integration Test Files&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Integration tests&lt;/strong&gt; for a DSC resource actually consist of two different files:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/nJStapJ7yH-588.png 588w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/nJStapJ7yH-588.webp 588w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/nJStapJ7yH-588.jpeg&quot; alt=&quot;ss_dsc_inttestfiles&quot; width=&quot;588&quot; height=&quot;132&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;*.config.ps1&lt;/strong&gt; - The DSC Configuration file that will use the DSC Resource being tested.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;*.Integration.Tests.ps1&lt;/strong&gt; - The Integration Test script file containing the &lt;strong&gt;Pester&lt;/strong&gt; tests.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;These files should be stored in the &lt;strong&gt;Tests&#92;Integration&lt;/strong&gt; folder in the DSC Resource module:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/k_CBZWzwrl-382.png 382w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/k_CBZWzwrl-382.webp 382w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/k_CBZWzwrl-382.jpeg&quot; alt=&quot;ss_dsc_inttestfolders&quot; width=&quot;382&quot; height=&quot;167&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;You must also ensure that the names of these files exactly matches the name of the resource itself. For example, if your &lt;strong&gt;DSC Resource&lt;/strong&gt; is called &lt;strong&gt;BMD_MyResource&lt;/strong&gt; then these files must be called:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;BMD_MyResource.config.ps1&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;BMD_MyResource.Integration.Tests.ps1&lt;/strong&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;creating-a-new-integration-test&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/#creating-a-new-integration-test&quot; class=&quot;heading-anchor&quot;&gt;Creating a New Integration Test&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Luckily, a good amount of the work in implementing &lt;strong&gt;integration tests&lt;/strong&gt; is already done for you. Like &lt;strong&gt;unit tests&lt;/strong&gt;, templates for the two integration files are available in the &lt;a href=&quot;https://github.com/PowerShell/DscResources/tree/master/Tests.Template&quot; rel=&quot;noopener&quot;&gt;DscResources repository&lt;/a&gt; in GitHub:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/1T4umrAuwK-650.png 650w, https://danielscottraynsford.com/img/1T4umrAuwK-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/1T4umrAuwK-650.webp 650w, https://danielscottraynsford.com/img/1T4umrAuwK-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/1T4umrAuwK-650.jpeg&quot; alt=&quot;ss_dsc_inttesttemplatesrepo&quot; width=&quot;960&quot; height=&quot;309&quot; srcset=&quot;https://danielscottraynsford.com/img/1T4umrAuwK-650.jpeg 650w, https://danielscottraynsford.com/img/1T4umrAuwK-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;You need to copy the &lt;strong&gt;integration test template&lt;/strong&gt; files and rename them to match your &lt;strong&gt;DSC Resource&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;The easiest way to do this is to &lt;strong&gt;clone&lt;/strong&gt; the repository containing the &lt;strong&gt;test template&lt;/strong&gt; files and copy the &lt;strong&gt;integration_template.ps1&lt;/strong&gt; and &lt;strong&gt;integration_config_template.ps1&lt;/strong&gt; files to your &lt;strong&gt;Tests/Integration&lt;/strong&gt; folder:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;git clone https:&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;github&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com/PowerShell/DSCResources&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;git
&lt;span class=&quot;token function&quot;&gt;Copy-Item&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;DSCResources&#92;Tests&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Template&#92;integration_config_template&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1 &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;ciSCSI&#92;Tests&#92;Integration&#92;BMD_ciSCSIVirtualDisk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1
&lt;span class=&quot;token function&quot;&gt;Copy-Item&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;DSCResources&#92;Tests&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Template&#92;integration_template&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1 &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;ciSCSI&#92;Tests&#92;Integration&#92;BMD_ciSCSIVirtualDisk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Integration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Tests&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/KS-55ZyEDV-650.png 650w, https://danielscottraynsford.com/img/KS-55ZyEDV-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/KS-55ZyEDV-650.webp 650w, https://danielscottraynsford.com/img/KS-55ZyEDV-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/KS-55ZyEDV-650.jpeg&quot; alt=&quot;ss_dsc_createnewinttestfromtemplate&quot; width=&quot;960&quot; height=&quot;205&quot; srcset=&quot;https://danielscottraynsford.com/img/KS-55ZyEDV-650.jpeg 650w, https://danielscottraynsford.com/img/KS-55ZyEDV-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;You’ll now have two new &lt;strong&gt;integration test&lt;/strong&gt; files that you can open in your PowerShell editor of choice.&lt;/p&gt;&lt;h3 id=&quot;modifying-the-config-file&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/#modifying-the-config-file&quot; class=&quot;heading-anchor&quot;&gt;Modifying the Config File&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The first file I usually edit is the &lt;code&gt;&#92;config.ps1&lt;/code&gt; file:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/nmqfQVWVOt-650.png 650w, https://danielscottraynsford.com/img/nmqfQVWVOt-960.png 960w, https://danielscottraynsford.com/img/nmqfQVWVOt-1265.png 1265w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/nmqfQVWVOt-650.webp 650w, https://danielscottraynsford.com/img/nmqfQVWVOt-960.webp 960w, https://danielscottraynsford.com/img/nmqfQVWVOt-1265.webp 1265w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/nmqfQVWVOt-650.jpeg&quot; alt=&quot;ss_dsc_newinttestconfigtemplate&quot; width=&quot;1265&quot; height=&quot;908&quot; srcset=&quot;https://danielscottraynsford.com/img/nmqfQVWVOt-650.jpeg 650w, https://danielscottraynsford.com/img/nmqfQVWVOt-960.jpeg 960w, https://danielscottraynsford.com/img/nmqfQVWVOt-1265.jpeg 1265w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Next, you’ll want to change any &lt;code&gt;&amp;lt;ResourceName&amp;gt;&lt;/code&gt; occurrences in this file to the name of your resource. I also like to remove the &lt;strong&gt;#TODO&lt;/strong&gt; bits at the same time so I know what I’ve completed:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;configuration &lt;span class=&quot;token string&quot;&gt;&#39;BMD_ciSCSIVirtualDisk_config&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&#39;BMD_ciSCSIVirtualDisk&#39;&lt;/span&gt;
    node localhost &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
       BMD_ciSCSIVirtualDisk Integration_Test &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;# TODO: Fill Configuration Code Here&lt;/span&gt;
       &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, we need to configure the &lt;strong&gt;config&lt;/strong&gt; file with the parameters we want to use as tests of the resource.&lt;/p&gt;&lt;p&gt;The best way of doing this is actually to create a &lt;strong&gt;hash table&lt;/strong&gt; object at the beginning of the file with the parameters that we’re going to set. This is so that we can use this &lt;strong&gt;hash table&lt;/strong&gt; object in the other &lt;strong&gt;integration file&lt;/strong&gt; (*.Integration.Tests.ps1) when we’re comparing the values that are expected to be set.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$VirtualDisk&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Path            = &lt;span class=&quot;token function&quot;&gt;Join-Path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:Temp &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ChildPath &lt;span class=&quot;token string&quot;&gt;&#39;TestiSCSIVirtualDisk.vhdx&#39;&lt;/span&gt;
    Ensure          = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
    DiskType        = &lt;span class=&quot;token string&quot;&gt;&#39;Dynamic&#39;&lt;/span&gt;
    Size            = 100MB
    Description     = &lt;span class=&quot;token string&quot;&gt;&#39;Integration Test iSCSI Virtual Disk&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

Configuration BMD_ciSCSIVirtualDisk_Config &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name BMD_ciSCSIVirtualDisk_Config
    node localhost &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        BMD_ciSCSIVirtualDis Ikntegration_Test &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Path            = &lt;span class=&quot;token variable&quot;&gt;$VirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path
            Ensure          = &lt;span class=&quot;token variable&quot;&gt;$VirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Ensure
            DiskType        = &lt;span class=&quot;token variable&quot;&gt;$VirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DiskType
            SizeBytes       = &lt;span class=&quot;token variable&quot;&gt;$VirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Size
            Description     = &lt;span class=&quot;token variable&quot;&gt;$VirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Description
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you can see in the example above, I create a &lt;strong&gt;$VirtualDisk&lt;/strong&gt; hash table that contains all the parameters and values that will be used to test this DSC Resource. The &lt;strong&gt;$VirtualDisk&lt;/strong&gt; object is then also accessible in the &lt;strong&gt;*.Integration.Tests.ps1&lt;/strong&gt; file.&lt;/p&gt;&lt;h3 id=&quot;modifying-the-integration-tests-file&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/#modifying-the-integration-tests-file&quot; class=&quot;heading-anchor&quot;&gt;Modifying the Integration Tests File&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Now that the &lt;strong&gt;integration tests config&lt;/strong&gt; file has been completed it is time to move on to the &lt;strong&gt;integration test script (*.Integration.Tests.ps1)&lt;/strong&gt; itself, so open it in your editor of choice:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/45f1j65uv--650.png 650w, https://danielscottraynsford.com/img/45f1j65uv--960.png 960w, https://danielscottraynsford.com/img/45f1j65uv--1265.png 1265w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/45f1j65uv--650.webp 650w, https://danielscottraynsford.com/img/45f1j65uv--960.webp 960w, https://danielscottraynsford.com/img/45f1j65uv--1265.webp 1265w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/45f1j65uv--650.jpeg&quot; alt=&quot;ss_dsc_newinttesttemplate&quot; width=&quot;1265&quot; height=&quot;908&quot; srcset=&quot;https://danielscottraynsford.com/img/45f1j65uv--650.jpeg 650w, https://danielscottraynsford.com/img/45f1j65uv--960.jpeg 960w, https://danielscottraynsford.com/img/45f1j65uv--1265.jpeg 1265w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Next, customize the &lt;strong&gt;TODO&lt;/strong&gt; area in the &lt;strong&gt;header&lt;/strong&gt; with the your &lt;strong&gt;DSC Resource Name&lt;/strong&gt; and &lt;strong&gt;DSC Module Name&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/Z77XOP4zha-472.png 472w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Z77XOP4zha-472.webp 472w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Z77XOP4zha-472.jpeg&quot; alt=&quot;ss_dsc_inttestsheader&quot; width=&quot;472&quot; height=&quot;76&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Feel free to remove the &lt;strong&gt;TODO&lt;/strong&gt; comments if you want (I always do).&lt;/p&gt;&lt;h3 id=&quot;initialization-code&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/#initialization-code&quot; class=&quot;heading-anchor&quot;&gt;Initialization Code&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;After customizing the header we need to add any code that might be required to set this machine up to actually perform these&amp;nbsp;&lt;strong&gt;integration tests&lt;/strong&gt;. The first thing I like to do is add code to check that these integration tests can actually be performed on this machine. In my example resource, the &lt;strong&gt;iSCSI Virtual Disk&lt;/strong&gt; resource will require the &lt;strong&gt;iSCSI Target Server&lt;/strong&gt; feature to be installed, which also means the OS must be a Server OS. So, first thing in the &lt;strong&gt;try/catch&lt;/strong&gt; block I add these checks:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Ensure that the tests can be performed on this computer&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$ProductType&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-CimInstance&lt;/span&gt; Win32_OperatingSystem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ProductType
Describe &lt;span class=&quot;token string&quot;&gt;&#39;Environment&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Context &lt;span class=&quot;token string&quot;&gt;&#39;Operating System&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        It &lt;span class=&quot;token string&quot;&gt;&#39;Should be a Server OS&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$ProductType&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be 3
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ProductType&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; 3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;Break&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$Installed&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-WindowsFeature&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name FS-iSCSITarget-Server&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Installed
Describe &lt;span class=&quot;token string&quot;&gt;&#39;Environment&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Context &lt;span class=&quot;token string&quot;&gt;&#39;Windows Features&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        It &lt;span class=&quot;token string&quot;&gt;&#39;Should have the iSCSI Target Feature Installed&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$Installed&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;   
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Installed&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;Break&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will cause the &lt;strong&gt;try/catch&lt;/strong&gt; block to be &lt;strong&gt;exited&lt;/strong&gt; straight away if these tests can’t actually be performed on this machine.&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;The &lt;strong&gt;cleanup&lt;/strong&gt; code in the &lt;strong&gt;finally&lt;/strong&gt; block will still be called if we &lt;strong&gt;exit&lt;/strong&gt; with a &lt;strong&gt;break&lt;/strong&gt; command._&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;After this you might also need to add code to configure anything that these i&lt;strong&gt;ntegration tests&lt;/strong&gt; might depend on. For example, if you were implementing &lt;strong&gt;integration tests&lt;/strong&gt; for testing an &lt;strong&gt;iSCSI Server Target&lt;/strong&gt;, you’d need to make sure that there was an &lt;strong&gt;iSCSI Virtual Disk&lt;/strong&gt; available to use, so you’d need to create one at this point. However, in the &lt;strong&gt;integration tests&lt;/strong&gt; for the &lt;strong&gt;iSCSI Virtual Disk&lt;/strong&gt; resource I don’t need anything else.&lt;/p&gt;&lt;h3 id=&quot;testing-the-resource-was-applied&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/#testing-the-resource-was-applied&quot; class=&quot;heading-anchor&quot;&gt;Testing the Resource Was Applied&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Next, we need to add the tests that check that after the &lt;strong&gt;DSC Configuration&lt;/strong&gt; has been applied to the machine that the changes have actually been made and that the parameters match those set by the &lt;strong&gt;Configuration&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;To do this, we complete this section:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/IrbA0lBLkJ-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/IrbA0lBLkJ-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/IrbA0lBLkJ-650.jpeg&quot; alt=&quot;ss_dsc_inttestsvalidate&quot; width=&quot;650&quot; height=&quot;56&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;In this case, I’ve changed it to:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;It &lt;span class=&quot;token string&quot;&gt;&#39;Should have set the resource and all the parameters should match&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# Get the Rule details&lt;/span&gt;
  &lt;span class=&quot;token variable&quot;&gt;$virtualDiskNew&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$VirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path
  &lt;span class=&quot;token variable&quot;&gt;$VirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path               &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token variable&quot;&gt;$virtualDiskNew&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path
  &lt;span class=&quot;token variable&quot;&gt;$VirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DiskType           &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token variable&quot;&gt;$virtualDiskNew&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DiskType
  &lt;span class=&quot;token variable&quot;&gt;$VirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Size               &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token variable&quot;&gt;$virtualDiskNew&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Size
  &lt;span class=&quot;token variable&quot;&gt;$VirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Description        &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token variable&quot;&gt;$virtualDiskNew&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Description
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What this code does is gets the iSCSI Virtual Disk that is at the path specified in the &lt;strong&gt;$VirtualDisk.path&lt;/strong&gt; into a variable &lt;strong&gt;$VirtualDiskNew&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;The parameters in &lt;strong&gt;$VirtualDiskNew&lt;/strong&gt; are then matched to ensure they are the same as those in the &lt;strong&gt;$VirtualDisk&lt;/strong&gt; hash table object that was created in the &lt;strong&gt;DSC Configuration&lt;/strong&gt; script (*.config.ps1).&lt;/p&gt;&lt;h3 id=&quot;cleaning-up&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/#cleaning-up&quot; class=&quot;heading-anchor&quot;&gt;Cleaning Up&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;It is important that after the tests have been run that any changes that were made to the testing computer are reverted. So, after the end of the last test I add any clean up code. In my case, I want to remove the &lt;strong&gt;iSCSI Virtual Disk&lt;/strong&gt; that was created:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Clean up&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Remove-iSCSIVirtualDisk&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$VirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path
&lt;span class=&quot;token function&quot;&gt;Remove-Item&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$VirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above code just removes the &lt;strong&gt;iSCSI Virtual Disk&lt;/strong&gt; and then also makes sure that the &lt;strong&gt;VHD&lt;/strong&gt; file was also deleted. This is also very important because if the clean up does not occur and the tests are run again on the same computer they may fail.&lt;/p&gt;&lt;h2 id=&quot;and-were-done&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/#and-were-done&quot; class=&quot;heading-anchor&quot;&gt;And We’re Done&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Now, that may all seem like quite a bit of work, but it becomes second nature after creating a few of them. They will also save you far more time in addressing future issues with the resource every time you make a simple change to the MOF (but forget to change the resource code). These tests will give users and other maintainers much more confidence in your resources as well.&lt;/p&gt;&lt;p&gt;This series actually ended up being a bit longer than I intended, but hopefully you’ve stuck with it and it has helped in some small way. If you’ve got this far and you’re wanting to know what to do next, why not head over to the &lt;a href=&quot;https://github.com/PowerShell/DscResources&quot; rel=&quot;noopener&quot;&gt;PowerShell DSCResources GitHub repository&lt;/a&gt; and see if you could help out on some resources. You could start off adding some small but useful parameter to an existing resource, fixing a bug or contribute an entire new resource to an existing module. There are numerous issues that need to be addressed on these resources, many of which are requests for new features or resources.&lt;/p&gt;&lt;p&gt;If you have an idea for a new resource in an existing module, raise an issue in the DSC Resource Module repository and offer to create the new resource. You may find that someone is already working on one, but if not, then this is a great opportunity to get started. It is quite a rewarding feeling the first time one of your contributions gets published in the official community DSC Resources!&lt;/p&gt;&lt;p&gt;So, thanks again for reading.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Speed up iSCSI PowerShell cmdlets</title>
      <link href="https://danielscottraynsford.com/blog/speed-up-iscsi-powershell-cmdlets/" />
      <updated>2016-01-02T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/speed-up-iscsi-powershell-cmdlets/</id>
      <content type="html">
				&lt;p&gt;I’ve been spending a bit of time lately completing a set of &lt;strong&gt;DSC Resources&lt;/strong&gt; for configuring &lt;strong&gt;iSCSI&lt;/strong&gt; &lt;strong&gt;Targets&lt;/strong&gt; and &lt;strong&gt;iSCSI Initiators&lt;/strong&gt;. One thing I’ve noticed is that these &lt;strong&gt;iSCSI&lt;/strong&gt; cmdlets are extremely slow:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/iNn-thanVX-566.png 566w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/iNn-thanVX-566.webp 566w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/iNn-thanVX-566.jpeg&quot; alt=&quot;ss_iscsi_measuregetiscsivirtualdisk&quot; width=&quot;566&quot; height=&quot;231&quot;&gt;&lt;/picture&gt;&lt;br&gt;21 seconds is just too slow!&lt;/p&gt;&lt;p&gt;Now, if I was just calling this cmdlet every now and then it really wouldn’t matter so much - as long as it works. However, because this cmdlet is going to be called every few minutes when used in a &lt;strong&gt;DSC Resource&lt;/strong&gt; it is unacceptable.&lt;/p&gt;&lt;p&gt;I’ve seen this sort of issue before. It is often caused by the cmdlet looking for things on the network that don’t exist. This means that a TCP timeout might occur before the cmdlet will complete. So I thought a way of possibly eliminating this is to specify the computer the cmdlet should work against - in the case of a &lt;strong&gt;DSC Resource&lt;/strong&gt; it is always &lt;strong&gt;LocalHost&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;So I gave this a shot:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/7acCIYJtSx-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/7acCIYJtSx-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/7acCIYJtSx-650.jpeg&quot; alt=&quot;ss_iscsi_measuregetiscsivirtualdisklocalhost&quot; width=&quot;650&quot; height=&quot;201&quot;&gt;&lt;/picture&gt;&lt;br&gt;49 milliseconds is very acceptable.&lt;/p&gt;&lt;p&gt;That is a 42,000% speed increase, which is definitely acceptable.&lt;/p&gt;&lt;p&gt;To confirm that the cmdlet is still working as expected:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/sMq_v5R_U8-597.png 597w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/sMq_v5R_U8-597.webp 597w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/sMq_v5R_U8-597.jpeg&quot; alt=&quot;ss_iscsi_getiscsivirtualdisklocalhost&quot; width=&quot;597&quot; height=&quot;697&quot;&gt;&lt;/picture&gt;&lt;br&gt;Yes, all the iSCSI Virtual Disks are there.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Important Note:&lt;/strong&gt; You must set the &lt;strong&gt;ComputerName&lt;/strong&gt; to the exact test &quot;&lt;strong&gt;LocalHost&quot;&lt;/strong&gt; and not the actual computer name of the local machine. If you use the local computer name, the cmdlet will still be slow:&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/aInQfaqsNZ-650.png 650w, https://danielscottraynsford.com/img/aInQfaqsNZ-853.png 853w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/aInQfaqsNZ-650.webp 650w, https://danielscottraynsford.com/img/aInQfaqsNZ-853.webp 853w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/aInQfaqsNZ-650.jpeg&quot; alt=&quot;ss_iscsi_measureiscsivirtualdisklocalcomputer&quot; width=&quot;853&quot; height=&quot;230&quot; srcset=&quot;https://danielscottraynsford.com/img/aInQfaqsNZ-650.jpeg 650w, https://danielscottraynsford.com/img/aInQfaqsNZ-853.jpeg 853w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;br&gt;21 seconds again, no good.&lt;/p&gt;&lt;p&gt;All of the &lt;strong&gt;iSCSI Target&lt;/strong&gt; cmdlets in the&amp;nbsp;&lt;strong&gt;iSCSI Target&lt;/strong&gt; module seem to suffer from this problem, so adding the -&lt;strong&gt;ComputerName LocalHost&lt;/strong&gt; parameter to them should speed things up across the board. Obviously, this is only going to work if you’re actually manipulating &lt;strong&gt;iSCSI Targets&lt;/strong&gt; on the &lt;strong&gt;LocalHost&lt;/strong&gt; - if you’re trying to configure a remote computer then you’ll need to set the remote computer name.&lt;/p&gt;&lt;p&gt;Hope this one shaves a number of seconds off some scripts out there.&lt;/p&gt;&lt;p&gt;Also, Happy 2016!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Remove an iSCSI Target Portal with PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/remove-an-iscsi-target-portal-with-powershell/" />
      <updated>2015-12-26T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/remove-an-iscsi-target-portal-with-powershell/</id>
      <content type="html">
				&lt;p&gt;I ran into a small problem with removing &lt;strong&gt;iSCSI Target Portals&lt;/strong&gt; using &lt;strong&gt;PowerShell&lt;/strong&gt; the other day and thought it might be worth documenting.&lt;/p&gt;&lt;p&gt;Pretend you have an &lt;strong&gt;iSCSI Target Portal&lt;/strong&gt; configured with a &lt;strong&gt;Target Portal Address&lt;/strong&gt; of 192.168.129.24:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/_trYQy4rbw-487.png 487w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/_trYQy4rbw-487.webp 487w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/_trYQy4rbw-487.jpeg&quot; alt=&quot;ss_iscsi_gettargetportal&quot; width=&quot;487&quot; height=&quot;174&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;You might therefore expect that you could remove this &lt;strong&gt;Target Portal&lt;/strong&gt; with the command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Remove-IscsiTargetPortal&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TargetPortalAddress 192&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;168&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;129&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;24&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Unfortunately this won’t work:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/xOVLAEQque-650.png 650w, https://danielscottraynsford.com/img/xOVLAEQque-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/xOVLAEQque-650.webp 650w, https://danielscottraynsford.com/img/xOVLAEQque-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/xOVLAEQque-650.jpeg&quot; alt=&quot;ss_iscsi_removetargetportal1&quot; width=&quot;960&quot; height=&quot;209&quot; srcset=&quot;https://danielscottraynsford.com/img/xOVLAEQque-650.jpeg 650w, https://danielscottraynsford.com/img/xOVLAEQque-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;And neither does this:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/Aur551oMds-650.png 650w, https://danielscottraynsford.com/img/Aur551oMds-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Aur551oMds-650.webp 650w, https://danielscottraynsford.com/img/Aur551oMds-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Aur551oMds-650.jpeg&quot; alt=&quot;ss_iscsi_removetargetportal2&quot; width=&quot;960&quot; height=&quot;210&quot; srcset=&quot;https://danielscottraynsford.com/img/Aur551oMds-650.jpeg 650w, https://danielscottraynsford.com/img/Aur551oMds-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;What you actually have to do is specify both the &lt;strong&gt;Target Portal Address&lt;/strong&gt; and the &lt;strong&gt;Initiator Portal Address&lt;/strong&gt; when deleting an&amp;nbsp;&lt;strong&gt;iSCSI&lt;/strong&gt; &lt;strong&gt;Target Portal&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Remove-IscsiTargetPortal&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TargetPortalAddress 192&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;168&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;129&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;24 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;InitiatorPortalAddress 192&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;168&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;129&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;30&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/niy2boUrs6-650.png 650w, https://danielscottraynsford.com/img/niy2boUrs6-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/niy2boUrs6-650.webp 650w, https://danielscottraynsford.com/img/niy2boUrs6-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/niy2boUrs6-650.jpeg&quot; alt=&quot;ss_iscsi_removetargetportalcorrect&quot; width=&quot;960&quot; height=&quot;153&quot; srcset=&quot;https://danielscottraynsford.com/img/niy2boUrs6-650.jpeg 650w, https://danielscottraynsford.com/img/niy2boUrs6-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Over and out.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>WMF 5.0 Download Removed from Download Center</title>
      <link href="https://danielscottraynsford.com/blog/wmf-50-download-removed-from-download-center/" />
      <updated>2015-12-24T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/wmf-50-download-removed-from-download-center/</id>
      <content type="html">
				&lt;p&gt;Bit of a bump in the WMF 5.0 road today: Microsoft has removed the WMF 5.0 RTM download from the download center because of a significant bug:&lt;/p&gt;&lt;p&gt;&lt;em&gt;We recently released Windows Management Framework (WMF) 5.0 RTM delivering many requested improvements and fixes, via the Microsoft Download Center as announced in a &lt;a href=&quot;http://blogs.msdn.com/b/powershell/archive/2015/12/16/windows-management-framework-wmf-5-0-rtm-is-now-available.aspx&quot; rel=&quot;noopener&quot;&gt;previous blog post&lt;/a&gt;. However, we have discovered a &lt;a href=&quot;https://windowsserver.uservoice.com/forums/301869-powershell/suggestions/11148471-bug-wmf5-rtm-psmodulepath&quot; rel=&quot;noopener&quot;&gt;bug&lt;/a&gt; which resets the PowerShell module environment during installation. As this issue can have a serious impact on our customers, we are taking the action to stop delivery of WMF 5.0 RTM, and have removed the packages from the Download Center. Additionally, we will be unpublishing Azure DSC Extension Handler versions 2.11 and 2.12 as they automatically install WMF 5.0 RTM.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;So if you’re planning on rolling WMF5.0 out to production, best hold off for the moment. See the original blog post &lt;a href=&quot;http://blogs.msdn.com/b/powershell/archive/2015/12/23/windows-management-framework-wmf-5-0-currently-removed-from-download-center.aspx&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Creating Professional DSC Resources – Part 6</title>
      <link href="https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/" />
      <updated>2015-12-23T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/</id>
      <content type="html">
				&lt;p&gt;The purpose of this series of articles is to try and document a few of the lessons I learned while releasing new DSC resources as well as contributing to the existing &lt;strong&gt;Microsoft Community DSC resources&lt;/strong&gt;. These articles are not intended to tell you how to write DSC resources from a programming perspective, but to give you some ideas on what might be expected of a DSC resource you’re releasing to the public. For example, &lt;strong&gt;unit&lt;/strong&gt; and &lt;strong&gt;integration&lt;/strong&gt; tests (don’t worry if you aren’t familiar with those terms).&lt;/p&gt;&lt;p&gt;These articles are also not intended to tell you what you &lt;strong&gt;must&lt;/strong&gt; do to release your resource, but more document what will help your resource be easier to use and extend by other people. Some of these these things are obvious for people who have come from the &lt;strong&gt;development&lt;/strong&gt; community, but may be quite new to &lt;strong&gt;operations&lt;/strong&gt; people.&lt;/p&gt;&lt;p&gt;If you missed any previous articles you can find them here:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-1/&quot;&gt;Creating Professional DSC Resources – Part 1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-2/&quot;&gt;Creating Professional DSC Resources - Part 2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-3/&quot;&gt;Creating Professional DSC Resources - Part 3&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-4/&quot;&gt;Creating Professional DSC Resources - Part 4&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-5/&quot;&gt;Creating Professional DSC Resources - Part 5&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;recap&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/#recap&quot; class=&quot;heading-anchor&quot;&gt;Recap&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In the last couple of articles I covered the importance of &lt;strong&gt;automated testing&lt;/strong&gt; and covered &lt;strong&gt;unit testing&lt;/strong&gt; in particular (I’ll get to &lt;strong&gt;integration testing&lt;/strong&gt; later). I had covered creating new &lt;strong&gt;unit tests&lt;/strong&gt; using the&amp;nbsp;&lt;strong&gt;unit&lt;/strong&gt; &lt;strong&gt;test templates&lt;/strong&gt; that are available &lt;a href=&quot;https://github.com/PowerShell/xNetworking/tree/dev/Templates&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; (although they will probably move &lt;a href=&quot;https://github.com/PowerShell/DscResources&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;). I also covered how to complete the &lt;strong&gt;Pester Test Initialization&lt;/strong&gt; and the &lt;strong&gt;Get-TargetResource&lt;/strong&gt; and &lt;strong&gt;Set-TargetResource&lt;/strong&gt; function areas of the &lt;strong&gt;unit test&lt;/strong&gt;.&lt;/p&gt;&lt;h2 id=&quot;unit-testing-completion&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/#unit-testing-completion&quot; class=&quot;heading-anchor&quot;&gt;Unit Testing Completion&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The final task in completing the &lt;strong&gt;unit tests&lt;/strong&gt; is to complete the **Set-TargetResource&amp;nbsp;** in tests and also optionally tests for any other supporting functions your &lt;strong&gt;DSC Resource&lt;/strong&gt; may have required.&lt;/p&gt;&lt;p&gt;In these &lt;strong&gt;unit tests&lt;/strong&gt; I am using a &lt;strong&gt;DSC Resource&lt;/strong&gt; for creating &lt;em&gt;iSCSI Virtual Disks&lt;/em&gt; to illustrate the process. You don’t need to know anything about &lt;em&gt;iSCSI Virtual Disks&lt;/em&gt; to understand these articles or resources, but if you’re interested to know the &lt;strong&gt;cmdlets&lt;/strong&gt; I’m using for these, see &lt;a href=&quot;https://technet.microsoft.com/en-us/library/jj612803%28v=wps.630%29.aspx&quot; rel=&quot;noopener&quot;&gt;this&lt;/a&gt; page. I’m using the &lt;strong&gt;*_iSCSIVirtualDisk&lt;/strong&gt; cmdlets in this &lt;strong&gt;DSC Resource&lt;/strong&gt;.&lt;/p&gt;&lt;h3 id=&quot;function-test-targetresource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/#function-test-targetresource&quot; class=&quot;heading-anchor&quot;&gt;Function Test-TargetResource&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This area will contain the actual &lt;strong&gt;Pester&lt;/strong&gt; tests that test the &lt;strong&gt;Test-TargetResource&lt;/strong&gt; function. These are fairly similar to the &lt;strong&gt;Set-TargetResource&lt;/strong&gt; except we will be checking these two things:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The output of the &lt;strong&gt;Test-TargetFunction&lt;/strong&gt; is correct. E.g. it returns &lt;strong&gt;false&lt;/strong&gt; if changes are required, which will cause &lt;strong&gt;Set-TargetFunction&lt;/strong&gt; to be called.&lt;/li&gt;&lt;li&gt;The expected &lt;strong&gt;Mocks&lt;/strong&gt; are called by the &lt;strong&gt;Function&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;This area may contain a large number of tests depending on the complexity of your &lt;strong&gt;DSC&lt;/strong&gt; &lt;strong&gt;Resource&lt;/strong&gt;. In most cases, you should expect there to create the tests from the following list, but often you will need even more for 100% code coverage:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Does the function return &lt;strong&gt;false&lt;/strong&gt; when the &lt;em&gt;resource being configured&lt;/em&gt; &lt;strong&gt;does exist&lt;/strong&gt; and &lt;strong&gt;should&lt;/strong&gt;, but one of the configured parameters &lt;strong&gt;does not match&lt;/strong&gt; the current values? This test is usually repeated for &lt;em&gt;each parameter&lt;/em&gt; in the &lt;strong&gt;DSC Resource&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Does the function return &lt;strong&gt;true&lt;/strong&gt; when the &lt;em&gt;resource being configured&lt;/em&gt; &lt;strong&gt;does exist&lt;/strong&gt; and &lt;strong&gt;should&lt;/strong&gt;, and &lt;strong&gt;all&lt;/strong&gt; the configured parameters &lt;strong&gt;match&lt;/strong&gt; the current values?&lt;/li&gt;&lt;li&gt;Does the function return&amp;nbsp;&lt;strong&gt;false&lt;/strong&gt; when the &lt;em&gt;resource being configured&lt;/em&gt; &lt;strong&gt;does not exist&lt;/strong&gt; but &lt;strong&gt;should&lt;/strong&gt;?&lt;/li&gt;&lt;li&gt;Does the function return&amp;nbsp;&lt;strong&gt;false&lt;/strong&gt; when the &lt;em&gt;resource being configured&lt;/em&gt; &lt;strong&gt;does exist&lt;/strong&gt; but &lt;strong&gt;should not&lt;/strong&gt;?&lt;/li&gt;&lt;li&gt;Does the function return&amp;nbsp;&lt;strong&gt;true&lt;/strong&gt; when the &lt;em&gt;resource being configured&lt;/em&gt; &lt;strong&gt;does not exist&lt;/strong&gt; and &lt;strong&gt;should not&lt;/strong&gt;?&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The bottom four of these tests are very similar. So I’ll only show examples of the top two &lt;strong&gt;contexts&lt;/strong&gt; here.&lt;/p&gt;&lt;h4 id=&quot;context-virtual-disk-exists-and-should-but-has-a-different&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/#context-virtual-disk-exists-and-should-but-has-a-different&quot; class=&quot;heading-anchor&quot;&gt;Context ‘Virtual Disk exists and should but has a different …’&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;In this scenario we &lt;strong&gt;Mock&lt;/strong&gt; the &lt;strong&gt;Get-iSCSIVirtualDisk&lt;/strong&gt; cmdlet to return the object we defined in the &lt;strong&gt;Pester Test Initialization&lt;/strong&gt; section. This is the behavior we’d expect if the &lt;em&gt;resource being configured&lt;/em&gt; does exist:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Context &lt;span class=&quot;token string&quot;&gt;&#39;Virtual Disk exists and should but has a different Description&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    
    Mock &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MockWith &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$MockVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    It &lt;span class=&quot;token string&quot;&gt;&#39;should return false&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
            &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$TestVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Clone&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Description = &lt;span class=&quot;token string&quot;&gt;&#39;Different&#39;&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Test-TargetResource&lt;/span&gt; @Splat &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token boolean&quot;&gt;$False&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Not &lt;span class=&quot;token keyword&quot;&gt;Throw&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    It &lt;span class=&quot;token string&quot;&gt;&#39;should call expected Mocks&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 1
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This &lt;strong&gt;context&lt;/strong&gt; will perform two tests:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Should return false&lt;/strong&gt; - The &lt;strong&gt;Test-TargetResource&lt;/strong&gt; should return false because we are changing the &lt;strong&gt;Description&lt;/strong&gt; parameter so that the resource will require changes (e.g. &lt;strong&gt;Set-TargetResource&lt;/strong&gt; should be called).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Should call the expected mocks&lt;/strong&gt; - The &lt;strong&gt;Test-TargetResource&lt;/strong&gt; should call the &lt;strong&gt;mocked&lt;/strong&gt; cmdlets the expected number of times. In all contexts in this function this will always be just once.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The purpose of &lt;strong&gt;cloning&lt;/strong&gt; the &lt;strong&gt;$TestVirtualDisk&lt;/strong&gt; object is so we can modify the properties to simulate a property difference without modifying the &lt;strong&gt;$TestVirtualDisk&lt;/strong&gt; object.&lt;/p&gt;&lt;p&gt;You should expect to repeat this &lt;strong&gt;context&lt;/strong&gt; for each parameter that might be different.&lt;/p&gt;&lt;h4 id=&quot;context-virtual-disk-does-not-exist-but-should&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/#context-virtual-disk-does-not-exist-but-should&quot; class=&quot;heading-anchor&quot;&gt;Context ‘Virtual Disk does not exist but should’&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;In this scenario we &lt;strong&gt;Mock&lt;/strong&gt; the &lt;strong&gt;Get-iSCSIVirtualDisk&lt;/strong&gt; cmdlet to return nothing. This is the behavior we’d expect if the &lt;em&gt;resource being configured&lt;/em&gt; does &lt;strong&gt;not&lt;/strong&gt; exist:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Context &lt;span class=&quot;token string&quot;&gt;&#39;Virtual Disk does not exist but should&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    
    Mock &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt;

    It &lt;span class=&quot;token string&quot;&gt;&#39;should return false&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$TestVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Clone&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Test-TargetResource&lt;/span&gt; @Splat &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token boolean&quot;&gt;$False&lt;/span&gt;
        
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    It &lt;span class=&quot;token string&quot;&gt;&#39;should call expected Mocks&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 1
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you can see, there is not too much different with these tests and you shouldn’t have any problems figuring out the remaining ones. Just remember, the goal is always to get 100% code coverage.&lt;/p&gt;&lt;h3 id=&quot;unit-testing-supporting-functions&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/#unit-testing-supporting-functions&quot; class=&quot;heading-anchor&quot;&gt;Unit Testing Supporting Functions&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;It is quite common that you might have implemented some &lt;em&gt;supporting&lt;/em&gt; functions in your &lt;strong&gt;DSC Resource&lt;/strong&gt;. These &lt;em&gt;supporting&lt;/em&gt; functions are usually called by your standard&amp;nbsp;&lt;strong&gt;*-TargetResource&lt;/strong&gt; functions. If that is the case there are two &lt;strong&gt;important&lt;/strong&gt; things you should do:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Write &lt;strong&gt;unit tests&lt;/strong&gt; that cover all code paths in your &lt;em&gt;supporting&lt;/em&gt; functions.&lt;/li&gt;&lt;li&gt;Add &lt;strong&gt;mocks&lt;/strong&gt; to your&amp;nbsp;&lt;strong&gt;*-TargetResource&lt;/strong&gt; &lt;strong&gt;unit tests&lt;/strong&gt; that prevent any constructive/destructive &lt;strong&gt;cmdlets&lt;/strong&gt; that exist in your &lt;em&gt;supporting&lt;/em&gt; functions from being called.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The first item is fairly self explanatory. For example, I often implement a &lt;strong&gt;get-*&lt;/strong&gt; function in my &lt;strong&gt;DSC Resources&lt;/strong&gt; which is used to pull the actual objects that will be used by the &lt;strong&gt;*-TargetResource&lt;/strong&gt; functions (e.g. &lt;strong&gt;Get-VirtualDisk&lt;/strong&gt;):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;Function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Get-VirtualDisk&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;param&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[parameter(Mandatory = $true)]&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[System.String]&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Path&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$VirtualDisk&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$Path&lt;/span&gt; `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ErrorAction Stop
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;[Microsoft.Iscsi.Target.Commands.IscsiCmdException]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$VirtualDisk&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;Throw&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$VirtualDisk&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To &lt;strong&gt;unit test&lt;/strong&gt; this function I’d write &lt;strong&gt;unit tests&lt;/strong&gt; that tested the following contexts:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Context ‘Virtual Disk does not exist’&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Context ‘Virtual Disk does exist’&lt;/strong&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;For example:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Describe &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Global&lt;/span&gt;:DSCResourceName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;Get-VirtualDisk&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    Context &lt;span class=&quot;token string&quot;&gt;&#39;Virtual Disk does not exist&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        
        Mock &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt;

        It &lt;span class=&quot;token string&quot;&gt;&#39;should return null&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$TestVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Clone&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$Result&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-VirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path 
            &lt;span class=&quot;token variable&quot;&gt;$Result&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;             
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        It &lt;span class=&quot;token string&quot;&gt;&#39;should call expected Mocks&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 1
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    Context &lt;span class=&quot;token string&quot;&gt;&#39;Virtual Disk does exist&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        
        Mock &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MockWith &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$MockVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        It &lt;span class=&quot;token string&quot;&gt;&#39;should return expected parameters&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$TestVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Clone&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$Result&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-VirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path 
            &lt;span class=&quot;token variable&quot;&gt;$Result&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path                    &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token variable&quot;&gt;$MockVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path
            &lt;span class=&quot;token variable&quot;&gt;$Result&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DiskType                &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token variable&quot;&gt;$MockVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DiskType
            &lt;span class=&quot;token variable&quot;&gt;$Result&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Size                    &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token variable&quot;&gt;$MockVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Size
            &lt;span class=&quot;token variable&quot;&gt;$Result&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Description             &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token variable&quot;&gt;$MockVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Description
            &lt;span class=&quot;token variable&quot;&gt;$Result&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ParentPath              &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token variable&quot;&gt;$MockVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ParentPath
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        It &lt;span class=&quot;token string&quot;&gt;&#39;should call expected Mocks&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 1
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you can see, there isn’t much to it.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Earn a Chocolate Fish&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;: If you look at the above &lt;strong&gt;supporting function&lt;/strong&gt; and &lt;strong&gt;unit tests&lt;/strong&gt; carefully, you’ll notice that I haven’t got 100% code coverage on it!&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;To get 100% code coverage I would have had to implement a &lt;strong&gt;unit test&lt;/strong&gt; that covered the situation where the &lt;strong&gt;Get-iSCSIVirtualDisk&lt;/strong&gt; function threw an exception that wasn’t a &lt;strong&gt;[Microsoft.Iscsi.Target.Commands.IscsiCmdException]&lt;/strong&gt; exception.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;In case you’re wondering, the &lt;strong&gt;Get-iSCSIVirtualDisk&lt;/strong&gt; function throws a &lt;strong&gt;[Microsoft.Iscsi.Target.Commands.IscsiCmdException]&lt;/strong&gt; when the cmdlet is called with the &lt;strong&gt;path&lt;/strong&gt; parameter set to a path that does not contain a valid &lt;strong&gt;iSCSI&lt;/strong&gt; &lt;strong&gt;Virtual Hard Disk&lt;/strong&gt; file.&lt;/em&gt;&lt;/p&gt;&lt;h3 id=&quot;unit-testing-exceptions&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/#unit-testing-exceptions&quot; class=&quot;heading-anchor&quot;&gt;Unit Testing Exceptions&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;When creating &lt;strong&gt;unit tests&lt;/strong&gt; you’ll often need to test a scenario where the function that is being tested is &lt;strong&gt;expected&lt;/strong&gt; to throw an exception. If you read the &lt;strong&gt;Pester&lt;/strong&gt; documentation, you’d might write a test for an exception like this:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Context &lt;span class=&quot;token string&quot;&gt;&#39;Virtual Disk exists and should but has a different ParentPath&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    
    Mock &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MockWith &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$MockVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    It &lt;span class=&quot;token string&quot;&gt;&#39;should throw an exception&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Test-TargetResource&lt;/span&gt; @Splat &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should &lt;span class=&quot;token keyword&quot;&gt;Throw&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    It &lt;span class=&quot;token string&quot;&gt;&#39;should call expected Mocks&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 1
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This would of course will work. It will ensure that the code throws an exception in this situation. The problem is we aren’t really sure if it is the exception that we expected it to throw. It could have been thrown by some other part of our code.&lt;/p&gt;&lt;p&gt;So to improve on this we need to do things:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Customize the exception that is thrown.&lt;/li&gt;&lt;li&gt;Change the &lt;strong&gt;unit test&lt;/strong&gt; so that it checks for the customized exception.&lt;/li&gt;&lt;/ol&gt;&lt;h4 id=&quot;customize-the-exception&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/#customize-the-exception&quot; class=&quot;heading-anchor&quot;&gt;Customize the Exception&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;To create a custom exception we need to create a new &lt;strong&gt;exception object&lt;/strong&gt; containing our &lt;strong&gt;custom error message&lt;/strong&gt;. The &lt;strong&gt;exception object&lt;/strong&gt; is then used to create a custom &lt;strong&gt;Error Record&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$errorId&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;iSCSIVirtualDiskRequiresRecreateError&#39;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$errorCategory&lt;/span&gt; = &lt;span class=&quot;token namespace&quot;&gt;[System.Management.Automation.ErrorCategory]&lt;/span&gt;::InvalidArgument
&lt;span class=&quot;token variable&quot;&gt;$errorMessage&lt;/span&gt; = $&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$LocalizedData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;iSCSIVirtualDiskRequiresRecreateError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$Path&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$exception&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TypeName System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;InvalidOperationException `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ArgumentList &lt;span class=&quot;token variable&quot;&gt;$errorMessage&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$errorRecord&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TypeName System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Management&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Automation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ErrorRecord `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ArgumentList &lt;span class=&quot;token variable&quot;&gt;$exception&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$errorId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$errorCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$PSCmdlet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ThrowTerminatingError&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$errorRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the above code, you just need to customize the &lt;strong&gt;$errorId&lt;/strong&gt; and &lt;strong&gt;$errorMessage&lt;/strong&gt; variables. The &lt;strong&gt;$errorId&lt;/strong&gt; should just contain a simply string identifier for this particular type of error, but the &lt;strong&gt;$errorMessage&lt;/strong&gt; can contain a full description of the error, including related parameters.&lt;/p&gt;&lt;p&gt;Once you’ve created the &lt;strong&gt;$errorRecord&lt;/strong&gt; object you can call the &lt;strong&gt;ThrowTerminatingError&lt;/strong&gt; method of the &lt;strong&gt;$PSCmdLet&lt;/strong&gt; object, passing the &lt;strong&gt;$errorRecord&lt;/strong&gt; object as the parameter.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Important:&lt;/strong&gt; the &lt;strong&gt;$PSCmdLet&lt;/strong&gt; object is only available in &lt;strong&gt;Functions&lt;/strong&gt; that include the &lt;strong&gt;[CmdletBinding()]&lt;/strong&gt; function attribute. So ensure your &lt;strong&gt;*-TargetResource&lt;/strong&gt; and &lt;strong&gt;supporting functions&lt;/strong&gt; include this attribute if you want to be able to access this object.&lt;/em&gt;&lt;/p&gt;&lt;h4 id=&quot;test-for-the-customized-exception&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/#test-for-the-customized-exception&quot; class=&quot;heading-anchor&quot;&gt;Test for the Customized Exception&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;To test for the custom exception object we need to create an identical object in the &lt;strong&gt;unit test&lt;/strong&gt; and test for it:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Context &lt;span class=&quot;token string&quot;&gt;&#39;Virtual Disk exists and should but has a different ParentPath&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    
    Mock &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MockWith &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$MockVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    It &lt;span class=&quot;token string&quot;&gt;&#39;should throw an iSCSIVirtualDiskRequiresRecreateError exception&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$TestVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Clone&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ParentPath = &lt;span class=&quot;token string&quot;&gt;&#39;c:&#92;NewParent.vhdx&#39;&lt;/span&gt;

        &lt;span class=&quot;token variable&quot;&gt;$errorId&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;iSCSIVirtualDiskRequiresRecreateError&#39;&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$errorCategory&lt;/span&gt; = &lt;span class=&quot;token namespace&quot;&gt;[System.Management.Automation.ErrorCategory]&lt;/span&gt;::InvalidArgument
        &lt;span class=&quot;token variable&quot;&gt;$errorMessage&lt;/span&gt; = $&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$LocalizedData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;iSCSIVirtualDiskRequiresRecreateError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path
        &lt;span class=&quot;token variable&quot;&gt;$exception&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TypeName System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;InvalidOperationException `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ArgumentList &lt;span class=&quot;token variable&quot;&gt;$errorMessage&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$errorRecord&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;TypeName System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Management&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Automation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ErrorRecord `
            &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ArgumentList &lt;span class=&quot;token variable&quot;&gt;$exception&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$errorId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$errorCategory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;
            
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Test-TargetResource&lt;/span&gt; @Splat &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should &lt;span class=&quot;token keyword&quot;&gt;Throw&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$errorRecord&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    It &lt;span class=&quot;token string&quot;&gt;&#39;should call expected Mocks&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 1
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above code creates an identical &lt;strong&gt;exception object&lt;/strong&gt; to the one produced by the exception in our &lt;strong&gt;DSC Resource&lt;/strong&gt; code. The &lt;strong&gt;exception object&lt;/strong&gt; can then be passed to the &lt;strong&gt;should&lt;/strong&gt; &lt;strong&gt;throw&lt;/strong&gt; cmdlet. If a different exception is thrown by the code then the test will fail - it will only pass if the exception object is &lt;strong&gt;exactly&lt;/strong&gt; the same.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Important:&lt;/strong&gt; Make sure both the &lt;strong&gt;$errorId&lt;/strong&gt; and &lt;strong&gt;$errorMessage&lt;/strong&gt; variables are exactly the same as what would be produced by the code when your &lt;strong&gt;unit test&lt;/strong&gt; calls it. This includes ensuring that if your &lt;strong&gt;$errorMessage&lt;/strong&gt; contains any parameters that the &lt;strong&gt;unit test&lt;/strong&gt; &lt;strong&gt;$errorMessage&lt;/strong&gt; contains the same parameter values.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;That about completes creating &lt;strong&gt;unit tests&lt;/strong&gt;. After you’ve implemented a few &lt;strong&gt;unit tests&lt;/strong&gt; you’ll no doubt come up with your own method of implementing them, but hopefully this has given you a place to start.&lt;/p&gt;&lt;h2 id=&quot;up-next-integration-tests&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/#up-next-integration-tests&quot; class=&quot;heading-anchor&quot;&gt;Up Next - Integration Tests&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In the next article, I’ll cover the &lt;strong&gt;integration tests&lt;/strong&gt;. There are often the most difficult to implement, but if you can take the time to implement them then your &lt;strong&gt;DSC Resources&lt;/strong&gt; are guaranteed to be extremely robust and bugs are far less likely to slip through.&lt;/p&gt;&lt;p&gt;Further parts in this series:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/&quot;&gt;Creating Professional DSC Resources - Part 7&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
 			</content>
    </entry><entry>
      <title>Creating Professional DSC Resources - Part 5</title>
      <link href="https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-5/" />
      <updated>2015-12-20T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-5/</id>
      <content type="html">
				&lt;p&gt;The purpose of this series of articles is to try and document a few of the lessons I learned while releasing new DSC resources as well as contributing to the existing &lt;strong&gt;Microsoft Community DSC resources&lt;/strong&gt;. These articles are not intended to tell you how to write DSC resources from a programming perspective, but to give you some ideas on what might be expected of a DSC resource you’re releasing to the public. For example, &lt;strong&gt;unit&lt;/strong&gt; and &lt;strong&gt;integration&lt;/strong&gt; tests (don’t worry if you aren’t familiar with those terms).&lt;/p&gt;&lt;p&gt;These articles are also not intended to tell you what you &lt;strong&gt;must&lt;/strong&gt; do to release your resource, but more to document what will help your resource be easier to use and extend by other people. Some of these things are obvious for people who have come from the &lt;strong&gt;development&lt;/strong&gt; community, but may be quite new to &lt;strong&gt;operations&lt;/strong&gt; people.&lt;/p&gt;&lt;p&gt;If you missed any previous articles, you can find them here:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-1/&quot;&gt;Creating Professional DSC Resources – Part 1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-2/&quot;&gt;Creating Professional DSC Resources - Part 2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-3/&quot;&gt;Creating Professional DSC Resources - Part 3&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-4/&quot;&gt;Creating Professional DSC Resources - Part 4&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;recap&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-5/#recap&quot; class=&quot;heading-anchor&quot;&gt;Recap&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Yesterday I talked about the importance of &lt;strong&gt;automated testing&lt;/strong&gt; and covered &lt;strong&gt;unit testing&lt;/strong&gt; in particular (I’ll get to &lt;strong&gt;integration testing&lt;/strong&gt; later). I had covered creating new &lt;strong&gt;unit tests&lt;/strong&gt; using the&amp;nbsp;&lt;strong&gt;unit test templates&lt;/strong&gt; that are available &lt;a href=&quot;https://github.com/PowerShell/xNetworking/tree/dev/Templates&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; (although they will probably move &lt;a href=&quot;https://github.com/PowerShell/DscResources&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;). I also covered how to complete the &lt;strong&gt;Pester Test Initialization&lt;/strong&gt; and the &lt;strong&gt;Function Get-TargetResource&lt;/strong&gt; areas of the &lt;strong&gt;unit test&lt;/strong&gt;.&lt;/p&gt;&lt;h2 id=&quot;unit-testing-continued&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-5/#unit-testing-continued&quot; class=&quot;heading-anchor&quot;&gt;Unit Testing Continued&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The next task in completing the &lt;strong&gt;unit tests&lt;/strong&gt; is to complete the &lt;strong&gt;Set-TargetResource&lt;/strong&gt; area in the &lt;strong&gt;unit test&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;In these &lt;strong&gt;unit tests&lt;/strong&gt;, I am using a &lt;strong&gt;DSC Resource&lt;/strong&gt; for creating &lt;em&gt;iSCSI Virtual Disks&lt;/em&gt; to illustrate the process. You don’t need to know anything about &lt;em&gt;iSCSI Virtual Disks&lt;/em&gt; to understand these articles or resources, but if you’re interested to know the &lt;strong&gt;cmdlets&lt;/strong&gt; I’m using for these, see &lt;a href=&quot;https://technet.microsoft.com/en-us/library/jj612803%28v=wps.630%29.aspx&quot; rel=&quot;noopener&quot;&gt;this&lt;/a&gt; page. I’m using the &lt;strong&gt;*_iSCSIVirtualDisk&lt;/strong&gt; cmdlets in this &lt;strong&gt;DSC Resource&lt;/strong&gt;.&lt;/p&gt;&lt;h3 id=&quot;function-set-targetresource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-5/#function-set-targetresource&quot; class=&quot;heading-anchor&quot;&gt;Function Set-TargetResource&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This area will contain the actual &lt;strong&gt;Pester&lt;/strong&gt; tests that test the &lt;strong&gt;Set-TargetResource&lt;/strong&gt; function. Unlike the&amp;nbsp;&lt;strong&gt;Get-TargetResource&lt;/strong&gt;, this area may contain a large number of tests depending on the complexity of your &lt;strong&gt;DSC Resource&lt;/strong&gt;. In most cases, you should expect to create the tests from the following list, but often you will need even more for 100% code coverage:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Does each parameter get set/updated correctly when the &lt;em&gt;resource being configured&lt;/em&gt; &lt;strong&gt;does exist&lt;/strong&gt; and &lt;strong&gt;should&lt;/strong&gt;? This test is usually repeated for &lt;em&gt;each parameter&lt;/em&gt; in the &lt;strong&gt;DSC Resource&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Does it work when the &lt;em&gt;resource being configured&lt;/em&gt; &lt;strong&gt;does not exist&lt;/strong&gt; but &lt;strong&gt;should&lt;/strong&gt;?&lt;/li&gt;&lt;li&gt;Does it work when the &lt;em&gt;resource being configured&lt;/em&gt; &lt;strong&gt;does exist&lt;/strong&gt; but &lt;strong&gt;should not&lt;/strong&gt;?&lt;/li&gt;&lt;li&gt;Does it work when the &lt;em&gt;resource being configured&lt;/em&gt; &lt;strong&gt;does not exist&lt;/strong&gt; and &lt;strong&gt;should not&lt;/strong&gt;?&lt;/li&gt;&lt;/ul&gt;&lt;h4 id=&quot;context-virtual-disk-exists-and-should-but-has-a-different&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-5/#context-virtual-disk-exists-and-should-but-has-a-different&quot; class=&quot;heading-anchor&quot;&gt;Context ‘Virtual Disk exists and should but has a different …’&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;In this scenario, we &lt;strong&gt;Mock&lt;/strong&gt; the &lt;strong&gt;Get-iSCSIVirtualDisk&lt;/strong&gt; cmdlet to return the object we defined in the &lt;strong&gt;Pester Test Initialization&lt;/strong&gt; section. This is the behavior we’d expect if the &lt;em&gt;resource being configured&lt;/em&gt; does exist.&lt;/p&gt;&lt;p&gt;We are also going to &lt;strong&gt;Mock&lt;/strong&gt; the &lt;strong&gt;Set-iSCSIVirtualDisk&lt;/strong&gt;, &lt;strong&gt;New-iSCSIVirtualDisk&lt;/strong&gt;, and &lt;strong&gt;Remove-iSCSIVirtualDisk&lt;/strong&gt;. This is so we can ensure the expected cmdlets are called as well as prevent the &lt;em&gt;real&lt;/em&gt; cmdlets from being run:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Context &lt;span class=&quot;token string&quot;&gt;&#39;Virtual Disk exists and should but has a different Description&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Mock &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MockWith &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$MockVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    Mock &lt;span class=&quot;token function&quot;&gt;New-iSCSIVirtualDisk&lt;/span&gt;
    Mock &lt;span class=&quot;token function&quot;&gt;Set-iSCSIVirtualDisk&lt;/span&gt;
    Mock &lt;span class=&quot;token function&quot;&gt;Remove-iSCSIVirtualDisk&lt;/span&gt;

    It &lt;span class=&quot;token string&quot;&gt;&#39;should not throw error&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
            &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$TestVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Clone&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Description = &lt;span class=&quot;token string&quot;&gt;&#39;Different&#39;&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Set-TargetResource&lt;/span&gt; @Splat
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Not &lt;span class=&quot;token keyword&quot;&gt;Throw&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    It &lt;span class=&quot;token string&quot;&gt;&#39;should call expected Mocks&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 1
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;New-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 0
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Set-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 1
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Remove-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 0
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This &lt;strong&gt;context&lt;/strong&gt; will perform two tests:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Should not throw error&lt;/strong&gt; - The &lt;strong&gt;Set-TargetResource&lt;/strong&gt; should not throw an error when called in this context.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Should call the expected mocks&lt;/strong&gt; - The &lt;strong&gt;Set-TargetResource&lt;/strong&gt; should call the &lt;strong&gt;mocked&lt;/strong&gt; cmdlets the expected number of times.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The purpose of &lt;strong&gt;cloning&lt;/strong&gt; the &lt;strong&gt;$TestVirtualDisk&lt;/strong&gt; object is so we can modify the properties to simulate a property difference without modifying the &lt;strong&gt;$TestVirtualDisk&lt;/strong&gt; object.&lt;/p&gt;&lt;p&gt;It is also important to ensure that we are not only checking that the expected &lt;strong&gt;cmdlets&lt;/strong&gt; are called, but also that the other &lt;strong&gt;cmdlets&lt;/strong&gt; in this &lt;strong&gt;function&lt;/strong&gt; are &lt;strong&gt;not called&lt;/strong&gt;. This is why we are checking the &lt;strong&gt;New-iSCSIVirtualDisk&lt;/strong&gt; and &lt;strong&gt;Remove-iSCSIVirtualDisk&lt;/strong&gt; are being called zero times.&lt;/p&gt;&lt;p&gt;You should expect to repeat this &lt;strong&gt;context&lt;/strong&gt; for each parameter that might be updated.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; It is possible that updating some parameters may not be possible because of limitations in the underlying cmdlets. In this case I like to throw an &lt;strong&gt;exception&lt;/strong&gt; so that the user is made aware that they are configuring a scenarios that can not be performed. In that case the test would be to ensure the correct &lt;strong&gt;exception&lt;/strong&gt; occurs. I’ll cover testing &lt;strong&gt;exceptions&lt;/strong&gt; in a later article.&lt;/em&gt;&lt;/p&gt;&lt;h4 id=&quot;context-virtual-disk-does-not-exist-but-should&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-5/#context-virtual-disk-does-not-exist-but-should&quot; class=&quot;heading-anchor&quot;&gt;Context ‘Virtual Disk does not exist but should’&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;In this scenario we &lt;strong&gt;Mock&lt;/strong&gt; the &lt;strong&gt;Get-iSCSIVirtualDisk&lt;/strong&gt; cmdlet to return nothing. This is the behavior we’d expect if the &lt;em&gt;resource being configured&lt;/em&gt; does &lt;strong&gt;not&lt;/strong&gt; exist.&lt;/p&gt;&lt;p&gt;We are also going to &lt;strong&gt;Mock&lt;/strong&gt; the &lt;strong&gt;Set-iSCSIVirtualDisk&lt;/strong&gt;, &lt;strong&gt;New-iSCSIVirtualDisk&lt;/strong&gt;, and &lt;strong&gt;Remove-iSCSIVirtualDisk&lt;/strong&gt;. This is so we can ensure the expected cmdlets are called as well as preventing the &lt;em&gt;real&lt;/em&gt; cmdlets from being run:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Context &lt;span class=&quot;token string&quot;&gt;&#39;Virtual Disk does not exist but should&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Mock &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MockWith &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    Mock &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt;
    Mock &lt;span class=&quot;token function&quot;&gt;New-iSCSIVirtualDisk&lt;/span&gt;
    Mock &lt;span class=&quot;token function&quot;&gt;Set-iSCSIVirtualDisk&lt;/span&gt;
    Mock &lt;span class=&quot;token function&quot;&gt;Remove-iSCSIVirtualDisk&lt;/span&gt;

    It &lt;span class=&quot;token string&quot;&gt;&#39;should not throw error&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
            &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$TestVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Clone&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Set-TargetResource&lt;/span&gt; @Splat
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Not &lt;span class=&quot;token keyword&quot;&gt;Throw&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    It &lt;span class=&quot;token string&quot;&gt;&#39;should call expected Mocks&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 1
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;New-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 1
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Set-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 0
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Remove-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 0
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;strong&gt;context&lt;/strong&gt; tests are very similar to all the other tests so I won’t go into detail on them here. It is important to note that the expected &lt;strong&gt;Mocks&lt;/strong&gt; will be different.&lt;/p&gt;&lt;h4 id=&quot;context-virtual-disk-exists-but-should-not&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-5/#context-virtual-disk-exists-but-should-not&quot; class=&quot;heading-anchor&quot;&gt;Context ‘Virtual Disk exists but should not’&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;In this scenario we &lt;strong&gt;Mock&lt;/strong&gt; the &lt;strong&gt;Get-iSCSIVirtualDisk&lt;/strong&gt; cmdlet to return the object we defined in the &lt;strong&gt;Pester Test Initialization&lt;/strong&gt; section. This is the behavior we’d expect if the &lt;em&gt;resource being configured&lt;/em&gt; does exist.&lt;/p&gt;&lt;p&gt;We are also going to &lt;strong&gt;Mock&lt;/strong&gt; the &lt;strong&gt;Set-iSCSIVirtualDisk&lt;/strong&gt;, &lt;strong&gt;New-iSCSIVirtualDisk&lt;/strong&gt;, and &lt;strong&gt;Remove-iSCSIVirtualDisk&lt;/strong&gt;. This is so we can ensure the expected cmdlets are called as well as preventing the &lt;em&gt;real&lt;/em&gt; cmdlets from being run:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Context &lt;span class=&quot;token string&quot;&gt;&#39;Virtual Disk exists but should not&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    
    Mock &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MockWith &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$MockVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    Mock &lt;span class=&quot;token function&quot;&gt;New-iSCSIVirtualDisk&lt;/span&gt;
    Mock &lt;span class=&quot;token function&quot;&gt;Set-iSCSIVirtualDisk&lt;/span&gt;
    Mock &lt;span class=&quot;token function&quot;&gt;Remove-iSCSIVirtualDisk&lt;/span&gt;

    It &lt;span class=&quot;token string&quot;&gt;&#39;should not throw error&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
            &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$TestVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Clone&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Ensure = &lt;span class=&quot;token string&quot;&gt;&#39;Absent&#39;&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Set-TargetResource&lt;/span&gt; @Splat
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Not &lt;span class=&quot;token keyword&quot;&gt;Throw&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    It &lt;span class=&quot;token string&quot;&gt;&#39;should call expected Mocks&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 1
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;New-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 0
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Set-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 0
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Remove-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 1
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;strong&gt;context&lt;/strong&gt; tests are very similar to all the other tests so I won’t go into detail on them here. It is important to note that the expected &lt;strong&gt;Mocks&lt;/strong&gt; will be different.&lt;/p&gt;&lt;h4 id=&quot;context-virtual-disk-does-not-exist-and-should-not&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-5/#context-virtual-disk-does-not-exist-and-should-not&quot; class=&quot;heading-anchor&quot;&gt;Context ‘Virtual Disk does not exist and should not’&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;In this scenario we &lt;strong&gt;Mock&lt;/strong&gt; the &lt;strong&gt;Get-iSCSIVirtualDisk&lt;/strong&gt; cmdlet to return the object we defined in the &lt;strong&gt;Pester Test Initialization&lt;/strong&gt; section. This is the behavior we’d expect if the &lt;em&gt;resource being configured&lt;/em&gt; does&amp;nbsp;&lt;strong&gt;not&lt;/strong&gt; exist.&lt;/p&gt;&lt;p&gt;We are also going to &lt;strong&gt;Mock&lt;/strong&gt; the &lt;strong&gt;Set-iSCSIVirtualDisk&lt;/strong&gt;, &lt;strong&gt;New-iSCSIVirtualDisk&lt;/strong&gt;, and &lt;strong&gt;Remove-iSCSIVirtualDisk&lt;/strong&gt;. This is so we can ensure the expected cmdlets are called as well as preventing the &lt;em&gt;real&lt;/em&gt; cmdlets from being run:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Context &lt;span class=&quot;token string&quot;&gt;&#39;Virtual Disk does not exist and should not&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    
    Mock &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt;
    Mock &lt;span class=&quot;token function&quot;&gt;New-iSCSIVirtualDisk&lt;/span&gt;
    Mock &lt;span class=&quot;token function&quot;&gt;Set-iSCSIVirtualDisk&lt;/span&gt;
    Mock &lt;span class=&quot;token function&quot;&gt;Remove-iSCSIVirtualDisk&lt;/span&gt;
        
    It &lt;span class=&quot;token string&quot;&gt;&#39;should not throw error&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
            &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$TestVirtualDisk&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Clone&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$Splat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Ensure = &lt;span class=&quot;token string&quot;&gt;&#39;Absent&#39;&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Set-TargetResource&lt;/span&gt; @Splat
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Not &lt;span class=&quot;token keyword&quot;&gt;Throw&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    It &lt;span class=&quot;token string&quot;&gt;&#39;should call expected Mocks&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Get-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 1
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;New-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 0
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Set-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 0
        &lt;span class=&quot;token function&quot;&gt;Assert-MockCalled&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;commandName &lt;span class=&quot;token function&quot;&gt;Remove-iSCSIVirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Exactly 0
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;strong&gt;context&lt;/strong&gt; tests are very similar to all the other tests so I won’t go into detail on them here. It is important to note that the expected &lt;strong&gt;Mocks&lt;/strong&gt; will be different.&lt;/p&gt;&lt;h2 id=&quot;unit-tests-to-be-continued&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-5/#unit-tests-to-be-continued&quot; class=&quot;heading-anchor&quot;&gt;Unit Tests to be Continued&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In the next article, I’ll cover the &lt;strong&gt;unit tests&lt;/strong&gt; for&amp;nbsp;&lt;strong&gt;Get-TargetResource&lt;/strong&gt; as well as unit testing any additional functions. Thanks again for reading, and I hope it is useful.&lt;/p&gt;&lt;p&gt;Further parts in this series:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/12/23/creating-professional-dsc-resources-part-6/&quot; rel=&quot;noopener&quot;&gt;Creating Professional DSC Resources - Part 6&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://dscottraynsford.wordpress.com/2016/01/25/creating-professional-dsc-resources-part-7/&quot; rel=&quot;noopener&quot;&gt;Creating Professional DSC Resources - Part 7&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
 			</content>
    </entry><entry>
      <title>Markdown Preview in Visual Studio Code</title>
      <link href="https://danielscottraynsford.com/blog/markdown-preview-in-visual-studio-code/" />
      <updated>2015-12-19T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/markdown-preview-in-visual-studio-code/</id>
      <content type="html">
				&lt;p&gt;I used to keep around a copy of &lt;a href=&quot;http://markdownpad.com/&quot; rel=&quot;noopener&quot;&gt;Markdown Pad&lt;/a&gt; to enable me to preview any &lt;a href=&quot;https://guides.github.com/features/mastering-markdown/&quot; rel=&quot;noopener&quot;&gt;markdown&lt;/a&gt; files I created. However, after an update to a &lt;strong&gt;Awesomium&lt;/strong&gt; &lt;strong&gt;SDK&lt;/strong&gt; Markdown Pad stopped working. I reported the issue but it hasn’t been resolved yet.&lt;/p&gt;&lt;p&gt;But after &lt;a href=&quot;https://pshirwin.wordpress.com/&quot; rel=&quot;noopener&quot;&gt;Irwin Strachan&lt;/a&gt; mentioned using &lt;strong&gt;Visual Studio Code&lt;/strong&gt; as a &lt;strong&gt;Markdown&lt;/strong&gt; editor, I wondered if there was an extension that allowed it to &lt;strong&gt;Preview&lt;/strong&gt; the &lt;strong&gt;Markdown&lt;/strong&gt; as well - because that was really all that was missing for me to do away with &lt;strong&gt;Markdown Pad&lt;/strong&gt;. So I went and did a search for &lt;strong&gt;Markdown&lt;/strong&gt; extensions in &lt;strong&gt;VS Code&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/tl3pP9WUT6-650.png 650w, https://danielscottraynsford.com/img/tl3pP9WUT6-849.png 849w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/tl3pP9WUT6-650.webp 650w, https://danielscottraynsford.com/img/tl3pP9WUT6-849.webp 849w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/tl3pP9WUT6-650.jpeg&quot; alt=&quot;ss_vscode_markdownextensions&quot; width=&quot;849&quot; height=&quot;418&quot; srcset=&quot;https://danielscottraynsford.com/img/tl3pP9WUT6-650.jpeg 650w, https://danielscottraynsford.com/img/tl3pP9WUT6-849.jpeg 849w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;br&gt;Want to install extensions in VS Code? Press F1 and type ‘ext install’.&lt;/p&gt;&lt;p&gt;I found a few, but after playing around with them a bit I noticed a small button in the latest version of &lt;strong&gt;Visual Studio Code&lt;/strong&gt; at the top right corner when a &lt;strong&gt;markdown&lt;/strong&gt; file is open:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/Ei4cfx0TID-221.png 221w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Ei4cfx0TID-221.webp 221w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Ei4cfx0TID-221.jpeg&quot; alt=&quot;ss_vscode_previewbutton&quot; width=&quot;221&quot; height=&quot;86&quot;&gt;&lt;/picture&gt;&lt;br&gt;The Preview button!&lt;/p&gt;&lt;p&gt;I wonder what that might do?&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/UvrSys-44i-650.png 650w, https://danielscottraynsford.com/img/UvrSys-44i-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/UvrSys-44i-650.webp 650w, https://danielscottraynsford.com/img/UvrSys-44i-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/UvrSys-44i-650.jpeg&quot; alt=&quot;ss_vscode_preview&quot; width=&quot;960&quot; height=&quot;723&quot; srcset=&quot;https://danielscottraynsford.com/img/UvrSys-44i-650.jpeg 650w, https://danielscottraynsford.com/img/UvrSys-44i-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;br&gt;That is a great looking &lt;strong&gt;markdown&lt;/strong&gt; preview.&lt;/p&gt;&lt;p&gt;Oh, now look at that! Just what I needed. I don’t know which version of &lt;strong&gt;Visual Studio Code&lt;/strong&gt; this was added in, but it is fantastic!&lt;/p&gt;&lt;p&gt;You can also use the &lt;strong&gt;Visual Studio Code&lt;/strong&gt; split window function to &lt;strong&gt;edit&lt;/strong&gt; and &lt;strong&gt;Preview&lt;/strong&gt; at the same time:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/ItXPnpT8nL-650.png 650w, https://danielscottraynsford.com/img/ItXPnpT8nL-960.png 960w, https://danielscottraynsford.com/img/ItXPnpT8nL-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ItXPnpT8nL-650.webp 650w, https://danielscottraynsford.com/img/ItXPnpT8nL-960.webp 960w, https://danielscottraynsford.com/img/ItXPnpT8nL-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ItXPnpT8nL-650.jpeg&quot; alt=&quot;ss_vscode_splitpreview&quot; width=&quot;1400&quot; height=&quot;733&quot; srcset=&quot;https://danielscottraynsford.com/img/ItXPnpT8nL-650.jpeg 650w, https://danielscottraynsford.com/img/ItXPnpT8nL-960.jpeg 960w, https://danielscottraynsford.com/img/ItXPnpT8nL-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;br&gt;Changing the markdown automatically updates the preview - nice!&lt;/p&gt;&lt;p&gt;While we’re on the topic of &lt;strong&gt;markdown&lt;/strong&gt;, Irwin also pointed me at &lt;a href=&quot;http://markdowntutorial.com/&quot; rel=&quot;noopener&quot;&gt;this awesome markdown tutorial&lt;/a&gt; - it makes learning &lt;strong&gt;markdown&lt;/strong&gt; fun!&lt;/p&gt;&lt;p&gt;Now, &lt;strong&gt;Visual Studio&lt;/strong&gt; &lt;strong&gt;Code&lt;/strong&gt; is not on par with &lt;a href=&quot;http://www.powertheshell.com/isesteroids/&quot; rel=&quot;noopener&quot;&gt;ISE Steroids&lt;/a&gt; when it comes to editing &lt;strong&gt;PowerShell&lt;/strong&gt; files, but when you’re dealing with lots of different types of files (including PS files) it can be a real time saver. I highly recommend downloading a copy of &lt;a href=&quot;https://code.visualstudio.com/Download&quot; rel=&quot;noopener&quot;&gt;Visual Studio Code&lt;/a&gt;, installing the &lt;strong&gt;PowerShell&lt;/strong&gt; &lt;strong&gt;Extension&lt;/strong&gt; and giving it a try.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Windows Management Framework (WMF) 5.0 RTM Available</title>
      <link href="https://danielscottraynsford.com/blog/windows-management-framework-wmf-50-rtm-available/" />
      <updated>2015-12-18T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/windows-management-framework-wmf-50-rtm-available/</id>
      <content type="html">
				&lt;p&gt;I don’t know about you, but I am excited about the release to manufacture of &lt;a href=&quot;http://blogs.msdn.com/b/powershell/archive/2015/12/16/windows-management-framework-wmf-5-0-rtm-is-now-available.aspx&quot; rel=&quot;noopener&quot;&gt;Windows Management Framework (WMF) 5.0&lt;/a&gt;. It has been a long time coming and I’m hoping this release will resolve the issues I’ve been having with DSC configurations built on Windows 10 build 10586 (see &lt;a href=&quot;https://danielscottraynsford.com/blog/windows-10-build-10586-powershell-problems/&quot;&gt;this article&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;You can download the WMF 5.0 installer for &lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=50395&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Creating Professional DSC Resources - Part 4</title>
      <link href="https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-4/" />
      <updated>2015-12-18T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-4/</id>
      <content type="html">
				&lt;p&gt;The purpose of this series of articles is to try and document a few of the lessons I learned while releasing new DSC resources as well as contributing to the existing &lt;strong&gt;Microsoft Community DSC resources&lt;/strong&gt;. These articles are not intended to tell you how to write DSC resources from a programming perspective but to give you some ideas on what might be expected of a DSC resource you’re releasing to the public. For example, &lt;strong&gt;unit&lt;/strong&gt; and &lt;strong&gt;integration&lt;/strong&gt; tests (don’t worry if you aren’t familiar with those terms).&lt;/p&gt;&lt;p&gt;These articles are also not intended to tell you what you &lt;strong&gt;must&lt;/strong&gt; do to release your resource but more to document what will help your resource be easier to use and extend by other people. Some of these things are obvious for people who have come from the &lt;strong&gt;development&lt;/strong&gt; community but may be quite new to &lt;strong&gt;operations&lt;/strong&gt; people.&lt;/p&gt;&lt;p&gt;If you missed any previous articles, you can find them here:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-1/&quot;&gt;Creating Professional DSC Resources – Part 1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-2/&quot;&gt;Creating Professional DSC Resources - Part 2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-3/&quot;&gt;Creating Professional DSC Resources - Part 3&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;automated-testing&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-4/#automated-testing&quot; class=&quot;heading-anchor&quot;&gt;Automated Testing&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Automated testing&lt;/strong&gt; is something that is familiar to most &lt;strong&gt;developers&lt;/strong&gt;, but for &lt;strong&gt;operations&lt;/strong&gt; people, it is usually a new concept. However, it is one of the most important things you can add to your &lt;strong&gt;DSC Resources&lt;/strong&gt;—and most DSC resource projects won’t even accept your code contributions if they don’t contain &lt;strong&gt;automated tests&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;So, what are &lt;strong&gt;automated tests&lt;/strong&gt;? Well, they are just &lt;strong&gt;PowerShell scripts&lt;/strong&gt; that &lt;em&gt;you&lt;/em&gt; create and run that will check your &lt;strong&gt;DSC Resource&lt;/strong&gt; is working correctly. Usually, &lt;strong&gt;automated tests&lt;/strong&gt; are run on your &lt;strong&gt;DSC Resources&lt;/strong&gt; every time you commit your code—and they’ll tell you if anything has gone wrong. I could spend the next day listing reasons why &lt;strong&gt;automated testing&lt;/strong&gt; is extremely important, but that is not the purpose of this post.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;PowerShell&lt;/strong&gt; contains a great &lt;strong&gt;automated test&lt;/strong&gt; framework called &lt;a href=&quot;https://github.com/pester/Pester&quot; rel=&quot;noopener&quot;&gt;Pester&lt;/a&gt; that allows you to &lt;em&gt;describe&lt;/em&gt; your tests using special &lt;strong&gt;PowerShell&lt;/strong&gt; functions.&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;If you aren’t familiar with &lt;strong&gt;Pester&lt;/strong&gt; and &lt;strong&gt;automated testing&lt;/strong&gt;, you should get familiar with it before reading any further. &lt;a href=&quot;http://blogs.technet.com/b/heyscriptingguy/archive/2015/12/14/what-is-pester-and-why-should-i-care.aspx&quot; rel=&quot;noopener&quot;&gt;This series&lt;/a&gt; is a fantastic place to start. Even if you’re familiar with &lt;strong&gt;Pester&lt;/strong&gt;, it is a good read.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;An example of a &lt;strong&gt;Pester&lt;/strong&gt; test on a &lt;strong&gt;DSC Resource&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Describe &lt;span class=&quot;token string&quot;&gt;&#39;MSFT_xFirewall&#92;Get-TargetResource&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Context &lt;span class=&quot;token string&quot;&gt;&#39;Absent should return correctly&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        Mock &lt;span class=&quot;token function&quot;&gt;Get-NetFirewallRule&lt;/span&gt;

        It &lt;span class=&quot;token string&quot;&gt;&quot;Should return absent on firewall rule &lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$FirewallRule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$result&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-TargetResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&#39;FirewallRule&#39;&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token string&quot;&gt;&#39;FirewallRule&#39;&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Ensure &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token string&quot;&gt;&#39;Absent&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    Context &lt;span class=&quot;token string&quot;&gt;&#39;Present should return correctly&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$result&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-TargetResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$FirewallRule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name

        &lt;span class=&quot;token comment&quot;&gt;# Looping these tests&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$parameter&lt;/span&gt; in &lt;span class=&quot;token variable&quot;&gt;$ParameterList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$ParameterSource&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Invoke-Expression&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Command &lt;span class=&quot;token string&quot;&gt;&quot;`$(&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$parameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;source&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$ParameterNew&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Invoke-Expression&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Command &lt;span class=&quot;token string&quot;&gt;&quot;`&lt;span class=&quot;token variable&quot;&gt;$result&lt;/span&gt;.&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$parameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            It &lt;span class=&quot;token string&quot;&gt;&quot;should have the correct &lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$parameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; on firewall rule &lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$FirewallRule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$ParameterSource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should Be &lt;span class=&quot;token variable&quot;&gt;$ParameterNew&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above test is a&amp;nbsp;&lt;strong&gt;unit&lt;/strong&gt; test of the&amp;nbsp;&lt;strong&gt;xFirewall&lt;/strong&gt; resource in the&amp;nbsp;&lt;strong&gt;xNetworking&lt;/strong&gt; module. Don’t worry if you don’t completely understand this yet—that is the purpose of this article—although you should understand the basic structure of the &lt;strong&gt;Pester&lt;/strong&gt; test. If you don’t, you’ll definitely want to go and review &lt;a href=&quot;http://blogs.technet.com/b/heyscriptingguy/archive/2015/12/14/what-is-pester-and-why-should-i-care.aspx&quot; rel=&quot;noopener&quot;&gt;this series&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;types-of-automated-tests&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-4/#types-of-automated-tests&quot; class=&quot;heading-anchor&quot;&gt;Types of Automated Tests&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;There are two types of &lt;strong&gt;automated tests&lt;/strong&gt; you should create for your &lt;strong&gt;DSC Resources&lt;/strong&gt;:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Unit Tests&lt;/strong&gt; - These test that each function in your &lt;strong&gt;DSC Resource&lt;/strong&gt; works correctly in &lt;strong&gt;isolation&lt;/strong&gt;. This means that if you call the&amp;nbsp;&lt;strong&gt;function&lt;/strong&gt; with a set of parameters, you get the expected output.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Integration Tests&lt;/strong&gt; - These tests ensure that your &lt;strong&gt;DSC Resource&lt;/strong&gt; works in a real environment—e.g., works correctly when they are actually &lt;strong&gt;integrated&lt;/strong&gt; into a &lt;strong&gt;DSC Configuration&lt;/strong&gt; file.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;For every &lt;strong&gt;DSC Resource&lt;/strong&gt; in your &lt;strong&gt;DSC Resource Module&lt;/strong&gt;, you should ensure that there is one &lt;strong&gt;unit test&lt;/strong&gt; file and one &lt;strong&gt;integration test&lt;/strong&gt; file (although usually for integration tests, a support file is also needed, but we’ll cover this later).&lt;/p&gt;&lt;h2 id=&quot;test-folders&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-4/#test-folders&quot; class=&quot;heading-anchor&quot;&gt;Test Folders&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;You should place all tests inside a &lt;strong&gt;Tests&lt;/strong&gt; folder in the root of your &lt;strong&gt;DSC Module&lt;/strong&gt; folder:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/qgrqDuv0qO-156.png 156w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/qgrqDuv0qO-156.webp 156w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/qgrqDuv0qO-156.jpeg&quot; alt=&quot;Test Folders&quot; width=&quot;156&quot; height=&quot;191&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Unit&lt;/strong&gt; tests should be placed in a &lt;strong&gt;Unit&lt;/strong&gt; folder within&amp;nbsp;&lt;strong&gt;Tests&lt;/strong&gt;, and … I’m sure you get where I’m going here.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;In the next article, I’ll cover &lt;strong&gt;unit tests&lt;/strong&gt; for &lt;strong&gt;Set-TargetResource&lt;/strong&gt; and &lt;strong&gt;Test-TargetResource&lt;/strong&gt;, as well as unit testing any additional functions. Thanks again for reading, and I hope it is useful.&lt;/p&gt;&lt;p&gt;Further parts in this series:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-5/&quot;&gt;Creating Professional DSC Resources - Part 5&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/&quot;&gt;Creating Professional DSC Resources - Part 6&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/&quot;&gt;Creating Professional DSC Resources - Part 7&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
 			</content>
    </entry><entry>
      <title>Creating Professional DSC Resources - Part 3</title>
      <link href="https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-3/" />
      <updated>2015-12-16T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-3/</id>
      <content type="html">
				&lt;p&gt;The purpose of this series of articles is to try and document a few of the lessons I learned while releasing new DSC resources as well as contributing to the existing &lt;strong&gt;Microsoft Community DSC resources&lt;/strong&gt;. These articles are not intended to tell you how to write DSC resources from a programming perspective, but to give you some ideas on what might be expected of a DSC resource you’re releasing to the public. For example, &lt;strong&gt;unit&lt;/strong&gt; and &lt;strong&gt;integration&lt;/strong&gt; tests (don’t worry if you aren’t familiar with those terms).&lt;/p&gt;&lt;p&gt;These articles are also not intended to tell you what you &lt;strong&gt;must&lt;/strong&gt; do to release your resource, but more document what will help your resource be easier to use and extend by other people. Some of these these things are obvious for people who have come from the &lt;strong&gt;development&lt;/strong&gt; community, but may be quite new to &lt;strong&gt;operations&lt;/strong&gt; people.&lt;/p&gt;&lt;p&gt;If you missed any previous articles you can find them here:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-1/&quot;&gt;Creating Professional DSC Resources – Part 1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-2/&quot;&gt;Creating Professional DSC Resources - Part 2&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;coding-style&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-3/#coding-style&quot; class=&quot;heading-anchor&quot;&gt;Coding Style&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Everyone has there own preferences of how they like their code to look. Just take a look at all the PowerShell repositories on &lt;strong&gt;GitHub&lt;/strong&gt; and you’d see a lot of different coding styles.&lt;/p&gt;&lt;p&gt;When I refer to &lt;em&gt;coding style&lt;/em&gt;, this covers a many different things, for example:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Variable name format&lt;/li&gt;&lt;li&gt;Tab format (spaces or tabs?)&lt;/li&gt;&lt;li&gt;Function name format&lt;/li&gt;&lt;li&gt;Maximum line length&lt;/li&gt;&lt;li&gt;Comments&lt;/li&gt;&lt;li&gt;Curly braces allowed at the end of a line&lt;/li&gt;&lt;li&gt;Allow more than one blank line in a row&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you’re writing code that only you will look at and maintain, it doesn’t much matter what your &lt;em&gt;coding style&lt;/em&gt; is - as long as you can understand it 6 months later. However, if you would like other people to use and possibly&amp;nbsp; contribute and maintain your code or if you are contributing to community project, you’ll want to adopt a &lt;em&gt;standard coding style&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;The best reason for adopting a &lt;em&gt;standard coding style&lt;/em&gt; is to ensure &lt;strong&gt;consistency&lt;/strong&gt; in your code - especially across DSC resources within the same module. This makes it much easier for people to become familiar with your coding style and therefore easier for them to read and understand.&lt;/p&gt;&lt;p&gt;If you don’t have a particular &lt;em&gt;coding style&lt;/em&gt; you have adopted, Microsoft has released a simple &lt;a href=&quot;https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md&quot; rel=&quot;noopener&quot;&gt;style guidelines document&lt;/a&gt; for &lt;strong&gt;DSC Resources&lt;/strong&gt; that you can adopt quite easily.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Tip&lt;/strong&gt;: Using the Microsoft style guidelines document is a requirement for contributing code to the &lt;strong&gt;Microsoft Community DSC Resources&lt;/strong&gt;. So if you’re planning on contributing code or even entire resource modules to this project, I’d recommend you adopt the Microsoft style guidelines.&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;message-localization&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-3/#message-localization&quot; class=&quot;heading-anchor&quot;&gt;Message Localization&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Adding support for language localization to your &lt;strong&gt;DSC Resources&lt;/strong&gt; is trivial, but it is often over looked. Supporting data localization is &lt;strong&gt;much&lt;/strong&gt; &lt;strong&gt;easier&lt;/strong&gt; to add when you are creating your &lt;strong&gt;DSC Resource&lt;/strong&gt; rather than adding it later on. This doesn’t mean you have to provide the language files of course, just provide support so that someone else could contribute them should they want to.&lt;/p&gt;&lt;p&gt;To ensure language support, create a new file in the same folder as your&amp;nbsp;&lt;strong&gt;DSC Resource&lt;/strong&gt; with the same name as the&amp;nbsp;&lt;strong&gt;DSC Resource&lt;/strong&gt; but with a&amp;nbsp;&lt;strong&gt;psd1&lt;/strong&gt; extension. In that file&amp;nbsp; create a &lt;strong&gt;Data&lt;/strong&gt; section named &lt;strong&gt;LocalizedData&lt;/strong&gt; containing the messages for your default &lt;strong&gt;culture&lt;/strong&gt;. For example:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; LocalizedData
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# culture=&quot;en-US&quot;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;ConvertFrom-StringData&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;StringData @&lt;span class=&quot;token string&quot;&gt;&#39;
GettingiSCSIVirtualDiskMessage=Getting iSCSI Virtual Disk &quot;{0}&quot;.
iSCSIVirtualDiskExistsMessage=iSCSI Virtual Disk &quot;{0}&quot; exists.
iSCSIVirtualDiskDoesNotExistMessage=iSCSI Virtual Disk &quot;{0}&quot; does not exist.
SettingiSCSIVirtualDiskMessage=Setting iSCSI Virtual Disk &quot;{0}&quot;.
EnsureiSCSIVirtualDiskExistsMessage=Ensuring iSCSI Virtual Disk &quot;{0}&quot; exists.
EnsureiSCSIVirtualDiskDoesNotExistMessage=Ensuring iSCSI Virtual Disk &quot;{0}&quot; does not exist.
iSCSIVirtualDiskCreatedMessage=iSCSI Virtual Disk &quot;{0}&quot; has been created.
iSCSIVirtualDiskUpdatedMessage=iSCSI Virtual Disk &quot;{0}&quot; has been updated.
iSCSIVirtualDiskRemovedMessage=iSCSI Virtual Disk &quot;{0}&quot; has been removed.
TestingiSCSIVirtualDiskMessage=Testing iSCSI Virtual Disk &quot;{0}&quot;.
iSCSIVirtualDiskParameterNeedsUpdateMessage=iSCSI Virtual Disk &quot;{0}&quot; {1} is different. Change required.
iSCSIVirtualDiskDoesNotExistAndShouldNotMessage=iSCSI Virtual Disk &quot;{0}&quot; does not exist and should not. Change not required.
iSCSIVirtualDiskRequiresRecreateError=iSCSI Virtual Disk &quot;{0}&quot; needs to be deleted and recreated. Please perform this manually.
&#39;&lt;/span&gt;@
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Each line contains a localized message - in this case for the &lt;strong&gt;culture en-us&lt;/strong&gt;. You could of course use a different default &lt;strong&gt;culture&lt;/strong&gt; if you wanted to.&lt;/p&gt;&lt;p&gt;At the beginning of your &lt;strong&gt;DSC Resource&lt;/strong&gt; you would the following command to ensure the appropriate localization strings are imported:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Import-LocalizedData&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;BindingVariable LocalizedData &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Filename BMD_cMyNewResource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;psd1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Alternately, if you want to support &lt;strong&gt;Message Localization&lt;/strong&gt; but don’t want to have to supply your default messages in a separate file, you can place the &lt;strong&gt;LocalizedData&lt;/strong&gt; section for your default culture at the top of your &lt;strong&gt;DSC Resource&lt;/strong&gt; and exclude the &lt;strong&gt;Import-LocalizedData&lt;/strong&gt; command.&lt;/p&gt;&lt;h3 id=&quot;using-localization-messages&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-3/#using-localization-messages&quot; class=&quot;heading-anchor&quot;&gt;Using Localization Messages&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Once you’ve got the messages in, using them is extremely easy:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$LocalizedData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;iSCSIVirtualDiskExistsMessage &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token variable&quot;&gt;$Path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can of course consume the messages anyway you like, but all of your localized messages are just properties of the &lt;strong&gt;LocalizedData&lt;/strong&gt; object.&lt;/p&gt;&lt;h3 id=&quot;other-localization-files&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-3/#other-localization-files&quot; class=&quot;heading-anchor&quot;&gt;Other Localization Files&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;You put &lt;strong&gt;LocalizedData&lt;/strong&gt; for other languages in separate PowerShell files in sub-folders named after the culture the file contains. For example, you might create a sub-folder called &lt;strong&gt;en-uk&lt;/strong&gt; and place a file called &lt;strong&gt;BMD_cMyNewResource&lt;/strong&gt;**.psd1** containing the &lt;strong&gt;en-uk&lt;/strong&gt; messages.&lt;/p&gt;&lt;h2 id=&quot;example-dsc-configuration-files&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-3/#example-dsc-configuration-files&quot; class=&quot;heading-anchor&quot;&gt;Example DSC Configuration Files&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Another element of any easy to use &lt;strong&gt;DSC Resource&lt;/strong&gt; are example &lt;strong&gt;DSC Configuration&lt;/strong&gt; files. These can usually be found in the &lt;strong&gt;Examples&lt;/strong&gt; folder in the root of the &lt;strong&gt;DSC Module&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/HmlaTV9ZRv-590.png 590w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/HmlaTV9ZRv-590.webp 590w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/HmlaTV9ZRv-590.jpeg&quot; alt=&quot;ss_dsc_examplesfolder&quot; width=&quot;590&quot; height=&quot;172&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;There should usually be a number of different &lt;strong&gt;DSC Configuration&lt;/strong&gt; files in this folder, showing common scenarios for using your &lt;strong&gt;DSC Resources&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/6AiLnh3fDK-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/6AiLnh3fDK-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/6AiLnh3fDK-650.jpeg&quot; alt=&quot;ss_dsc_examples&quot; width=&quot;650&quot; height=&quot;288&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;file name&lt;/strong&gt; of any &lt;strong&gt;example&lt;/strong&gt; files should be prefixed with &lt;strong&gt;Sample&lt;/strong&gt; or &lt;strong&gt;Example&lt;/strong&gt; so that they can be easily identified and differentiated from types of &lt;strong&gt;DSC Module&lt;/strong&gt; files. The summary of the purpose of the configuration should also be included in the &lt;strong&gt;file name&lt;/strong&gt;. This is fairly obvious I realize, but I have seen public &lt;strong&gt;DSC Resources&lt;/strong&gt; named Example_1, Example_2, Example_3 etc - which reduces usability of the examples.&lt;/p&gt;&lt;p&gt;Each &lt;strong&gt;example&lt;/strong&gt; should contain a &lt;strong&gt;DSC Configuration&lt;/strong&gt; in a form that it could be used without any modification to actually test the resource. This might include installing prerequisite Windows Features or starting services etc. This allows a potential user to &lt;strong&gt;test drive&lt;/strong&gt; the resource without investing a whole lot of work trying to figure out how to use it.&lt;/p&gt;&lt;p&gt;For example:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;configuration Sample_ciSCSIInitiator
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;Param&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
         &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$NodeName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;LocalHost&#39;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Module ciSCSI

    Node &lt;span class=&quot;token variable&quot;&gt;$NodeName&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        Service iSCSIService 
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
            Name = &lt;span class=&quot;token string&quot;&gt;&#39;MSiSCSI&#39;&lt;/span&gt;
            StartupType = &lt;span class=&quot;token string&quot;&gt;&#39;Automatic&#39;&lt;/span&gt;
            State = &lt;span class=&quot;token string&quot;&gt;&#39;Running&#39;&lt;/span&gt;  
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        ciSCSITargetPortal iSCSITargetPortal
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Ensure = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
            TargetPortalAddress = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.128.10&#39;&lt;/span&gt; 
            InitiatorPortalAddress = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.128.20&#39;&lt;/span&gt;
            DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[WindowsFeature]iSCSIService&quot;&lt;/span&gt; 
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# End of ciSCSITargetPortal Resource&lt;/span&gt;

        ciSCSITarget iSCSITarget
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Ensure = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
            NodeAddress = &lt;span class=&quot;token string&quot;&gt;&#39;iqn.1991-05.com.microsoft:fileserver01-cluster-target&#39;&lt;/span&gt;
            TargetPortalAddress = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.128.10&#39;&lt;/span&gt;
            InitiatorPortalAddress = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.128.20&#39;&lt;/span&gt; 
            IsPersistent = &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt; 
            DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[ciSCSITargetPortal]iSCSITargetPortal&quot;&lt;/span&gt; 
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# End of ciSCSITarget Resource&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# End of Node&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# End of Configuration&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above &lt;strong&gt;DSC Resource&lt;/strong&gt; example will ensure the &lt;strong&gt;MSiSCSI&lt;/strong&gt; service is running before configuring an &lt;strong&gt;iSCSI Initiator&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Make the &lt;strong&gt;name&lt;/strong&gt; of the &lt;strong&gt;configuration&lt;/strong&gt; the same as the sample configuration file (without the extension of course).&lt;/em&gt;&lt;/p&gt;&lt;p&gt;It is also a great idea to copy the content of any &lt;strong&gt;example&lt;/strong&gt; &lt;strong&gt;DSC Configuration&lt;/strong&gt; files into the &lt;strong&gt;Examples&lt;/strong&gt; section of the &lt;strong&gt;&lt;a href=&quot;http://Readme.md&quot; rel=&quot;noopener&quot;&gt;Readme.md&lt;/a&gt;&lt;/strong&gt; of your &lt;strong&gt;DSC Resource Module&lt;/strong&gt; along with a brief description of what the configuration will produce. You’ll find that all &lt;strong&gt;Microsoft Community DSC Resources&lt;/strong&gt; do this.&lt;/p&gt;&lt;h2 id=&quot;in-the-next-article&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-3/#in-the-next-article&quot; class=&quot;heading-anchor&quot;&gt;In The Next Article&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I intended on covering creating &lt;strong&gt;unit&lt;/strong&gt; and &lt;strong&gt;integration&lt;/strong&gt; &lt;strong&gt;tests&lt;/strong&gt; in this article, but as that is by far the most involved part of creating a &lt;strong&gt;community DSC resource&lt;/strong&gt; I’ve decided I’ll dedicate an entire part to each. So, rest assured the next ones will contain this very important component of any public &lt;strong&gt;DSC Resource&lt;/strong&gt;. Thank you for reading this far and I hope you’re finding it useful.&lt;/p&gt;&lt;p&gt;Further parts in this series:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-4/&quot;&gt;Creating Professional DSC Resources - Part 4&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-5/&quot;&gt;Creating Professional DSC Resources - Part 5&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/&quot;&gt;Creating Professional DSC Resources - Part 6&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
 			</content>
    </entry><entry>
      <title>Creating Professional DSC Resources - Part 2</title>
      <link href="https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-2/" />
      <updated>2015-12-14T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-2/</id>
      <content type="html">
				&lt;p&gt;The purpose of this series of articles is to try and document a few of the lessons I learned while releasing new DSC resources as well as contributing to the existing &lt;strong&gt;Microsoft Community DSC resources&lt;/strong&gt;. These articles are not intended to tell you how to write DSC resources from a programming perspective, but to give you some ideas on what might be expected of a DSC resource you’re releasing to the public. For example, &lt;strong&gt;unit&lt;/strong&gt; and &lt;strong&gt;integration&lt;/strong&gt; tests (don’t worry if you aren’t familiar with those terms).&lt;/p&gt;&lt;p&gt;These articles are also not intended to tell you what you &lt;strong&gt;must&lt;/strong&gt; do to release your resource, but more document what will help your resource be easier to use and extend by other people. Some of these these things are obvious for people who have come from the &lt;strong&gt;development&lt;/strong&gt; community, but may be quite new to &lt;strong&gt;operations&lt;/strong&gt; people.&lt;/p&gt;&lt;p&gt;If you missed any previous articles you can find them here:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-1/&quot;&gt;Creating Professional DSC Resources - Part 1&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;before-you-start-coding&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-2/#before-you-start-coding&quot; class=&quot;heading-anchor&quot;&gt;Before You Start Coding&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;So, you have an idea or need for a set of super new&amp;nbsp;&lt;strong&gt;DSC Resources&lt;/strong&gt;. Before you write a single line of code you should first go and &lt;a href=&quot;https://github.com/PowerShell/DscResources&quot; rel=&quot;noopener&quot;&gt;take a look at the documentation&lt;/a&gt; provided by the &lt;strong&gt;DSC Community&lt;/strong&gt;. These guys (many of them from Microsoft) have been doing this stuff for a while and they’ve come up with a set of best practices and instructions on how to get started.&lt;/p&gt;&lt;p&gt;The above &lt;strong&gt;GitHub&lt;/strong&gt; repository should be your first port of call and for DSC creation and it is worth keeping an eye on this &lt;strong&gt;repository&lt;/strong&gt; by &lt;strong&gt;watching&lt;/strong&gt; it:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/uUjjlNuBd--650.png 650w, https://danielscottraynsford.com/img/uUjjlNuBd--960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/uUjjlNuBd--650.webp 650w, https://danielscottraynsford.com/img/uUjjlNuBd--960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/uUjjlNuBd--650.jpeg&quot; alt=&quot;ss_github_watch&quot; width=&quot;960&quot; height=&quot;147&quot; srcset=&quot;https://danielscottraynsford.com/img/uUjjlNuBd--650.jpeg 650w, https://danielscottraynsford.com/img/uUjjlNuBd--960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This will cause you to be notified whenever any changes to this &lt;strong&gt;repository&lt;/strong&gt; are made (which isn’t that often). So if the best practices are updated you’ll be kept in the loop!&lt;/p&gt;&lt;p&gt;This &lt;strong&gt;repository&lt;/strong&gt; also contains some &lt;a href=&quot;https://github.com/PowerShell/DscResources/tree/master/DscResource.Template&quot; rel=&quot;noopener&quot;&gt;template files&lt;/a&gt; you can use to create a new &lt;strong&gt;DSC Resource module&lt;/strong&gt;.&lt;/p&gt;&lt;h2 id=&quot;creating-the-resource-module&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-2/#creating-the-resource-module&quot; class=&quot;heading-anchor&quot;&gt;Creating the Resource Module&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Sure, there is no reason why you can’t just jump straight in and knock out a &lt;em&gt;PSD1&lt;/em&gt; file and some &lt;em&gt;PSM1/MOF&lt;/em&gt; files and be done with it. But creating a resource that other people can easily use, usually requires a few other files.&lt;/p&gt;&lt;p&gt;First, you need to decide on the name for the DSC Resource module folder. This is usually simple enough, but if you think your module may contain more than one resource it is worth naming it with a more generic name. For example, if you were creating a&amp;nbsp;&lt;strong&gt;DSC Resource&lt;/strong&gt; &lt;strong&gt;module&lt;/strong&gt; that will contain resources for configuring&amp;nbsp;&lt;strong&gt;iSCSI Targets&lt;/strong&gt; and&amp;nbsp;&lt;strong&gt;iSCSI Initiators&lt;/strong&gt; you might name your folder &lt;strong&gt;ciSCSI&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Tip:&lt;/strong&gt;&lt;/em&gt; &lt;em&gt;Your resource folder should begin with a lower case&lt;/em&gt; &lt;em&gt;&lt;strong&gt;c.&lt;/strong&gt;&lt;/em&gt; &lt;em&gt;This indicates it is a &lt;strong&gt;Community DSC resource&lt;/strong&gt;. This is not a requirement, but it tells people that the resource was created by the community (in this case you). &lt;strong&gt;DSC Resource modules&lt;/strong&gt; starting with an &lt;strong&gt;x&lt;/strong&gt; indicate that this is a &lt;strong&gt;Microsoft Community DSC resource&lt;/strong&gt; and maintained by the &lt;strong&gt;PowerShell&lt;/strong&gt; team as well as the community, but are not built in to &lt;strong&gt;DSC&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Once you’ve created the folder to store your new &lt;strong&gt;DSC Resource module&lt;/strong&gt;, you should make a copy of all the files in the&amp;nbsp;&lt;strong&gt;GitHub&lt;/strong&gt; repository folder found &lt;a href=&quot;https://github.com/PowerShell/DscResources/tree/master/DscResource.Template&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; to the root of your new &lt;strong&gt;DSC&lt;/strong&gt; &lt;strong&gt;Resource&lt;/strong&gt; folder:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/WkbJJktUa0-650.png 650w, https://danielscottraynsford.com/img/WkbJJktUa0-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/WkbJJktUa0-650.webp 650w, https://danielscottraynsford.com/img/WkbJJktUa0-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/WkbJJktUa0-650.jpeg&quot; alt=&quot;ss_dsc_newresourcefolder&quot; width=&quot;960&quot; height=&quot;602&quot; srcset=&quot;https://danielscottraynsford.com/img/WkbJJktUa0-650.jpeg 650w, https://danielscottraynsford.com/img/WkbJJktUa0-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;The easiest way to get a copy of these files is to use &lt;strong&gt;Git&lt;/strong&gt; to&amp;nbsp;&lt;em&gt;clone&lt;/em&gt; the &lt;strong&gt;DSCResource&lt;/strong&gt; repository on your computer and then copy the files from the&amp;nbsp;&lt;strong&gt;DSCResource.Template&lt;/strong&gt; folder to your new DSC module folder:&lt;/p&gt;&lt;h2 id=&quot;the-dscresourcetemplate-files&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-2/#the-dscresourcetemplate-files&quot; class=&quot;heading-anchor&quot;&gt;The DSCResource.Template Files&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;At the time of writing this the &lt;strong&gt;DSCResource.Template&lt;/strong&gt; folder only contains two files:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://Readme.md&quot; rel=&quot;noopener&quot;&gt;Readme.md&lt;/a&gt;&lt;/strong&gt; - tells people how to use your &lt;strong&gt;DSC Resource&lt;/strong&gt; as well as containing usage examples and &lt;strong&gt;Version&lt;/strong&gt; information. You should fill this in as soon as possible and keep it up-to-date everytime you change your &lt;strong&gt;DSC Resource&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;AppVeyor.yml&lt;/strong&gt; - this is a file that configures the &lt;strong&gt;AppVeyor&lt;/strong&gt; &lt;strong&gt;Continuous Integration (CI)&lt;/strong&gt; for your &lt;strong&gt;DSC Resource Module&lt;/strong&gt;. I will cover &lt;strong&gt;AppVeyor CI&lt;/strong&gt; later in the series. At the moment don’t worry about this file, just copy it to your module folder and leave it alone.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;markdown&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-2/#markdown&quot; class=&quot;heading-anchor&quot;&gt;Markdown&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The &lt;strong&gt;&lt;a href=&quot;http://readme.md&quot; rel=&quot;noopener&quot;&gt;readme.md&lt;/a&gt;&lt;/strong&gt; file (like most other files with an &lt;strong&gt;.md&lt;/strong&gt; file extension) is text file in the &lt;strong&gt;Markdown&lt;/strong&gt; format. Because this file is being made available up on &lt;strong&gt;GitHub&lt;/strong&gt; you can use &lt;strong&gt;&lt;a href=&quot;https://help.github.com/articles/github-flavored-markdown/&quot; rel=&quot;noopener&quot;&gt;GitHub Flavored Markdown&lt;/a&gt;.&lt;/strong&gt; &lt;strong&gt;Markdown&lt;/strong&gt; is really easy to create straight in your editor of choice and you’ll find it in use all over &lt;strong&gt;GitHub&lt;/strong&gt;, so it is a good idea to get somewhat familiar with it (it should only take you about 2 minutes to get a handle on it).&lt;/p&gt;&lt;p&gt;If you want an example of how your &lt;strong&gt;&lt;a href=&quot;http://Readme.md&quot; rel=&quot;noopener&quot;&gt;Readme.md&lt;/a&gt;&lt;/strong&gt; file might be constructed, &lt;a href=&quot;https://github.com/PowerShell/xNetworking/blob/dev/README.md&quot; rel=&quot;noopener&quot;&gt;have a look at this example&lt;/a&gt;. Of course you are completely free to format it any way you like, but keeping to a standard makes it easy for users to know what they can expect.&lt;/p&gt;&lt;h2 id=&quot;in-the-next-article&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-2/#in-the-next-article&quot; class=&quot;heading-anchor&quot;&gt;In The Next Article&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As I’m trying to keep these parts short I’ll break for today and continue tomorrow. In the next part I intend to cover code guidelines and examples, with &lt;strong&gt;unit&lt;/strong&gt; and &lt;strong&gt;integration&lt;/strong&gt; testing to follow. I hope you have found this interesting!&lt;/p&gt;&lt;p&gt;Further parts in this series:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-3/&quot;&gt;Creating Professional DSC Resources - Part 3&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-4/&quot;&gt;Creating Professional DSC Resources - Part 4&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-5/&quot;&gt;Creating Professional DSC Resources - Part 5&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/&quot;&gt;Creating Professional DSC Resources - Part 6&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/&quot;&gt;Creating Professional DSC Resources - Part 7&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
 			</content>
    </entry><entry>
      <title>Creating Professional DSC Resources -Part 1</title>
      <link href="https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-1/" />
      <updated>2015-12-14T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-1/</id>
      <content type="html">
				&lt;h2 id=&quot;introduction&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-1/#introduction&quot; class=&quot;heading-anchor&quot;&gt;Introduction&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Writing &lt;strong&gt;desired state configuration (DSC) resources&lt;/strong&gt; can be a little bit tricky at first if you’ve not come from a programming background. But once you’ve written one or two, you’ll quickly find yourself churning them out like &lt;a href=&quot;http://imgur.com/vtX5yJh&quot; rel=&quot;noopener&quot;&gt;Disney releases Star Wars branded rubbish&lt;/a&gt;. That is how it went for me.&lt;/p&gt;&lt;p&gt;However, I quickly found that there is a big difference between writing a simple resource for your own consumption and releasing a &lt;strong&gt;community&lt;/strong&gt; &lt;strong&gt;DSC resource&lt;/strong&gt; that may be used and abused by tens, hundreds or even thousands of people. After I decided to release my first resource (&lt;a href=&quot;https://github.com/PlagueHO/cWSMan&quot; rel=&quot;noopener&quot;&gt;this one&lt;/a&gt;) to the public, I quickly found that it really wasn’t measuring up to what was being released by &lt;a href=&quot;https://github.com/PowerShell&quot; rel=&quot;noopener&quot;&gt;Microsoft&lt;/a&gt; and other folk in the community. So I spent about 6 months releasing and re-releasing my resources as I got to know the best practices.&lt;/p&gt;&lt;p&gt;So that gets us to the purpose of this post: to try and document the lessons I learned over the last year creating my own DSC resources and contributing to the Microsoft Community DSC resources. Hopefully by doing this it might save someone else a bit of time and avoid the joys or re-writing your resources.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Tip:&lt;/strong&gt; The &lt;strong&gt;best practices&lt;/strong&gt; for creating DSC resources change quite often as better ways of doing things are discovered it is useful to keep and eye on some of the more active DSC resources (like &lt;a href=&quot;https://github.com/PowerShell/xNetworking&quot; rel=&quot;noopener&quot;&gt;this one&lt;/a&gt;) in the &lt;strong&gt;Microsoft Community DSC resources&lt;/strong&gt;. Even since I started contributing the processes and best practices have changed quite a lot.&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;where-to-start&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-1/#where-to-start&quot; class=&quot;heading-anchor&quot;&gt;Where to Start&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If have no idea what &lt;strong&gt;Desired State Configuration (DSC)&lt;/strong&gt; is, then &lt;a href=&quot;http://blogs.technet.com/b/privatecloud/archive/2013/08/30/introducing-powershell-desired-state-configuration-dsc.aspx&quot; rel=&quot;noopener&quot;&gt;this&lt;/a&gt; is a good place to start.&lt;/p&gt;&lt;p&gt;If you’ve &lt;em&gt;not&lt;/em&gt; created a &lt;strong&gt;custom DSC resource&lt;/strong&gt; before, then you should first get to know the basic process before reading this guide. There are many &lt;a href=&quot;http://powershell.org/wp/2014/03/13/building-desired-state-configuration-custom-resources/&quot; rel=&quot;noopener&quot;&gt;really great tutorials on creating DSC resources&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;If you have written a resource or two or are familiar with the process, but you want to know how to go about releasing your &lt;strong&gt;custom DSC resource&lt;/strong&gt; to the community then this post might interest you. So read on!&lt;/p&gt;&lt;h2 id=&quot;does-it-already-exist&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-1/#does-it-already-exist&quot; class=&quot;heading-anchor&quot;&gt;Does it Already Exist?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Before even getting started writing a &lt;strong&gt;custom DSC Resource&lt;/strong&gt; it is a good idea to see if it already exists. It might not be included in the standard DS resources or in the &lt;strong&gt;Microsoft Community DSC Resources&lt;/strong&gt;, but someone else may have already created one that will work for you. So why re-invent the wheel?&lt;/p&gt;&lt;p&gt;The best places to look for DSC resources is to search on &lt;a href=&quot;https://www.powershellgallery.com/PSModule?q=DSC&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt; (you’ll find the &lt;strong&gt;Microsoft Community DSC Resources&lt;/strong&gt; here as well). If you can’t find anything that looks like it’ll work for you, then you could also do a search on &lt;a href=&quot;https://github.com/search?utf8=%E2%9C%93&amp;amp;q=DSC+language%3APowerShell+language%3APowerShell&amp;amp;type=Repositories&amp;amp;ref=advsearch&amp;amp;l=PowerShell&amp;amp;l=PowerShell&quot; rel=&quot;noopener&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;If you have &lt;strong&gt;WMF 5.0&lt;/strong&gt; installed (or have installed the &lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=49186&quot; rel=&quot;noopener&quot;&gt;PowerShell Package Modules&lt;/a&gt; installed) then you can search the &lt;strong&gt;PowerShell Gallery&lt;/strong&gt; for DSC resources using &lt;strong&gt;PowerShell&lt;/strong&gt; cmdlets:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Find-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Tag DSC&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/K2ppZfDSyi-650.png 650w, https://danielscottraynsford.com/img/K2ppZfDSyi-859.png 859w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/K2ppZfDSyi-650.webp 650w, https://danielscottraynsford.com/img/K2ppZfDSyi-859.webp 859w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/K2ppZfDSyi-650.jpeg&quot; alt=&quot;ss_powershell_findmoduletagdsc&quot; width=&quot;859&quot; height=&quot;634&quot; srcset=&quot;https://danielscottraynsford.com/img/K2ppZfDSyi-650.jpeg 650w, https://danielscottraynsford.com/img/K2ppZfDSyi-859.jpeg 859w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;This will only find modules tagged with &lt;strong&gt;DSC&lt;/strong&gt;. Some resources may not be tagged with this so they will not appear in this search. Therefore you may need to tweak the search.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;If you find a resource you want to examine, you can install it using:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name cFSRM&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or if you’re not sure you want to trust it you can use the &lt;strong&gt;save-module&lt;/strong&gt; cmdlet to save a copy to a folder so you can open and examine the resource code:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Save-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name cFSRM &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path c:&#92;temp&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you find the DSC resource module else where you’ll need to download and install it manually - the same way install any other module.&lt;/p&gt;&lt;h2 id=&quot;modifying-an-existing-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-1/#modifying-an-existing-resource&quot; class=&quot;heading-anchor&quot;&gt;Modifying an Existing Resource&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you find something that is close to what you need, but is missing some parameter or functionality, rather than starting a whole new resource you could ask the maintainer if they would be able to add the feature for you. Or even better, you could ask the maintainer if they’d object to you making the change and submitting it back to them via a &lt;strong&gt;Pull Request&lt;/strong&gt;. Even if they’re not interested including your change, at least you can modify their resource adding the functionality you need - which will save you a lot of time**.** But in my experience, most maintainers are grateful for the assistance and welcome new features (as long as they go along with the resources overall design).&lt;/p&gt;&lt;p&gt;In fact, I’ve learned more from helping out on the community resources than from writing my own. If you have some spare time and want to help the community out, I’d strongly recommend helping out on the &lt;a href=&quot;https://github.com/PowerShell&quot; rel=&quot;noopener&quot;&gt;Microsoft Community DSC Resources&lt;/a&gt;. Just head over there, take a look at a resource you’re interested in then have a look at the &lt;strong&gt;issues&lt;/strong&gt; for the resource and you’re bound to find a request or two for additional features. If you don’t find a request for a new feature but you see that one is missing that you’d like to see implemented, raise an issue yourself and offer to create it.&lt;/p&gt;&lt;p&gt;At the time I wrote this post there were some big holes in the functionality of most of the existing DSC community resources. The &lt;a href=&quot;https://github.com/PowerShell/xDnsServer&quot; rel=&quot;noopener&quot;&gt;xDNSServer&lt;/a&gt; resource is a prime example.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Tip:&lt;/strong&gt; one of the best ways to learn how to write professional quality resources is to look at some of the &lt;strong&gt;Microsoft Community DSC Resources&lt;/strong&gt;. I’d strongly recommend the &lt;a href=&quot;https://github.com/PowerShell/xNetworking&quot; rel=&quot;noopener&quot;&gt;xNetworking&lt;/a&gt; resource (no, not because I contributed to it a bit) because it is very active and when best practices are updated this tends to be one of the first resources to get updated.&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;source-control&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-1/#source-control&quot; class=&quot;heading-anchor&quot;&gt;Source Control&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;So you’ve decided you’re going to write a new DSC resource (or modify an existing one). First thing you’ll need to be a little bit familiar with is &lt;strong&gt;Source Control&lt;/strong&gt;. This used to be mainly the realm of developers, but it should now be a key tool in the &lt;strong&gt;Operations&lt;/strong&gt; toolbox. I’m going to assume that you are a little bit familiar with &lt;strong&gt;Git&lt;/strong&gt; and &lt;strong&gt;GitHub&lt;/strong&gt; for this series.&lt;/p&gt;&lt;p&gt;If you’re not familiar with using &lt;strong&gt;Git&lt;/strong&gt; and &lt;strong&gt;GitHub&lt;/strong&gt;, &lt;a href=&quot;https://github.com/PowerShell/DscResources/blob/master/GettingStartedWithGitHub.md&quot; rel=&quot;noopener&quot;&gt;this&lt;/a&gt; is the article you should read. It is written by the &lt;strong&gt;DSC Community&lt;/strong&gt; and covers the basics, all from a &lt;strong&gt;DSC&lt;/strong&gt; perspective. Even if you are familiar with &lt;strong&gt;Git&lt;/strong&gt; and &lt;strong&gt;GitHub&lt;/strong&gt; but haven’t used it for &lt;strong&gt;DSC resources&lt;/strong&gt;, this document is worth a read.&lt;/p&gt;&lt;p&gt;Here is a summary of the &lt;strong&gt;Source Control&lt;/strong&gt; things you should already know/have done:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;You should have a &lt;a href=&quot;https://github.com/&quot; rel=&quot;noopener&quot;&gt;GitHub&lt;/a&gt; account (it’s free). This is the most likely place you’ll be storing your DSC Resources, and you’ll find the other community ones here too.&lt;/li&gt;&lt;li&gt;You should have downloaded and installed the &lt;a href=&quot;https://git-scm.com/downloads&quot; rel=&quot;noopener&quot;&gt;Git&lt;/a&gt; client, &lt;a href=&quot;https://desktop.github.com/&quot; rel=&quot;noopener&quot;&gt;GitHub Desktop&lt;/a&gt; client or be using a tool like &lt;a href=&quot;https://code.visualstudio.com/Download&quot; rel=&quot;noopener&quot;&gt;Microsoft Code&lt;/a&gt; that has a&amp;nbsp;&lt;strong&gt;Git&lt;/strong&gt; client built in.&lt;/li&gt;&lt;li&gt;Ideally you should be familiar with what &lt;strong&gt;Forks,&amp;nbsp;Branches&lt;/strong&gt; and a &lt;strong&gt;Pull Requests&lt;/strong&gt; are. There are some pretty &lt;a href=&quot;http://rogerdudler.github.io/git-guide/&quot; rel=&quot;noopener&quot;&gt;amazing guides&lt;/a&gt; out there that will tell you everything you need to know in about 10 minutes.&lt;/li&gt;&lt;li&gt;Decided if you’re going to create a &lt;strong&gt;new&lt;/strong&gt; resource or &lt;strong&gt;fork&lt;/strong&gt; someone elses and modify it.&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;gitter-asking-questions&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-1/#gitter-asking-questions&quot; class=&quot;heading-anchor&quot;&gt;Gitter: Asking Questions&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Something I didn’t find out till many months after I started to contribute was that there is a &lt;a href=&quot;https://gitter.im/PowerShell/DscResources&quot; rel=&quot;noopener&quot;&gt;Gitter chat channel&lt;/a&gt; where you can chat directly with some of the many of the people who contribute to the &lt;strong&gt;Microsoft Community DSC Resources&lt;/strong&gt;. It is a great place to lurk and just see what is going on. If you want to contribute to a resource but you’re not sure what, if you ask, I’m sure you’ll receive lots of suggestions as to what needs some work. You’ll learn a lot just watching the discussion and if you need some help it’s a great place to ask.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Tip:&lt;/strong&gt; If you have a &lt;strong&gt;GitHub&lt;/strong&gt; account, you can sign in to &lt;strong&gt;Gitter&lt;/strong&gt; with it.&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;what-next&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-1/#what-next&quot; class=&quot;heading-anchor&quot;&gt;What Next?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;So, that is pretty the introduction over with. In the next article in this series I’m going to cover what you should do when you’re going to start a &lt;strong&gt;DSC Resource&lt;/strong&gt; from scratch - one that you intend on releasing to the community. The final part of the series I’ll cover adding features to an existing &lt;strong&gt;Microsoft Community DSC Resource&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Hopefully this is of interest to a few out there and it doesn’t scare off any potential DSC contributors. It really is a great feeling to contribute back to the community.&lt;/p&gt;&lt;p&gt;Further parts in this series:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-2/&quot;&gt;Creating Professional DSC Resources - Part 2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-3/&quot;&gt;Creating Professional DSC Resources - Part 3&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-4/&quot;&gt;Creating Professional DSC Resources - Part 4&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-5/&quot;&gt;Creating Professional DSC Resources - Part 5&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-6/&quot;&gt;Creating Professional DSC Resources - Part 6&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-professional-dsc-resources-part-7/&quot;&gt;Creating Professional DSC Resources - Part 7&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;
 			</content>
    </entry><entry>
      <title>File Server Resource Manager DSC Resource</title>
      <link href="https://danielscottraynsford.com/blog/file-server-resource-manager-dsc-resource/" />
      <updated>2015-12-10T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/file-server-resource-manager-dsc-resource/</id>
      <content type="html">
				&lt;h2 id=&quot;with-our-powers-combined-we-are-captain-fsrm&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-dsc-resource/#with-our-powers-combined-we-are-captain-fsrm&quot; class=&quot;heading-anchor&quot;&gt;With Our Powers Combined, We Are Captain FSRM&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I’ve been recently working on combining my three &lt;strong&gt;File Server Resource Manager DSC Resources&lt;/strong&gt; into a single module. This made more sense and will make it a lot easier to maintain.&lt;/p&gt;&lt;h2 id=&quot;integration-testing&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-dsc-resource/#integration-testing&quot; class=&quot;heading-anchor&quot;&gt;Integration Testing&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;At the same time as combining these resources, I also added &lt;strong&gt;integration tests&lt;/strong&gt; (based on the ones that were added recently to the &lt;a href=&quot;https://github.com/PowerShell/xNetworking/blob/dev/Tests/Integration/MSFT_xFirewall.Integration.Tests.ps1&quot; rel=&quot;noopener&quot;&gt;Microsoft xNetworking&lt;/a&gt; resource). This identified a number of bugs that had previously been overlooked. If you’re in the DSC resource writing game (or just starting), I strongly recommend adding &lt;strong&gt;integration tests&lt;/strong&gt; early on—it’ll definitely save you a lot of grief and just make life more enjoyable for you and everyone who uses your resource. I intend on writing an introduction article to &lt;strong&gt;unit and integration testing DSC resources&lt;/strong&gt; over the next month—so keep an eye out for it if you’re interested.&lt;/p&gt;&lt;p&gt;The new &lt;strong&gt;File Server Resource Manager (cFSRM)&lt;/strong&gt; resource replaces these old resources which have now been deprecated:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/cFSRMFileScreens-DSC-402a7f85&quot; rel=&quot;noopener&quot;&gt;cFSRMQuotas DSC Resource - configure FSRM Quotas, Quota Templates, and Auto Quotas&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/cFSRMFileScreens-DSC-402a7f85&quot; rel=&quot;noopener&quot;&gt;cFSRMFileScreens DSC Resource - configure FSRM File Screens &amp;amp; Templates&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/cFSRMClassifications-DSC-8ed89153&quot; rel=&quot;noopener&quot;&gt;cFSRMClassifications DSC Resource - configure FSRM File Classifications&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;So, if you’re using any of the above resources, you should update your &lt;strong&gt;DSC Configuration&lt;/strong&gt; files to use the new &lt;strong&gt;cFSRM&lt;/strong&gt; one. The resources are completely compatible with the old ones, so you should just need to update the &lt;strong&gt;Import-DSCModule&lt;/strong&gt; cmdlet in any configuration files.&lt;/p&gt;&lt;h2 id=&quot;installing-the-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-dsc-resource/#installing-the-resource&quot; class=&quot;heading-anchor&quot;&gt;Installing the Resource&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;You can find the new &lt;strong&gt;cFSRM&lt;/strong&gt; resource on &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/cFSRM-DSC-Resource-58c7e57f&quot; rel=&quot;noopener&quot;&gt;Microsoft Script Center&lt;/a&gt; or on the &lt;a href=&quot;https://www.powershellgallery.com/packages/cFSRM/&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;For those of you using &lt;strong&gt;Windows Management Framework 5.0&lt;/strong&gt; (or have the &lt;strong&gt;PowerShellGet&lt;/strong&gt; module installed), you can just use the command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name cFSRM&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;using-the-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-dsc-resource/#using-the-resource&quot; class=&quot;heading-anchor&quot;&gt;Using the Resource&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Rather than go into detail on using this resource, you can find the full documentation and usage examples &lt;a href=&quot;https://github.com/PlagueHO/cFSRM&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;If you need some additional guidance or other specific examples, please feel free to let me know and I’ll do my best to help you out.&lt;/p&gt;&lt;h2 id=&quot;feedback&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-dsc-resource/#feedback&quot; class=&quot;heading-anchor&quot;&gt;Feedback&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you’re interested in contributing to this resource, providing feedback, raising issues, or requesting features, please feel free (anything is appreciated). You’ll find the resource GitHub repository &lt;a href=&quot;https://github.com/PlagueHO/cFSRM&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; where you can fork, issue pull requests, and raise issues/feature requests.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Get the BIOS GUID of a Hyper-V VM</title>
      <link href="https://danielscottraynsford.com/blog/get-the-bios-guid-of-a-hyper-v-vm/" />
      <updated>2015-11-29T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/get-the-bios-guid-of-a-hyper-v-vm/</id>
      <content type="html">
				&lt;p&gt;I’ve just spent the last few hours looking into how I can get the BIOS GUID from a Hyper-V VM from inside the Host OS. I needed this so I could use it to pre-stage devices in Windows Deployment Services. I could have used the MAC address of course, but I decided I wanted to use the &lt;strong&gt;BIOS GUID&lt;/strong&gt; instead.&lt;/p&gt;&lt;p&gt;So after a fair bit of hunting all I could turn up was an older &lt;a href=&quot;http://blogs.technet.com/b/m2/archive/2008/07/04/how-to-get-the-bios-guid-from-a-hyper-v-vm.aspx&quot; rel=&quot;noopener&quot;&gt;VBS script&lt;/a&gt;. I decided this wasn’t ideal and so went about investigating how I might do this in PowerShell (this is a PowerShell blog mainly after all). Well after a few minutes I came up with this (rather long) command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$VMName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;My VM&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-CimInstance&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Namespace Root&#92;Virtualization&#92;V2 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ClassName Msvm_VirtualSystemSettingData &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Filter&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ElementName = &#39;&lt;span class=&quot;token variable&quot;&gt;$VMName&lt;/span&gt;&#39;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;BiosGUID&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It uses WMI/CIM, but does seem to work nicely (don’t forget to set the name of the VM):&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/3tccvWkMYT-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/3tccvWkMYT-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/3tccvWkMYT-650.jpeg&quot; alt=&quot;ss_ps_getbiosguid&quot; width=&quot;650&quot; height=&quot;111&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Good night!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Nano Server TP4</title>
      <link href="https://danielscottraynsford.com/blog/nano-server-tp4/" />
      <updated>2015-11-20T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/nano-server-tp4/</id>
      <content type="html">
				&lt;p&gt;Just a quick one for Friday. After downloading the new Windows Server 2016 TP4 ISO, I quickly fired up my &lt;strong&gt;New-NanoServerVHD&lt;/strong&gt; script to see how it went. Unfortunately, I ran straight into a bug in the &lt;strong&gt;Convert-WindowsImage&lt;/strong&gt; script. The bug in this script only occurs when the WIM file being converted only contains a single image—which as of TP4 includes the NanoServer.wim.&lt;/p&gt;&lt;p&gt;If you try and run the &lt;strong&gt;New-NanoServerVHD&lt;/strong&gt; script using the &lt;em&gt;unfixed&lt;/em&gt; version of the &lt;strong&gt;Convert-WindowsImage&lt;/strong&gt; script and TP4, you’ll run into the following error message:&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;ERROR : The variable cannot be validated because the value $null is not a valid value for the Edition variable&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So, after reporting the error to the original script creator, I went ahead and fixed the problem myself and uploaded a working version to GitHub (until it has been fixed in the official version). You can download my fixed version from &lt;a href=&quot;https://raw.githubusercontent.com/PlagueHO/Powershell/master/New-NanoServerVHD/Convert-WindowsImage.ps1&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;installing-nano-server-tp4&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/nano-server-tp4/#installing-nano-server-tp4&quot; class=&quot;heading-anchor&quot;&gt;Installing Nano Server TP4&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;After fixing the &lt;strong&gt;bug&lt;/strong&gt; in the &lt;code&gt;Convert-WindowsImage.ps1&lt;/code&gt; file, here are some updated instructions on using this script to quickly create a new Nano Server TP4 VHD or VHDx.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/Create-a-New-Nano-Server-61f674f1&quot; title=&quot;Create a New Nano Server VHD&quot; rel=&quot;noopener&quot;&gt;Create a New Nano Server VHD&lt;/a&gt;&lt;/p&gt;&lt;p&gt;It is fairly straightforward to install and use:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Create a &lt;strong&gt;Working Folder&lt;/strong&gt; on your computer. In this example, I used &lt;code&gt;C:&#92;Nano&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Download the &lt;code&gt;_New-NanoServerVHD.ps1_&lt;/code&gt; to the &lt;strong&gt;Working Folder&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Download the &lt;code&gt;_Convert-WindowsImage.ps1_&lt;/code&gt; (&lt;a href=&quot;https://raw.githubusercontent.com/PlagueHO/Powershell/master/New-NanoServerVHD/Convert-WindowsImage.ps1&quot; rel=&quot;noopener&quot;&gt;download here&lt;/a&gt;) to the &lt;strong&gt;Working Folder&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Download the Windows Server 2016 Technical Preview ISO (&lt;a href=&quot;https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-technical-preview&quot; rel=&quot;noopener&quot;&gt;download here&lt;/a&gt;) to the &lt;strong&gt;Working Folder&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Open an Administrative PowerShell window.&lt;/li&gt;&lt;li&gt;Change directory to the &lt;strong&gt;Working Folder&lt;/strong&gt; (&lt;code&gt;cd C:&#92;Nano&lt;/code&gt;).&lt;/li&gt;&lt;li&gt;Execute the following command (customizing the parameters to your needs):&lt;/li&gt;&lt;/ol&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;&lt;span class=&quot;token function&quot;&gt;New-NanoServerVHD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1 `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServerISO &lt;span class=&quot;token string&quot;&gt;&#39;C:&#92;Nano&#92;10586.0.151029-1700.TH2_RELEASE_SERVER_OEMRET_X64FRE_EN-US.ISO&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DestVHD C:&#92;Nano&#92;NanoServer01&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vhdx `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VHDFormat VHDX `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ComputerName NANOTEST01 `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AdministratorPassword &lt;span class=&quot;token string&quot;&gt;&#39;P@ssword!1&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Packages &lt;span class=&quot;token string&quot;&gt;&#39;Containers&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;OEM-Drivers&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Guest&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;IIS&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DNS&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;IPAddress &lt;span class=&quot;token string&quot;&gt;&#39;192.168.1.65&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;available-packages-in-tp4&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/nano-server-tp4/#available-packages-in-tp4&quot; class=&quot;heading-anchor&quot;&gt;Available Packages in TP4&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;There are a bunch of new packages that are now available in TP4 for integrating into your Nano Server builds. I’m not quite sure of the exact purpose of some of them, but I’ve listed them here:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Compute&lt;/strong&gt;: Hyper-V Server&lt;/li&gt;&lt;li&gt;&lt;strong&gt;OEM-Drivers&lt;/strong&gt;: Standard OEM Drivers&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Storage&lt;/strong&gt;: Storage Server&lt;/li&gt;&lt;li&gt;&lt;strong&gt;FailoverCluster&lt;/strong&gt;: Failover Cluster Server&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ReverseForwarders&lt;/strong&gt;: ReverseForwarders to allow some older App Servers to run&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Guest&lt;/strong&gt;: Hyper-V Guest Tools&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Containers&lt;/strong&gt;: Support for Hyper-V and Windows containers&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Defender&lt;/strong&gt;: Windows Defender&lt;/li&gt;&lt;li&gt;&lt;strong&gt;DCB&lt;/strong&gt;: Unsure&lt;/li&gt;&lt;li&gt;&lt;strong&gt;DNS&lt;/strong&gt;: DNS Server&lt;/li&gt;&lt;li&gt;&lt;strong&gt;DSC&lt;/strong&gt;: PowerShell Desired State Configuration Support&lt;/li&gt;&lt;li&gt;&lt;strong&gt;IIS&lt;/strong&gt;: Internet Information Server (Web Server)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;NPDS&lt;/strong&gt;: Unsure&lt;/li&gt;&lt;li&gt;&lt;strong&gt;SCVMM&lt;/strong&gt;: System Center VMM&lt;/li&gt;&lt;li&gt;&lt;strong&gt;SCVMM-Compute&lt;/strong&gt;: System Center VMM Compute&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Over and out.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Windows Server 2016 TP4 is available</title>
      <link href="https://danielscottraynsford.com/blog/windows-server-2016-tp4-is-available/" />
      <updated>2015-11-19T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/windows-server-2016-tp4-is-available/</id>
      <content type="html">
				&lt;p&gt;After yesterday’s exciting release of &lt;a href=&quot;https://danielscottraynsford.com/blog/powershell-language-support-in-visual-studio-code/&quot;&gt;Visual Studio Code with PowerShell support&lt;/a&gt;, today we get &lt;a href=&quot;https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-technical-preview&quot; rel=&quot;noopener&quot;&gt;Windows Server 2016 Technical Preview 4&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I’m diving straight into the Nano Server to see what has changed and if Containers is working.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>PowerShell Language Support in Visual Studio Code</title>
      <link href="https://danielscottraynsford.com/blog/powershell-language-support-in-visual-studio-code/" />
      <updated>2015-11-18T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/powershell-language-support-in-visual-studio-code/</id>
      <content type="html">
				&lt;p&gt;I feel like my birthday has come early: Microsoft has added &lt;a href=&quot;http://blogs.msdn.com/b/powershell/archive/2015/11/17/announcing-windows-powershell-for-visual-studio-code-and-more.aspx&quot; rel=&quot;noopener&quot;&gt;PowerShell language support in Visual Studio Code&lt;/a&gt;:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/jR2-t9Upa3-650.png 650w, https://danielscottraynsford.com/img/jR2-t9Upa3-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/jR2-t9Upa3-650.webp 650w, https://danielscottraynsford.com/img/jR2-t9Upa3-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/jR2-t9Upa3-650.jpeg&quot; alt=&quot;ss_vscode_powershell&quot; width=&quot;960&quot; height=&quot;444&quot; srcset=&quot;https://danielscottraynsford.com/img/jR2-t9Upa3-650.jpeg 650w, https://danielscottraynsford.com/img/jR2-t9Upa3-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;I mainly use PowerShell ISE with &lt;a href=&quot;http://www.powertheshell.com/isesteroids2/&quot; rel=&quot;noopener&quot;&gt;ISE Steroids&lt;/a&gt; for most of my coding, but I find when I’m needing to switch between working on multiple files of different types &lt;strong&gt;Visual Studio Code&lt;/strong&gt; is so much faster and slicker. &lt;strong&gt;Visual Studio 2015&lt;/strong&gt; with &lt;strong&gt;PoSH&lt;/strong&gt; tools would be the ideal way to go, but the &lt;strong&gt;PoSH&lt;/strong&gt; extension is quite slow with any files larger than 20KB. So I’m really excited with what this can do.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Windows 10 Build 10586 - PowerShell Problems</title>
      <link href="https://danielscottraynsford.com/blog/windows-10-build-10586-powershell-problems/" />
      <updated>2015-11-15T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/windows-10-build-10586-powershell-problems/</id>
      <content type="html">
				&lt;h2 id=&quot;powershell-direct-broken&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-10-build-10586-powershell-problems/#powershell-direct-broken&quot; class=&quot;heading-anchor&quot;&gt;PowerShell Direct Broken&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Unfortunately my Sunday afternoon of planned study has been slightly derailed, as last night I just upgraded my primary work machine to Windows 10 Build 10586. Everything went fine with the upgrade and seemed to be working just perfectly. However, when I started to get to work with my Hyper-V lab machines today I ran into a significant bug with PowerShell on this build:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;PowerShell Direct&lt;/strong&gt; no longer connects to any of my VMs. It pauses for approximately 30 seconds and then reports a rather mysterious error:&lt;/p&gt;&lt;p&gt;An error has occurred which Windows PowerShell cannot handle. A remote session might have ended.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_powershelldirect_errormessage.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/QiqNIMYcLw-650.png 650w, https://danielscottraynsford.com/img/QiqNIMYcLw-960.png 960w, https://danielscottraynsford.com/img/QiqNIMYcLw-1241.png 1241w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/QiqNIMYcLw-650.webp 650w, https://danielscottraynsford.com/img/QiqNIMYcLw-960.webp 960w, https://danielscottraynsford.com/img/QiqNIMYcLw-1241.webp 1241w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/QiqNIMYcLw-650.jpeg&quot; alt=&quot;But it was working yesterday!&quot; width=&quot;1241&quot; height=&quot;372&quot; srcset=&quot;https://danielscottraynsford.com/img/QiqNIMYcLw-650.jpeg 650w, https://danielscottraynsford.com/img/QiqNIMYcLw-960.jpeg 960w, https://danielscottraynsford.com/img/QiqNIMYcLw-1241.jpeg 1241w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;But it was working yesterday!&lt;/p&gt;&lt;p&gt;Passing credentials that are correct or incorrect for the VM have no effect - the error message is always the same.&lt;/p&gt;&lt;p&gt;Fortunately connecting via plain old &lt;strong&gt;PowerShell Remoting&lt;/strong&gt; still works fine, but I have many scripts that are dependent on using &lt;strong&gt;PowerShell Direct&lt;/strong&gt; that are no longer functioning - including my &lt;a href=&quot;https://github.com/PlagueHO/LabBuilder&quot; rel=&quot;noopener&quot;&gt;LabBuilder&lt;/a&gt; project, which I use every day to get my Hyper-V lab up and running for my studies.&lt;/p&gt;&lt;p&gt;I’ve logged this issue in Windows Connect right &lt;a href=&quot;https://connect.microsoft.com/PowerShell/Feedback/Details/2018831&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;. So if you’re being affected by this issue please go and vote it up to see if it can’t be resolved quickly. This was a fantastic feature that was only added recently and it is sad to see it being broken so soon!&lt;/p&gt;&lt;h2 id=&quot;encrypting-credentials-in-dsc-mof-files&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-10-build-10586-powershell-problems/#encrypting-credentials-in-dsc-mof-files&quot; class=&quot;heading-anchor&quot;&gt;Encrypting Credentials in DSC MOF Files&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Anyone who has put properly encrypted credentials in DSC configuration files knows that they need to be encrypted using a certificate that was issued to the Node that will be reading the configuration. This is usually fairly straight forward, but some care needs to be taken when generating the certificate for the Node to use.&lt;/p&gt;&lt;p&gt;I have an automated process designed for my Lab where any new VM’s that get built are automatically issued with a &lt;strong&gt;self-signed&lt;/strong&gt; certificate that will be used to encrypt the DSC config files. This certificate is automatically downloaded to the host (via &lt;strong&gt;PowerShell Direct&lt;/strong&gt; of course) and then used to &lt;strong&gt;encrypt&lt;/strong&gt; the &lt;em&gt;DSC config&lt;/em&gt; files for that node. All completely seamless and automatic. Until build 10586, when these certificates are no longer able to be used to encrypt the MOF file. Instead I get this error:&lt;/p&gt;&lt;p&gt;ConvertTo-MOFInstance : System.ArgumentException error processing property ‘Password’ OF TYPE ‘MSFT_Credential’: Certificate ‘8E474886A6AA72859BDC3C2FBEEFAAD7E089A5DD’ cannot be used for encryption. Encryption certificates&lt;br&gt;must contain the Data Encipherment or Key Encipherment key usage, and include the Document Encryption Enhanced Key Usage (1.3.6.1.4.1.311.80.1).&lt;/p&gt;&lt;p&gt;Ok, so this isn’t the end of the world and it is pretty clear what has changed here. After looking at my existing self-signed certificates they didn’t include the &lt;strong&gt;EKU&lt;/strong&gt; (Enhanced Key Usage) of &lt;strong&gt;Document Encrpytion&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_certificate_selfsignedbad.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/NBtyCc3KOj-405.png 405w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/NBtyCc3KOj-405.webp 405w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/NBtyCc3KOj-405.jpeg&quot; alt=&quot;My previous certificates - now useless because the Document Encryption EKU is missing.&quot; width=&quot;405&quot; height=&quot;515&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;My previous certificates - now useless because the Document Encryption EKU is missing.&lt;/p&gt;&lt;p&gt;It seems this is now required to encrypt credentials in MOF Files. I guess this makes sense and I’m sure not that many people are going to run into the problem. But in case you do, you’ll need to reissue these certificates including the following EKU:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Document Encryption (1.3.6.1.4.1.311.80.1)&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;You also need to ensure the Key Usage contains either &lt;strong&gt;Data Encipherment&lt;/strong&gt; or &lt;strong&gt;Key Encipherment&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Finally, I have one strong recommendation related to the topic of encrypting DSC credentials: Don’t use the built in PowerShell cmdlet &lt;strong&gt;New-SelfSignedCertificate&lt;/strong&gt; to create self-signed certificates for this purpose. It creates certificates that are not compatible for other reasons (I won’t go into detail but you can look the issue up I’m sure). Instead I strongly recommend you use &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/Self-signed-certificate-5920a7c6&quot; rel=&quot;noopener&quot;&gt;this script&lt;/a&gt; on MSDN Script Center.&lt;/p&gt;&lt;h2 id=&quot;decryption-failed&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-10-build-10586-powershell-problems/#decryption-failed&quot; class=&quot;heading-anchor&quot;&gt;Decryption Failed&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Update 2015-12-18:&lt;/strong&gt; Installing &lt;strong&gt;Windows Management Framework (WMF) 5.0 RTM&lt;/strong&gt; on the &lt;strong&gt;DSC node&lt;/strong&gt; resolves the &lt;strong&gt;Decryption Failed&lt;/strong&gt; error described below. So if you’re experiencing this issue, install &lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=50395&quot; rel=&quot;noopener&quot;&gt;this&lt;/a&gt; update on any &lt;strong&gt;DSC nodes&lt;/strong&gt; experiencing this problem.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Edit: Karl&lt;/strong&gt; in his comment on this post mentioned a problem he was having where the DSC node was failing to decrypt any credentials provided in DSC MOF files created on the build 10586. He was receiving a &lt;strong&gt;Dercyption Failed&lt;/strong&gt; error when the MOF was being applied to the node:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/8wmiGR8AGL-650.png 650w, https://danielscottraynsford.com/img/8wmiGR8AGL-877.png 877w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/8wmiGR8AGL-650.webp 650w, https://danielscottraynsford.com/img/8wmiGR8AGL-877.webp 877w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/8wmiGR8AGL-650.jpeg&quot; alt=&quot;ss_dsc_decryptionfailed&quot; width=&quot;877&quot; height=&quot;167&quot; srcset=&quot;https://danielscottraynsford.com/img/8wmiGR8AGL-650.jpeg 650w, https://danielscottraynsford.com/img/8wmiGR8AGL-877.jpeg 877w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;I hadn’t noticed this issue because I hadn’t been working on DSC for a week, but when I tried to apply a rebuilt MOF file I experienced the same issue.&lt;/p&gt;&lt;p&gt;It was also reported that the &lt;strong&gt;password&lt;/strong&gt; property format of the &lt;strong&gt;MSFT_Credential&lt;/strong&gt; object in the &lt;strong&gt;MOF&lt;/strong&gt; seems to have changed in one of the recent releases from:&lt;/p&gt;&lt;p&gt;instance of MSFT_Credential as $MSFT_Credential2ref&lt;br&gt;{&lt;br&gt;&amp;nbsp; Password = “…Base64Password…”;&lt;br&gt;&amp;nbsp; UserName = “&lt;a href=&quot;http://LABBUILDER.COM&quot; rel=&quot;noopener&quot;&gt;LABBUILDER.COM&lt;/a&gt;&#92;&#92;Administrator”;&lt;br&gt;};&lt;/p&gt;&lt;p&gt;To:&lt;/p&gt;&lt;p&gt;instance of MSFT_Credential as $MSFT_Credential2ref&lt;br&gt;{&lt;br&gt;&amp;nbsp; Password = “-----BEGIN CMS-----&#92;n…base64Password…&#92;n-----END CMS-----”;&lt;br&gt;&amp;nbsp; UserName = “&lt;a href=&quot;http://LABBUILDER.COM&quot; rel=&quot;noopener&quot;&gt;LABBUILDER.COM&lt;/a&gt;&#92;&#92;Administrator”;&lt;br&gt;};&lt;/p&gt;&lt;p&gt;After a full day of investigating this issue, I can confirm it has been caused by a change the Microsoft has made in the &lt;em&gt;PSDesiredStateConfigration&lt;/em&gt; module supplied with this build (specifically the &lt;strong&gt;Get-EncryptedPassword&lt;/strong&gt; function, in case you’re interested). This issue has been reported to Microsoft on&amp;nbsp;&lt;strong&gt;PowerShell Connect&lt;/strong&gt; &lt;a href=&quot;https://connect.microsoft.com/PowerShell/Feedback/Details/2080033&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;. Please go an &lt;strong&gt;upvote&lt;/strong&gt; it if you’re having this problem (even if you’re not).&amp;nbsp;&lt;strong&gt;Karl&lt;/strong&gt; has posted this issue on &lt;a href=&quot;http://stackoverflow.com/questions/34006865/dsc-problems-with-credentials-and-build-10586&quot; rel=&quot;noopener&quot;&gt;Stack Overflow&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;In the mean time I have posted a work around (roll back to a previous version of the &lt;strong&gt;PSDesiredStateConfiguration&lt;/strong&gt; module on the &lt;a href=&quot;http://stackoverflow.com/questions/34006865/dsc-problems-with-credentials-and-build-10586&quot; rel=&quot;noopener&quot;&gt;Stack Overflow page&lt;/a&gt;) - so if you want to work around this problem, &lt;a href=&quot;http://stackoverflow.com/questions/34006865/dsc-problems-with-credentials-and-build-10586&quot; rel=&quot;noopener&quot;&gt;go&lt;/a&gt; and take a look.&lt;/p&gt;&lt;h2 id=&quot;dsc-is-practically-broken-in-10586&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-10-build-10586-powershell-problems/#dsc-is-practically-broken-in-10586&quot; class=&quot;heading-anchor&quot;&gt;DSC is Practically Broken in 10586&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: After finishing this last post I’ve run into some critical problems with &lt;strong&gt;DSC&lt;/strong&gt; on build 10586. Specifically, when I build a DSC configuration on this machine and include the &lt;strong&gt;PSDesiredStateConfiguration&lt;/strong&gt; resource (by way of &lt;strong&gt;Import-DSCResource&lt;/strong&gt; cmdlet) the MOF file that is created references a 1.0 version of the module - &lt;strong&gt;which doesn’t exist:&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_dsc_badmofversion.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/NpraT5KJ3R-650.png 650w, https://danielscottraynsford.com/img/NpraT5KJ3R-952.png 952w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/NpraT5KJ3R-650.webp 650w, https://danielscottraynsford.com/img/NpraT5KJ3R-952.webp 952w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/NpraT5KJ3R-650.jpeg&quot; alt=&quot;Version 1.0 isn&#39;t on the machine!&quot; width=&quot;952&quot; height=&quot;200&quot; srcset=&quot;https://danielscottraynsford.com/img/NpraT5KJ3R-650.jpeg 650w, https://danielscottraynsford.com/img/NpraT5KJ3R-952.jpeg 952w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;Version 1.0 isn’t on the machine!&lt;/p&gt;&lt;p&gt;Applying the MOF file to any node immediately throws an error because of course this module doesn’t exist (1.1 is the earliest version of this module that are on any of the nodes).&lt;/p&gt;&lt;p&gt;However, if I &lt;em&gt;force&lt;/em&gt; the module version to &lt;strong&gt;1.1&lt;/strong&gt; in the &lt;strong&gt;Import-DSCResource&lt;/strong&gt; cmdlet then the MOF file that is created has the correct module version and can be applied to the node without any issue:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_dsc_howtofixmoduleversion.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/okfWWOM21_-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/okfWWOM21_-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/okfWWOM21_-650.jpeg&quot; alt=&quot;Forcing the Module Version.&quot; width=&quot;650&quot; height=&quot;90&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;Forcing the Module Version.&lt;/p&gt;&lt;p&gt;But of course going around all my config files and forcing the module version to 1.1 is a very unsatisfactory solution. Also, I’m not sure if it is just the &lt;strong&gt;PSDesiredStateConfiguration&lt;/strong&gt; resource that has this problem or all modules. I haven’t had the time to investigate this further yet.&lt;/p&gt;&lt;p&gt;If you are suffering from any of these issues in build 10586, please let me know.&lt;/p&gt;&lt;p&gt;Thanks for reading!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Pester as an Operation Validation Framework</title>
      <link href="https://danielscottraynsford.com/blog/pester-as-an-operation-validation-framework/" />
      <updated>2015-11-13T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/pester-as-an-operation-validation-framework/</id>
      <content type="html">
				&lt;p&gt;In &lt;a href=&quot;https://channel9.msdn.com/Shows/about-it/Episode-003-Jeffrey-on-Nano-Containers-and-the-Modern-App-Platform&quot; rel=&quot;noopener&quot;&gt;this&lt;/a&gt; latest video on Channel 9, Jeffrey Snover (the grand wizard of PowerShell) is suggesting what might be on the horizon in Windows Server 2016. In it, he says they’re looking at using Pester (or a form of it) to allow you to create Operational Validation tests for your servers and environment, so that after any environmental changes are made, the environment is validated automatically. This sounds like a fantastic idea to me and such an obvious fit for Pester! After doing a bit of digging around, it seems like this idea has been around for a while—see this &lt;a href=&quot;https://pshirwin.wordpress.com/2015/11/06/pester-script-to-test-dns-configuration/&quot; rel=&quot;noopener&quot;&gt;post here&lt;/a&gt; for an example of how it can be used in practice.&lt;/p&gt;&lt;p&gt;Of course, there does feel like there is a little bit of an overlap here with DSC, but I’m sure the implementation will play well with DSC. All of these new ideas and technologies (Nano, Containers, DSC, Operational Pester tests, etc.) are just more tools in the “Infrastructure as Code” tool belt. So I’m very happy!&lt;/p&gt;&lt;p&gt;I suggest watching the whole video (found &lt;a href=&quot;https://channel9.msdn.com/Shows/about-it/Episode-003-Jeffrey-on-Nano-Containers-and-the-Modern-App-Platform&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;) as it is really interesting, but if you want to just jump to the bit about Pester, it starts at about 11:48. I’m really eager to see where Microsoft is going with this stuff in Windows Server 2016. Roll on TP4!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>WFAS Firewall Rules Group vs. DisplayGroup</title>
      <link href="https://danielscottraynsford.com/blog/wfas-firewall-rules-group-vs-displaygroup/" />
      <updated>2015-11-10T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/wfas-firewall-rules-group-vs-displaygroup/</id>
      <content type="html">
				&lt;p&gt;Recently I’ve been helping resolve a couple of issues with the behavior of the &lt;strong&gt;xFirewall&lt;/strong&gt; resource in the &lt;a href=&quot;https://github.com/PowerShell/xNetworking&quot; rel=&quot;noopener&quot;&gt;xNetworking&lt;/a&gt; DSC Module. One of these was trying to implement both the Group and DisplayGroup parameters when creating a new Firewall Rule.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_firewall_rulegroupdisplaygroup.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/8t7egquL9d-650.png 650w, https://danielscottraynsford.com/img/8t7egquL9d-831.png 831w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/8t7egquL9d-650.webp 650w, https://danielscottraynsford.com/img/8t7egquL9d-831.webp 831w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/8t7egquL9d-650.jpeg&quot; alt=&quot;A Firewall Rule Group/Display Group.&quot; width=&quot;831&quot; height=&quot;272&quot; srcset=&quot;https://danielscottraynsford.com/img/8t7egquL9d-650.jpeg 650w, https://danielscottraynsford.com/img/8t7egquL9d-831.jpeg 831w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;A Firewall Rule Group/Display Group.&lt;/p&gt;&lt;p&gt;The obvious assumption is that the &lt;strong&gt;Group&lt;/strong&gt; and &lt;strong&gt;DisplayGroup&lt;/strong&gt; parameters behave in a similar fashion to the &lt;strong&gt;Name&lt;/strong&gt; and &lt;strong&gt;DisplayName&lt;/strong&gt; parameters. Unfortunately, this is not the case and it took me quite a lot of digging to figure out how they actually work.&lt;/p&gt;&lt;h2 id=&quot;why-is-this-important&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/wfas-firewall-rules-group-vs-displaygroup/#why-is-this-important&quot; class=&quot;heading-anchor&quot;&gt;Why is this Important?&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This became an issue for me while trying to determine how to best implement these parameters in the &lt;strong&gt;xFirewall&lt;/strong&gt; resource. It became clear that it is not actually possible to set the &lt;strong&gt;DisplayGroup&lt;/strong&gt; parameter using any of the &lt;strong&gt;*-netfirewallrule&lt;/strong&gt; cmdlets. So adding this parameter to the resource caused a lot of problems and was also causing quite a bit of confusion, especially as the relationship between &lt;strong&gt;Group&lt;/strong&gt; and &lt;strong&gt;DisplayGroup&lt;/strong&gt; is not clearly documented anywhere I could find.&lt;/p&gt;&lt;p&gt;You can’t even set the &lt;strong&gt;DisplayGroup&lt;/strong&gt; parameter via &lt;strong&gt;NETSH&lt;/strong&gt; or in the &lt;strong&gt;WFAS (Windows Firewall with Advanced Security) UI&lt;/strong&gt;. In fact, the &lt;strong&gt;WFAS UI&lt;/strong&gt; only shows the &lt;strong&gt;DisplayGroup&lt;/strong&gt; and is labeled as &lt;strong&gt;Group&lt;/strong&gt;—the actual &lt;strong&gt;Group&lt;/strong&gt; is hidden completely:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_firewall_groupwfasui.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/f3ocdb6O7c-650.png 650w, https://danielscottraynsford.com/img/f3ocdb6O7c-960.png 960w, https://danielscottraynsford.com/img/f3ocdb6O7c-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/f3ocdb6O7c-650.webp 650w, https://danielscottraynsford.com/img/f3ocdb6O7c-960.webp 960w, https://danielscottraynsford.com/img/f3ocdb6O7c-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/f3ocdb6O7c-650.jpeg&quot; alt=&quot;WFAS UI showing the DisplayGroup (labeled as Group)&quot; width=&quot;1400&quot; height=&quot;747&quot; srcset=&quot;https://danielscottraynsford.com/img/f3ocdb6O7c-650.jpeg 650w, https://danielscottraynsford.com/img/f3ocdb6O7c-960.jpeg 960w, https://danielscottraynsford.com/img/f3ocdb6O7c-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;WFAS UI showing the DisplayGroup (labeled as Group)&lt;/p&gt;&lt;h2 id=&quot;the-relationship&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/wfas-firewall-rules-group-vs-displaygroup/#the-relationship&quot; class=&quot;heading-anchor&quot;&gt;The Relationship&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;So, onto the actual reason for this post: What is the relationship between &lt;strong&gt;Group&lt;/strong&gt; and &lt;strong&gt;DisplayGroup&lt;/strong&gt; and how does &lt;strong&gt;DisplayGroup&lt;/strong&gt; get set? I’m hoping this will help someone else who is trying to understand this undocumented behavior.&lt;/p&gt;&lt;p&gt;There are two possible values that the &lt;strong&gt;DisplayGroup&lt;/strong&gt; can have and &lt;em&gt;these can never be set directly&lt;/em&gt;. They are:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;If &lt;strong&gt;Group&lt;/strong&gt; does not start with an &lt;strong&gt;@&lt;/strong&gt; then the &lt;strong&gt;DisplayGroup&lt;/strong&gt; will be the same as &lt;strong&gt;Group&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;If &lt;strong&gt;Group&lt;/strong&gt; starts with an &lt;strong&gt;@&lt;/strong&gt; then the&amp;nbsp;&lt;strong&gt;DisplayGroup&lt;/strong&gt; will be a value pulled from a &lt;em&gt;DLL&lt;/em&gt; or &lt;em&gt;Windows Universal App resource&lt;/em&gt;. This creates the rule as a &lt;strong&gt;predefined&lt;/strong&gt; rule.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;For example, here is the &lt;strong&gt;DisplayGroup&lt;/strong&gt; being pulled from a &lt;strong&gt;DLL&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_firewall_ruledll.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/9yJr8QL2K0-650.png 650w, https://danielscottraynsford.com/img/9yJr8QL2K0-958.png 958w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/9yJr8QL2K0-650.webp 650w, https://danielscottraynsford.com/img/9yJr8QL2K0-958.webp 958w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/9yJr8QL2K0-650.jpeg&quot; alt=&quot;ss_firewall_ruledll&quot; width=&quot;958&quot; height=&quot;271&quot; srcset=&quot;https://danielscottraynsford.com/img/9yJr8QL2K0-650.jpeg 650w, https://danielscottraynsford.com/img/9yJr8QL2K0-958.jpeg 958w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;And here is one being pulled from a &lt;strong&gt;Universal App Resource&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_firewall_ruleuniversalapp.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/lVLG--RHFi-650.png 650w, https://danielscottraynsford.com/img/lVLG--RHFi-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/lVLG--RHFi-650.webp 650w, https://danielscottraynsford.com/img/lVLG--RHFi-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/lVLG--RHFi-650.jpeg&quot; alt=&quot;ss_firewall_ruleuniversalapp&quot; width=&quot;960&quot; height=&quot;218&quot; srcset=&quot;https://danielscottraynsford.com/img/lVLG--RHFi-650.jpeg 650w, https://danielscottraynsford.com/img/lVLG--RHFi-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;So, what is the take away from all this?&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;DisplayGroup&lt;/strong&gt; can’t be set manually by you and will never be different to the &lt;strong&gt;Group&lt;/strong&gt; unless the &lt;strong&gt;Group&lt;/strong&gt; starts with an &lt;strong&gt;@&lt;/strong&gt;.&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;A &lt;strong&gt;predefined&lt;/strong&gt; rule will prevent most settings of the rule from being changed in the &lt;strong&gt;WFAS UI&lt;/strong&gt;, although they still can be changed in PowerShell.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_firewall_rulepredefined.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/pHzlo26MpZ-433.png 433w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/pHzlo26MpZ-433.webp 433w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/pHzlo26MpZ-433.jpeg&quot; alt=&quot;A predefined rule can&#39;t be changed via the WFAS UI.&quot; width=&quot;433&quot; height=&quot;581&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;A predefined rule can’t be changed via the WFAS UI.&lt;/p&gt;&lt;p&gt;And one final piece of info: The &lt;strong&gt;Group&lt;/strong&gt; (and by extension the &lt;strong&gt;DisplayGroup&lt;/strong&gt;) can’t be changed for an existing rule. You can only change it by deleting and recreating the rule!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>How to tell if a PowerShell variable has not been Declared</title>
      <link href="https://danielscottraynsford.com/blog/how-to-tell-if-a-powershell-variable-has-not-been-declared/" />
      <updated>2015-11-10T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/how-to-tell-if-a-powershell-variable-has-not-been-declared/</id>
      <content type="html">
				&lt;p&gt;In most situations in PowerShell, I am really only interested if a variable has a value or not (e.g. not null). Checking for this is easy:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$myvariable&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Write-Host&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;$MyVariable is null&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Write-Host&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;$MyVariable has a non-null and non-blank value&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But what if I want to know if a variable is &lt;strong&gt;not&lt;/strong&gt; declared at all? The method of doing that is not so obvious and being PowerShell there are many ways of doing it. By far the clearest I think is to use the &lt;strong&gt;Test-Path&lt;/strong&gt; cmdlet using the &lt;strong&gt;Variable&lt;/strong&gt; provider:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Test-Path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path Variable:&#92;MyVariable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Write-Host&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;$MyVariable is declared&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Write-Host&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Message &lt;span class=&quot;token string&quot;&gt;&#39;$MyVariable is not declared&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If there is a cleaner or officially recommended way of doing this I’d be most keen to hear about it.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>File Server Resource Manager (FSRM) Classifications DSC Resource</title>
      <link href="https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-classifications-dsc-resource/" />
      <updated>2015-11-04T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-classifications-dsc-resource/</id>
      <content type="html">
				&lt;h2 id=&quot;introduction&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-classifications-dsc-resource/#introduction&quot; class=&quot;heading-anchor&quot;&gt;Introduction&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I’ve been spending a bit of time lately working on some issues and improvements on the &lt;a href=&quot;https://github.com/PowerShell/xNetworking&quot; rel=&quot;noopener&quot;&gt;xNetworking&lt;/a&gt; DSC Resource so haven’t been spending as much time working on the series of&amp;nbsp;File Server Resource Manager (FSRM) DSC Modules as I’d like. That said, I have managed to complete another module. This one is used for configuring &lt;strong&gt;Classification Properties and Property Values&lt;/strong&gt;, &lt;strong&gt;Classification Configuration&lt;/strong&gt; and &lt;strong&gt;Classification Rules&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;If you missed any of the previous FSRM DSC Modules:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/10/26/file-server-resource-manager-fsrm-file-screen-dsc-resource/&quot; rel=&quot;noopener&quot;&gt;File Server Resource Manager (FSRM) File Screen DSC Resource&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/10/23/file-server-resource-manager-fsrm-quotas-dsc-resource/&quot; rel=&quot;noopener&quot;&gt;File Server Resource Manager (FSRM) Quotas DSC Resource&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;resources&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-classifications-dsc-resource/#resources&quot; class=&quot;heading-anchor&quot;&gt;Resources&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This module contains the following resources:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;cFSRMClassification-&lt;/strong&gt; configures FSRM Classification settings. &lt;strong&gt;cFSRMClassificationProperty-&lt;/strong&gt; configures FSRM Classification Property Definitions. &lt;strong&gt;cFSRMClassificationPropertyValue-&lt;/strong&gt; configures FSRM Classification Property Definition Values. This resource only needs to be used if the Description of a Classification Property Definition Value must be set. &lt;strong&gt;cFSRMClassificationRule-&lt;/strong&gt; configures FSRM Classification Rules.&lt;/p&gt;&lt;p&gt;The purpose of the resources should be fairly self explanatory, as long as you have a basic understanding of how FSRM Classifications are used.&lt;/p&gt;&lt;h2 id=&quot;installing-the-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-classifications-dsc-resource/#installing-the-resource&quot; class=&quot;heading-anchor&quot;&gt;Installing the Resource&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you have installed &lt;strong&gt;WMF 5.0&lt;/strong&gt; you can just download this&amp;nbsp;directly from the &lt;a href=&quot;https://www.powershellgallery.com/&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt; by running this command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name cFSRMClassifications&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Otherwise you’ll need to download this from the Microsoft Script Center &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/cFSRMClassifications-DSC-8ed89153&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; and unzip it into your &lt;strong&gt;PowerShell modules path&lt;/strong&gt;.&lt;/p&gt;&lt;h2 id=&quot;using-the-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-classifications-dsc-resource/#using-the-resource&quot; class=&quot;heading-anchor&quot;&gt;Using the Resource&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As per the last post on these resources, rather than go into detail on using this resource, I thought I’d try and keep it short and just provide a &lt;a href=&quot;https://github.com/PlagueHO/cFSRMClassifications&quot; rel=&quot;noopener&quot;&gt;link to the documentation&lt;/a&gt;. This covers the parameters available in the resources as well as some usage examples.&lt;/p&gt;&lt;p&gt;If you need some additional guidance or other specific examples, please feel free to let me know and I’ll do my best to help you out.&lt;/p&gt;&lt;p&gt;Hopefully this resource finds some use out there, but either way it has been extremely helpful to me really imprint the underlying FSRM features and usage into my own mind.&lt;/p&gt;&lt;h2 id=&quot;feedback&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-classifications-dsc-resource/#feedback&quot; class=&quot;heading-anchor&quot;&gt;Feedback&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you’re interested in contributing to this resource, providing feedback or raising issues or requesting features, please feel free (anything is appreciated). You’ll find the resource GitHub repository &lt;a href=&quot;https://github.com/PlagueHO/cFSRMClassifications&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; where you can fork, issue pull requests and raise issues/feature requests.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>NanoServer Container Base Image - It does Exist...Somewhere!</title>
      <link href="https://danielscottraynsford.com/blog/nanoserver-container-base-image-it-does-existsomewhere/" />
      <updated>2015-10-29T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/nanoserver-container-base-image-it-does-existsomewhere/</id>
      <content type="html">
				&lt;p&gt;A really interesting video from Microsoft was just released with Mark Russinovich (CTO of Azure if you don’t already know) &lt;a href=&quot;https://youtu.be/YoA_MMlGPRc&quot; rel=&quot;noopener&quot;&gt;demonstrating Windows Server Containers&lt;/a&gt;. What is really interesting about this demo is that he is demonstrating containers using a &lt;strong&gt;Windows NanoServer Base Image:&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_video_nanoservercontainers.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/YhlF4z0eey-650.png 650w, https://danielscottraynsford.com/img/YhlF4z0eey-960.png 960w, https://danielscottraynsford.com/img/YhlF4z0eey-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/YhlF4z0eey-650.webp 650w, https://danielscottraynsford.com/img/YhlF4z0eey-960.webp 960w, https://danielscottraynsford.com/img/YhlF4z0eey-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/YhlF4z0eey-650.jpeg&quot; alt=&quot;Nano Server Containers Base Image - it does exist.&quot; width=&quot;1400&quot; height=&quot;780&quot; srcset=&quot;https://danielscottraynsford.com/img/YhlF4z0eey-650.jpeg 650w, https://danielscottraynsford.com/img/YhlF4z0eey-960.jpeg 960w, https://danielscottraynsford.com/img/YhlF4z0eey-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;Nano Server Containers Base Image - it does exist.&lt;/p&gt;&lt;p&gt;If you’ve read any of my previous posts &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/08/26/how-to-use-containers-on-windows-nano-server/&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/08/27/docker-and-containers-on-nano-server-continued/&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; you’ll know I spent quite some time looking at this and trying to get it going with TP3. I deduced it was not possible yet without the Windows &lt;strong&gt;NanoServer Base Image for containers&lt;/strong&gt; - which had not been provided by Microsoft.&lt;/p&gt;&lt;p&gt;Other eagle-eyed viewers will also note that he appears to be running a &lt;em&gt;Nano Server container&lt;/em&gt; on a &lt;em&gt;Full Server container host&lt;/em&gt;, which I didn’t actually think was possible. From what I originally understood about containers, you could only instantiate a container using a base container image matching the version of the OS the container host used. For example, you &lt;strong&gt;cannot&lt;/strong&gt; instantiate a &lt;em&gt;Server Core container&lt;/em&gt; on a &lt;em&gt;NanoServer container host&lt;/em&gt;—I confirmed this was the case in TP3. But perhaps I misunderstood, or perhaps containers can be instantiated on “up” version container hosts but not “down” version.&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;Actually, on further examination he is &lt;strong&gt;remoting&lt;/strong&gt; into a different server that is acting as a &lt;em&gt;Container Host&lt;/em&gt; (10.205.158.127). So I can’t assume that this remote host is a Full Server—it could well be a NanoServer. So the above paragraph isn’t relevant.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;I also notice that he demos &lt;strong&gt;Hyper-V Containers&lt;/strong&gt;, which as far as I am aware aren’t working on TP3. So this would indicate a more recent build than TP3.&lt;/p&gt;&lt;p&gt;So perhaps we’ll see this image being made available in the Windows Server 2016 TP4 release?&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>File Server Resource Manager (FSRM) File Screen DSC Resource</title>
      <link href="https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-file-screen-dsc-resource/" />
      <updated>2015-10-26T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-file-screen-dsc-resource/</id>
      <content type="html">
				&lt;h2 id=&quot;introduction&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-file-screen-dsc-resource/#introduction&quot; class=&quot;heading-anchor&quot;&gt;Introduction&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Continuing on with implementing File Server Resource Manager (FSRM) DSC Modules, I’ve added a new module for configuring &lt;strong&gt;File Screens&lt;/strong&gt;, &lt;strong&gt;File Screen Templates&lt;/strong&gt; and &lt;strong&gt;File Screen Exceptions&lt;/strong&gt;. If you missed it the previous module for configuring quotas can be found &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/10/23/file-server-resource-manager-fsrm-quotas-dsc-resource/&quot; rel=&quot;noopener&quot;&gt;here.&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;resources&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-file-screen-dsc-resource/#resources&quot; class=&quot;heading-anchor&quot;&gt;Resources&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This module contains the following resources:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;cFSRMFileScreen&lt;/strong&gt; - configures FSRM File Screen. &lt;strong&gt;cFSRMFileScreenAction&lt;/strong&gt; - configures FSRM File Screen Actions for File Screens. &lt;strong&gt;cFSRMFileScreenTemplate&lt;/strong&gt; - configures FSRM File Screen Templates. &lt;strong&gt;cFSRMFileScreenTemplateAction&lt;/strong&gt; - configures FSRM File Screen Template Actions for File Screen Templates. &lt;strong&gt;cFSRMFileScreenExclusion&lt;/strong&gt; - configures FSRM File Screen Exclusions.&lt;/p&gt;&lt;p&gt;The purpose of the resources should be fairly self explanatory, as long as you have a basic understanding of how FSRM File Screens are used.&lt;/p&gt;&lt;h2 id=&quot;installing-the-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-file-screen-dsc-resource/#installing-the-resource&quot; class=&quot;heading-anchor&quot;&gt;Installing the Resource&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you have installed &lt;strong&gt;WMF 5.0&lt;/strong&gt; you can just download this&amp;nbsp;directly from the &lt;a href=&quot;https://www.powershellgallery.com/&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt; by running this command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name cFSRMFileScreens&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Otherwise you’ll need to download this from the Microsoft Script Center &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/cFSRMFileScreens-DSC-402a7f85&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; and unzip it into your &lt;strong&gt;PowerShell modules path&lt;/strong&gt;.&lt;/p&gt;&lt;h2 id=&quot;using-the-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-file-screen-dsc-resource/#using-the-resource&quot; class=&quot;heading-anchor&quot;&gt;Using the Resource&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As per the last post on these resources, rather than go into detail on using this resource, I thought I’d try and keep it short and just provide a &lt;a href=&quot;https://github.com/PlagueHO/cFSRMFileScreens&quot; rel=&quot;noopener&quot;&gt;link to the documentation&lt;/a&gt;. This covers the parameters available in the resources as well as some usage examples.&lt;/p&gt;&lt;p&gt;If you need some additional guidance or other specific examples, please feel free to let me know and I’ll do my best to help you out.&lt;/p&gt;&lt;p&gt;Hopefully this resource finds some use out there, but either way it has been extremely helpful to me really imprint the underlying FSRM features and usage into my own mind.&lt;/p&gt;&lt;h2 id=&quot;feedback&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-file-screen-dsc-resource/#feedback&quot; class=&quot;heading-anchor&quot;&gt;Feedback&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you’re interested in contributing to this resource, providing feedback or raising issues or requesting features, please feel free (anything is appreciated). You’ll find the resource GitHub repository &lt;a href=&quot;https://github.com/PlagueHO/cFSRMFileScreens&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; where you can fork, issue pull requests and raise issues/feature requests.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>File Server Resource Manager (FSRM) Quotas DSC Resource</title>
      <link href="https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-quotas-dsc-resource/" />
      <updated>2015-10-23T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-quotas-dsc-resource/</id>
      <content type="html">
				&lt;h3 id=&quot;introduction&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-quotas-dsc-resource/#introduction&quot; class=&quot;heading-anchor&quot;&gt;Introduction&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;After implementing (but not yet completing) the my &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/10/11/distributed-file-system-dsc-resource-update/&quot; rel=&quot;noopener&quot;&gt;DFS Replication Groups&lt;/a&gt; resource last week, I had an epiphany about another resource that I had begun writing some time ago but had run into problems with. The epiphany allowed me to resolve the issues holding up completion of this resource as well as dig more deeply into the &lt;strong&gt;FSRM&lt;/strong&gt; for my studies.&lt;/p&gt;&lt;h3 id=&quot;resources&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-quotas-dsc-resource/#resources&quot; class=&quot;heading-anchor&quot;&gt;Resources&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Initially I was going to create all of the &lt;strong&gt;FSRM Resources&lt;/strong&gt; (File Groups, File Classifications etc) in a single module, but I quickly realized that this wasn’t ideal as the number of modules to support this was actually quite large. Therefore I’ve decided to break this down into more manageable chunks. This is the first chunk. It contains the following resources:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;cFSRMFileQuota&lt;/strong&gt; - configures FSRM Quotas.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;cFSRMFileQuotaAction&lt;/strong&gt; - configures FSRM Quota Actions for Quotas.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;cFSRMFileQuotaTemplate&lt;/strong&gt; - configures FSRM Quota Templates.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;cFSRMFileQuotaTemplateAction&lt;/strong&gt; - configures FSRM Quota Template Actions for Quota Templates.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;cFSRMAutoQuota&lt;/strong&gt; - configures FSRM Auto Quotas.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The purpose of the resources should be fairly self explanatory, as long as you have a basic understanding of how FSRM Quotas are used. If you aren’t familiar with FSRM Quotas, &lt;a href=&quot;http://blogs.technet.com/b/josebda/archive/2008/08/20/the-basics-of-windows-server-2008-fsrm-file-server-resource-manager.aspx&quot; rel=&quot;noopener&quot;&gt;this&lt;/a&gt; is a good place to start - although why you’d be reading this if you’re not familiar with FSRM Quotas already is beyond me.&lt;/p&gt;&lt;p&gt;There are some other Quota management DSC Resources available online and they look very easy to use, but they don’t provide the complete set of functionality that these resources do because I tried to ensure that every Quota is available and as complete as possible. Which resources to use depends on your needs.&lt;/p&gt;&lt;h3 id=&quot;installing-the-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-quotas-dsc-resource/#installing-the-resource&quot; class=&quot;heading-anchor&quot;&gt;Installing the Resource&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;If you have installed &lt;strong&gt;WMF 5.0&lt;/strong&gt; you can just download this&amp;nbsp;directly from the &lt;a href=&quot;https://www.powershellgallery.com/&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt; by running this command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name cFSRMQuotas&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Otherwise you’ll need to download this from the Microsoft Script Center &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/cFSRMQuotas-DSC-Resource-114ec8cc&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; and unzip it into your &lt;strong&gt;PowerShell modules path&lt;/strong&gt;.&lt;/p&gt;&lt;h3 id=&quot;using-the-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-quotas-dsc-resource/#using-the-resource&quot; class=&quot;heading-anchor&quot;&gt;Using the Resource&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Rather than go into detail on using this resource in this post, I thought I’d try and keep it short and just provide a &lt;a href=&quot;https://github.com/PlagueHO/cFSRMQuotas&quot; rel=&quot;noopener&quot;&gt;link to the documentation&lt;/a&gt;. This covers the parameters available in the resources as well as some usage examples.&lt;/p&gt;&lt;p&gt;If you need some additional guidance or other specific examples, please feel free to let me know and I’ll do my best to help you out.&lt;/p&gt;&lt;h3 id=&quot;summary&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-quotas-dsc-resource/#summary&quot; class=&quot;heading-anchor&quot;&gt;Summary&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Well, there is not much more to say about this. Hopefully someone finds it useful. I intend to add complete the other chunks of the FSRM Resources over the coming weeks when I have time.&lt;/p&gt;&lt;h3 id=&quot;feedback&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/file-server-resource-manager-fsrm-quotas-dsc-resource/#feedback&quot; class=&quot;heading-anchor&quot;&gt;Feedback&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;If you’re interested in contributing to this resource, providing feedback or raising issues or requesting features, please feel free (anything is appreciated). You’ll find the resource GitHub repository &lt;a href=&quot;https://github.com/PlagueHO/cFSRMQuotas&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; where you can fork, issue pull requests and raise issues/feature requests.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>DSC Resource Kit Updates are Available</title>
      <link href="https://danielscottraynsford.com/blog/dsc-resource-kit-updates-are-available/" />
      <updated>2015-10-23T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/dsc-resource-kit-updates-are-available/</id>
      <content type="html">
				&lt;p&gt;The Microsoft DSC Resource Kit has been updated with a bunch of new stuff. You can check this out &lt;a href=&quot;http://blogs.msdn.com/b/powershell/archive/2015/10/23/dsc-resource-kit-updates-are-here.aspx&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;If you’re using DSC, you’re no doubt familiar with the Microsoft DSC Resource Kit as it provides some of the most useful resources available outside of the base DSC Resources. You should really go and check it out to see what sort of thing you can be configuring with DSC straight out of the box.&lt;/p&gt;&lt;p&gt;I’m especially proud of this release as it contains some stuff I’ve be contributing to outside of my own resources. I’ve been helping out with adding some code to the xNetworking resource.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>IPv6, DHCP and Get-NetIPInterface - DHCP State can be WRONG!</title>
      <link href="https://danielscottraynsford.com/blog/ipv6-dhcp-and-get-netipinterface-dhcp-state-can-be-wrong/" />
      <updated>2015-10-17T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/ipv6-dhcp-and-get-netipinterface-dhcp-state-can-be-wrong/</id>
      <content type="html">
				&lt;p&gt;Recently I’ve been attempting to help out with the awesome Microsoft Community DSC Resources by throwing in a bit of code here and there - especially into the &lt;a href=&quot;https://github.com/PowerShell/xNetworking&quot; rel=&quot;noopener&quot;&gt;xNetworking&lt;/a&gt; resource. I started contributing to them because I had a need for some specific features in these resources for some other projects I was working on.&lt;/p&gt;&lt;p&gt;Anyway, long story short I found myself investigating an odd little bug with the xIPAddress resource (it configures an IPv4 or IPv6 address on a Network adapter). The problem was that even though I had a network adapter with a &lt;strong&gt;statically&lt;/strong&gt; assigned &lt;strong&gt;IPv6&lt;/strong&gt; address, the &lt;strong&gt;Get-NetIPInterface&lt;/strong&gt; cmdlet&amp;nbsp;&lt;em&gt;always&lt;/em&gt; seemed to say that &lt;strong&gt;DHCP&lt;/strong&gt; was &lt;strong&gt;enabled&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_ip_dhcpmisreported.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/-4nBBWdRZz-650.png 650w, https://danielscottraynsford.com/img/-4nBBWdRZz-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/-4nBBWdRZz-650.webp 650w, https://danielscottraynsford.com/img/-4nBBWdRZz-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/-4nBBWdRZz-650.jpeg&quot; alt=&quot;The IPv6 address is clearly statically assigned but it says DHCP is enabled!&quot; width=&quot;960&quot; height=&quot;495&quot; srcset=&quot;https://danielscottraynsford.com/img/-4nBBWdRZz-650.jpeg 650w, https://danielscottraynsford.com/img/-4nBBWdRZz-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;The IPv6 address is clearly statically assigned but it says DHCP is enabled!&lt;/p&gt;&lt;p&gt;I am not sure if this is a bug in &lt;strong&gt;Get-NetIPInterface&lt;/strong&gt; that causes the &lt;strong&gt;DHCP&lt;/strong&gt; property to be misreported for &lt;strong&gt;IPv6&lt;/strong&gt; interfaces or if using this property to determine &lt;strong&gt;DHCP&lt;/strong&gt; status on an &lt;strong&gt;IPv6&lt;/strong&gt; address is not recommended.&lt;/p&gt;&lt;p&gt;Either way, I’m a bit stumped. I need an alternate and reliable way that can be used to detect the DHCP state of an &lt;strong&gt;IPv6&lt;/strong&gt; interface. I’ve looked at using the &lt;strong&gt;PrefixOrigin&lt;/strong&gt; and/or &lt;strong&gt;SuffixOrigin&lt;/strong&gt; properties of objects returned by &lt;strong&gt;Get-NetIPAddress&lt;/strong&gt; but this feels a little bit untrustworthy to me.&lt;/p&gt;&lt;p&gt;Well, if anyone reads this and has any ideas I’d be very grateful to hear about it!&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; After a bit more investigation on this, it seems you can quite happily set the &lt;strong&gt;DHCP&lt;/strong&gt; property on an &lt;strong&gt;IPv6&lt;/strong&gt; Interface using the &lt;strong&gt;Set-NetIPInterface&lt;/strong&gt; cmdlet to whatever you like, regardless of whether or not a &lt;em&gt;static IP address&lt;/em&gt; is assigned. So it seems that the &lt;strong&gt;DHCP&lt;/strong&gt; property returned by the &lt;strong&gt;Get-NetIPInterface&lt;/strong&gt; cmdlet for &lt;strong&gt;IPv6&lt;/strong&gt; addresses is meaningless. But I’d still love to know for sure.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Distributed File System DSC Resource Update</title>
      <link href="https://danielscottraynsford.com/blog/distributed-file-system-dsc-resource-update/" />
      <updated>2015-10-11T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/distributed-file-system-dsc-resource-update/</id>
      <content type="html">
				&lt;p&gt;After releasing the &lt;strong&gt;DFS DSC Resource Module&lt;/strong&gt; &lt;a href=&quot;https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/&quot;&gt;yesterday&lt;/a&gt;, I had an idea of how to simplify it if you’re deploying a DFS folder that contains the same path content path for all members. I added a &lt;strong&gt;ContentPaths&lt;/strong&gt; parameter (an array of strings) to the &lt;strong&gt;cDFSRepGroup&lt;/strong&gt; resource so that if the folder exists in the same location on every member, you won’t need to use the &lt;strong&gt;cDFSRepGroupMembership&lt;/strong&gt; resource to individually set the Content Path for each member.&lt;/p&gt;&lt;p&gt;For example:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;configuration Sample_cDFSRepGroup_Simple &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Module cDFS

    Node &lt;span class=&quot;token variable&quot;&gt;$NodeName&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[PSCredential]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Credential&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Management&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Automation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PSCredential &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;CONTOSO.COM&#92;Administrator&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConvertTo-SecureString&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MyP@ssw0rd!1&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AsPlainText &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;# Install the Prerequisite features first&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Requires Windows Server 2012 R2 Full install&lt;/span&gt;
        WindowsFeature RSATDFSMgmtConInstall &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Ensure = &lt;span class=&quot;token string&quot;&gt;&quot;Present&quot;&lt;/span&gt;
            Name   = &lt;span class=&quot;token string&quot;&gt;&quot;RSAT-DFS-Mgmt-Con&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;# Configure the Replication Group&lt;/span&gt;
        cDFSRepGroup RGPublic &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            GroupName            = &lt;span class=&quot;token string&quot;&gt;&#39;Public&#39;&lt;/span&gt;
            Description          = &lt;span class=&quot;token string&quot;&gt;&#39;Public files for use by all departments&#39;&lt;/span&gt;
            Ensure               = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
            Members              = &lt;span class=&quot;token string&quot;&gt;&#39;FileServer1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;FileServer2&#39;&lt;/span&gt;
            Folders              = &lt;span class=&quot;token string&quot;&gt;&#39;Software&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Misc&#39;&lt;/span&gt;
            Topology             = &lt;span class=&quot;token string&quot;&gt;&#39;Fullmesh&#39;&lt;/span&gt;
            ContentPaths         = &lt;span class=&quot;token string&quot;&gt;&#39;d:&#92;public&#92;software&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;d:&#92;public&#92;misc&#39;&lt;/span&gt;
            PSDSCRunAsCredential = &lt;span class=&quot;token variable&quot;&gt;$Credential&lt;/span&gt;
            DependsOn            = &lt;span class=&quot;token string&quot;&gt;&quot;[WindowsFeature]RSATDFSMgmtConInstall&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# End of RGPublic Resource&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# End of Node&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# End of Configuration&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above example creates a &lt;strong&gt;DFS Replication Group&lt;/strong&gt; called &lt;strong&gt;Public&lt;/strong&gt; containing two folders, &lt;strong&gt;Software&lt;/strong&gt; and&amp;nbsp;&lt;strong&gt;Misc&lt;/strong&gt;. The DFS Replication Group replicates to two members, &lt;strong&gt;FileServer1&lt;/strong&gt; and &lt;strong&gt;FileServer2&lt;/strong&gt;. It is maintaining a &lt;strong&gt;Fullmesh&lt;/strong&gt; connection topology.&lt;/p&gt;&lt;p&gt;The thing to note is that the &lt;strong&gt;ContentPaths&lt;/strong&gt; array should have the elements in a matching order to the &lt;strong&gt;Folders&lt;/strong&gt; parameter. So this:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Folders = &lt;span class=&quot;token string&quot;&gt;&#39;Misc&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Software&#39;&lt;/span&gt;
ContentPaths = &lt;span class=&quot;token string&quot;&gt;&#39;d:&#92;public&#92;software&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;d:&#92;public&#92;misc&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Would result in the &lt;strong&gt;Misc&lt;/strong&gt; folder being set with the &lt;strong&gt;Content Path&lt;/strong&gt; ‘d:&#92;public&#92;software’ and the &lt;strong&gt;Public&lt;/strong&gt; folder being set with the &lt;strong&gt;Content Path&lt;/strong&gt; ‘d:&#92;public&#92;misc’ - which is probably not ideal.&lt;/p&gt;&lt;h4 id=&quot;the-primary-member&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/distributed-file-system-dsc-resource-update/#the-primary-member&quot; class=&quot;heading-anchor&quot;&gt;The Primary Member&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Every &lt;strong&gt;Resource Group&lt;/strong&gt; Folder needs a &lt;strong&gt;Primary Member&lt;/strong&gt; set for initial replication to take place. If you use this automatic assigning of content paths the &lt;strong&gt;Primary Member&lt;/strong&gt; will automatically be set to the computer listed first in the &lt;strong&gt;Members&lt;/strong&gt; parameter. If you want to change this you’ll need to use the manual &lt;strong&gt;cDFSRepGroupMembership&lt;/strong&gt; resource instead.&lt;/p&gt;&lt;h4 id=&quot;partially-setting-content-paths&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/distributed-file-system-dsc-resource-update/#partially-setting-content-paths&quot; class=&quot;heading-anchor&quot;&gt;Partially Setting Content Paths&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;It is actually possible to only automatically configure some of the content paths in a &lt;strong&gt;DFS Replication Group&lt;/strong&gt; by leaving the appropriate &lt;strong&gt;ContentPaths&lt;/strong&gt; array entry blank. This would allow you to automatically configure some folders but leave other folders to be manually configured.&lt;/p&gt;&lt;p&gt;For example:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;cDFSRepGroup RGPublic &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    GroupName            = &lt;span class=&quot;token string&quot;&gt;&#39;Public&#39;&lt;/span&gt;
    Description          = &lt;span class=&quot;token string&quot;&gt;&#39;Public files for use by all departments&#39;&lt;/span&gt;
    Ensure               = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
    Members              = &lt;span class=&quot;token string&quot;&gt;&#39;FileServer1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;FileServer2&#39;&lt;/span&gt;
    Folders              = &lt;span class=&quot;token string&quot;&gt;&#39;Software&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Misc&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Video&#39;&lt;/span&gt;
    Topology             = &lt;span class=&quot;token string&quot;&gt;&#39;Fullmesh&#39;&lt;/span&gt;
    ContentPaths         = &lt;span class=&quot;token string&quot;&gt;&#39;d:&#92;public&#92;software&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;e:&#92;video&#39;&lt;/span&gt;
    PSDSCRunAsCredential = &lt;span class=&quot;token variable&quot;&gt;$Credential&lt;/span&gt;
    DependsOn            = &lt;span class=&quot;token string&quot;&gt;&quot;[WindowsFeature]RSATDFSMgmtConInstall&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# End of RGPublic Resource&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This would create a &lt;strong&gt;Replication Group&lt;/strong&gt; called &lt;strong&gt;Public&lt;/strong&gt;, with three folders &lt;strong&gt;Software, Misc&lt;/strong&gt; and &lt;strong&gt;Video&lt;/strong&gt;. The &lt;strong&gt;Software&lt;/strong&gt; and &lt;strong&gt;Video&lt;/strong&gt; folders will be automatically configured with &lt;strong&gt;Content Paths&lt;/strong&gt; but the &lt;strong&gt;Misc&lt;/strong&gt; folder will be left unconfigured so that it can be configured manually.&lt;/p&gt;&lt;h4 id=&quot;optional-use&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/distributed-file-system-dsc-resource-update/#optional-use&quot; class=&quot;heading-anchor&quot;&gt;Optional Use&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Using the &lt;strong&gt;ContentPaths&lt;/strong&gt; or &lt;strong&gt;Topology&lt;/strong&gt; parameters &lt;em&gt;is&lt;/em&gt; optional. You can still define the folder Content Paths manually using the &lt;strong&gt;cDFSRepGroupMembership&lt;/strong&gt;&amp;nbsp;resource and/or configure the connection topology manually using the &lt;strong&gt;cDFSRepGroupConnection&lt;/strong&gt; resource if you want to.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: It is not recommended that you define a &lt;strong&gt;ContentPath&lt;/strong&gt; for a folder in the &lt;strong&gt;cDFSRepGroup ContentPaths&lt;/strong&gt; parameter if you are also setting it in a &lt;strong&gt;cDFSRepGroupMembership&lt;/strong&gt; resource. The same applies to defining and automatic &lt;strong&gt;Topology&lt;/strong&gt; and using the &lt;strong&gt;cDFSRepGroupConnection&lt;/strong&gt; resource.&lt;/p&gt;&lt;p&gt;And again, in case you missed it, the post covering the original resource is &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/10/10/windows-distributed-file-system-dsc-resource/&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Windows Distributed File System DSC Resource</title>
      <link href="https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/" />
      <updated>2015-10-10T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/</id>
      <content type="html">
				&lt;h3 id=&quot;introduction&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/#introduction&quot; class=&quot;heading-anchor&quot;&gt;Introduction&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;While studying for my MS 70.411 exam, I found that one way of getting a good understanding of a feature is to perform as many feature tasks as possible using PowerShell. One especially useful way of doing this for me was to implement a &lt;em&gt;DSC resource&lt;/em&gt; for the feature. So, this week the feature was &lt;a href=&quot;https://technet.microsoft.com/en-us/library/jj127250.aspx&quot; rel=&quot;noopener&quot;&gt;Distributed File System Replication Groups&lt;/a&gt;. I’ll refer to &lt;em&gt;Distributed File Systems&lt;/em&gt; as &lt;strong&gt;DFS&lt;/strong&gt; in future to save typing.&lt;/p&gt;&lt;p&gt;Note: I am going to implement &lt;strong&gt;DFS Namespaces&lt;/strong&gt; as well, but that will be left to next week.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: After releasing this version I had an idea for some improvements to simplify this resource. See the details &lt;a href=&quot;https://danielscottraynsford.com/blog/distributed-file-system-dsc-resource-update/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&quot;node-vs-active-directory&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/#node-vs-active-directory&quot; class=&quot;heading-anchor&quot;&gt;Node vs. Active Directory&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The first thing to note with implementing a DSC resource for Windows Distributed File System is that the resource is actually setting the Desired State of Active Directory elements rather than that of a Node. What that means is that when you use the PowerShell (or Management Console) to manage &lt;strong&gt;Windows DFS Replication Groups&lt;/strong&gt; or &lt;strong&gt;DFS Namespaces&lt;/strong&gt; you’re actually configuring items in the &lt;em&gt;Active Directory database&lt;/em&gt; - you’re not changing anything on the actual node/computer you’re running the commands on.&lt;/p&gt;&lt;p&gt;This means that a DSC Resource for configuring &lt;strong&gt;Windows DFS&lt;/strong&gt; could be run on any computer within the AD Domain. This is actually very handy as it turns out. At first though, I wasn’t sure DSC should be used for configuring elements that aren’t actually on a Node/Computer, but I couldn’t see why not, and then I remembered that there are other resources that do this (&lt;a href=&quot;https://github.com/PowerShell/xActiveDirectory&quot; rel=&quot;noopener&quot;&gt;xActiveDirectory&lt;/a&gt; for example).&lt;/p&gt;&lt;h3 id=&quot;server-core-not-supported&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/#server-core-not-supported&quot; class=&quot;heading-anchor&quot;&gt;Server Core Not Supported&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The first problem I ran into when implementing this DSC Resource is that you &lt;strong&gt;can’t&lt;/strong&gt; install the DFS Replication (DFSR) PowerShell module onto a &lt;strong&gt;Windows Server Core&lt;/strong&gt; installation. This is because the &lt;strong&gt;PowerShell DFSR&lt;/strong&gt; &lt;strong&gt;module&lt;/strong&gt; is&amp;nbsp;&lt;em&gt;only&lt;/em&gt; installed with the &lt;strong&gt;DFS Management Tools&lt;/strong&gt; feature, which requires a &lt;strong&gt;Full Server install&lt;/strong&gt; (or at least the &lt;em&gt;Graphical Management Tools and Infrastructure feature&lt;/em&gt;).&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_dfs_installmanagementtools.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/TDY589fPYf-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/TDY589fPYf-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/TDY589fPYf-650.jpeg&quot; alt=&quot;This feature is required to enable the DFSR PowerShell Module.&quot; width=&quot;650&quot; height=&quot;460&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;This feature is required to enable the DFSR PowerShell Module.&lt;/p&gt;&lt;p&gt;This isn’t the end of the world, but it is annoying because all my file servers are Server Core. Therefore I’d need to run this resource on a &lt;strong&gt;node&lt;/strong&gt; with a &lt;strong&gt;Full Server&lt;/strong&gt; &lt;strong&gt;install&lt;/strong&gt; that is also &lt;strong&gt;part of the AD Domain.&lt;/strong&gt; So it is great that this resource can be run on any Full Server install (or even a Desktop with RSAT).&lt;/p&gt;&lt;h3 id=&quot;setting-ad-credentials&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/#setting-ad-credentials&quot; class=&quot;heading-anchor&quot;&gt;Setting AD Credentials&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Because this resource calls PowerShell CmdLets that interact with the &lt;strong&gt;AD Database&lt;/strong&gt;, &lt;strong&gt;AD credentials&lt;/strong&gt; need to be supplied that can have the &lt;em&gt;appropriate permissions&lt;/em&gt;. This means that the &lt;strong&gt;PSDSCRunAsCredential&lt;/strong&gt; property must be set for each resource entry, which in turn means this Resource &lt;strong&gt;can only be used on nodes with Windows Management Framework 5.0 (WMF 5.0) or greater installed&lt;/strong&gt;. If you’re not familiar with this property, see &lt;a href=&quot;http://blogs.msdn.com/b/powershell/archive/2015/07/06/validate-powershell-dsc-runascredential.aspx&quot; rel=&quot;noopener&quot;&gt;this link&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&quot;installing-the-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/#installing-the-resource&quot; class=&quot;heading-anchor&quot;&gt;Installing the Resource&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Because this resource requires &lt;strong&gt;WMF 5.0&lt;/strong&gt; you can just download this&amp;nbsp;directly from the &lt;a href=&quot;https://www.powershellgallery.com/&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt; by running this command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;content&#92;blog&#92;2015&#92;10&#92;2015-10-10-windows-distributed-file-system-dsc-resource.md&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name cDFS&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;using-the-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/#using-the-resource&quot; class=&quot;heading-anchor&quot;&gt;Using the Resource&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The following example creates a &lt;strong&gt;DFS Replication Group&lt;/strong&gt; called &lt;strong&gt;Public&lt;/strong&gt; containing two members, &lt;strong&gt;FileServer1&lt;/strong&gt; and &lt;strong&gt;FileServer2&lt;/strong&gt;. The &lt;strong&gt;Replication Group&lt;/strong&gt; contains a single folder called &lt;strong&gt;Software&lt;/strong&gt;. A description will be set on the &lt;strong&gt;Software&lt;/strong&gt; folder and it will be set to exclude the directory &lt;strong&gt;Temp&lt;/strong&gt; from replication.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;content&#92;blog&#92;2015&#92;10&#92;2015-10-10-windows-distributed-file-system-dsc-resource.md&lt;/span&gt;
configuration Sample_cDFSRepGroup &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Module cDFS

    Node &lt;span class=&quot;token variable&quot;&gt;$NodeName&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[PSCredential]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Credential&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Management&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Automation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PSCredential &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;CONTOSO.COM&#92;Administrator&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConvertTo-SecureString&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MyP@ssw0rd!1&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AsPlainText &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;# Install the Prerequisite features first&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Requires Windows Server 2012 R2 Full install&lt;/span&gt;
        WindowsFeature RSATDFSMgmtConInstall &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Ensure = &lt;span class=&quot;token string&quot;&gt;&quot;Present&quot;&lt;/span&gt;
            Name   = &lt;span class=&quot;token string&quot;&gt;&quot;RSAT-DFS-Mgmt-Con&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;# Configure the Replication Group&lt;/span&gt;
        cDFSRepGroup RGPublic &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            GroupName            = &lt;span class=&quot;token string&quot;&gt;&#39;Public&#39;&lt;/span&gt;
            Description          = &lt;span class=&quot;token string&quot;&gt;&#39;Public files for use by all departments&#39;&lt;/span&gt;
            Ensure               = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
            Members              = &lt;span class=&quot;token string&quot;&gt;&#39;FileServer1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;FileServer2&#39;&lt;/span&gt;
            Folders              = &lt;span class=&quot;token string&quot;&gt;&#39;Software&#39;&lt;/span&gt;
            PSDSCRunAsCredential = &lt;span class=&quot;token variable&quot;&gt;$Credential&lt;/span&gt;
            DependsOn            = &lt;span class=&quot;token string&quot;&gt;&quot;[WindowsFeature]RSATDFSMgmtConInstall&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        cDFSRepGroupConnection RGPublicC1 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            GroupName                = &lt;span class=&quot;token string&quot;&gt;&#39;Public&#39;&lt;/span&gt;
            Ensure                   = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
            SourceComputerName       = &lt;span class=&quot;token string&quot;&gt;&#39;FileServer1&#39;&lt;/span&gt;
            DestinationComputerName  = &lt;span class=&quot;token string&quot;&gt;&#39;FileServer2&#39;&lt;/span&gt;
            PSDSCRunAsCredential     = &lt;span class=&quot;token variable&quot;&gt;$Credential&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        cDFSRepGroupConnection RGPublicC2 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            GroupName                = &lt;span class=&quot;token string&quot;&gt;&#39;Public&#39;&lt;/span&gt;
            Ensure                   = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
            SourceComputerName       = &lt;span class=&quot;token string&quot;&gt;&#39;FileServer2&#39;&lt;/span&gt;
            DestinationComputerName  = &lt;span class=&quot;token string&quot;&gt;&#39;FileServer1&#39;&lt;/span&gt;
            PSDSCRunAsCredential     = &lt;span class=&quot;token variable&quot;&gt;$Credential&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        cDFSRepGroupFolder RGSoftwareFolder &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            GroupName                = &lt;span class=&quot;token string&quot;&gt;&#39;Public&#39;&lt;/span&gt;
            FolderName               = &lt;span class=&quot;token string&quot;&gt;&#39;Software&#39;&lt;/span&gt;
            Description              = &lt;span class=&quot;token string&quot;&gt;&#39;DFS Share for storing software installers&#39;&lt;/span&gt;
            DirectoryNameToExclude   = &lt;span class=&quot;token string&quot;&gt;&#39;Temp&#39;&lt;/span&gt;
            PSDSCRunAsCredential     = &lt;span class=&quot;token variable&quot;&gt;$Credential&lt;/span&gt;
            DependsOn                = &lt;span class=&quot;token string&quot;&gt;&#39;[cDFSRepGroup]RGPublic&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        cDFSRepGroupMembership RGPublicSoftwareFS1 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            GroupName                = &lt;span class=&quot;token string&quot;&gt;&#39;Public&#39;&lt;/span&gt;
            FolderName               = &lt;span class=&quot;token string&quot;&gt;&#39;Software&#39;&lt;/span&gt;
            ComputerName             = &lt;span class=&quot;token string&quot;&gt;&#39;FileServer1&#39;&lt;/span&gt;
            ContentPath              = &lt;span class=&quot;token string&quot;&gt;&#39;d:&#92;Public&#92;Software&#39;&lt;/span&gt;
            PrimaryMember            = &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;
            PSDSCRunAsCredential     = &lt;span class=&quot;token variable&quot;&gt;$Credential&lt;/span&gt;
            DependsOn                = &lt;span class=&quot;token string&quot;&gt;&#39;[cDFSRepGroupFolder]RGSoftwareFolder&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        cDFSRepGroupMembership RGPublicSoftwareFS2 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            GroupName                = &lt;span class=&quot;token string&quot;&gt;&#39;Public&#39;&lt;/span&gt;
            FolderName               = &lt;span class=&quot;token string&quot;&gt;&#39;Software&#39;&lt;/span&gt;
            ComputerName             = &lt;span class=&quot;token string&quot;&gt;&#39;FileServer2&#39;&lt;/span&gt;
            ContentPath              = &lt;span class=&quot;token string&quot;&gt;&#39;e:&#92;Data&#92;Public&#92;Software&#39;&lt;/span&gt;
            PSDSCRunAsCredential     = &lt;span class=&quot;token variable&quot;&gt;$Credential&lt;/span&gt;
            DependsOn                = &lt;span class=&quot;token string&quot;&gt;&#39;[cDFSRepGroupFolder]RGPublicSoftwareFS1&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;example-breakdown&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/#example-breakdown&quot; class=&quot;heading-anchor&quot;&gt;Example Breakdown&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The resource usage hopefully is fairly straight forward and the Module itself contains documentation in the &lt;strong&gt;&lt;a href=&quot;http://Readme.md&quot; rel=&quot;noopener&quot;&gt;Readme.md&lt;/a&gt;&lt;/strong&gt; (you can also see it &lt;a href=&quot;https://github.com/PlagueHO/cDFS/blob/master/README.md&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;). But I’ll provide a quick breakdown of the resources just in case.&lt;/p&gt;&lt;h4 id=&quot;windowsfeature-rsatdfsmgmtconinstall&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/#windowsfeature-rsatdfsmgmtconinstall&quot; class=&quot;heading-anchor&quot;&gt;WindowsFeature RSATDFSMgmtConInstall&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Install the Windows Feature that is required to use this DSC Resource. It installs the &lt;strong&gt;Windows DFSR/DFSN PowerShell Modules&lt;/strong&gt;.&lt;/p&gt;&lt;h4 id=&quot;cdfsrepgroup&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/#cdfsrepgroup&quot; class=&quot;heading-anchor&quot;&gt;cDFSRepGroup&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;This resource creates, configures or removes a &lt;strong&gt;DFS Replication Group&lt;/strong&gt;. You should specify both the &lt;strong&gt;Members&lt;/strong&gt; and the &lt;strong&gt;Folders&lt;/strong&gt; that are in this Replication Group. Both of these properties take an array of strings so you can specify more than one member (not much of a Distributed File System without that right?) and more than one folder. You of course also need to specify a &lt;strong&gt;DFS Replication Group Name&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;This resource also contains an optional &lt;strong&gt;Topology&lt;/strong&gt; parameter that defaults to &lt;strong&gt;Manual&lt;/strong&gt;. If this parameter is set to &lt;strong&gt;Fullmesh&lt;/strong&gt; then a &lt;strong&gt;Full Mesh&lt;/strong&gt; connection topology will be configured automatically for this &lt;strong&gt;Replication Group&lt;/strong&gt;, based on the members specified in the resource.&lt;/p&gt;&lt;h4 id=&quot;cdfsrepgroupconnection&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/#cdfsrepgroupconnection&quot; class=&quot;heading-anchor&quot;&gt;cDFSRepGroupConnection&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;This is an optional resource that allows the &lt;strong&gt;Replication Group Connections&lt;/strong&gt; to be defined manually. I used the above example, so that it was obvious how they should be used. It allows a &lt;strong&gt;Replication Group Connection&lt;/strong&gt; to be defined for a &lt;strong&gt;Replication Group&lt;/strong&gt; between two members. A description can also be set on each&amp;nbsp;connection. The connections can be disabled and also have RDC (Remote Differential Compression) disabled.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; this resource should only be used if the &lt;strong&gt;Topology&lt;/strong&gt; parameter of the &lt;strong&gt;cDFSRepGroup&lt;/strong&gt; resource is set to &lt;strong&gt;Manual&lt;/strong&gt; (which is the default). If you set the &lt;strong&gt;Topology&lt;/strong&gt; parameter to &lt;strong&gt;Fullmesh&lt;/strong&gt;, a set of&amp;nbsp;&lt;strong&gt;Replication Group Connections&lt;/strong&gt; will automatically be created in a &lt;strong&gt;Full Mesh&lt;/strong&gt; structure. The &lt;strong&gt;Hub and Spoke&lt;/strong&gt; structure is not currently supported but may be in the future.&lt;/p&gt;&lt;h4 id=&quot;cdfsrepgroupfolder&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/#cdfsrepgroupfolder&quot; class=&quot;heading-anchor&quot;&gt;cDFSRepGroupFolder&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;This is an optional resource that can be used to configure specific properties of any of the folders in a &lt;strong&gt;DFS Replication Group&lt;/strong&gt;. It is &lt;strong&gt;not&lt;/strong&gt; used to create a folder within the &lt;strong&gt;Replication Group&lt;/strong&gt;, that is the job of the &lt;em&gt;cDFSRepGroup&lt;/em&gt; resource. This job of this resource is to configure the following properties of a &lt;strong&gt;Replication Group Folder&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;FilenameToExclude&lt;/strong&gt; - if this is not specified the default value that DFS assigns is automatically used.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;DirectoryNameToExclude&lt;/strong&gt;&amp;nbsp;- if this is not specified the default value that DFS assigns is automatically used.&lt;/li&gt;&lt;/ul&gt;&lt;h4 id=&quot;cdfsrepgroupmembership&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/#cdfsrepgroupmembership&quot; class=&quot;heading-anchor&quot;&gt;cDFSRepGroupMembership&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;This resource is used to configure the actual &lt;em&gt;content folders&lt;/em&gt; on each member of the&amp;nbsp;&lt;strong&gt;Replication Group Folder&lt;/strong&gt;. An instance of this resource should be used for each combination of &lt;strong&gt;member&lt;/strong&gt; and &lt;strong&gt;folder&lt;/strong&gt; in a &lt;strong&gt;Replication Group&lt;/strong&gt; to set the&amp;nbsp;&lt;strong&gt;Content Folder&lt;/strong&gt;. It can also be used to set the following optional properties:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;StagingPath&lt;/strong&gt; - this can be used to override the default staging path. Usually this should be left to the default.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;ReadOnly&lt;/strong&gt; - this property can be used to make this content folder read only.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;PrimaryMember&lt;/strong&gt; - this property allows a &lt;strong&gt;Primary Member&lt;/strong&gt; of the replication group to be set. At least one member of each Replication Group folder &lt;strong&gt;must&lt;/strong&gt; set as the &lt;strong&gt;Primary Member&lt;/strong&gt; otherwise initial replication will never take place.&lt;/li&gt;&lt;/ul&gt;&lt;h4 id=&quot;common-parameters&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/#common-parameters&quot; class=&quot;heading-anchor&quot;&gt;Common Parameters&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;There are a couple of parameters that are common to each resource:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;GroupName&lt;/strong&gt; - this is the name of the&amp;nbsp;&lt;strong&gt;Replication Group&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Domain&lt;/strong&gt; - this is the name of the AD Domain this &lt;strong&gt;Replication Group&lt;/strong&gt; is part of. If not specified then the AD Domain that the computer that is running the config is part of is used. Usually it should not be specified.&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;summary&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/#summary&quot; class=&quot;heading-anchor&quot;&gt;Summary&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Well, there is not much more to say about this. Hopefully someone finds it useful. I intend to add &lt;strong&gt;DFS Namespace&lt;/strong&gt; support over the next week or so, so if you’re needing that, keep an eye out.&lt;/p&gt;&lt;h3 id=&quot;feedback&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/windows-distributed-file-system-dsc-resource/#feedback&quot; class=&quot;heading-anchor&quot;&gt;Feedback&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;If you’re interested in contributing to this resource, providing feedback or raising issues or requesting features, please feel free (anything is appreciated). You’ll find the resource GitHub repository &lt;a href=&quot;https://github.com/PlagueHO/cDFS&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; where you can fork, issue pull requests and raise issues/feature requests.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Programming Sucks</title>
      <link href="https://danielscottraynsford.com/blog/programming-sucks/" />
      <updated>2015-10-07T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/programming-sucks/</id>
      <content type="html">
				&lt;p&gt;Even if you’re not a programmer, this is one of the funniest things I’ve read in a long time (and, terrifyingly, not completely untrue):&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://www.stilldrinking.org/programming-sucks&quot; rel=&quot;noopener&quot;&gt;Programming Sucks&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Go and read it now.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Creating WS-Man Listeners with DSC</title>
      <link href="https://danielscottraynsford.com/blog/creating-ws-man-listeners-with-dsc/" />
      <updated>2015-10-05T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/creating-ws-man-listeners-with-dsc/</id>
      <content type="html">
				&lt;h3 id=&quot;introduction&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-ws-man-listeners-with-dsc/#introduction&quot; class=&quot;heading-anchor&quot;&gt;Introduction&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;After my last post showing &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/09/27/install-an-ssl-ws-management-listener-with-gpo/&quot; rel=&quot;noopener&quot;&gt;how to create an SSL/HTTPS listener using GPO&lt;/a&gt;, I thought this might be a good fit for a Desired State Configuration Resource. So after a rainy Saturday morning coding I had it working nicely.&lt;/p&gt;&lt;p&gt;You might ask “what is the point of adding HTTPS/SSL WS-Man Listeners when HTTP WS-Man Listeners are usually enabled by default”? Well, first off, it ensures you’re going to be connecting to the server you actually think you’re connecting to. This is pretty important and helps protect against &lt;em&gt;DNS poisoning&lt;/em&gt; and &lt;em&gt;man-in-the-middle&lt;/em&gt; attacks. It also means you don’t have to set the WS-Man client trusted hosts setting on your client machines to bypass host name checking for your servers:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_wsman_nomoretrustedhosts.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/Pnp81p7yfk-650.png 650w, https://danielscottraynsford.com/img/Pnp81p7yfk-860.png 860w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Pnp81p7yfk-650.webp 650w, https://danielscottraynsford.com/img/Pnp81p7yfk-860.webp 860w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Pnp81p7yfk-650.jpeg&quot; alt=&quot;No more of this!&quot; width=&quot;860&quot; height=&quot;195&quot; srcset=&quot;https://danielscottraynsford.com/img/Pnp81p7yfk-650.jpeg 650w, https://danielscottraynsford.com/img/Pnp81p7yfk-860.jpeg 860w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;No more of this!&lt;/p&gt;&lt;h3 id=&quot;https/ssl-and-certificates&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-ws-man-listeners-with-dsc/#https/ssl-and-certificates&quot; class=&quot;heading-anchor&quot;&gt;HTTPS/SSL and Certificates&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This DSC Resource essentially allows you to create, configure and remove HTTP and HTTPS/SSL WS-Man Listeners. That is pretty much it.&lt;/p&gt;&lt;p&gt;However, the most common use is going to be creating an HTTPS/SSL listener by automatically detecting the appropriate certificate to use. It uses the exact same method of doing this as described in &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/09/27/install-an-ssl-ws-management-listener-with-gpo/&quot; rel=&quot;noopener&quot;&gt;this&lt;/a&gt; post, so I won’t go over it here. But essentially, all you need to do is provide the full name of the Issuing CA that will have issued the certificate to the computer. The certificate would normally have been created and assigned to each server using &lt;strong&gt;Certificate Autoenrollment&lt;/strong&gt; using an &lt;em&gt;Active Directory Certificate Services PKI&lt;/em&gt; and &lt;em&gt;GPO&lt;/em&gt;, but this is not required - you could use any certificate enrollment method.&lt;/p&gt;&lt;h3 id=&quot;installing-the-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-ws-man-listeners-with-dsc/#installing-the-resource&quot; class=&quot;heading-anchor&quot;&gt;Installing the Resource&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The first thing that needs to be done is installing the &lt;strong&gt;cWSMan&lt;/strong&gt; Module. If you’re using WMF 5.0 you can get this directly from the &lt;a href=&quot;https://www.powershellgallery.com/&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt; by running this command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;content&#92;blog&#92;2015&#92;10&#92;2015-10-05-creating-ws-man-listeners-with-dsc.md&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name cWSMan &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MinimumVersion 1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you’re using WMF 4.0 then you’ll need to get this from the &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/cWSMan-DSC-Resource-c29af3fd&quot; rel=&quot;noopener&quot;&gt;Microsoft Script Center&lt;/a&gt;. But of course, you’re using &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/06/09/installing-windows-management-framework-5-0-with-a-gpo/&quot; rel=&quot;noopener&quot;&gt;WMF 5.0&lt;/a&gt; right?&lt;/p&gt;&lt;p&gt;Once it is installed you can integrate it into your DSC Scripts.&lt;/p&gt;&lt;h3 id=&quot;using-the-resource&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-ws-man-listeners-with-dsc/#using-the-resource&quot; class=&quot;heading-anchor&quot;&gt;Using the Resource&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The most likely thing you’re going to want to do is install an HTTPS/SSL Listener. To do that all you need to do is something like this:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;content&#92;blog&#92;2015&#92;10&#92;2015-10-05-creating-ws-man-listeners-with-dsc.md&lt;/span&gt;
configuration Sample_cWSManListener_HTTPS &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Module cWSMan

    Node Server01 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        cWSManListener HTTPS &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Transport = &lt;span class=&quot;token string&quot;&gt;&#39;HTTPS&#39;&lt;/span&gt;
            Ensure    = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
            Issuer    = &lt;span class=&quot;token string&quot;&gt;&#39;CN=CONTOSO.COM Issuing CA, DC=CONTOSO, DC=COM&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# End of cWSManListener Resource&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# End of Node&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# End of Configuration&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This would install an HTTPS/SSL Listener onto the default port of 5986 using a certificate that was issued by &lt;em&gt;&lt;a href=&quot;http://CN=CONTOSO.COM&quot; rel=&quot;noopener&quot;&gt;CN=CONTOSO.COM&lt;/a&gt; Issuing CA, DC=CONTOSO, DC=COM&lt;/em&gt;. There really is nothing to it - it is actually more fiddly getting your PKI set up than doing this part.&lt;/p&gt;&lt;p&gt;You can also configure the &lt;strong&gt;port&lt;/strong&gt; and &lt;strong&gt;address to bind&lt;/strong&gt; the HTTPS/SSL Listener to by passing the &lt;strong&gt;port&lt;/strong&gt; and &lt;strong&gt;address&lt;/strong&gt; parameters as well:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;content&#92;blog&#92;2015&#92;10&#92;2015-10-05-creating-ws-man-listeners-with-dsc.md&lt;/span&gt;
configuration Sample_cWSManListener_HTTPS &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Module cWSMan

    Node Server01 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        cWSManListener HTTPS &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Transport = &lt;span class=&quot;token string&quot;&gt;&#39;HTTPS&#39;&lt;/span&gt;
            Ensure    = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
            Issuer    = &lt;span class=&quot;token string&quot;&gt;&#39;CN=CONTOSO.COM Issuing CA, DC=CONTOSO, DC=COM&#39;&lt;/span&gt;
            Port      = 7000
            Address   = &lt;span class=&quot;token string&quot;&gt;&#39;192.168.1.55&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# End of cWSManListener Resource&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# End of Node&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# End of Configuration&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you don’t provide the &lt;strong&gt;port&lt;/strong&gt; and &lt;strong&gt;address&lt;/strong&gt; parameters they default to 5986 (or 5985 for HTTP listeners) and ‘*’ respectively.&lt;/p&gt;&lt;p&gt;You can also use this resource to &lt;em&gt;remove&lt;/em&gt; an HTTP or HTTPS listener. For example you might want to remove the default HTTP listener so that it can’t be used once your HTTPS listener has been created. To do that:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;content&#92;blog&#92;2015&#92;10&#92;2015-10-05-creating-ws-man-listeners-with-dsc.md&lt;/span&gt;
configuration Remove_cWSManListener_HTTP &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Import-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Module cWSMan

    Node Server01 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        cWSManListener HTTP &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Transport = &lt;span class=&quot;token string&quot;&gt;&#39;HTTP&#39;&lt;/span&gt;
            Ensure    = &lt;span class=&quot;token string&quot;&gt;&#39;Absent&#39;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# End of cWSManListener Resource&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# End of Node&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# End of Configuration&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;feedback&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/creating-ws-man-listeners-with-dsc/#feedback&quot; class=&quot;heading-anchor&quot;&gt;Feedback&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;If you’re interested in contributing to this resource, providing feedback or raising issues or requesting features, please feel free (anything is appreciated). You’ll find the resource GitHub repository &lt;a href=&quot;https://github.com/PlagueHO/cWSMan&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; where you can fork, issue pull requests and raise issues/feature requests.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>NAP, DHCP and Windows 10 - Nope!</title>
      <link href="https://danielscottraynsford.com/blog/nap-dhcp-and-windows-10-nope/" />
      <updated>2015-09-29T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/nap-dhcp-and-windows-10-nope/</id>
      <content type="html">
				&lt;p&gt;I just spent a good hour trying to figure out why my &lt;strong&gt;Windows 10&lt;/strong&gt; clients were not getting assigned an IP Address from my DHCP servers once I enabled &lt;em&gt;NAP integration on the scope&lt;/em&gt;. The reason, of course, is obvious: &lt;a href=&quot;http://windowsitpro.com/blog/3-reasons-why-network-access-protection-being-phased-out&quot; rel=&quot;noopener&quot;&gt;NAP was deprecated in Windows Server 2012 R2&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The NAP client is &lt;strong&gt;not&lt;/strong&gt; available on Windows 10 computers. You can’t even see the Network Access Policy node when you edit a GPO using &lt;strong&gt;Windows 10 RSAT&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/tXzY_SwyuH-353.png 353w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/tXzY_SwyuH-353.webp 353w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/tXzY_SwyuH-353.jpeg&quot; alt=&quot;NAP on Windows 10? Nope.&quot; width=&quot;353&quot; height=&quot;528&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;So if you’re wanting to configure your Windows 10 computers with DHCP and you’re using NAP, you’ll need to disable it or create a special scope without NAP enabled with a &lt;strong&gt;DHCP Scope Policy&lt;/strong&gt; for your Windows 10 clients. As this technology has been deprecated, you’re probably better off &lt;strong&gt;removing NAP entirely&lt;/strong&gt;. Pity I’m having to spend time studying it for my 70.411 exam.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Install an SSL WS-Management Listener with GPO</title>
      <link href="https://danielscottraynsford.com/blog/install-an-ssl-ws-management-listener-with-gpo/" />
      <updated>2015-09-27T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/install-an-ssl-ws-management-listener-with-gpo/</id>
      <content type="html">
				&lt;h2 id=&quot;introduction&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-an-ssl-ws-management-listener-with-gpo/#introduction&quot; class=&quot;heading-anchor&quot;&gt;Introduction&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;One of the things I like to do whenever I install a new server is to enable an &lt;strong&gt;HTTPS/SSL WS-Management Listener&lt;/strong&gt; on it so that I can disable the more insecure HTTP WS-Management listener. For more information on WS-Management Listeners see &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/aa384372%28v=vs.85%29.aspx&quot; rel=&quot;noopener&quot;&gt;this MSDN article&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;There are many benefits to using a secure &lt;strong&gt;HTTPS/SSL WS-Management Listener&lt;/strong&gt;:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt; - the communication channel between client and server is encrypted using SSL.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Authentication&lt;/strong&gt; - the server is authenticated to the client so you can trust you’re talking to the server you think you’re talking to.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The downside to this is that you need a &lt;em&gt;valid and trusted server authentication certificate&lt;/em&gt; on the server to enable this - but if you have a &lt;strong&gt;PKI&lt;/strong&gt; then this is no big deal as you’ll probably have &lt;em&gt;certificate autoenrollment&lt;/em&gt; enabled. If you don’t have a &lt;strong&gt;PKI&lt;/strong&gt;, then you should look into &lt;a href=&quot;https://technet.microsoft.com/en-us/library/cc772393%28v=ws.10%29.aspx&quot; rel=&quot;noopener&quot;&gt;installing one&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Installing these listeners &lt;em&gt;manually&lt;/em&gt; is fairly straight forward and requires only a single command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;content&#92;blog&#92;2015&#92;09&#92;2015-09-27-install-an-ssl-ws-management-listener-with-gpo.md&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;New-WSManInstance&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResourceURI winrm/config/Listener `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SelectorSet @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Address=&lt;span class=&quot;token string&quot;&gt;&#39;*&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; Transport=&lt;span class=&quot;token string&quot;&gt;&quot;HTTPS&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; `
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ValueSet @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Hostname=&lt;span class=&quot;token string&quot;&gt;&quot;SERVER01.CONTOSO.COM&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; CertificateThumbprint=&lt;span class=&quot;token string&quot;&gt;&quot;09 49 93 24 53 81 32 16 b7 44 8b 47 ca af 56 3a ef 9f 10 2d&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;All you need to do is enter the appropriate &lt;strong&gt;hostname&lt;/strong&gt; and &lt;strong&gt;certificate thumbprint&lt;/strong&gt; for a &lt;em&gt;server authentication&lt;/em&gt; certificate that exists on the server. But who wants to do this manually, right?&lt;/p&gt;&lt;h2 id=&quot;installing-with-gpo&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-an-ssl-ws-management-listener-with-gpo/#installing-with-gpo&quot; class=&quot;heading-anchor&quot;&gt;Installing with GPO&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The slightly tricky part of installing this automatically onto your servers with a GPO is detecting which certificate to use. The certificate must:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;exist in the &lt;strong&gt;local computer personal certificate store&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;have an Extended Key Usage of &lt;strong&gt;Server Authentication&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;be issued by a &lt;strong&gt;CA trusted&lt;/strong&gt; by any client connecting to the server.&lt;/li&gt;&lt;li&gt;The &lt;strong&gt;Subject&lt;/strong&gt; must contain a &lt;strong&gt;Common Name&lt;/strong&gt; that contains &lt;em&gt;either&lt;/em&gt; the FQDN computer name or the flat computer name (e.g. &lt;a href=&quot;http://CN=SERVER1.CONTOSO.COM&quot; rel=&quot;noopener&quot;&gt;CN=SERVER1.CONTOSO.COM&lt;/a&gt; or CN=SERVER1).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;It is easy to ensure a certificate meets these criteria by using a GPO enabling &lt;strong&gt;certificate autoenrollment&lt;/strong&gt; for computer certificates and that the Computer &lt;strong&gt;autoenrollment certificate template&lt;/strong&gt; will create certificates meeting these requirements. See &lt;a href=&quot;https://technet.microsoft.com/en-us/library/cc731522.aspx&quot; rel=&quot;noopener&quot;&gt;this page&lt;/a&gt; for some basic information on certificate autoenrollment. There are some much more detailed instructions on this around the net if you’re happy to search.&lt;/p&gt;&lt;p&gt;Once you’ve ensured all your computers have been issued such a certificate you would normally need to lookup the certificate on each computer and get the certificate thumbprint and execute the command I showed earlier. This would be a complete pain on any more than 10 computers, and probably pure insanity at a lot of facilities.&lt;/p&gt;&lt;p&gt;So, I put together the following PowerShell commands that could be used to automatically pull the certificate thumbprint for an appropriate certificate:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;content&#92;blog&#92;2015&#92;09&#92;2015-09-27-install-an-ssl-ws-management-listener-with-gpo.md&lt;/span&gt;
&lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Issuer&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;CN=CONTOSO.COM Issuing CA, DC=CONTOSO, DC=COM&#39;&lt;/span&gt;
&lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$HostName&lt;/span&gt; = &lt;span class=&quot;token namespace&quot;&gt;[System.Net.Dns]&lt;/span&gt;::GetHostByName&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:computerName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Hostname
&lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Thumbprint&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path Cert:&#92;localmachine&#92;my &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Where-Object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Extensions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;EnhancedKeyUsages&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;FriendlyName &lt;span class=&quot;token operator&quot;&gt;-contains&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Server Authentication&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-and&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;IssuerName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Issuer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-and&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$HostName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-in&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DNSNameList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Unicode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-and&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Subject &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CN=&lt;span class=&quot;token variable&quot;&gt;$HostName&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Select-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;First 1
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Thumbprint&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;All you’d need to set was the Issuer to whatever the &lt;em&gt;Distinguished Name&lt;/em&gt; of your &lt;strong&gt;issuing CA&lt;/strong&gt; is - which should be the same for all computers. This simplifies things a lot because the same code could be run on any computer and should always return the correct thumbprint.&lt;/p&gt;&lt;p&gt;The next step was to put it into a script where you could just pass the &lt;em&gt;Distinguished Name&lt;/em&gt; of the &lt;strong&gt;issuing CA&lt;/strong&gt; as a parameter. I did this and also added some other optional parameters and uploaded the result to &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/PowerShell-used-to-easily-22067907&quot; rel=&quot;noopener&quot;&gt;Microsoft Script Center&lt;/a&gt;. So you can download this script and put it into a GPO Startup Script:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_gpo_httpswsmanlistener.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/38f0z2UoU4-650.png 650w, https://danielscottraynsford.com/img/38f0z2UoU4-960.png 960w, https://danielscottraynsford.com/img/38f0z2UoU4-1365.png 1365w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/38f0z2UoU4-650.webp 650w, https://danielscottraynsford.com/img/38f0z2UoU4-960.webp 960w, https://danielscottraynsford.com/img/38f0z2UoU4-1365.webp 1365w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/38f0z2UoU4-650.jpeg&quot; alt=&quot;Installing an HTTPS WS-Management Listener with GPO&quot; width=&quot;1365&quot; height=&quot;728&quot; srcset=&quot;https://danielscottraynsford.com/img/38f0z2UoU4-650.jpeg 650w, https://danielscottraynsford.com/img/38f0z2UoU4-960.jpeg 960w, https://danielscottraynsford.com/img/38f0z2UoU4-1365.jpeg 1365w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;Installing an HTTPS WS-Management Listener with GPO&lt;/p&gt;&lt;p&gt;The script is actually a little bit smarter than the above command. If a certificate with a subject can’t be found that matches the &lt;strong&gt;FQDN&lt;/strong&gt; of the computer it will automatically look for one that just uses the flat computer name. You can control this behavior by setting the &lt;strong&gt;DNSNameType&lt;/strong&gt; parameter.&lt;/p&gt;&lt;p&gt;There are some other optional parameters that control other the behavior of the script as well:&lt;/p&gt;&lt;h3 id=&quot;dnsnametype&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-an-ssl-ws-management-listener-with-gpo/#dnsnametype&quot; class=&quot;heading-anchor&quot;&gt;DNSNameType&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The allowed DNS Name types that will be used to find a matching certificate. If set to &lt;strong&gt;FQDN&lt;/strong&gt; then the script will only try to find a certificate with a subject matching the &lt;strong&gt;FQDN&lt;/strong&gt; of the computer. If set to &lt;strong&gt;ComputerName&lt;/strong&gt; it will only match on the computer name of the computer. By default this is set to &lt;strong&gt;Both&lt;/strong&gt; which will try to match on &lt;strong&gt;FQDN&lt;/strong&gt; first and then &lt;strong&gt;ComputerName&lt;/strong&gt; if it can’t find one matching the &lt;strong&gt;FQDN&lt;/strong&gt;.&lt;/p&gt;&lt;h3 id=&quot;matchalternate&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-an-ssl-ws-management-listener-with-gpo/#matchalternate&quot; class=&quot;heading-anchor&quot;&gt;MatchAlternate&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The certificate found must also have an alternate subject name containing the DNS name found in the subject as well. This places additional restrictions on the certificate that is used, but is not usually required. This defaults to &lt;strong&gt;False&lt;/strong&gt;.&lt;/p&gt;&lt;h3 id=&quot;port&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-an-ssl-ws-management-listener-with-gpo/#port&quot; class=&quot;heading-anchor&quot;&gt;Port&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This parameter lets you specify an alternate &lt;strong&gt;port&lt;/strong&gt; to install the HTTPS/SSL listener on. If you don’t specify it, it will use the default port of &lt;strong&gt;5986&lt;/strong&gt;.&lt;/p&gt;&lt;h2 id=&quot;dont-forget-your-firewall&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-an-ssl-ws-management-listener-with-gpo/#dont-forget-your-firewall&quot; class=&quot;heading-anchor&quot;&gt;Don’t Forget your Firewall&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;It is important to remember that by default this listener is installed onto port &lt;strong&gt;5986&lt;/strong&gt;, which is &lt;em&gt;not&lt;/em&gt; usually open inbound. So you’ll want to &lt;em&gt;add a firewall rule&lt;/em&gt; to ensure this port can be reached - another job for GPO. You could even add this setting into the GPO that installs the HTTPS/SSL listener.&lt;/p&gt;&lt;h2 id=&quot;installing-with-dsc&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-an-ssl-ws-management-listener-with-gpo/#installing-with-dsc&quot; class=&quot;heading-anchor&quot;&gt;Installing with DSC&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In theory it should be possible to adapt this code to run in a &lt;strong&gt;DSC Script Resource&lt;/strong&gt;. I haven’t tried this yet, but you can be assured that I will fairly soon. If there was some interest in this I could convert it into a proper &lt;strong&gt;DSC Resource&lt;/strong&gt; (unless there was one already - I haven’t checked). If you are interested, let me know.&lt;/p&gt;&lt;h2 id=&quot;links&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-an-ssl-ws-management-listener-with-gpo/#links&quot; class=&quot;heading-anchor&quot;&gt;Links&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;If you want to make a copy of the repository, you’ll find it here:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/PlagueHO/WSManGPOTools&quot; rel=&quot;noopener&quot;&gt;https://github.com/PlagueHO/WSManGPOTools&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Right, that is me out for another Sunday. Thanks for reading.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>New PowerShell Icon in Windows 10 Build 10547</title>
      <link href="https://danielscottraynsford.com/blog/new-powershell-icon-in-windows-10-build-10547/" />
      <updated>2015-09-20T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/new-powershell-icon-in-windows-10-build-10547/</id>
      <content type="html">
				&lt;p&gt;I know this is a bit of a non event, but anyone notice the new PowerShell icon in Windows 10 insider build 10547?&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_new_powershell_icon1.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/DOI7mnGGmf-40.webp 40w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/DOI7mnGGmf-40.png&quot; alt=&quot;The New PowerShell icon&quot; width=&quot;40&quot; height=&quot;40&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I wonder if there are any other PowerShell changes in this build?&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Installing a Two-Tier PKI using nothing but Desired State Configuration – Part 2</title>
      <link href="https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-2/" />
      <updated>2015-09-13T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-2/</id>
      <content type="html">
				&lt;p&gt;Continuing on from yesterday, the goal of this series is show how it is possible to install a two-tier Active Directory Certificate Services environment using only Desired State Configuration. In &lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-1/&quot;&gt;Part 1&lt;/a&gt;, I covered the basic DSC setup and requirements, the AllNodes hash table and the first part of the Root CA configuration script.&lt;/p&gt;&lt;h3 id=&quot;other-parts-in-this-series&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-2/#other-parts-in-this-series&quot; class=&quot;heading-anchor&quot;&gt;Other Parts in this Series&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-1/&quot;&gt;Installing a Two-Tier PKI using nothing but Desired State Configuration - Part 1&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Lets get going then!&lt;/p&gt;&lt;h3 id=&quot;step-2-installing-the-subordinate-ca&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-2/#step-2-installing-the-subordinate-ca&quot; class=&quot;heading-anchor&quot;&gt;Step 2: Installing the Subordinate CA&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;In this configuration we’ll need both &lt;em&gt;Local Credentials&lt;/em&gt; for installing the Web Enrollment feature and &lt;em&gt;Domain Credentials&lt;/em&gt; for joining the Sub CA to the domain and for registering the CA in AD:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Assemble the Local Admin and Domain Admin Credentials&lt;/span&gt;
Node &lt;span class=&quot;token variable&quot;&gt;$AllNodes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NodeName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LocalAdminPassword&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[PSCredential]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$LocalAdminCredential&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Management&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Automation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PSCredential &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Administrator&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConvertTo-SecureString&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LocalAdminPassword &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AsPlainText &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DomainAdminPassword&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[PSCredential]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$DomainAdminCredential&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Management&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Automation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PSCredential &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DomainName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;Administrator&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConvertTo-SecureString&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DomainAdminPassword &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AsPlainText &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Just like the Root CA the &lt;strong&gt;ADCS Certificate Authority&lt;/strong&gt; and the &lt;strong&gt;ADCS Web Enrollment&lt;/strong&gt; features need to be installed. But I’m also going to install the &lt;strong&gt;Online Responder&lt;/strong&gt; service as well - you of course don’t need to. I really should configure the CRLPublicationURLs node property as well to make use of this Online Responder, but I’m sure you can figure that part out.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Install the RSAT PowerShell Module which is required by the xWaitForResource&lt;/span&gt;
WindowsFeature RSATADPowerShell &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Ensure = &lt;span class=&quot;token string&quot;&gt;&quot;Present&quot;&lt;/span&gt;
    Name   = &lt;span class=&quot;token string&quot;&gt;&quot;RSAT-AD-PowerShell&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Install the CA Service&lt;/span&gt;
WindowsFeature ADCSCA &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Name      = &lt;span class=&quot;token string&quot;&gt;&#39;ADCS-Cert-Authority&#39;&lt;/span&gt;
    Ensure    = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[WindowsFeature]RSATADPowerShell&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Install the Web Enrollment Service&lt;/span&gt;
WindowsFeature WebEnrollmentCA &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Name      = &lt;span class=&quot;token string&quot;&gt;&#39;ADCS-Web-Enrollment&#39;&lt;/span&gt;
    Ensure    = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[WindowsFeature]ADCSCA&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Install the Online Responder Service&lt;/span&gt;
WindowsFeature OnlineResponderCA &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Name      = &lt;span class=&quot;token string&quot;&gt;&#39;ADCS-Online-Cert&#39;&lt;/span&gt;
    Ensure    = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[WindowsFeature]WebEnrollmentCA&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You might have noticed that we’re also installing the &lt;strong&gt;RSAT-AD-PowerShell&lt;/strong&gt;. This is required by the &lt;strong&gt;xWaitForADDomain DSC resource&lt;/strong&gt;. If you don’t install this feature the domain will never be detected and the DSC Script will progress no further (I found this out the hard way).&lt;/p&gt;&lt;p&gt;On the agenda next, this machine needs to be joined to the domain. It is important to check the domain is up before trying to join it. In my case I was also creating the DC’s (by DSC of course) at the same time as the CA’s so sometimes there was a long wait for the Domain to come up (which is why the large retry count):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Wait for the Domain to be available so we can join it.&lt;/span&gt;
xWaitForADDomain DscDomainWait &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    DomainName          = &lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DomainName
    DomainUserCredential = &lt;span class=&quot;token variable&quot;&gt;$DomainAdminCredential&lt;/span&gt;
    RetryCount          = 100
    RetryIntervalSec    = 10
    DependsOn           = &lt;span class=&quot;token string&quot;&gt;&quot;[WindowsFeature]OnlineResponderCA&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Join this Server to the Domain so that it can be an Enterprise CA.&lt;/span&gt;
xComputer JoinDomain &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Name     = &lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NodeName
    DomainName = &lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DomainName
    Credential = &lt;span class=&quot;token variable&quot;&gt;$DomainAdminCredential&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[xWaitForADDomain]DscDomainWait&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The next step is to create a &lt;strong&gt;CAPolicy.inf&lt;/strong&gt; file, but this file is slightly different from the one created on the &lt;em&gt;Root CA&lt;/em&gt;. The process is the same though:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Create the CAPolicy.inf file that sets basic parameters for certificate issuance for this CA.&lt;/span&gt;
File CAPolicy &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Ensure = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
    DestinationPath = &lt;span class=&quot;token string&quot;&gt;&#39;C:&#92;&#92;Windows&#92;&#92;CAPolicy.inf&#39;&lt;/span&gt;
    Contents = &lt;span class=&quot;token string&quot;&gt;&quot;&#92;[Version&#92;]&#92;`r&#92;`n Signature= &#92;`&quot;&lt;span class=&quot;token variable&quot;&gt;$Windows&lt;/span&gt; NT$&#92;`&quot;&#92;`r&#92;`n&#92;[Certsrv&#92;_Server&#92;]&#92;`r&#92;`n RenewalKeyLength=2048&#92;`r&#92;`n RenewalValidityPeriod=Years&#92;`r&#92;`n RenewalValidityPeriodUnits=10&#92;`r&#92;`n LoadDefaultTemplates=1&#92;`r&#92;`n AlternateSignatureAlgorithm=1&#92;`r&#92;`n&quot;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Type&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;File&#39;&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&#39;&#92;[xComputer&#92;]JoinDomain&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Easy enough so far. What I did next was create a &lt;strong&gt;CertEnroll folder&lt;/strong&gt; (c:&#92;windows&#92;System32&#92;CertSrv&#92;CertEnroll) where the &lt;em&gt;Root CA certificate&lt;/em&gt; needed to be put. The Web Enrollment Service would have created this too but I can’t configure this service until later. So I’m going to create it manually:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Make a CertEnroll folder to put the Root CA certificate into.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# The CA Web Enrollment server would also create this but we need it now.&lt;/span&gt;
File CertEnrollFolder &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Ensure = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
    DestinationPath = &lt;span class=&quot;token string&quot;&gt;&#39;C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#39;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Type&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;Directory&#39;&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&#39;&#92;[File&#92;]CAPolicy&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next up I wanted to download the Root CA Cert to this Sub CA. Strictly this isn’t required till later but I was basically emulating the steps in &lt;a href=&quot;http://social.technet.microsoft.com/wiki/contents/articles/15037.ad-cs-step-by-step-guide-two-tier-pki-hierarchy-deployment.aspx#Publish_the_Root_CA_Certificate_and_CRL&quot; rel=&quot;noopener&quot;&gt;this document&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The important thing to remember here though is that we need to ensure the Root CA DSC has reached the point where the Root CA certificate and Certificate Revocation List (CRL) is produced and available to us. So this is where we use the new &lt;a href=&quot;http://blogs.msdn.com/b/powershell/archive/2015/07/09/validate-powershell-dsc-waitfor.aspx&quot; rel=&quot;noopener&quot;&gt;PowerShell DSC 5.0 WaitFor&lt;/a&gt; resource:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Wait for the RootCA Web Enrollment to complete so we can grab the Root CA certificate # file.&lt;/span&gt;
WaitForAny RootCA &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    ResourceName = &lt;span class=&quot;token string&quot;&gt;&#39;&#92;[xADCSWebEnrollment&#92;]ConfigWebEnrollment&#39;&lt;/span&gt;
    NodeName = &lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCAName
    RetryIntervalSec = 30
    RetryCount = 30
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;&#92;[File&#92;]CertEnrollFolder&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Download the Root CA certificate file.&lt;/span&gt;
xRemoteFile DownloadRootCACRTFile &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    DestinationPath = &lt;span class=&quot;token string&quot;&gt;&quot;C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCAName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;_&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.crt&quot;&lt;/span&gt;
    Uri = &lt;span class=&quot;token string&quot;&gt;&quot;http://&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCAName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;/CertEnroll/&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCAName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;_&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.crt&quot;&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&#39;&#92;[WaitForAny&#92;]RootCA&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Download the Root CA certificate revocation list.&lt;/span&gt;
xRemoteFile DownloadRootCACRLFile &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    DestinationPath = &lt;span class=&quot;token string&quot;&gt;&quot;C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.crl&quot;&lt;/span&gt;
    Uri = &lt;span class=&quot;token string&quot;&gt;&quot;http://&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCAName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;/CertEnroll/&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.crl&quot;&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&#39;&#92;[xRemoteFile&#92;]DownloadRootCACRTFile&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; using HTTP to copy files between the &lt;em&gt;Root CA&lt;/em&gt; and the &lt;em&gt;Sub CA’s&lt;/em&gt; is not strictly recommended by Microsoft when installing a &lt;em&gt;two-tier PKI&lt;/em&gt; because that means the Root CA system has to be connected to the network. Because the &lt;em&gt;Root CA&lt;/em&gt; and &lt;em&gt;Sub CA DSC&lt;/em&gt; scripts need the machines to directly interact there isn’t any way around this that I can see. But if you were using this in a production environment you could put the &lt;em&gt;Root CA&lt;/em&gt; machine onto an &lt;strong&gt;isolated virtual network&lt;/strong&gt; consisting of the &lt;em&gt;Root CA&lt;/em&gt; and &lt;em&gt;Sub CA&lt;/em&gt; machines only. It is not a perfect solution but it should be reasonable for most situations. The Root CA can still be taken off line and removed after the Sub CA’s have been created.&lt;/p&gt;&lt;p&gt;Following this the &lt;em&gt;Root CA Certificate&lt;/em&gt; and &lt;em&gt;CRL&lt;/em&gt; can be imported into the &lt;strong&gt;local machine root certificate store&lt;/strong&gt; and also the &lt;strong&gt;Active Directory domain&lt;/strong&gt;. This is done in a single script resource:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Install the Root CA Certificate to the LocalMachine Root Store and DS&lt;/span&gt;
Script InstallRootCACert &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    PSDSCRunAsCredential = &lt;span class=&quot;token variable&quot;&gt;$DomainAdminCredential&lt;/span&gt; SetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Registering the Root CA Certificate C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCAName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;_&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.crt in DS...&quot;&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;&#92;system32&#92;&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;dspublish &lt;span class=&quot;token string&quot;&gt;&quot;C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCAName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;_&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.crt&quot;&lt;/span&gt; RootCA
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Registering the Root CA CRL C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.crl in DS...&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;&#92;system32&#92;&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;dspublish &lt;span class=&quot;token string&quot;&gt;&quot;C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.crl&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCAName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Installing the Root CA Certificate C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCAName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;_&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.crt...&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;&#92;system32&#92;&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;addstore &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f root &lt;span class=&quot;token string&quot;&gt;&quot;C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCAName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;_&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.crt&quot;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Installing the Root CA CRL C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.crl...&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;&#92;system32&#92;&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;addstore &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;f root &lt;span class=&quot;token string&quot;&gt;&quot;C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.crl&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    GetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;Return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Installed = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path Cert:&#92;&#92;LocalMachine&#92;&#92;Root &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Where-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FilterScript &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$&#92;_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Subject &lt;span class=&quot;token operator&quot;&gt;-Like&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CN=&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;,&#92;*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$&#92;_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Issuer &lt;span class=&quot;token operator&quot;&gt;-Like&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CN=&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;,&#92;*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Count &lt;span class=&quot;token operator&quot;&gt;-EQ&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    TestScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path Cert:&#92;&#92;LocalMachine&#92;&#92;Root &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Where-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FilterScript &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$&#92;_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Subject &lt;span class=&quot;token operator&quot;&gt;-Like&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CN=&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;,&#92;*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$&#92;_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Issuer &lt;span class=&quot;token operator&quot;&gt;-Like&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CN=&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;,&#92;*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Count &lt;span class=&quot;token operator&quot;&gt;-EQ&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Root CA Certificate Needs to be installed...&quot;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$False&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$True&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&#39;&#92;[xRemoteFile&#92;]DownloadRootCACRTFile&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I’d actually prefer to break the above code into for separate resources and detect if each one has occurred (and I might do for a later version), but this configuration is extremely large as it is.&lt;/p&gt;&lt;p&gt;Notice here we also used another PowerShell DSC 5.0 feature, the &lt;strong&gt;PSDSCRunAsCredential&lt;/strong&gt; parameter. This parameter is available in all &lt;em&gt;DSC Resources&lt;/em&gt; and allows us to specify an alternate credential to run this DSC Resource as. By default a DSC Resource is run as &lt;strong&gt;NT AUTHORITY/SYSTEM&lt;/strong&gt;, which is usually OK, but in this case some of the commands write certificates into DS and therefore need to be run under a &lt;strong&gt;Domain Admin&lt;/strong&gt; account.&lt;/p&gt;&lt;p&gt;Onwards: It is now time to configure the AD CS Certificate Authority and Web Enrollment. Except this time the Certificate Authority configuration will produce a &lt;strong&gt;certificate request (REQ)&lt;/strong&gt; that has to be issued by our &lt;em&gt;Root CA&lt;/em&gt;. So what I did was ensure the REQ file is put into the &lt;em&gt;CertEnroll&lt;/em&gt; folder - this should make it accessible by in the &lt;em&gt;http:&#92;&#92;SA_SUBCA&#92;CertEnroll&#92;&lt;/em&gt; web site.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Configure the Sub CA which will create the Certificate REQ file that Root CA will use&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# to issue a certificate for this Sub CA.&lt;/span&gt;
xADCSCertificationAuthority ConfigCA &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Ensure                    = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
    Credential                = &lt;span class=&quot;token variable&quot;&gt;$DomainAdminCredential&lt;/span&gt;
    CAType                    = &lt;span class=&quot;token string&quot;&gt;&#39;EnterpriseSubordinateCA&#39;&lt;/span&gt;
    CACommonName              = &lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACommonName
    CADistinguishedNameSuffix = &lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix
    OverwriteExistingCAinDS   = &lt;span class=&quot;token boolean&quot;&gt;$True&lt;/span&gt;
    OutputCertRequestFile     = &lt;span class=&quot;token string&quot;&gt;&quot;c:&#92;windows&#92;system32&#92;certsrv&#92;certenroll&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NodeName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.req&quot;&lt;/span&gt;
    DependsOn                 = &lt;span class=&quot;token string&quot;&gt;&#39;[Script]InstallRootCACert&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Configure the Web Enrollment Feature&lt;/span&gt;
xADCSWebEnrollment ConfigWebEnrollment &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Ensure     = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
    Name       = &lt;span class=&quot;token string&quot;&gt;&#39;ConfigWebEnrollment&#39;&lt;/span&gt;
    Credential = &lt;span class=&quot;token variable&quot;&gt;$LocalAdminCredential&lt;/span&gt;
    DependsOn  = &lt;span class=&quot;token string&quot;&gt;&#39;[xADCSCertificationAuthority]ConfigCA&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Seems simple enough - except one small problem. By default IIS doesn’t include &lt;strong&gt;REQ&lt;/strong&gt; files as supported &lt;em&gt;mime types&lt;/em&gt; so the &lt;strong&gt;file can’t be downloaded&lt;/strong&gt;. To get around this we need to add &lt;strong&gt;REQ&lt;/strong&gt; as a supported &lt;em&gt;mime type&lt;/em&gt;. Unfortunately there is no DSC resource to do this so it’s time to resort to the Script resource:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Set the IIS Mime Type to allow the REQ request to be downloaded by the Root CA&lt;/span&gt;
Script SetREQMimeType &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    SetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Add-WebConfigurationProperty&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PSPath IIS:&#92; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Filter&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;staticContent &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Value @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;fileExtension=&lt;span class=&quot;token string&quot;&gt;&#39;.req&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;mimeType=&lt;span class=&quot;token string&quot;&gt;&#39;application/pkcs10&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    GetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&#39;MimeType&#39;&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-WebConfigurationProperty&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Filter&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;//staticContent/mimeMap[@fileExtension=&#39;.req&#39;]&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PSPath IIS:&#92; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mimeType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    TestScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-not&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-WebConfigurationProperty&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Filter&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;//staticContent/mimeMap[@fileExtension=&#39;.req&#39;]&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PSPath IIS:&#92; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;# Mime type is not set&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$False&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Mime Type is already set&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$True&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&#39;[xADCSWebEnrollment]ConfigWebEnrollment&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Right, now an issuing certificate needs to be issued to this &lt;em&gt;Sub CA&lt;/em&gt; by the &lt;em&gt;Root CA&lt;/em&gt; using the &lt;strong&gt;REQ&lt;/strong&gt; that has been created in the &lt;strong&gt;CertEnroll virtual folder&lt;/strong&gt; on the &lt;em&gt;Sub CA&lt;/em&gt;. To do this we need to go back to the &lt;strong&gt;Root CA DSC script&lt;/strong&gt; and continue on with it.&lt;/p&gt;&lt;h3 id=&quot;step-3-issuing-the-sub-ca-certificate-on-the-root-ca&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-2/#step-3-issuing-the-sub-ca-certificate-on-the-root-ca&quot; class=&quot;heading-anchor&quot;&gt;Step 3: Issuing the Sub CA certificate on the Root CA&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This is the second component of the &lt;strong&gt;Root CA DSC configuration&lt;/strong&gt;. It is a bit more complicated than the first part because it may need to be run more than once - once for each Sub CA that is being created. Therefore the whole part is wrapped in &lt;strong&gt;foreach&lt;/strong&gt; loop. This is also the purpose of the &lt;strong&gt;SubCAs&lt;/strong&gt; array property of the &lt;strong&gt;AllNodes&lt;/strong&gt; object. Each &lt;strong&gt;Sub CA&lt;/strong&gt; that will be bought up should be in the list:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;SubCAs=@&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA&#92;_SUBCA1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA&#92;_SUBCA2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA&#92;_SUBCA3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So now that we’ve got that covered we can start adding to the Root CA DSC Configuration. So here’s the start of that &lt;strong&gt;foreach&lt;/strong&gt; loop I was talking about:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Generate Issuing certificates for any SubCAs Foreach ($SubCA in $Node.SubCAs) {&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first thing to do is wait for the &lt;em&gt;Sub CA&lt;/em&gt; to complete creation of the &lt;strong&gt;REQ&lt;/strong&gt; file and download it. So once again we use the &lt;strong&gt;WaitForAny&lt;/strong&gt; resource. Also note the use of the &lt;strong&gt;$SubCA&lt;/strong&gt; variable that is defined by the &lt;strong&gt;foreach&lt;/strong&gt; loop:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Wait for SubCA to generate REQ&lt;/span&gt;
WaitForAny &lt;span class=&quot;token string&quot;&gt;&quot;WaitForSubCA_&lt;span class=&quot;token variable&quot;&gt;$SubCA&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    ResourceName     = &lt;span class=&quot;token string&quot;&gt;&#39;[xADCSCertificationAuthority]ConfigCA&#39;&lt;/span&gt;
    NodeName         = &lt;span class=&quot;token variable&quot;&gt;$SubCA&lt;/span&gt;
    RetryIntervalSec = 30
    RetryCount       = 30
    DependsOn        = &lt;span class=&quot;token string&quot;&gt;&#39;[Script]ADCSAdvConfig&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Download the REQ from the SubCA &lt;/span&gt;
xRemoteFile &lt;span class=&quot;token string&quot;&gt;&quot;DownloadSubCA&#92;_&lt;span class=&quot;token variable&quot;&gt;$SubCA&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    DestinationPath = &lt;span class=&quot;token string&quot;&gt;&quot;C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token variable&quot;&gt;$SubCA&lt;/span&gt;.req&quot;&lt;/span&gt;
    Uri = &lt;span class=&quot;token string&quot;&gt;&quot;http://&lt;span class=&quot;token variable&quot;&gt;$SubCA&lt;/span&gt;/CertEnroll/&lt;span class=&quot;token variable&quot;&gt;$SubCA&lt;/span&gt;.req&quot;&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;&#92;[WaitForAny&#92;]WaitForSubCA&#92;_&lt;span class=&quot;token variable&quot;&gt;$SubCA&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To make things simple I just downloaded the &lt;strong&gt;REQ&lt;/strong&gt; to the &lt;strong&gt;CertEnroll folder&lt;/strong&gt; of this &lt;em&gt;Root CA&lt;/em&gt;. Now, things got a little bit tough here. There is no DSC Resource or even PowerShell modules for issuing a certificate from the &lt;strong&gt;REQ&lt;/strong&gt;. We have to fall back to using the DSC Script resource and the &lt;strong&gt;CertReq.exe&lt;/strong&gt; and &lt;strong&gt;CertUtil.exe&lt;/strong&gt; tools. This is a little bit fiddly and reminds me why I love PowerShell’s object based output. I won’t go into detail of what is going on here, but if you want me to expand on it let me know.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Generate the Issuing Certificate from the REQ&lt;/span&gt;
Script &lt;span class=&quot;token string&quot;&gt;&quot;IssueCert&#92;_&lt;span class=&quot;token variable&quot;&gt;$SubCA&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    SetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Submitting C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:SubCA.req to &lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$RequestResult&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;&#92;System32&#92;&#92;Certreq.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Config &lt;span class=&quot;token string&quot;&gt;&quot;.&#92;&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Submit &lt;span class=&quot;token string&quot;&gt;&quot;C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:SubCA.req&quot;&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$Matches&lt;/span&gt; = &lt;span class=&quot;token namespace&quot;&gt;[Regex]&lt;/span&gt;::Match&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$RequestResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;RequestId:&#92;&#92;s(&#92;[0-9&#92;]&#92;*)&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Matches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Groups&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Count &lt;span class=&quot;token operator&quot;&gt;-lt&lt;/span&gt; 2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Error getting Request ID from SubCA certificate submission.&quot;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;Throw&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Error getting Request ID from SubCA certificate submission.&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[int]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$RequestId&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$Matches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Groups&#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;1&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Issuing &lt;span class=&quot;token variable&quot;&gt;$RequestId&lt;/span&gt; in &lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$SubmitResult&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;&#92;System32&#92;&#92;CertUtil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Resubmit &lt;span class=&quot;token variable&quot;&gt;$RequestId&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$SubmitResult&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-notlike&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Certificate issued.&#92;*&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Unexpected result issuing SubCA request.&quot;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;Throw&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Unexpected result issuing SubCA request.&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Retrieving C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:SubCA.req from &lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$RetrieveResult&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;&#92;System32&#92;&#92;Certreq.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Config &lt;span class=&quot;token string&quot;&gt;&quot;.&#92;&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Retrieve &lt;span class=&quot;token variable&quot;&gt;$RequestId&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:SubCA.crt&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    GetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;Return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Generated&#39;&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Test-Path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:SubCA.crt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    TestScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-not&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Test-Path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:SubCA.crt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;# SubCA Cert is not yet created&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$False&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# SubCA Cert has been created&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$True&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;&#92;[xRemoteFile&#92;]DownloadSubCA&#92;_&lt;span class=&quot;token variable&quot;&gt;$SubCA&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That is all we actually need to do in the loop on the &lt;em&gt;Root CA&lt;/em&gt;. It is now up to each &lt;em&gt;Sub CA&lt;/em&gt; to download the new &lt;strong&gt;Issuing Certificate&lt;/strong&gt; and install it.&lt;/p&gt;&lt;h3 id=&quot;step-4-installing-the-issuing-certificate-on-the-sub-ca&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-2/#step-4-installing-the-issuing-certificate-on-the-sub-ca&quot; class=&quot;heading-anchor&quot;&gt;Step 4: Installing the Issuing Certificate on the Sub CA&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Now that an Issuing Certificate is available to be downloaded from the &lt;em&gt;Root CA&lt;/em&gt; for each &lt;em&gt;Sub CA&lt;/em&gt;, the configuration script for each &lt;em&gt;Sub CA&lt;/em&gt; can continue. But as always the script needs to use the &lt;strong&gt;WaitFor resource&lt;/strong&gt; (really have to love this resource) to ensure that the certificate is available:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Wait for the Root CA to have completed issuance of the certificate for this SubCA.&lt;/span&gt;
WaitForAny SubCACer &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    ResourceName = &lt;span class=&quot;token string&quot;&gt;&quot;&#92;[Script&#92;]IssueCert&#92;_&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NodeName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
    NodeName = &lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCAName
    RetryIntervalSec = 30
    RetryCount = 30
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;&#92;[Script&#92;]SetREQMimeType&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Download the Certificate for this SubCA.&lt;/span&gt;
xRemoteFile DownloadSubCACERFile &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    DestinationPath = &lt;span class=&quot;token string&quot;&gt;&quot;C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NodeName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.cer&quot;&lt;/span&gt;
    Uri = &lt;span class=&quot;token string&quot;&gt;&quot;http://&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RootCAName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;/CertEnroll/&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NodeName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.cer&quot;&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&#39;&#92;[WaitForAny&#92;]SubCACer&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Register the Sub CA Certificate with the Certification Authority&lt;/span&gt;
Script RegisterSubCA &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    PSDSCRunAsCredential = &lt;span class=&quot;token variable&quot;&gt;$DomainAdminCredential&lt;/span&gt;
    SetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Registering the Sub CA Certificate with the Certification Authority C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NodeName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;_&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.crt...&quot;&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;&#92;system32&#92;&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;installCert &lt;span class=&quot;token string&quot;&gt;&quot;C:&#92;&#92;Windows&#92;&#92;System32&#92;&#92;CertSrv&#92;&#92;CertEnroll&#92;&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NodeName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;_&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACommonName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.crt&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    GetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;Return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    TestScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;If&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-not&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;&#92;System&#92;&#92;CurrentControlSet&#92;&#92;Services&#92;&#92;CertSvc&#92;&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CACertHash&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Sub CA Certificate needs to be registered with the Certification Authority...&quot;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$False&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;Return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$True&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&#39;&#92;[xRemoteFile&#92;]DownloadSubCACERFile&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: It is always important to remember that when using the &lt;strong&gt;Script DSC Resource&lt;/strong&gt; if you want to use any variables that are declared outside the resource you’ll need to prefix them with the &lt;strong&gt;Using:&lt;/strong&gt; keyword. I have wasted many hours tracking down issues caused by missing this vital keyword!&lt;/p&gt;&lt;p&gt;Again, we’re running the above script resource using the &lt;strong&gt;PSDSCRunAsCredential&lt;/strong&gt; parameter to run it using &lt;strong&gt;Domain Admin credentials&lt;/strong&gt; so that the command can register the certificates into AD DS.&lt;/p&gt;&lt;p&gt;Once this is done the AIA and CDP extensions can be configured using the same method as we did for the Root CA. This will also start up the &lt;em&gt;Certificate Service:&lt;/em&gt;&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Script ADCSAdvConfig &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    SetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;system32&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;setreg CA&#92;DSConfigDN &lt;span class=&quot;token string&quot;&gt;&quot;CN=Configuration,&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;system32&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;setreg CA&#92;DSDomainDN &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CRLPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;System32&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;setreg CA&#92;CRLPublicationURLs $&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CRLPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACertPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;System32&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;setreg CA&#92;CACertPublicationURLs $&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACertPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Restart-Service&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name CertSvc
        &lt;span class=&quot;token function&quot;&gt;Add-Content&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&#39;c:&#92;windows&#92;setup&#92;scripts&#92;certutil.log&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Value &lt;span class=&quot;token string&quot;&gt;&quot;Certificate Service Restarted ...&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    GetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&#39;DSConfigDN&#39;&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DSConfigDN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&#39;DSDomainDN&#39;&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DSDomainDN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&#39;CRLPublicationURLs&#39;&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CRLPublicationURLs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&#39;CACertPublicationURLs&#39;&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CACertPublicationURLs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    TestScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DSConfigDN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CN=Configuration,&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DSDomainDN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CRLPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CRLPublicationURLs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CRLPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACertPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CACertPublicationURLs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACertPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&#39;[Script]RegisterSubCA&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;step-5-shut-down-the-root-ca&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-2/#step-5-shut-down-the-root-ca&quot; class=&quot;heading-anchor&quot;&gt;Step 5: Shut down the Root CA&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Once all the &lt;em&gt;Sub CAs&lt;/em&gt; have installed their certificates the &lt;em&gt;Root CA&lt;/em&gt; can be shutdown. This is a nice way of identifying that everything has gone according to plan and all &lt;em&gt;Sub CAs&lt;/em&gt; can now issue certificates. It also helps reduce the amount of time the &lt;em&gt;Root CA&lt;/em&gt; is online. To do this, once again we use the &lt;strong&gt;WaitFor DSC Resource&lt;/strong&gt;. If there is more than one &lt;em&gt;Sub CA&lt;/em&gt; being installed then the &lt;em&gt;Root CA&lt;/em&gt; script should wait for the last one to be complete.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;WaitForAny &lt;span class=&quot;token string&quot;&gt;&quot;WaitForComplete_&lt;span class=&quot;token variable&quot;&gt;$SubCA&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    ResourceName     = &lt;span class=&quot;token string&quot;&gt;&#39;[Script]InstallSubCACert&#39;&lt;/span&gt;
    NodeName         = &lt;span class=&quot;token variable&quot;&gt;$SubCA&lt;/span&gt;
    RetryIntervalSec = 30
    RetryCount       = 30
    DependsOn        = &lt;span class=&quot;token string&quot;&gt;&quot;[Script]IssueCert_&lt;span class=&quot;token variable&quot;&gt;$SubCA&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

Script ShutdownRootCA &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    SetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Stop-Computer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    GetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    TestScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# SubCA Cert is not yet created&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&quot;[WaitForAny]WaitForComplete_&lt;span class=&quot;token variable&quot;&gt;$SubCA&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point all the Sub CAs should be operational and the Root CA will have been shut down ready to be put away in a safe somewhere. There are still some minor tasks yet to complete such as configuring the Online Responder, generating and installing a Web Server certificate for the Web Enrollment Server etc. But seeing as this part is now getting extremely long I think I’ll leave them till Part 3 in the next few days. I hope this has been useful!&lt;/p&gt;&lt;h3 id=&quot;additional-information&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-2/#additional-information&quot; class=&quot;heading-anchor&quot;&gt;Additional Information&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;It is probably very useful to see the full complete DSC configuration files. These files change frequently as I optimize and test the process. As noted they are actually part of another project I’m working on - LabBuilder. They are currently available in my &lt;a href=&quot;https://github.com/PlagueHO/LabBuilder&quot; rel=&quot;noopener&quot;&gt;LabBuilder project repository on GitHub&lt;/a&gt;.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/PlagueHO/LabBuilder/blob/master/LabBuilder/DSCLibrary/STANDALONE_ROOTCA.DSC.ps1&quot; rel=&quot;noopener&quot;&gt;Root CA&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/PlagueHO/LabBuilder/blob/master/LabBuilder/DSCLibrary/MEMBER_SUBCA.DSC.ps1&quot; rel=&quot;noopener&quot;&gt;Sub CA&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I will cover the LabBuilder project another day once I have completed testing and documentation on it.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Installing a Two-Tier PKI using nothing but Desired State Configuration - Part 1</title>
      <link href="https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-1/" />
      <updated>2015-09-12T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-1/</id>
      <content type="html">
				&lt;p&gt;I am a firm believer in the concept of &lt;a href=&quot;http://devops.com/2014/05/05/meet-infrastructure-code/&quot; rel=&quot;noopener&quot;&gt;Infrastructure as Code&lt;/a&gt;. I do think technologies such as &lt;a href=&quot;https://www.chef.io/&quot; rel=&quot;noopener&quot;&gt;Chef&lt;/a&gt; and &lt;a href=&quot;https://technet.microsoft.com/en-us/library/dn249912.aspx&quot; rel=&quot;noopener&quot;&gt;Windows PowerShell Desired State Configuration (DSC)&lt;/a&gt; will eventually replace ‘clickety-click’ administration in medium to large environments, and even some smaller sites. If you’re not familiar with these technologies or concepts I’d strongly recommend you take a look at the above links.&lt;/p&gt;&lt;p&gt;Note: This post is going to be quite long and it does assume you have a basic understanding of &lt;a href=&quot;https://technet.microsoft.com/en-us/library/dn249912.aspx&quot; rel=&quot;noopener&quot;&gt;Desired State Configuration (DSC)&lt;/a&gt; and &lt;a href=&quot;http://social.technet.microsoft.com/wiki/contents/articles/1137.active-directory-certificate-services-ad-cs-introduction.aspx&quot; rel=&quot;noopener&quot;&gt;Windows Active Directory Certificate Services (AD CS)&lt;/a&gt;. If you’re not comfortable creating basic DSC configuration files or have never installed AD CS then you might want to get familiar with doing this before jumping into this post.&lt;/p&gt;&lt;h3 id=&quot;other-parts-in-this-series&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-1/#other-parts-in-this-series&quot; class=&quot;heading-anchor&quot;&gt;Other Parts in this Series&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/09/13/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-2/&quot; rel=&quot;noopener&quot;&gt;Installing a Two-Tier PKI using nothing but Desired State Configuration - Part 2&lt;/a&gt;&lt;/p&gt;&lt;h3 id=&quot;the-goal&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-1/#the-goal&quot; class=&quot;heading-anchor&quot;&gt;The Goal&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;But it’s all well and good to say I think it is the future, but how about I put my money where my mouth is and actually use DSC to implement something much more complicated than a simple IIS web site. Sure, DSC can and should be used for straight forward infrastructure configuration, but what about something like a two-tier PKI with an offline &lt;strong&gt;Standalone Root CA&lt;/strong&gt; as well as one or more &lt;strong&gt;Enterprise Subordinate CAs&lt;/strong&gt;? As part of my &lt;a href=&quot;https://github.com/PlagueHO/LabBuilder&quot; rel=&quot;noopener&quot;&gt;LabBuilder&lt;/a&gt; project I was going to have to find out. Basically, I was going to try and implement &lt;a href=&quot;http://social.technet.microsoft.com/wiki/contents/articles/15037.ad-cs-step-by-step-guide-two-tier-pki-hierarchy-deployment.aspx&quot; rel=&quot;noopener&quot;&gt;this&lt;/a&gt; using nothing more than Desired State Configuration. If you’re interested in seeing how this is done, continue reading.&lt;/p&gt;&lt;h3 id=&quot;why-is-this-complicated&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-1/#why-is-this-complicated&quot; class=&quot;heading-anchor&quot;&gt;Why is this Complicated?&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The difficulty with installing a two-tier PKI is that the Root CA and Issuing/Sub CA installation processes are interdependent. For example, to install the Sub CA an Issuing certificate must be issued by the Root CA, but this can only be done by the Sub CA issuing the request. The Request gets copied to the Root CA and issued and then the Issuing Certificate copied back to the Sub CA and installed. Therefore, there would need to be at least two DSC configurations, one on the Root CA and one on each Sub CA and they would be running at the same time, interacting with each other and waiting for various processed on each machine to complete before proceeding. This will become clearer later on in this post. To allow a DSC configuration to wait for a step to complete on another machine in another DSC configuration requires the &lt;a href=&quot;http://blogs.msdn.com/b/powershell/archive/2015/07/09/validate-powershell-dsc-waitfor.aspx&quot; rel=&quot;noopener&quot;&gt;WaitFor DSC resource&lt;/a&gt; that is only available in &lt;a href=&quot;http://blogs.msdn.com/b/powershell/archive/2015/08/31/windows-management-framework-5-0-production-preview-is-now-available.aspx&quot; rel=&quot;noopener&quot;&gt;WMF 5.0&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&quot;requirements&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-1/#requirements&quot; class=&quot;heading-anchor&quot;&gt;Requirements&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;To be able to do this you’ll need several things:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;A Hyper-V Host with the following Guest VMs:&lt;ol&gt;&lt;li&gt;A standalone clean Windows Server 2012 R2 Core server that will become the Standalone Root CA. In my system this computer is called &lt;strong&gt;SS_ROOTCA&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;A Domain Controller that the Enterprise Issuing/Sub CA will become a part of. My domain was called &lt;strong&gt;&lt;a href=&quot;http://LABBUILDER.COM&quot; rel=&quot;noopener&quot;&gt;LABBUILDER.COM&lt;/a&gt;&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;One or more standalone clean Windows Server 2012 R2 Core servers that will become the Enterprise Issuing/Sub CAs. In my system I was only using a single Sub CA and it was named&amp;nbsp; &lt;strong&gt;SA_SUBCA&lt;/strong&gt;. But you could use multiple Sub CAs, the following scripts do support more than one Sub CA.&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li&gt;A computer to create the DSC configuration files on that has &lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=45520&quot; rel=&quot;noopener&quot;&gt;RSAT&lt;/a&gt; installed (the version appropriate to the operating system).&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://blogs.msdn.com/b/powershell/archive/2015/08/31/windows-management-framework-5-0-production-preview-is-now-available.aspx&quot; rel=&quot;noopener&quot;&gt;WMF 5.0&lt;/a&gt; installed on all the above servers as well as the computer you’re creating the DSC configuration on.&lt;/li&gt;&lt;li&gt;The above servers need to be able to communicate with one another via networking (virtual or physical).&lt;/li&gt;&lt;/ol&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;In a production environment it is recommended that the Root CA is kept &lt;em&gt;offline&lt;/em&gt; and is &lt;em&gt;never connected to a network&lt;/em&gt;. Therefore this DSC process wouldn’t actually work. It could be made to work by doing some tricky things like waiting for external storage to be connected and so on, but I’m not even going to go there for this post.&lt;/p&gt;&lt;/blockquote&gt;&lt;h3 id=&quot;server-core-vs-full&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-1/#server-core-vs-full&quot; class=&quot;heading-anchor&quot;&gt;Server Core vs. Full&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I used &lt;strong&gt;Windows Server Core&lt;/strong&gt; installations for my PKI servers, but you could just as easily use Full installations if you wanted. However, I think given what I’m trying to achieve, using Server Core makes more sense - and besides, it’s simply the way to go when you’re talking about &lt;em&gt;Infrastructure as Code&lt;/em&gt;. In fact, if AD CS was available on &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/05/08/install-windows-server-nano-the-easy-way/&quot; rel=&quot;noopener&quot;&gt;Server Nano&lt;/a&gt; I would be trying to use that instead. You can still use RSAT to work with these Server Core installations should you need to.&lt;/p&gt;&lt;h3 id=&quot;resources&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-1/#resources&quot; class=&quot;heading-anchor&quot;&gt;Resources&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The DSC configuration files are going to require a few additional &lt;strong&gt;DSC resources&lt;/strong&gt;. These DSC resources will need to be installed onto the PKI servers &lt;em&gt;and&lt;/em&gt; the computer you’re using to compile the DSC Configurations into MOF files. The resources you’ll need are:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;PSDesiredStateConfiguration&lt;/strong&gt; - this is build in to the core DSC installation, so it doesn’t need to be downloaded.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;xADCSDeployment&lt;/strong&gt; - this is a &lt;a href=&quot;https://github.com/PowerShell/xAdcsDeployment&quot; rel=&quot;noopener&quot;&gt;community DSC resource&lt;/a&gt; required to perform post installation configuration of ADCS.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;xPSDesiredStateConfiguration&lt;/strong&gt; - we need this &lt;a href=&quot;https://github.com/PowerShell/xPSDesiredStateConfiguration&quot; rel=&quot;noopener&quot;&gt;community DSC resource&lt;/a&gt; for the xRemoteFile DSC Resource.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;xComputerManagement&lt;/strong&gt; - this &lt;a href=&quot;https://github.com/PowerShell/xComputerManagement&quot; rel=&quot;noopener&quot;&gt;community DSC resource&lt;/a&gt; is required to join the Sub CA to the domain.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The easiest way to do this on PowerShell 5.0 is using the &lt;strong&gt;Find-Module&lt;/strong&gt; and &lt;strong&gt;Install-Module&lt;/strong&gt; cmdlets from the &lt;strong&gt;PowerShellGet&lt;/strong&gt; module to download these from the &lt;a href=&quot;https://www.powershellgallery.com/&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt;:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Find-Module&lt;/span&gt; xPSDesiredStateConfiguration&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; xADCSDeployment&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; xComputerManagement &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;allnodes&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-1/#allnodes&quot; class=&quot;heading-anchor&quot;&gt;AllNodes&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;To make things a little bit more generic I like to put all the variables that the DSC configuration files are going to require into an &lt;strong&gt;AllNodes&lt;/strong&gt; hash table, with one for each server. Also, normally I’ll have a self-signed certificate generated on each server and copied down to the computer that is creating the configuration files so that the various credentials can be encrypted - see &lt;a href=&quot;http://blogs.msdn.com/b/powershell/archive/2014/01/31/want-to-secure-credentials-in-windows-powershell-desired-state-configuration.aspx&quot; rel=&quot;noopener&quot;&gt;this post&lt;/a&gt; or &lt;a href=&quot;https://technet.microsoft.com/en-us/library/dn781430.aspx&quot; rel=&quot;noopener&quot;&gt;this post&lt;/a&gt; for details on how this works. This is optional and you can use the &lt;strong&gt;PSDscAllowPlainTextPassword = $true&lt;/strong&gt; option in the Node if you want to just send the credentials in the clear.&lt;/p&gt;&lt;h4 id=&quot;important-note-regarding-credential-encryption-in-dsc&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-1/#important-note-regarding-credential-encryption-in-dsc&quot; class=&quot;heading-anchor&quot;&gt;Important Note Regarding Credential Encryption in DSC&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;Don’t&lt;/strong&gt; use the &lt;em&gt;New-SelfSignedCertificate&lt;/em&gt; cmdlet to create a self-signed certificate to encrypt your credentials. It creates a certificate that will not work here (the private key is not accessible). Instead, use &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/Self-signed-certificate-5920a7c6&quot; rel=&quot;noopener&quot;&gt;this script&lt;/a&gt; from the script center. You will waste a lot of time trying to figure out what is wrong.&lt;/p&gt;&lt;h4 id=&quot;allnodes-for-root-ca&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-1/#allnodes-for-root-ca&quot; class=&quot;heading-anchor&quot;&gt;AllNodes for Root CA&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;These are the Node parameters that contain the variables that you’ll want to configure for the Root CA. They are fairly self explanatory but they will be covered later on in the post.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;AllNodes = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        NodeName = &lt;span class=&quot;token string&quot;&gt;&#39;SS_ROOTCA&#39;&lt;/span&gt;
        Thumbprint = &lt;span class=&quot;token string&quot;&gt;&#39;CDD4EEAE6000AC7F40C3802C171E30148030C072&#39;&lt;/span&gt;
        LocalAdminPassword = &lt;span class=&quot;token string&quot;&gt;&#39;P@ssword!1&#39;&lt;/span&gt;
        CACommonName = &lt;span class=&quot;token string&quot;&gt;&quot;LABBUILDER.COM Root CA&quot;&lt;/span&gt;
        CADistinguishedNameSuffix = &lt;span class=&quot;token string&quot;&gt;&quot;DC=LABBUILDER,DC=COM&quot;&lt;/span&gt;
        CRLPublicationURLs = &lt;span class=&quot;token string&quot;&gt;&quot;1:C:&#92;Windows&#92;system32&#92;CertSrv&#92;CertEnroll&#92;%3%8%9.crl&#92;n10:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10&#92;n2:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl&quot;&lt;/span&gt;
        CACertPublicationURLs = &lt;span class=&quot;token string&quot;&gt;&quot;1:C:&#92;Windows&#92;system32&#92;CertSrv&#92;CertEnroll&#92;%1_%3%4.crt&#92;n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11&#92;n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt&quot;&lt;/span&gt;
        SubCAs = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;SA_SUBCA&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;allnodes-for-sub-ca&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-1/#allnodes-for-sub-ca&quot; class=&quot;heading-anchor&quot;&gt;AllNodes for Sub CA&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;And these are the parameters for each Subordinate CA. If you had more than one Sub CA then you could add additional nodes. The variables are fairly self explanatory but they will be covered later on in the post.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;AllNodes = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        NodeName = &lt;span class=&quot;token string&quot;&gt;&#39;SA_SUBCA&#39;&lt;/span&gt;
        Thumbprint = &lt;span class=&quot;token string&quot;&gt;&#39;8F43288AD272F3103B6FB1428485EA3014C0BCFE&#39;&lt;/span&gt;
        LocalAdminPassword = &lt;span class=&quot;token string&quot;&gt;&#39;P@ssword!1&#39;&lt;/span&gt;
        DomainName = &lt;span class=&quot;token string&quot;&gt;&quot;LABBUILDER.COM&quot;&lt;/span&gt;
        DomainAdminPassword = &lt;span class=&quot;token string&quot;&gt;&quot;P@ssword!1&quot;&lt;/span&gt;
        PSDscAllowDomainUser = &lt;span class=&quot;token boolean&quot;&gt;$True&lt;/span&gt;
        CACommonName = &lt;span class=&quot;token string&quot;&gt;&quot;LABBUILDER.COM Issuing CA&quot;&lt;/span&gt;
        CADistinguishedNameSuffix = &lt;span class=&quot;token string&quot;&gt;&quot;DC=LABBUILDER,DC=COM&quot;&lt;/span&gt;
        CRLPublicationURLs = &lt;span class=&quot;token string&quot;&gt;&quot;65:C:&#92;Windows&#92;system32&#92;CertSrv&#92;CertEnroll&#92;%3%8%9.crl&#92;n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10&#92;n6:http://pki.labbuilder.com/CertEnroll/%3%8%9.crl&quot;&lt;/span&gt;
        CACertPublicationURLs = &lt;span class=&quot;token string&quot;&gt;&quot;1:C:&#92;Windows&#92;system32&#92;CertSrv&#92;CertEnroll&#92;%1_%3%4.crt&#92;n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11&#92;n2:http://pki.labbuilder.com/CertEnroll/%1_%3%4.crt&quot;&lt;/span&gt;
        RootCAName = &lt;span class=&quot;token string&quot;&gt;&quot;SS_ROOTCA&quot;&lt;/span&gt;
        RootCACRTName = &lt;span class=&quot;token string&quot;&gt;&quot;SS_ROOTCA_LABBUILDER.COM Root CA.crt&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;step-1-installing-the-root-ca&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-1/#step-1-installing-the-root-ca&quot; class=&quot;heading-anchor&quot;&gt;Step 1: Installing the Root CA&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;First things first. We need to create a credential object that will be used to perform various steps in the process. This is a local credential as this is a standalone server.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Node &lt;span class=&quot;token variable&quot;&gt;$AllNodes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NodeName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Assemble the Local Admin Credentials&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LocalAdminPassword&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[PSCredential]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$LocalAdminCredential&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-Object&lt;/span&gt; System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Management&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Automation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PSCredential &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Administrator&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConvertTo-SecureString&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LocalAdminPassword &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AsPlainText &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next up we’ll install the &lt;strong&gt;ADCS Certificate Authority&lt;/strong&gt; and the &lt;strong&gt;ADCS Web Enrollment&lt;/strong&gt; features. Normally on a standalone Root CA you wouldn’t bother installing the ADCS Web Enrollment feature, but in our case it is an easy way to have the &lt;em&gt;CertEnroll&lt;/em&gt; website virtual folder created which we use to transfer the Root CA Cert and the Issuing CA Cert (later on).&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Install the ADCS Certificate Authority&lt;/span&gt;
WindowsFeature ADCSCA &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Name   = &lt;span class=&quot;token string&quot;&gt;&#39;ADCS-Cert-Authority&#39;&lt;/span&gt;
    Ensure = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Install ADCS Web Enrollment - only required because it creates the CertEnroll virtual folder&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Which we use to pass certificates to the Issuing/Sub CAs&lt;/span&gt;
WindowsFeature ADCSWebEnrollment &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Ensure    = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
    Name      = &lt;span class=&quot;token string&quot;&gt;&#39;ADCS-Web-Enrollment&#39;&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&#39;[WindowsFeature]ADCSCA&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next on the agenda is we create a &lt;strong&gt;CAPolicy.inf&lt;/strong&gt; file - this file configures some basic parameters that will be used by the Root CA certificate and server:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Create the CAPolicy.inf file which defines basic properties about the ROOT CA certificate&lt;/span&gt;
File CAPolicy &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Ensure          = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
    DestinationPath = &lt;span class=&quot;token string&quot;&gt;&#39;C:&#92;Windows&#92;CAPolicy.inf&#39;&lt;/span&gt;
    Contents        = &lt;span class=&quot;token string&quot;&gt;&quot;[Version]`r`nSignature=`&quot;&lt;span class=&quot;token variable&quot;&gt;$Windows&lt;/span&gt; NT$`&quot;`r`n[Certsrv_Server]`r`nRenewalKeyLength=4096`r`nRenewalValidityPeriod=Years`r`nRenewalValidityPeriodUnits=20`r`nCRLDeltaPeriod=Days`r`nCRLDeltaPeriodUnits=0`r`n[CRLDistributionPoint]`r`n[AuthorityInformationAccess]`r`n&quot;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Type&lt;/span&gt;            = &lt;span class=&quot;token string&quot;&gt;&#39;File&#39;&lt;/span&gt;
    DependsOn       = &lt;span class=&quot;token string&quot;&gt;&#39;[WindowsFeature]ADCSWebEnrollment&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And now the &lt;strong&gt;ADCS Certificate Authority&lt;/strong&gt; and the &lt;strong&gt;ADCS Web Enrollment&lt;/strong&gt; features can be configured. Notice we are using some of the Nodes parameters here as well as the Local Administrator Credentials:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Configure the CA as Standalone Root CA&lt;/span&gt;
xADCSCertificationAuthority ConfigCA &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Ensure                   = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
    Credential               = &lt;span class=&quot;token variable&quot;&gt;$LocalAdminCredential&lt;/span&gt;
    CAType                   = &lt;span class=&quot;token string&quot;&gt;&#39;StandaloneRootCA&#39;&lt;/span&gt;
    CACommonName             = &lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACommonName
    CADistinguishedNameSuffix = &lt;span class=&quot;token variable&quot;&gt;$Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix
    ValidityPeriod           = &lt;span class=&quot;token string&quot;&gt;&#39;Years&#39;&lt;/span&gt;
    ValidityPeriodUnits      = 20
    DependsOn                = &lt;span class=&quot;token string&quot;&gt;&#39;[File]CAPolicy&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Configure the ADCS Web Enrollment&lt;/span&gt;
xADCSWebEnrollment ConfigWebEnrollment &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Ensure    = &lt;span class=&quot;token string&quot;&gt;&#39;Present&#39;&lt;/span&gt;
    Name      = &lt;span class=&quot;token string&quot;&gt;&#39;ConfigWebEnrollment&#39;&lt;/span&gt;
    Credential = &lt;span class=&quot;token variable&quot;&gt;$LocalAdminCredential&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&#39;[xADCSCertificationAuthority]ConfigCA&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, here is where things get interesting. We need to configure some of the more advanced properties of the CA such as the &lt;em&gt;AIA&lt;/em&gt; and &lt;em&gt;CDP extensions&lt;/em&gt;. The problem is that there is no DSC resource for doing this and there aren’t even any native PowerShell cmdlets either! So I had to resort to the &lt;em&gt;DSC Script resource&lt;/em&gt; in combination with the &lt;strong&gt;CertUtil.exe&lt;/strong&gt; tool and &lt;strong&gt;registry&lt;/strong&gt; entries:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Set the advanced CA properties&lt;/span&gt;
Script ADCSAdvConfig &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    SetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &amp;amp; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;system32&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;setreg CA&#92;DSConfigDN &lt;span class=&quot;token string&quot;&gt;&quot;CN=Configuration,&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
            &amp;amp; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;system32&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;setreg CA&#92;DSDomainDN &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CRLPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &amp;amp; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;System32&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;setreg CA&#92;CRLPublicationURLs $&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CRLPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACertPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &amp;amp; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;System32&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;setreg CA&#92;CACertPublicationURLs $&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACertPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Restart-Service&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name CertSvc
        &lt;span class=&quot;token function&quot;&gt;Add-Content&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&#39;c:&#92;windows&#92;setup&#92;scripts&#92;certutil.log&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Value &lt;span class=&quot;token string&quot;&gt;&quot;Certificate Service Restarted ...&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    GetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&#39;DSConfigDN&#39;&lt;/span&gt;          = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DSConfigDN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&#39;DSDomainDN&#39;&lt;/span&gt;          = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DSDomainDN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&#39;CRLPublicationURLs&#39;&lt;/span&gt;  = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CRLPublicationURLs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&#39;CACertPublicationURLs&#39;&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CACertPublicationURLs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    TestScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DSConfigDN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CN=Configuration,&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DSDomainDN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CRLPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CRLPublicationURLs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CRLPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACertPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CACertPublicationURLs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACertPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&#39;[xADCSWebEnrollment]ConfigWebEnrollment&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above section was actually detailed in my previous post &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/09/03/advanced-certificate-services-configuration-with-dsc/&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;. With all that done the Root CA is installed and ready to go. But this DSC configuration script is not yet finished, but we can’t go any further until the Sub CA DSC has progressed. It is important to keep in mind that these DSC scripts are running at the same time on different machines and will interact with one another during this process.&lt;/p&gt;&lt;p&gt;This also seems like an appropriate time to take a break. The really interesting stuff is yet to come, but it is getting close to my bedtime and so I’ll continue this in part 2 tomorrow. This will cover the &lt;strong&gt;Sub CA DSC configuration&lt;/strong&gt; and the final part of the &lt;strong&gt;Root CA DSC configuration&lt;/strong&gt;. Hopefully someone out there has stuck with me till this point! 😃&lt;/p&gt;&lt;h3 id=&quot;next-part&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-1/#next-part&quot; class=&quot;heading-anchor&quot;&gt;Next Part&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/09/13/installing-a-two-tier-pki-using-nothing-but-desired-state-configuration-part-2/&quot; rel=&quot;noopener&quot;&gt;Installing a Two-Tier PKI using nothing but Desired State Configuration - Part 2&lt;/a&gt;&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>PowerShell DON&#39;T of the Week - $Switch in a Switch { }</title>
      <link href="https://danielscottraynsford.com/blog/powershell-dont-of-the-week-dollarswitch-in-a-switch/" />
      <updated>2015-09-08T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/powershell-dont-of-the-week-dollarswitch-in-a-switch/</id>
      <content type="html">
				&lt;p&gt;I just spent the last hour bashing my head against my keyboard trying to figure out what I had done wrong in one of my scripts.&lt;/p&gt;&lt;p&gt;It turns out when you are inside a &lt;strong&gt;switch&lt;/strong&gt; construct, the variable &lt;code&gt;$Switch&lt;/code&gt; is &lt;strong&gt;redefined&lt;/strong&gt; (presumably by the switch construct itself) as an empty variable of type &lt;code&gt;System.Collections.IEnumerator&lt;/code&gt;. The value is set to &lt;code&gt;$null&lt;/code&gt;. This won’t be a problem if you’re not using a variable with the name &lt;code&gt;$Switch&lt;/code&gt;. Unfortunately, I was—because I was working with a set of Virtual Switches, so &lt;code&gt;$Switch&lt;/code&gt; seemed like a fair choice of variable name.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_powershell_switchgremlin.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/OvIoKFRFL6-650.png 650w, https://danielscottraynsford.com/img/OvIoKFRFL6-960.png 960w, https://danielscottraynsford.com/img/OvIoKFRFL6-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/OvIoKFRFL6-650.webp 650w, https://danielscottraynsford.com/img/OvIoKFRFL6-960.webp 960w, https://danielscottraynsford.com/img/OvIoKFRFL6-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/OvIoKFRFL6-650.jpeg&quot; alt=&quot;PowerShell Switch redefining variable&quot; width=&quot;1400&quot; height=&quot;189&quot; srcset=&quot;https://danielscottraynsford.com/img/OvIoKFRFL6-650.jpeg 650w, https://danielscottraynsford.com/img/OvIoKFRFL6-960.jpeg 960w, https://danielscottraynsford.com/img/OvIoKFRFL6-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I could go and research further into this and find out why this is, but I just don’t have time right now. If anyone else has looked into this, I’d be really interested to know why.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Get the IP Address of a VM Attached to an External Switch</title>
      <link href="https://danielscottraynsford.com/blog/get-the-ip-address-of-a-vm-attached-to-an-external-switch/" />
      <updated>2015-09-07T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/get-the-ip-address-of-a-vm-attached-to-an-external-switch/</id>
      <content type="html">
				&lt;p&gt;My LabBuilder project is coming along nicely and it is building a large lab environment in Hyper-V within a few minutes. However, a problem I ran into was that sometimes the host couldn’t connect (using &lt;em&gt;New-PSSession&lt;/em&gt; or equivalent) to a Guest VM to &lt;em&gt;copy files&lt;/em&gt; or &lt;em&gt;invoke commands&lt;/em&gt;. This was because I was usually using the computer name to connect to the Guest VM—which won’t always work. Instead, I needed to use the &lt;strong&gt;IP address&lt;/strong&gt; of the VM’s Virtual NIC that is attached to the &lt;strong&gt;External Switch&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;So what I needed was a command that I could fire on the &lt;strong&gt;host&lt;/strong&gt; that would tell me the &lt;strong&gt;IPv4&lt;/strong&gt; address of the external-facing &lt;strong&gt;NIC&lt;/strong&gt; on a VM. So this is what I came up with:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;content&#92;blog&#92;2015&#92;09&#92;2015-09-07-get-the-ip-address-of-a-vm-attached-to-an-external-switch.md&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$IPAddress&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-VMNetworkAdapter&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VMName &lt;span class=&quot;token string&quot;&gt;&#39;Server01&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Where&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SwitchName &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-VMSwitch&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SwitchType External&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;IPAddresses&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Where&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Contains&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;This wouldn’t be required at all, and I wouldn’t need to have the Guest VM connected to the Host via an External switch, if &lt;strong&gt;PowerShell Direct&lt;/strong&gt; was integrated into the &lt;strong&gt;New-PSSession&lt;/strong&gt; cmdlet. But unfortunately it isn’t yet. If you’d like to see this happen too, please go and vote for this &lt;a href=&quot;https://connect.microsoft.com/PowerShell/Feedback/Details/1761123&quot; rel=&quot;noopener&quot;&gt;PowerShell Connect feedback item&lt;/a&gt;. If you haven’t heard of&amp;nbsp;&lt;strong&gt;PowerShell Direct&lt;/strong&gt;, see &lt;a href=&quot;http://blogs.technet.com/b/virtualization/archive/2015/05/14/powershell-direct-running-powershell-inside-a-virtual-machine-from-the-hyper-v-host.aspx&quot; rel=&quot;noopener&quot;&gt;this&lt;/a&gt; post.&lt;/p&gt;&lt;/blockquote&gt;
 			</content>
    </entry><entry>
      <title>Convert a Domain Name to a Distinguished Name in PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/convert-a-domain-name-to-a-distinguished-name-in-powershell/" />
      <updated>2015-09-03T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/convert-a-domain-name-to-a-distinguished-name-in-powershell/</id>
      <content type="html">
				&lt;p&gt;Here is a small &lt;strong&gt;PowerShell&lt;/strong&gt; snippet to easily convert a domain name (e.g. &lt;code&gt;corp.bmdlab.com&lt;/code&gt;) to a distinguished name (&lt;code&gt;DC=corp,DC=bmdlab,DC=com&lt;/code&gt;):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;content&#92;blog&#92;2015&#92;09&#92;2015-09-03-convert-a-domain-name-to-a-distinguished-name.md&lt;/span&gt;
&lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Domain&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;corp.bmdlab.com&#39;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Create an empty string that the DN will be stored in&lt;/span&gt;
&lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$DN&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Assemble the DN by splitting the DC and then looping to concatenate the new&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$Domain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Split&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ForEach-Object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$DN&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&quot;DC=&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;,&lt;span class=&quot;token variable&quot;&gt;$DN&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# An extra , will be left on the end of DN, so strip it off&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$DN&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$DN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Substring&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$DN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;An even easier way would be to use the &lt;code&gt;Replace&lt;/code&gt; method on a string object:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;content&#92;blog&#92;2015&#92;09&#92;2015-09-03-convert-a-domain-name-to-a-distinguished-name.md&lt;/span&gt;
&lt;span class=&quot;token namespace&quot;&gt;[String]&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Domain&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;corp.bmdlab.com&#39;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Assemble the DN by replacing&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$DN&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;CN=&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Domain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Replace&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;,CN=&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That is all!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Advanced Certificate Services Configuration with DSC</title>
      <link href="https://danielscottraynsford.com/blog/advanced-certificate-services-configuration-with-dsc/" />
      <updated>2015-09-03T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/advanced-certificate-services-configuration-with-dsc/</id>
      <content type="html">
				&lt;p&gt;Recently I’ve been rebuilding my Hyper-V lab environment from scratch (as part of my MCSA/MCSE studying) and decided I would completely script the process using PowerShell only. My goal was to not require a single interactive session with any of the servers to set up the entire environment. This was a multi-site AD environment with several other member servers performing other duties including NPS &amp;amp; NAP, ADCS, WDS, WSUS, ADFS, ADRMS, IIS, DirectAccess, SQL, etc. I also wanted this to include a proper Multi-tier PKI environment with both a standalone Root CA and an Enterprise Subordinate CA (as is recommended by Microsoft).&lt;/p&gt;&lt;h3 id=&quot;the-problem&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/advanced-certificate-services-configuration-with-dsc/#the-problem&quot; class=&quot;heading-anchor&quot;&gt;The Problem&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;To configure an AD CS using DSC is very straightforward using the &lt;a href=&quot;https://github.com/PowerShell/xAdcsDeployment&quot; rel=&quot;noopener&quot;&gt;xADCSDeployment&lt;/a&gt; DSC Resource. However, anyone who has installed an enterprise PKI is probably familiar with the fact that &lt;strong&gt;AIA&lt;/strong&gt; and &lt;strong&gt;CDP&lt;/strong&gt; settings need to be configured for the CA so that any generated certificates can include these extensions. Unfortunately, the xADCSDeployment DSC Resource doesn’t support setting these Certificate Services options because the underlying AD CS PowerShell cmdlets don’t allow setting these options either.&lt;/p&gt;&lt;h3 id=&quot;the-solution&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/advanced-certificate-services-configuration-with-dsc/#the-solution&quot; class=&quot;heading-anchor&quot;&gt;The Solution&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The solution to this problem is to use the DSC &lt;a href=&quot;https://technet.microsoft.com/en-us/library/dn282130.aspx&quot; rel=&quot;noopener&quot;&gt;Script&lt;/a&gt; Resource to call the &lt;a href=&quot;https://technet.microsoft.com/en-us/library/cc732443.aspx&quot; rel=&quot;noopener&quot;&gt;CertUtil.exe&lt;/a&gt; application to set these values. To do this, I set the &lt;strong&gt;SetScript&lt;/strong&gt; resource parameter like this:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;SetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &amp;amp; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;system32&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;setreg CA&#92;DSConfigDN &lt;span class=&quot;token string&quot;&gt;&quot;CN=Configuration,&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
        &amp;amp; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;system32&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;setreg CA&#92;DSDomainDN &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CRLPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &amp;amp; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;System32&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;setreg CA&#92;CRLPublicationURLs $&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CRLPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACertPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &amp;amp; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;System32&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;setreg CA&#92;CACertPublicationURLs $&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACertPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Restart-Service&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name CertSvc
    &lt;span class=&quot;token function&quot;&gt;Add-Content&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&#39;c:&#92;windows&#92;setup&#92;scripts&#92;certutil.log&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Value &lt;span class=&quot;token string&quot;&gt;&quot;Certificate Service Restarted ...&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above code expects the &lt;code&gt;$Node&lt;/code&gt; object to contain several properties that it will use to set applicable settings in the CA server:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;CADistinguishedNameSuffix&lt;/strong&gt; - This is the Directory Services Distinguished Name (e.g., &lt;code&gt;DC=bmdlab,DC=com&lt;/code&gt;). If left blank, then your CDP and AIA LDAP Addresses will be incorrect.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;CRLPublicationURLs&lt;/strong&gt; - The CRL Publication URLs in the same format as you would normally pass to the &lt;strong&gt;certutil.exe&lt;/strong&gt; application.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;CACertPublicationURLs&lt;/strong&gt; - The CA Cert Publication URLs (AIA Extension) in the same format as you would normally pass to the &lt;strong&gt;certutil.exe&lt;/strong&gt; application.&lt;/li&gt;&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; To access the &lt;code&gt;$Node&lt;/code&gt; object in the script resource, you’ll need to use the &lt;strong&gt;Using&lt;/strong&gt; keyword; otherwise, the &lt;code&gt;$Node&lt;/code&gt; object won’t be available in the external script scope.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Finally, after these values have been set, the &lt;strong&gt;CertSvc&lt;/strong&gt; needs to be restarted.&lt;/p&gt;&lt;h3 id=&quot;what-about-testscript&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/advanced-certificate-services-configuration-with-dsc/#what-about-testscript&quot; class=&quot;heading-anchor&quot;&gt;What about TestScript?&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;You might look at the above code and wonder, “won’t the CertSvc be restarted every 30 minutes no matter what?” That is where the &lt;strong&gt;TestScript&lt;/strong&gt; resource parameter comes in. We’ll use this to decide if the current values of the CA are different from what we want them to be. In this case, we dive straight to the registry rather than using the &lt;strong&gt;CertUtil.exe&lt;/strong&gt; application.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;TestScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DSConfigDN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CN=Configuration,&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DSDomainDN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CRLPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CRLPublicationURLs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CRLPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACertPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CACertPublicationURLs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACertPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once again, I need to ensure the &lt;strong&gt;Using&lt;/strong&gt; scope is used with the &lt;strong&gt;Node&lt;/strong&gt; variable. If any of the node properties don’t match the registry value, then &lt;strong&gt;false&lt;/strong&gt; is returned, which triggers &lt;strong&gt;SetScript&lt;/strong&gt;. If all of the values match, &lt;strong&gt;true&lt;/strong&gt; is returned (meaning everything is the same), and &lt;strong&gt;SetScript&lt;/strong&gt; isn’t fired.&lt;/p&gt;&lt;h3 id=&quot;is-that-it&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/advanced-certificate-services-configuration-with-dsc/#is-that-it&quot; class=&quot;heading-anchor&quot;&gt;Is That It?&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Almost. Finally, I needed to implement the &lt;strong&gt;GetScript&lt;/strong&gt; parameter of the resource. This was the easiest part:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;GetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;DSConfigDN&#39;&lt;/span&gt;          = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DSConfigDN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;DSDomainDN&#39;&lt;/span&gt;          = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DSDomainDN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;CRLPublicationURLs&#39;&lt;/span&gt;  = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CRLPublicationURLs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;CACertPublicationURLs&#39;&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CACertPublicationURLs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I could also adjust the &lt;strong&gt;TestScript&lt;/strong&gt; to call the &lt;strong&gt;GetScript&lt;/strong&gt; and then use the returned hash table to compare with the &lt;strong&gt;Node&lt;/strong&gt; values instead of comparing them directly with the registry values. But I didn’t.&lt;/p&gt;&lt;h3 id=&quot;final-script&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/advanced-certificate-services-configuration-with-dsc/#final-script&quot; class=&quot;heading-anchor&quot;&gt;Final Script&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Here is what the final script looks like (I didn’t include everything in the DSC Configuration as there were lots of other resources for creating the machine):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Script ADCSAdvConfig &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    SetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &amp;amp; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;system32&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;setreg CA&#92;DSConfigDN &lt;span class=&quot;token string&quot;&gt;&quot;CN=Configuration,&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
            &amp;amp; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;system32&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;setreg CA&#92;DSDomainDN &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CRLPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &amp;amp; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;System32&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;setreg CA&#92;CRLPublicationURLs $&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CRLPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACertPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &amp;amp; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ENV&lt;/span&gt;:SystemRoot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&#92;System32&#92;certutil.exe&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;setreg CA&#92;CACertPublicationURLs $&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACertPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Restart-Service&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name CertSvc
        &lt;span class=&quot;token function&quot;&gt;Add-Content&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&#39;c:&#92;windows&#92;setup&#92;scripts&#92;certutil.log&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Value &lt;span class=&quot;token string&quot;&gt;&quot;Certificate Service Restarted ...&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    GetScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&#39;DSConfigDN&#39;&lt;/span&gt;          = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DSConfigDN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&#39;DSDomainDN&#39;&lt;/span&gt;          = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DSDomainDN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&#39;CRLPublicationURLs&#39;&lt;/span&gt;  = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CRLPublicationURLs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&#39;CACertPublicationURLs&#39;&lt;/span&gt; = &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CACertPublicationURLs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    TestScript = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DSConfigDN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CN=Configuration,&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DSDomainDN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CADistinguishedNameSuffix&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CRLPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CRLPublicationURLs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CRLPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACertPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HKLM:&#92;System&#92;CurrentControlSet&#92;Services&#92;CertSvc&#92;Configuration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetValue&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CACertPublicationURLs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$Using&lt;/span&gt;:Node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CACertPublicationURLs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    DependsOn = &lt;span class=&quot;token string&quot;&gt;&#39;[xADCSWebEnrollment]ConfigWebEnrollment&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Hopefully, someone will make sense of all this. It should also be useful in other similar situations where there are no relevant DSC resources.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>WMF 5.0 Production Preview is Available</title>
      <link href="https://danielscottraynsford.com/blog/wmf-50-production-preview-is-available/" />
      <updated>2015-09-01T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/wmf-50-production-preview-is-available/</id>
      <content type="html">
				&lt;p&gt;Have a look &lt;a href=&quot;http://blogs.msdn.com/b/powershell/archive/2015/08/31/windows-management-framework-5-0-production-preview-is-now-available.aspx&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;. Still not quite RTM, but close!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>The Demise of SMB1 in the Windows Stack</title>
      <link href="https://danielscottraynsford.com/blog/the-demise-of-smb1-in-the-windows-stack/" />
      <updated>2015-09-01T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/the-demise-of-smb1-in-the-windows-stack/</id>
      <content type="html">
				&lt;p&gt;If you’re interested in &lt;strong&gt;SMB&lt;/strong&gt; and the general progress of the protocol, I’d recommend this 30-minute video on Channel 9: &lt;a href=&quot;https://channel9.msdn.com/Blogs/Regular-IT-Guy/The-Demise-of-SMB-1-in-the-Windows-Stack?wt.mc_id=player&quot; rel=&quot;noopener&quot;&gt;The Demise of SMB1 in the Windows Stack&lt;/a&gt;. It motivated me to rid all my desktops and servers of SMB 1.0, the &lt;em&gt;ancient&lt;/em&gt; and &lt;em&gt;insecure&lt;/em&gt; protocol support.&lt;/p&gt;&lt;p&gt;To disable SMB 1.0 on a &lt;strong&gt;Windows Server&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Set-SmbServerConfiguration&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;EnableSMB1Protocol &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can uninstall SMB 1.0 on a &lt;strong&gt;Windows desktop&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Disable-WindowsOptionalFeature&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FeatureName SMB1Protocol &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Online&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Don’t do this if you have any older devices or operating systems that can only use SMB 1.0.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Install all DSC Resources in a Repository</title>
      <link href="https://danielscottraynsford.com/blog/install-all-dsc-resources-in-a-repository/" />
      <updated>2015-08-28T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/install-all-dsc-resources-in-a-repository/</id>
      <content type="html">
				&lt;p&gt;Something quick for Friday morning! Are you feeling enthusiastic about DSC? Do you want to &lt;strong&gt;update&lt;/strong&gt; or &lt;strong&gt;install&lt;/strong&gt; &lt;em&gt;every DSC resource&lt;/em&gt; in a repository onto your computer?&lt;/p&gt;&lt;p&gt;Try this (only on PowerShell 5.0 computers - what do you mean you’re not using PowerShell 5.0?!):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Find-DscResource&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Select-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ExpandProperty ModuleName &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Unique &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;ForEach-Object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This downloads and installs (or updates, if you have older versions) every DSC resource in &lt;strong&gt;all&lt;/strong&gt; PowerShell repositories registered on your computer. It can take a while.&lt;/p&gt;&lt;p&gt;If you want to limit the action to a specific repository, do this:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$RepoName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;PSGallery&#39;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;Find-DscResource&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Repository &lt;span class=&quot;token variable&quot;&gt;$RepoName&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Select-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ExpandProperty ModuleName &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Unique &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;ForEach-Object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Install-Module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Repository &lt;span class=&quot;token variable&quot;&gt;$RepoName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; only install DSC resources from repositories you trust. Mark trusted repositories so you don’t have to confirm each module installation:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Set-PSRepository&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&#39;PSGallery&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;InstallationPolicy Trusted&lt;/code&gt;&lt;/pre&gt;
 			</content>
    </entry><entry>
      <title>Detach from a Docker Container Without Stopping It</title>
      <link href="https://danielscottraynsford.com/blog/detach-from-a-docker-container-without-stopping-it/" />
      <updated>2015-08-28T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/detach-from-a-docker-container-without-stopping-it/</id>
      <content type="html">
				&lt;p&gt;Saturday-morning Docker fun times (still only on Windows Server Core) – here’s something I found out that might be useful. It is documented in the official &lt;a href=&quot;https://docs.docker.com/articles/basics/&quot; rel=&quot;noopener&quot;&gt;Docker docs&lt;/a&gt; but not in the &lt;a href=&quot;https://msdn.microsoft.com/en-us/virtualization/windowscontainers/quick_start/manage_docker&quot; rel=&quot;noopener&quot;&gt;Microsoft container docs&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Once you have attached to a Docker container via a CMD console, typing &lt;strong&gt;exit&lt;/strong&gt; detaches from the container &lt;em&gt;and&lt;/em&gt; &lt;strong&gt;stops&lt;/strong&gt; it. That’s usually &lt;em&gt;not&lt;/em&gt; what I want. To &lt;strong&gt;detach&lt;/strong&gt; from the container &lt;em&gt;without&lt;/em&gt; stopping it, press &lt;strong&gt;Ctrl + P&lt;/strong&gt; followed by &lt;strong&gt;Ctrl + Q&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_docker_detatchedbutrunningcontainer.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/T9gYCZKshI-650.png 650w, https://danielscottraynsford.com/img/T9gYCZKshI-960.png 960w, https://danielscottraynsford.com/img/T9gYCZKshI-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/T9gYCZKshI-650.webp 650w, https://danielscottraynsford.com/img/T9gYCZKshI-960.webp 960w, https://danielscottraynsford.com/img/T9gYCZKshI-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/T9gYCZKshI-650.jpeg&quot; alt=&quot;The container is still running after being detached.&quot; width=&quot;1400&quot; height=&quot;75&quot; srcset=&quot;https://danielscottraynsford.com/img/T9gYCZKshI-650.jpeg 650w, https://danielscottraynsford.com/img/T9gYCZKshI-960.jpeg 960w, https://danielscottraynsford.com/img/T9gYCZKshI-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;&lt;em&gt;The container is still running after being detached.&lt;/em&gt;&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;This only applies to &lt;strong&gt;Docker containers&lt;/strong&gt; that you connected to with &lt;code&gt;docker attach&lt;/code&gt; or &lt;code&gt;docker run&lt;/code&gt;. &lt;strong&gt;Windows Server Containers&lt;/strong&gt; that you entered with &lt;code&gt;Enter-PSSession&lt;/code&gt; can be exited with the standard &lt;strong&gt;Exit&lt;/strong&gt; command.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Well, that’s enough for a Saturday morning!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Docker and Containers on Nano Server Continued</title>
      <link href="https://danielscottraynsford.com/blog/docker-and-containers-on-nano-server-continued/" />
      <updated>2015-08-27T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/docker-and-containers-on-nano-server-continued/</id>
      <content type="html">
				&lt;p&gt;This is a continuation of my investigation of how to get Containers and also possibly the Docker engine running on &lt;strong&gt;Windows Server Nano 2016 TP 3&lt;/strong&gt;. The initial investigation into this can be found here: &lt;a href=&quot;https://danielscottraynsford.com/blog/how-to-use-containers-on-windows-nano-server/&quot;&gt;How to use Containers on Windows Nano Server.&lt;/a&gt;&lt;/p&gt;&lt;p&gt;This post is mainly documenting the process of manually creating containers on Windows Nano Server 2016 TP3 as well as some additional details about what I have managed to find out. The documentation on &lt;strong&gt;Windows Server Containers&lt;/strong&gt; from Microsoft is relatively thin at the moment (not surprising - this is very much in technical preview) and so a lot of the information here is speculation on my part. Still, it might be useful to get an idea of how things eventually will work. But of course a good deal of it could change in the near future. This information is really to help me get my head around the concepts and how it will work, but it might be useful for others.&lt;/p&gt;&lt;h3 id=&quot;step-1-create-a-nano-server-virtual-machine&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/docker-and-containers-on-nano-server-continued/#step-1-create-a-nano-server-virtual-machine&quot; class=&quot;heading-anchor&quot;&gt;Step 1 - Create a Nano Server Virtual Machine&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Anyone who has played around with Nano Server should already be very familiar with this step. The only thing to remember is that the following packages must be included in the VHDx:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Guest&lt;/strong&gt; - All Nano Server VHDx files running as&amp;nbsp; VM should have this package. If you’re installing Nano Server onto bare metal you won’t need this.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Compute&lt;/strong&gt; - Includes the Nano Server Hyper-V components. Required because Containers use Hyper-V networking and are a form of Virtualization.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;OEM-Drivers&lt;/strong&gt; - Not strictly required but I tend to include it anyway.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Containers&lt;/strong&gt; - This package provides the core of Windows Server Containers.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;If you’re unfamiliar with creating a Nano Server VHDx, please see &lt;a href=&quot;https://danielscottraynsford.com/blog/install-windows-server-nano-the-easy-way/&quot;&gt;this&lt;/a&gt; post.&lt;/p&gt;&lt;h3 id=&quot;step-2-configure-the-container-host-networking&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/docker-and-containers-on-nano-server-continued/#step-2-configure-the-container-host-networking&quot; class=&quot;heading-anchor&quot;&gt;Step 2 - Configure the Container Host Networking&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Any&amp;nbsp;&lt;em&gt;container&lt;/em&gt; that needs to be connected to a network (most of them usually) will need to connect to a&amp;nbsp;&lt;strong&gt;Hyper-V Virtual Switch&lt;/strong&gt; configured on this&amp;nbsp;&lt;strong&gt;Container Host&lt;/strong&gt;. There are two virtual switch types that can be configured for this purpose:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;NAT&lt;/strong&gt; - This seems to be a new switch type in Windows Server 2016 that causes performs some kind of NAT on the connected adapters.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;DHCP&lt;/strong&gt; - this is actually just a standard &lt;strong&gt;External&lt;/strong&gt; switch with a connection to a physical network adapter on the &lt;strong&gt;Container Host&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The installation script normally performs one of the above depending on which option you select. However on Nano Server both of these processes fail:&lt;/p&gt;&lt;h4 id=&quot;nat&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/docker-and-containers-on-nano-server-continued/#nat&quot; class=&quot;heading-anchor&quot;&gt;NAT&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Creating a &lt;strong&gt;NAT&lt;/strong&gt; &lt;strong&gt;VM Switch&lt;/strong&gt; on Nano Server actually works. But the command to create a NAT Network connection to the VM Switch fails because the &lt;strong&gt;NETNAT&lt;/strong&gt; module is not available on Nano Server.&lt;/p&gt;&lt;h4 id=&quot;dhcp&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/docker-and-containers-on-nano-server-continued/#dhcp&quot; class=&quot;heading-anchor&quot;&gt;DHCP&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_nano_containers_creatingadhcpswitch.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/uKevRB876G-650.png 650w, https://danielscottraynsford.com/img/uKevRB876G-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/uKevRB876G-650.webp 650w, https://danielscottraynsford.com/img/uKevRB876G-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/uKevRB876G-650.jpeg&quot; alt=&quot;Creating a standard External VM Switch on Nano Server&quot; width=&quot;960&quot; height=&quot;137&quot; srcset=&quot;https://danielscottraynsford.com/img/uKevRB876G-650.jpeg 650w, https://danielscottraynsford.com/img/uKevRB876G-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Creating a &lt;strong&gt;DHCP/External VM Switch&lt;/strong&gt; on Nano Server just fails with a cryptic error message. The same error occurs when creating a &lt;em&gt;Private&lt;/em&gt; or &lt;em&gt;Internal&lt;/em&gt; VM Switch, so I expect Hyper-V on Nano Server isn’t working so well (or at all). Not much point pursuing this method of networking.&lt;/p&gt;&lt;h3 id=&quot;step-3-install-a-base-os-image-from-a-wim-file&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/docker-and-containers-on-nano-server-continued/#step-3-install-a-base-os-image-from-a-wim-file&quot; class=&quot;heading-anchor&quot;&gt;Step 3 - Install a Base OS Image from a WIM File&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Every &lt;em&gt;container&lt;/em&gt; you create requires a &lt;strong&gt;Base OS Image&lt;/strong&gt;. This &lt;strong&gt;Base OS Image&lt;/strong&gt; contains all the &lt;em&gt;operating system&lt;/em&gt; files and &lt;em&gt;registry settings&lt;/em&gt; for the OS a container uses. &lt;strong&gt;Windows Server Containers&lt;/strong&gt; expects to be provided with at least one &lt;strong&gt;Base OS Image&lt;/strong&gt; in the form of a &lt;strong&gt;WIM&lt;/strong&gt; &lt;strong&gt;file&lt;/strong&gt;. You can’t create a container without one of these. At this point I am unsure if the&amp;nbsp;&lt;strong&gt;WIM file&lt;/strong&gt; that&amp;nbsp;&lt;strong&gt;Windows Server Containers&lt;/strong&gt; will use is a customized version of the &lt;strong&gt;WIM file&lt;/strong&gt; provided with an OS or if it is standard.&lt;/p&gt;&lt;p&gt;During an installation of &lt;strong&gt;Windows Server Containers&lt;/strong&gt; onto a &lt;strong&gt;Windows Server Core&lt;/strong&gt; operating system, the process automatically &lt;a href=&quot;http://aka.ms/ContainerOSImage&quot; rel=&quot;noopener&quot;&gt;downloads&lt;/a&gt; a WIM file that is used as the &lt;strong&gt;Base OS Image&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;To install a &lt;strong&gt;Base OS Image&lt;/strong&gt; from a &lt;strong&gt;WIM&lt;/strong&gt; file on the &lt;strong&gt;Container Host&lt;/strong&gt;, use:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Install-ContainerOSImage&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;WimPath CoreServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;wim &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Verbose&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/8B6ERFw6ix-650.png 650w, https://danielscottraynsford.com/img/8B6ERFw6ix-957.png 957w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/8B6ERFw6ix-650.webp 650w, https://danielscottraynsford.com/img/8B6ERFw6ix-957.webp 957w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/8B6ERFw6ix-650.jpeg&quot; alt=&quot;Installing a Base OS Image&quot; width=&quot;957&quot; height=&quot;305&quot; srcset=&quot;https://danielscottraynsford.com/img/8B6ERFw6ix-650.jpeg 650w, https://danielscottraynsford.com/img/8B6ERFw6ix-957.jpeg 957w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This function does several things:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Creates a new folder in &lt;code&gt;C:&#92;ProgramData&#92;Microsoft&#92;Windows&#92;Images&lt;/code&gt; whose name is the &lt;em&gt;canonical&lt;/em&gt; name of the new &lt;strong&gt;Base OS Image&lt;/strong&gt;.&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/QDOiwZ6w2--650.png 650w, https://danielscottraynsford.com/img/QDOiwZ6w2--960.png 960w, https://danielscottraynsford.com/img/QDOiwZ6w2--1294.png 1294w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/QDOiwZ6w2--650.webp 650w, https://danielscottraynsford.com/img/QDOiwZ6w2--960.webp 960w, https://danielscottraynsford.com/img/QDOiwZ6w2--1294.webp 1294w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/QDOiwZ6w2--650.jpeg&quot; alt=&quot;Contents of the Images folder&quot; width=&quot;1294&quot; height=&quot;194&quot; srcset=&quot;https://danielscottraynsford.com/img/QDOiwZ6w2--650.jpeg 650w, https://danielscottraynsford.com/img/QDOiwZ6w2--960.jpeg 960w, https://danielscottraynsford.com/img/QDOiwZ6w2--1294.jpeg 1294w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Inside that folder a sub-folder called &lt;strong&gt;files&lt;/strong&gt; is created and the image is expanded there.&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/rq3PQOlzf4-650.png 650w, https://danielscottraynsford.com/img/rq3PQOlzf4-960.png 960w, https://danielscottraynsford.com/img/rq3PQOlzf4-1233.png 1233w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/rq3PQOlzf4-650.webp 650w, https://danielscottraynsford.com/img/rq3PQOlzf4-960.webp 960w, https://danielscottraynsford.com/img/rq3PQOlzf4-1233.webp 1233w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/rq3PQOlzf4-650.jpeg&quot; alt=&quot;The contents of an Image files folder&quot; width=&quot;1233&quot; height=&quot;243&quot; srcset=&quot;https://danielscottraynsford.com/img/rq3PQOlzf4-650.jpeg 650w, https://danielscottraynsford.com/img/rq3PQOlzf4-960.jpeg 960w, https://danielscottraynsford.com/img/rq3PQOlzf4-1233.jpeg 1233w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Another sub-folder called &lt;strong&gt;hives&lt;/strong&gt; is created which contains the default registry hives for the image.&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/pkAhLzfwVi-650.png 650w, https://danielscottraynsford.com/img/pkAhLzfwVi-960.png 960w, https://danielscottraynsford.com/img/pkAhLzfwVi-1201.png 1201w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/pkAhLzfwVi-650.webp 650w, https://danielscottraynsford.com/img/pkAhLzfwVi-960.webp 960w, https://danielscottraynsford.com/img/pkAhLzfwVi-1201.webp 1201w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/pkAhLzfwVi-650.jpeg&quot; alt=&quot;The Image registry hives&quot; width=&quot;1201&quot; height=&quot;327&quot; srcset=&quot;https://danielscottraynsford.com/img/pkAhLzfwVi-650.jpeg 650w, https://danielscottraynsford.com/img/pkAhLzfwVi-960.jpeg 960w, https://danielscottraynsford.com/img/pkAhLzfwVi-1201.jpeg 1201w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Two metadata files are written – &lt;strong&gt;Metadata.json&lt;/strong&gt; and &lt;strong&gt;Version.wcx&lt;/strong&gt;.&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/KwQ10EglHx-650.png 650w, https://danielscottraynsford.com/img/KwQ10EglHx-960.png 960w, https://danielscottraynsford.com/img/KwQ10EglHx-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/KwQ10EglHx-650.webp 650w, https://danielscottraynsford.com/img/KwQ10EglHx-960.webp 960w, https://danielscottraynsford.com/img/KwQ10EglHx-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/KwQ10EglHx-650.jpeg&quot; alt=&quot;Image metadata&quot; width=&quot;1400&quot; height=&quot;153&quot; srcset=&quot;https://danielscottraynsford.com/img/KwQ10EglHx-650.jpeg 650w, https://danielscottraynsford.com/img/KwQ10EglHx-960.jpeg 960w, https://danielscottraynsford.com/img/KwQ10EglHx-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Finally, the image is added to the list of container images available for new containers.&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/2UzB7kG66T-650.png 650w, https://danielscottraynsford.com/img/2UzB7kG66T-960.png 960w, https://danielscottraynsford.com/img/2UzB7kG66T-1210.png 1210w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/2UzB7kG66T-650.webp 650w, https://danielscottraynsford.com/img/2UzB7kG66T-960.webp 960w, https://danielscottraynsford.com/img/2UzB7kG66T-1210.webp 1210w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/2UzB7kG66T-650.jpeg&quot; alt=&quot;All Base OS images installed&quot; width=&quot;1210&quot; height=&quot;551&quot; srcset=&quot;https://danielscottraynsford.com/img/2UzB7kG66T-650.jpeg 650w, https://danielscottraynsford.com/img/2UzB7kG66T-960.jpeg 960w, https://danielscottraynsford.com/img/2UzB7kG66T-1210.jpeg 1210w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;I have tried using &lt;strong&gt;Install.wim&lt;/strong&gt; from the ISO, &lt;strong&gt;NanoServer.wim&lt;/strong&gt; from the ISO, and the &lt;strong&gt;Core.wim&lt;/strong&gt; downloaded via the Core-edition container install script. Note that &lt;code&gt;Install.wim&lt;/code&gt; on the TP3 ISO still reports &lt;strong&gt;Windows Server 2012 R2 SERVERSTANDARDCORE&lt;/strong&gt; (double-checked via the version number inside the image).&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;Test-ContainerImage&lt;/strong&gt; cmdlet can be used to identify “problems” with container images:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_nano_containers_testcontainers.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/bPd_E1VH8r-650.png 650w, https://danielscottraynsford.com/img/bPd_E1VH8r-960.png 960w, https://danielscottraynsford.com/img/bPd_E1VH8r-1227.png 1227w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/bPd_E1VH8r-650.webp 650w, https://danielscottraynsford.com/img/bPd_E1VH8r-960.webp 960w, https://danielscottraynsford.com/img/bPd_E1VH8r-1227.webp 1227w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/bPd_E1VH8r-650.jpeg&quot; alt=&quot;Testing Containers&quot; width=&quot;1227&quot; height=&quot;153&quot; srcset=&quot;https://danielscottraynsford.com/img/bPd_E1VH8r-650.jpeg 650w, https://danielscottraynsford.com/img/bPd_E1VH8r-960.jpeg 960w, https://danielscottraynsford.com/img/bPd_E1VH8r-1227.jpeg 1227w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;None of the container images report any problems which is nice to know.&lt;/p&gt;&lt;h3 id=&quot;step-4-create-a-container&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/docker-and-containers-on-nano-server-continued/#step-4-create-a-container&quot; class=&quot;heading-anchor&quot;&gt;Step 4 - Create a Container&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This is obviously where things should start to get exciting! The next step is to create a shiny new container using one of our &lt;strong&gt;Base OS Images&lt;/strong&gt;. However, if you try and create a new container at this point a cryptic error message will occur:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_nano_containers_newcontainerfailure.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/CJ-oOqxwZ4-650.png 650w, https://danielscottraynsford.com/img/CJ-oOqxwZ4-960.png 960w, https://danielscottraynsford.com/img/CJ-oOqxwZ4-1227.png 1227w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/CJ-oOqxwZ4-650.webp 650w, https://danielscottraynsford.com/img/CJ-oOqxwZ4-960.webp 960w, https://danielscottraynsford.com/img/CJ-oOqxwZ4-1227.webp 1227w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/CJ-oOqxwZ4-650.jpeg&quot; alt=&quot;New Container? Nope!&quot; width=&quot;1227&quot; height=&quot;296&quot; srcset=&quot;https://danielscottraynsford.com/img/CJ-oOqxwZ4-650.jpeg 650w, https://danielscottraynsford.com/img/CJ-oOqxwZ4-960.jpeg 960w, https://danielscottraynsford.com/img/CJ-oOqxwZ4-1227.jpeg 1227w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I don’t know what causes this, but if you reboot your Nano Server VM the error goes away and you should be able to successfully create the container:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_nano_containers_firstcontainer.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/VmmO1LKuOZ-650.png 650w, https://danielscottraynsford.com/img/VmmO1LKuOZ-960.png 960w, https://danielscottraynsford.com/img/VmmO1LKuOZ-1227.png 1227w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/VmmO1LKuOZ-650.webp 650w, https://danielscottraynsford.com/img/VmmO1LKuOZ-960.webp 960w, https://danielscottraynsford.com/img/VmmO1LKuOZ-1227.webp 1227w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/VmmO1LKuOZ-650.jpeg&quot; alt=&quot;First Container - making progress&quot; width=&quot;1227&quot; height=&quot;443&quot; srcset=&quot;https://danielscottraynsford.com/img/VmmO1LKuOZ-650.jpeg 650w, https://danielscottraynsford.com/img/VmmO1LKuOZ-960.jpeg 960w, https://danielscottraynsford.com/img/VmmO1LKuOZ-1227.jpeg 1227w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Unfortunately only the Base OS image downloaded from Microsoft for Windows Server 2016 Core results in a valid container. It seems that certain customisations are required before an image can be &lt;em&gt;containerised&lt;/em&gt;.&lt;/p&gt;&lt;h3 id=&quot;step-5-start-the-container&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/docker-and-containers-on-nano-server-continued/#step-5-start-the-container&quot; class=&quot;heading-anchor&quot;&gt;Step 5 - Start the Container&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I’m not holding my breath here. This is what happens when the container is started:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_nano_containers_startupfailure.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/q7R4XAayeF-650.png 650w, https://danielscottraynsford.com/img/q7R4XAayeF-960.png 960w, https://danielscottraynsford.com/img/q7R4XAayeF-1227.png 1227w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/q7R4XAayeF-650.webp 650w, https://danielscottraynsford.com/img/q7R4XAayeF-960.webp 960w, https://danielscottraynsford.com/img/q7R4XAayeF-1227.webp 1227w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/q7R4XAayeF-650.jpeg&quot; alt=&quot;Starting up the Container - nope!&quot; width=&quot;1227&quot; height=&quot;184&quot; srcset=&quot;https://danielscottraynsford.com/img/q7R4XAayeF-650.jpeg 650w, https://danielscottraynsford.com/img/q7R4XAayeF-960.jpeg 960w, https://danielscottraynsford.com/img/q7R4XAayeF-1227.jpeg 1227w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Looking closely at the text of the error it would appear that there was a mismatch between the &lt;strong&gt;Container Host OS version&lt;/strong&gt; and that of the &lt;strong&gt;Base OS version&lt;/strong&gt; that the container was using. This is probably because the &lt;em&gt;Container Host&lt;/em&gt; is a &lt;strong&gt;Nano Server&lt;/strong&gt; and the &lt;strong&gt;Base OS&lt;/strong&gt; that was downloaded was for a &lt;strong&gt;Core Server&lt;/strong&gt;.&lt;/p&gt;&lt;h3 id=&quot;next-steps&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/docker-and-containers-on-nano-server-continued/#next-steps&quot; class=&quot;heading-anchor&quot;&gt;Next Steps&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;It would seem at this point we have to wait for Microsoft to provide a &lt;strong&gt;Base OS&lt;/strong&gt; file for &lt;strong&gt;Nano Server&lt;/strong&gt; and also fix the &lt;strong&gt;Virtual Switch&lt;/strong&gt; issues with Nano Server before any further progress can be made experimenting with Containers on Nano Server.&lt;/p&gt;&lt;p&gt;However, it may still be possible to get the Docker Engine working under Nano Server and see if that offers any more information. So that will be what I’ll look into next.&lt;/p&gt;&lt;p&gt;Also, it is interesting to dig around into the files that are created when the new container was created:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_nano_containers_containerfiles.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/eiRrHyvJWr-650.png 650w, https://danielscottraynsford.com/img/eiRrHyvJWr-960.png 960w, https://danielscottraynsford.com/img/eiRrHyvJWr-1227.png 1227w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/eiRrHyvJWr-650.webp 650w, https://danielscottraynsford.com/img/eiRrHyvJWr-960.webp 960w, https://danielscottraynsford.com/img/eiRrHyvJWr-1227.webp 1227w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/eiRrHyvJWr-650.jpeg&quot; alt=&quot;Files Created with a Container&quot; width=&quot;1227&quot; height=&quot;582&quot; srcset=&quot;https://danielscottraynsford.com/img/eiRrHyvJWr-650.jpeg 650w, https://danielscottraynsford.com/img/eiRrHyvJWr-960.jpeg 960w, https://danielscottraynsford.com/img/eiRrHyvJWr-1227.jpeg 1227w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;When a container is created the container files are stored in the &lt;strong&gt;C:&#92;ProgramData&#92;Microsoft&#92;Windows&#92;Hyper-V&#92;containers&lt;/strong&gt; folder. Unfortunately the files all binary so we aren’t able to dig around in them to glean any other information.&lt;/p&gt;&lt;p&gt;Well, that is enough for today.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>How to Use Containers on Windows Nano Server</title>
      <link href="https://danielscottraynsford.com/blog/how-to-use-containers-on-windows-nano-server/" />
      <updated>2015-08-26T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/how-to-use-containers-on-windows-nano-server/</id>
      <content type="html">
				&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; I wrote this article when examining &lt;strong&gt;containers&lt;/strong&gt; on &lt;strong&gt;Windows Nano Server TP3&lt;/strong&gt; – &lt;em&gt;which wasn’t in a working state&lt;/em&gt;. I have not yet had a chance to fully examine &lt;strong&gt;containers&lt;/strong&gt; on &lt;strong&gt;Windows Nano Server TP4&lt;/strong&gt;, but when I get a spare few hours I will no doubt deep-dive into it.&lt;/p&gt;&lt;p&gt;If you’re looking for instructions on installing and using &lt;strong&gt;containers&lt;/strong&gt; on &lt;strong&gt;Windows Nano Server TP4&lt;/strong&gt;, start &lt;a href=&quot;https://msdn.microsoft.com/en-us/virtualization/windowscontainers/deployment/deployment#nano&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;These instructions are more focused on setting up a container host on &lt;strong&gt;Windows Server Core TP4&lt;/strong&gt;, but I have managed to get them working on &lt;strong&gt;Windows Nano Server TP4&lt;/strong&gt; just fine:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/U__zsmyZZA-650.png 650w, https://danielscottraynsford.com/img/U__zsmyZZA-861.png 861w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/U__zsmyZZA-650.webp 650w, https://danielscottraynsford.com/img/U__zsmyZZA-861.webp 861w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/U__zsmyZZA-650.jpeg&quot; alt=&quot;ss_nano_containerhostworking&quot; width=&quot;861&quot; height=&quot;706&quot; srcset=&quot;https://danielscottraynsford.com/img/U__zsmyZZA-650.jpeg 650w, https://danielscottraynsford.com/img/U__zsmyZZA-861.jpeg 861w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;I do plan to document this process over the next week or so.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;You’d be forgiven for believing that it was just a simple click of a button (or addition of a package) to get Docker containers working on a shiny new &lt;strong&gt;Windows Nano Server TP3&lt;/strong&gt; install. That’s what I thought too. But after careful examination of the available &lt;a href=&quot;https://msdn.microsoft.com/en-us/virtualization/windowscontainers/containers_welcome&quot; rel=&quot;noopener&quot;&gt;documentation&lt;/a&gt; I found that there isn’t much information on actually getting containers working on Nano Server. Sure, there is lots of information on running them on a full or core version of &lt;strong&gt;Windows Server TP3&lt;/strong&gt;, but Nano is lacking. So, because I’m a bit obsessive, I decided I’d try adapting the standard installation process.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; Initially I had a bit of success, but I’ve run into some rather stop dead issues that I haven’t been able to resolve (see later on in this post).&lt;/p&gt;&lt;p&gt;I have continued the investigation &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/08/27/docker-and-containers-on-nano-server-continued/&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; with a much more in depth look at the issues.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;tl;dr: Containers on Windows Server Nano 2016 TP3 does not work yet! The Base OS WIM file for Windows Server Nano is required, but has not been provided.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;&lt;h3 id=&quot;problems-with-the-standard-containers-install-script&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/how-to-use-containers-on-windows-nano-server/#problems-with-the-standard-containers-install-script&quot; class=&quot;heading-anchor&quot;&gt;Problems with the Standard Containers Install Script&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;First up I grabbed a copy of &lt;a href=&quot;http://aka.ms/setupcontainers&quot; rel=&quot;noopener&quot;&gt;this script&lt;/a&gt; from Microsoft which is what is used to install containers on a full Windows Server 2016 install. I took a look at it and identified the things that wouldn’t work on a Nano Server 2016 install. This is what I found:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;The script can optionally configure a NAT switch - this requires the &lt;strong&gt;NetNat&lt;/strong&gt; PS module which isn’t available on Nano.&lt;/li&gt;&lt;li&gt;The script will install a VM Switch - therefore the &lt;strong&gt;Compute&lt;/strong&gt; package is required to be installed on the Nano Server (the &lt;strong&gt;Compute&lt;/strong&gt; package contains the Hyper-V components).&lt;/li&gt;&lt;li&gt;The script can download various files from the internet (using the alias &lt;strong&gt;wget&lt;/strong&gt;). &lt;strong&gt;Wget&lt;/strong&gt; and &lt;strong&gt;Invoke-WebRequest&lt;/strong&gt; are not available on Nano Server - so we’ll need to download the files to another machine and pre-copy them to the Nano Server.&lt;/li&gt;&lt;li&gt;The &lt;strong&gt;Expand-Archive&lt;/strong&gt; is used to extract the &lt;strong&gt;NSSM&lt;/strong&gt; executable, but this cmdlet is not available on Nano Server either - so we’ll need to extract the NSSM.exe on another machine and copy it to the server.&lt;/li&gt;&lt;/ol&gt;&lt;h3 id=&quot;the-process-of-installing-a-container-host&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/how-to-use-containers-on-windows-nano-server/#the-process-of-installing-a-container-host&quot; class=&quot;heading-anchor&quot;&gt;The Process of Installing a Container Host&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The process of actually installing a Container Host in Windows Nano Server is as follows:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Create a Nano Server VHDx with the packages &lt;strong&gt;Guest, OEM-Drivers, Compute&lt;/strong&gt; and &lt;strong&gt;Containers&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Create a new VM booting from the VHDx - this is our &lt;strong&gt;Container Host&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Upload a &lt;strong&gt;Base OS WIM file&lt;/strong&gt; to the &lt;strong&gt;Container Host&lt;/strong&gt; containing that will be used to create new containers.&lt;/li&gt;&lt;li&gt;Upload &lt;strong&gt;Docker.exe&lt;/strong&gt; to c:&#92;windows&#92;system32&#92; on the &lt;strong&gt;Container Host&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Upload &lt;strong&gt;NSSM.exe&lt;/strong&gt; to c:&#92;windows&#92;system32&#92; on the &lt;strong&gt;Container Host&lt;/strong&gt; - this is used to create and run the &lt;strong&gt;Docker Service&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Run the installation script on the &lt;strong&gt;Container Host&lt;/strong&gt; - this will install the networking components and configure the &lt;strong&gt;Docker&lt;/strong&gt; service as well as create the &lt;em&gt;container OS image&lt;/em&gt;.&lt;/li&gt;&lt;li&gt;Create a &lt;strong&gt;Container&lt;/strong&gt;!&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;In theory the &lt;strong&gt;Container Host&lt;/strong&gt; is now ready to go!&lt;/p&gt;&lt;h3 id=&quot;what-is-required-to-build-a-nano-server-container-host&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/how-to-use-containers-on-windows-nano-server/#what-is-required-to-build-a-nano-server-container-host&quot; class=&quot;heading-anchor&quot;&gt;What is Required to build a Nano Server Container Host&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;A bit of experience with PowerShell is a good help here!&lt;/p&gt;&lt;p&gt;So, to create a Nano Server Container Host you’ll need a few things:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;A machine that can run Generation 2 Hyper-V machines (Gen 1 will probably work but I’m using Gen 2) - this will host your Nano Server. This machine must also be running &lt;strong&gt;PowerShell 5.0&lt;/strong&gt; (I’m using some PS5.0 only cmdlets)!&lt;/li&gt;&lt;li&gt;A copy of the Windows Server 2016 TP 3 ISO from &lt;a href=&quot;https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-technical-preview&quot; rel=&quot;noopener&quot;&gt;here.&lt;/a&gt;&lt;/li&gt;&lt;li&gt;A working folder (I used D:&#92;Temp) where you’ll put all the scripts and other files etc.&lt;/li&gt;&lt;li&gt;The scripts (I’ll provide them all in a zip file), but they are:&lt;ol&gt;&lt;li&gt;&lt;strong&gt;New-ContainerHostNano.ps1&lt;/strong&gt; - this will do everything and is the only script you’ll run.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Install-ContainerHostNano.ps1&lt;/strong&gt; - this is the script that gets automatically run on the &lt;strong&gt;Container Host&lt;/strong&gt;. It is a version of the Microsoft one from &lt;a href=&quot;http://aka.ms/setupcontainers&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; that I have adjusted to work with Nano Server.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New-NanoServerVHD.ps1&lt;/strong&gt; - this is a script I wrote a while back to create Nano Server VHDx files (see this &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/05/08/install-windows-server-nano-the-easy-way/&quot; rel=&quot;noopener&quot;&gt;post&lt;/a&gt; for more details).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Convert-WindowsImage.ps1&lt;/strong&gt; - this script is required by &lt;strong&gt;New-NanoServerVHD.ps1&lt;/strong&gt; and is available on &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/Convert-WindowsImageps1-0fe23a8f&quot; rel=&quot;noopener&quot;&gt;Microsoft Script Center&lt;/a&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h3 id=&quot;how-can-i-use-all-this&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/how-to-use-containers-on-windows-nano-server/#how-can-i-use-all-this&quot; class=&quot;heading-anchor&quot;&gt;How Can I use all This?&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I haven’t really finished implementing or testing these scripts and I am encountering a problem creating the VM Switch on the Nano Server, but if you’re interested you can get a hold of the scripts in my &lt;a href=&quot;https://github.com/PlagueHO/Powershell/tree/master/Install-ContainerHostNano/Install-ContainerHostNano&quot; rel=&quot;noopener&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;To use them:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Create a &lt;strong&gt;working folder&lt;/strong&gt; (I used d:&#92;temp).&lt;/li&gt;&lt;li&gt;Download the four PS1 scripts from the &lt;a href=&quot;https://github.com/PlagueHO/Powershell/tree/master/Install-ContainerHostNano/Install-ContainerHostNano&quot; rel=&quot;noopener&quot;&gt;GitHub repository&lt;/a&gt; to the &lt;strong&gt;working folder&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Download the Windows Server 2016 TP3 ISO from &lt;a href=&quot;https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-technical-preview&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; and put it in the &lt;strong&gt;working folder&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Download the Base OS Container Image from &lt;a href=&quot;http://aka.ms/ContainerOSImage&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; (3.5GB download) and put it in the &lt;strong&gt;working folder&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Edit the &lt;strong&gt;New-ContainerHostNano.ps1&lt;/strong&gt; file in the working folder and customize the variables at the top to suit your paths and such - fairly self explanatory.&lt;/li&gt;&lt;li&gt;In an &lt;strong&gt;Administrative PowerShell&lt;/strong&gt; run the &lt;strong&gt;New-ContainerHostNano.ps1&lt;/strong&gt; file.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Please note: This is a &lt;strong&gt;work in progress&lt;/strong&gt;. There are definitely some bugs in it:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;An error is occurring when trying to create the VM Switch in DHCP mode or NAT mode.&lt;/li&gt;&lt;li&gt;If using NAT mode the NAT module isn’t included in Nano Server so although the VM switch gets created the NAT Network adapter can’t be created.&lt;/li&gt;&lt;li&gt;NSSM isn’t creating the Docker Service - which may just be an issue with running the PowerShell installation script remotely.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;em&gt;None of the above will stop containers being created though.&lt;/em&gt; The containers might not be able to communicate with the world via networking and the Docker management engine might not work, but in theory the containers should still work (at least that is my understanding).&lt;/p&gt;&lt;h3 id=&quot;the-big-problem&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/how-to-use-containers-on-windows-nano-server/#the-big-problem&quot; class=&quot;heading-anchor&quot;&gt;The BIG Problem&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Any container that you create requires a WIM file that contains the &lt;strong&gt;container base OS image&lt;/strong&gt; that container will use. Microsoft has so far only provided a base WIM file for &lt;strong&gt;WIndows Server 2016 Core&lt;/strong&gt; installations - they &lt;em&gt;haven’t&lt;/em&gt; provided a &lt;strong&gt;container base OS Image&lt;/strong&gt; for &lt;strong&gt;Windows Server 2016 Nano&lt;/strong&gt; yet. You can download the Core one from &lt;a href=&quot;http://aka.ms/ContainerOSImage&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; (3.5GB download).&lt;/p&gt;&lt;p&gt;If you try to use the NanoServer.WIM file from the Windows Server 2016 ISO as the container base OS image you can’t even create the container at all.&lt;/p&gt;&lt;p&gt;I did try putting the Core WIM file downloaded above onto the Nano Server. I could then create a container OK, but an error would occur starting it up:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_nano_containerfromcore.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/YlxiBCEMg7-650.png 650w, https://danielscottraynsford.com/img/YlxiBCEMg7-960.png 960w, https://danielscottraynsford.com/img/YlxiBCEMg7-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/YlxiBCEMg7-650.webp 650w, https://danielscottraynsford.com/img/YlxiBCEMg7-960.webp 960w, https://danielscottraynsford.com/img/YlxiBCEMg7-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/YlxiBCEMg7-650.jpeg&quot; alt=&quot;Nope - can&#39;t use the Core WIM with a Nano Server Container Host!&quot; width=&quot;1400&quot; height=&quot;565&quot; srcset=&quot;https://danielscottraynsford.com/img/YlxiBCEMg7-650.jpeg 650w, https://danielscottraynsford.com/img/YlxiBCEMg7-960.jpeg 960w, https://danielscottraynsford.com/img/YlxiBCEMg7-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;Nope - can’t use the Core WIM with a Nano Server Container Host!&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Update 2015-10-29:&lt;/strong&gt; There is a new video available online from Microsoft of Mark Russinovich (Azure CTO) doing a container demonstration using a Nano Server. It clearly shows that the &lt;strong&gt;NanoServer Base Container Image&lt;/strong&gt; does exist. So perhaps we’ll see this in the &lt;strong&gt;TP4&lt;/strong&gt; release.&lt;/p&gt;&lt;p&gt;The video can be seen &lt;a href=&quot;https://youtu.be/YoA_MMlGPRc&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Feel free to let me know if you can solve any of these issues! Any help is appreciated. I’ll continue to work on this and post any additional results.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>RSAT for Windows 10 is now Available!</title>
      <link href="https://danielscottraynsford.com/blog/rsat-for-windows-10-is-now-available/" />
      <updated>2015-08-25T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/rsat-for-windows-10-is-now-available/</id>
      <content type="html">
				&lt;p&gt;You can finally download &lt;strong&gt;Remote Server Administration tools for Windows 10&lt;/strong&gt; from &lt;a href=&quot;http://www.microsoft.com/en-us/download/details.aspx?id=45520&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;. Go and get it!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>PowerShell Cmdlets Available in the Containers Module on a Nano Server</title>
      <link href="https://danielscottraynsford.com/blog/powershell-cmdlets-available-in-the-containers-module-on-a-nano-server/" />
      <updated>2015-08-23T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/powershell-cmdlets-available-in-the-containers-module-on-a-nano-server/</id>
      <content type="html">
				&lt;p&gt;Just a Monday-morning quickie:&lt;/p&gt;&lt;p&gt;Here’s a list of all the cmdlets available in the &lt;strong&gt;Containers&lt;/strong&gt; PowerShell module on a Nano Server with the &lt;em&gt;containers&lt;/em&gt; package installed:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_nano_containers.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/V4dPooOBnD-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/V4dPooOBnD-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/V4dPooOBnD-650.jpeg&quot; alt=&quot;Containers – the next big thing!&quot; width=&quot;650&quot; height=&quot;368&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;&lt;em&gt;Containers – the next big thing!&lt;/em&gt;&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Function Install-ContainerOSImage                    1.0.0.0  Containers
Function Uninstall-ContainerOSImage                  1.0.0.0  Containers
Cmdlet   Add-ContainerNetworkAdapter                 1.0.0.0  Containers
Cmdlet   Connect-ContainerNetworkAdapter             1.0.0.0  Containers
Cmdlet   Disconnect-ContainerNetworkAdapter          1.0.0.0  Containers
Cmdlet   Export-ContainerImage                       1.0.0.0  Containers
Cmdlet   Get-Container                               1.0.0.0  Containers
Cmdlet   Get-ContainerHost                           1.0.0.0  Containers
Cmdlet   Get-ContainerImage                          1.0.0.0  Containers
Cmdlet   Get-ContainerNetworkAdapter                 1.0.0.0  Containers
Cmdlet   Import-ContainerImage                       1.0.0.0  Containers
Cmdlet   Move-ContainerImageRepository               1.0.0.0  Containers
Cmdlet   New-Container                               1.0.0.0  Containers
Cmdlet   New-ContainerImage                          1.0.0.0  Containers
Cmdlet   Remove-Container                            1.0.0.0  Containers
Cmdlet   Remove-ContainerImage                       1.0.0.0  Containers
Cmdlet   Remove-ContainerNetworkAdapter              1.0.0.0  Containers
Cmdlet   Set-ContainerNetworkAdapter                 1.0.0.0  Containers
Cmdlet   Start-Container                             1.0.0.0  Containers
Cmdlet   Stop-Container                              1.0.0.0  Containers
Cmdlet   Test-ContainerImage                         1.0.0.0  Containers&lt;/code&gt;&lt;/pre&gt;
 			</content>
    </entry><entry>
      <title>Comparing Objects using JSON in PowerShell for Pester Tests</title>
      <link href="https://danielscottraynsford.com/blog/comparing-objects-using-json-in-powershell-for-pester-tests/" />
      <updated>2015-08-23T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/comparing-objects-using-json-in-powershell-for-pester-tests/</id>
      <content type="html">
				&lt;p&gt;Recently I spent the good part of a weekend putting together &lt;em&gt;Pester Tests&lt;/em&gt; (click &lt;a href=&quot;http://www.powershellmagazine.com/2014/03/12/get-started-with-pester-powershell-unit-testing-framework/&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt; if you aren’t familiar with Pester) for my &lt;strong&gt;LabBuilder PowerShell&lt;/strong&gt; module- a module to build a set of Virtual Machines based on an XML configuration file. In the module I have several cmdlets that take an XML configuration file (sample below) and return an array of hash tables as well as some hash table properties containing other arrays - basically a fairly complex object structure.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_vs_pestertestconfigsample.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/TM5pKyrI7U-508.png 508w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/TM5pKyrI7U-508.webp 508w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/TM5pKyrI7U-508.jpeg&quot; alt=&quot;A Pester Test config file for the LabBuilder module&quot; width=&quot;508&quot; height=&quot;230&quot;&gt;&lt;/picture&gt;&lt;/a&gt;A Pester Test config file for the LabBuilder module&lt;/p&gt;&lt;p&gt;In the &lt;em&gt;Pester Tests&lt;/em&gt; for these cmdlets I wanted to ensure the object that was returned &lt;strong&gt;exactly&lt;/strong&gt; matched what I expected. So in the &lt;em&gt;Pester Test&lt;/em&gt; I programmatically created an object that matched what the&amp;nbsp;&lt;em&gt;Pester Test&lt;/em&gt; should expect the output of the cmdlets would be:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Sample of the expected object built inside the Pester test&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$ExpectedSwitches&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        name     = &lt;span class=&quot;token string&quot;&gt;&#39;General Purpose External&#39;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;type&lt;/span&gt;     = &lt;span class=&quot;token string&quot;&gt;&#39;External&#39;&lt;/span&gt;
        vlan     = &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;
        adapters = &lt;span class=&quot;token namespace&quot;&gt;[System.Collections.Hashtable[]]&lt;/span&gt;@&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name = &lt;span class=&quot;token string&quot;&gt;&#39;Cluster&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;    macaddress = &lt;span class=&quot;token string&quot;&gt;&#39;00155D010701&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name = &lt;span class=&quot;token string&quot;&gt;&#39;Management&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; macaddress = &lt;span class=&quot;token string&quot;&gt;&#39;00155D010702&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name = &lt;span class=&quot;token string&quot;&gt;&#39;SMB&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;        macaddress = &lt;span class=&quot;token string&quot;&gt;&#39;00155D010703&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name = &lt;span class=&quot;token string&quot;&gt;&#39;LM&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;         macaddress = &lt;span class=&quot;token string&quot;&gt;&#39;00155D010704&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name = &lt;span class=&quot;token string&quot;&gt;&#39;Pester Test Private Vlan&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;type&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;Private&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; vlan = &lt;span class=&quot;token string&quot;&gt;&#39;2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; adapters = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name = &lt;span class=&quot;token string&quot;&gt;&#39;Pester Test Private&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;      &lt;span class=&quot;token function&quot;&gt;type&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;Private&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; vlan = &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; adapters = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name = &lt;span class=&quot;token string&quot;&gt;&#39;Pester Test Internal Vlan&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;type&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;Internal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; vlan = &lt;span class=&quot;token string&quot;&gt;&#39;3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; adapters = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name = &lt;span class=&quot;token string&quot;&gt;&#39;Pester Test Internal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;     &lt;span class=&quot;token function&quot;&gt;type&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;Internal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; vlan = &lt;span class=&quot;token variable&quot;&gt;$null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; adapters = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What I needed to do was try and make sure the objects were the same. At first I tried to use the &lt;strong&gt;Compare-Object&lt;/strong&gt; cmdlet - this actually wasn’t useful in this situation as it doesn’t do any sort of deep property comparison. What was needed was to &lt;em&gt;serialize&lt;/em&gt; the objects and then perform a simple string comparison. The &lt;strong&gt;ConvertTo-JSON&lt;/strong&gt; cmdlet seemed to be just what was needed. I also decided to use the [String]::Compare() method instead of using the PowerShell -eq operator because the -eq operator seems to have issues with Unicode strings.&lt;/p&gt;&lt;p&gt;The &lt;em&gt;Pester&lt;/em&gt; test that I first tried was:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;Context &lt;span class=&quot;token string&quot;&gt;&#39;Valid configuration is passed&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token variable&quot;&gt;$Switches&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-LabSwitches&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Config &lt;span class=&quot;token variable&quot;&gt;$Config&lt;/span&gt;

    It &lt;span class=&quot;token string&quot;&gt;&#39;Returns Switches object that matches Expected object&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token namespace&quot;&gt;[string]&lt;/span&gt;::&lt;span class=&quot;token function&quot;&gt;Compare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Switches&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ConvertTo-Json&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Depth 4&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ExpectedSwitches&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ConvertTo-Json&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Depth 4&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Be 0
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This initially seemed to work, but if I changed any of the object properties below the root level (e.g. the &lt;strong&gt;adapter name&lt;/strong&gt; property) the comparison still reported the objects were the same when they weren’t. After reading the documentation it states that the &lt;strong&gt;ConvertTo-JSON&lt;/strong&gt; cmdlet provides a &lt;strong&gt;Depth&lt;/strong&gt; property that defaults to 2 - which limits the depth that an object structure would be converted to. In my case the object was actually 4 levels deep. So I needed to add a &lt;strong&gt;Depth&lt;/strong&gt; parameter to the &lt;strong&gt;ConvertTo-JSON&lt;/strong&gt; calls:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;[string]&lt;/span&gt;::&lt;span class=&quot;token function&quot;&gt;Compare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Switches&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ConvertTo-Json&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Depth 4&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ExpectedSwitches&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ConvertTo-Json&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Depth 4&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Be 0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This then did pretty much exactly what I wanted. However, I also needed the comparison to be case-insensitive, so I added a boolean parameter to the [String]::Compare static call:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Case-insensitive comparison&lt;/span&gt;
&lt;span class=&quot;token namespace&quot;&gt;[string]&lt;/span&gt;::&lt;span class=&quot;token function&quot;&gt;Compare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Switches&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ConvertTo-Json&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Depth 4&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ExpectedSwitches&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ConvertTo-Json&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Depth 4&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;          &lt;span class=&quot;token comment&quot;&gt;# ignore case&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Should &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Be 0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The end result was &lt;strong&gt;a deep object comparison&lt;/strong&gt; between a reference object and the object the cmdlet being tested returned. It is by no means perfect as if the properties or contents of any arrays in the object are out of order the comparison will report that there are differences, but because we control the format of these objects this shouldn’t be a problem and should enable some very test strict cmdlet tests.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_vs_pestertest_object_comparison.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/DC1VqdsX9--650.png 650w, https://danielscottraynsford.com/img/DC1VqdsX9--960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/DC1VqdsX9--650.webp 650w, https://danielscottraynsford.com/img/DC1VqdsX9--960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/DC1VqdsX9--650.jpeg&quot; alt=&quot;How the the Final Pester Test in Visual Studio 2015 (with POSH tools)&quot; width=&quot;960&quot; height=&quot;454&quot; srcset=&quot;https://danielscottraynsford.com/img/DC1VqdsX9--650.jpeg 650w, https://danielscottraynsford.com/img/DC1VqdsX9--960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;How the the Final Pester Test in Visual Studio 2015 (with POSH tools)&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Edit&lt;/strong&gt;: after writing a number of &lt;em&gt;Pester&lt;/em&gt; tests using the approach I realized it could be simplified slightly by replacing the generation of the comparison object with the actual JSON output produced by the reference object &lt;em&gt;embedded&lt;/em&gt; inline in a variable. For example:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_vs_pestertest_inline_json1.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/YVc2C4CEFW-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/YVc2C4CEFW-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/YVc2C4CEFW-650.jpeg&quot; alt=&quot;Performing the object comparison using JSON in a variable in the test.&quot; width=&quot;650&quot; height=&quot;672&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;Performing the object comparison using JSON in a variable in the test.&lt;/p&gt;&lt;p&gt;The JSON can be generated manually by hand (before writing the function itself) to stick to the Test Driven Design methodology or it can be generated from the object the function being tested created (once it it working correctly) and then written to a file using:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Generate reference JSON file&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Set-Content&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$env&lt;/span&gt;:TEMP&#92;Switches.json&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Value &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$Switches&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ConvertTo-Json&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Depth 4&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;strong&gt;$switches&lt;/strong&gt; variable contains the actual object that is produced by the&amp;nbsp; working command being tested.&lt;/p&gt;&lt;h3 id=&quot;a-word-of-caution-about-crlf&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/comparing-objects-using-json-in-powershell-for-pester-tests/#a-word-of-caution-about-crlf&quot; class=&quot;heading-anchor&quot;&gt;A Word of Caution about CRLF&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I have noticed that when opening the JSON file in something like Notepad++ and copying the JSON to the clipboard (to paste into my &lt;em&gt;Pester&lt;/em&gt; test) that an additional CRLF appears at the bottom. You need to ensure you don’t include this at the bottom of your variable too - otherwise the comparison will fail and the objects will appear to be different (when they aren’t).&lt;/p&gt;&lt;p&gt;This is what the end of the JSON variable definition should look like:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_vs_pestertest_inline_json_good.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/nOCDii-UdA-289.png 289w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/nOCDii-UdA-289.webp 289w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/nOCDii-UdA-289.jpeg&quot; alt=&quot;Good JSON CRLF Formatting&quot; width=&quot;289&quot; height=&quot;105&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;And this is what it should &lt;strong&gt;not&lt;/strong&gt; look like (the arrow indicates the location of the extra CRLF that should be removed):&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_vs_pestertest_inline_json_bad.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/fNZ2G5thd_-298.png 298w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/fNZ2G5thd_-298.webp 298w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/fNZ2G5thd_-298.jpeg&quot; alt=&quot;Good JSON CRLF formatting&quot; width=&quot;298&quot; height=&quot;100&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;I could have used the &lt;strong&gt;Export-CliXML&lt;/strong&gt; and &lt;strong&gt;Import-CliXML&lt;/strong&gt; CmdLets instead to perform the object serialization and comparison, but these cmdlets write the content to disk and also generate much larger strings which would take much longer to compare and ending up with a more complicated test.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Well, hopefully someone else will find this useful!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Nano Server Technical Preview 3 Available Now!</title>
      <link href="https://danielscottraynsford.com/blog/nano-server-technical-preview-3-available-now/" />
      <updated>2015-08-20T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/nano-server-technical-preview-3-available-now/</id>
      <content type="html">
				&lt;p&gt;Naturally, right as I need to be focusing on rebuilding my lab environment (using PowerShell scripts only of course), Microsoft goes and releases Windows Server 2016 Technical Preview 3, which contains lots of cool things like &lt;a href=&quot;http://weblogs.asp.net/scottgu/announcing-windows-server-2016-containers-preview&quot; rel=&quot;noopener&quot;&gt;containers&lt;/a&gt;. It also meant a new version of the awesome &lt;strong&gt;Nano Server&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;But this time, Microsoft has released an installer script called &lt;strong&gt;new-nanoserverimage.ps1&lt;/strong&gt; on the ISO in the &lt;strong&gt;Nano Server&lt;/strong&gt; folder. This means that my script &lt;strong&gt;new-nanoservervhd.ps1&lt;/strong&gt; isn’t really needed any more. The official Microsoft one contains few more features that the one I wrote and being official should really be used instead of my old one. But as I have a whole lot of scripts to create Nano VM’s already that use my script I thought I’d update &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/Create-a-New-Nano-Server-61f674f1&quot; rel=&quot;noopener&quot;&gt;my script&lt;/a&gt; anyway (and upload it to &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/Create-a-New-Nano-Server-61f674f1&quot; rel=&quot;noopener&quot;&gt;script center&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;So after updating my script I built some new VM’s containing the new packages &lt;strong&gt;containers&lt;/strong&gt; and &lt;strong&gt;defender&lt;/strong&gt;. I noticed something different straight away - the Nano Server now has a minimal head display called &lt;strong&gt;Emergency Management Console&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_nanoserver_tp3.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/Aq7MEyGuBg-650.png 650w, https://danielscottraynsford.com/img/Aq7MEyGuBg-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Aq7MEyGuBg-650.webp 650w, https://danielscottraynsford.com/img/Aq7MEyGuBg-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Aq7MEyGuBg-650.jpeg&quot; alt=&quot;Nano Server Emergency Management Console&quot; width=&quot;960&quot; height=&quot;720&quot; srcset=&quot;https://danielscottraynsford.com/img/Aq7MEyGuBg-650.jpeg 650w, https://danielscottraynsford.com/img/Aq7MEyGuBg-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;Nano Server Emergency Management Console&lt;/p&gt;&lt;p&gt;This allows you to easily see some basic information &lt;strong&gt;about&lt;/strong&gt; the running Nano Server on a monitor (or, more likely, a VM console). But you do first need to log in to the Nano Server before you can review this information:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_nanoserver_authenticate.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/i-Yo6bRWEg-650.png 650w, https://danielscottraynsford.com/img/i-Yo6bRWEg-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/i-Yo6bRWEg-650.webp 650w, https://danielscottraynsford.com/img/i-Yo6bRWEg-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/i-Yo6bRWEg-650.jpeg&quot; alt=&quot;Nano Server Authenticate&quot; width=&quot;960&quot; height=&quot;720&quot; srcset=&quot;https://danielscottraynsford.com/img/i-Yo6bRWEg-650.jpeg 650w, https://danielscottraynsford.com/img/i-Yo6bRWEg-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;Nano Server Authenticate&lt;/p&gt;&lt;p&gt;You can’t actually do much once inside the &lt;strong&gt;Emergency Management Console&lt;/strong&gt; except reboot and shut down the server. But this does mean that you no longer need to create a start-up task that shows the IP address and other details of the Nano Server on screen manually. I’ve left it in my script for now, but it could probably be removed once I’m sure it isn’t necessary.&lt;/p&gt;&lt;p&gt;Back to the lab scripting!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Prevent Template Virtual Machines from Accidentally Being Booted</title>
      <link href="https://danielscottraynsford.com/blog/prevent-template-virtual-machines-from-accidentally-being-booted/" />
      <updated>2015-08-19T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/prevent-template-virtual-machines-from-accidentally-being-booted/</id>
      <content type="html">
				&lt;p&gt;Here’s a quick tip for Wednesday night:&lt;/p&gt;&lt;p&gt;If you have a VM you have &lt;strong&gt;sysprepped&lt;/strong&gt; so that you can use it as a template to create other new VMs, set the &lt;strong&gt;VHD/VHDx&lt;/strong&gt; file(s) for the VM &lt;strong&gt;read-only&lt;/strong&gt; so that you won’t &lt;em&gt;unsysprep&lt;/em&gt; (is that a word?) it by accident. I have spent many wasted minutes &lt;em&gt;re-sysprepping&lt;/em&gt; VMs because I accidentally booted a template VM.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Replace NETSH TRACE START with PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/replace-netsh-trace-start-with-powershell/" />
      <updated>2015-08-10T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/replace-netsh-trace-start-with-powershell/</id>
      <content type="html">
				&lt;p&gt;Recently as part of studying for my MCSA I have been using Message Analyzer to look at Kerberos exchanges (among other things). Yes, I really know how to party! I usually did this by starting the trace on the KDC (DC) using the good old command:&lt;/p&gt;&lt;h3 id=&quot;the-netsh-way&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/replace-netsh-trace-start-with-powershell/#the-netsh-way&quot; class=&quot;heading-anchor&quot;&gt;The NETSH Way&lt;/a&gt;&lt;/h3&gt;&lt;pre class=&quot;language-plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;NETSH TRACE START CAPTURE=yes TRACEFILE=e:&#92;mytrace.etl
NETSH TRACE STOP&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;the-powershell-way&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/replace-netsh-trace-start-with-powershell/#the-powershell-way&quot; class=&quot;heading-anchor&quot;&gt;The PowerShell Way&lt;/a&gt;&lt;/h3&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# NETSH TRACE START CAPTURE=yes TRACEFILE=e:&#92;mytrace.etl&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;New-NetEventSession&lt;/span&gt;               &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&quot;Capture&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;CaptureMode SaveToFile &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;LocalFilePath &lt;span class=&quot;token string&quot;&gt;&quot;e:&#92;mytrace.etl&quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Add-NetEventPacketCaptureProvider&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SessionName &lt;span class=&quot;token string&quot;&gt;&quot;Capture&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Level 4 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;CaptureType Physical
&lt;span class=&quot;token function&quot;&gt;Start-NetEventSession&lt;/span&gt;             &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&quot;Capture&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To stop the trace:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# NETSH TRACE STOP&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Stop-NetEventSession&lt;/span&gt;   &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&quot;Capture&quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Remove-NetEventSession&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&quot;Capture&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Unfortunately this is a bit more verbose than the NETSH equivalent. It is also a bit of a pity the CmdLets aren’t written so the output of one can be piped to the next. But we can’t have everything.&lt;/p&gt;&lt;h3 id=&quot;more-features&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/replace-netsh-trace-start-with-powershell/#more-features&quot; class=&quot;heading-anchor&quot;&gt;More Features&lt;/a&gt;&lt;/h3&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Add-NetEventPacketCaptureProvider&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SessionName &lt;span class=&quot;token string&quot;&gt;&quot;Capture&quot;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Level 4 `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;CaptureType Physical `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;EtherType 0x0800 `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;IPAddresses 192&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;168&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;178&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;3 `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;IpProtocols 6&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;17&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Will cause the trace to capture only IPv4 traffic to/from 192.168.178.3 for TCP and UDP.&lt;/p&gt;&lt;h3 id=&quot;remote-capture-via-rpc&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/replace-netsh-trace-start-with-powershell/#remote-capture-via-rpc&quot; class=&quot;heading-anchor&quot;&gt;Remote Capture via RPC&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Looking at the documentation for the&amp;nbsp;&lt;strong&gt;New-EventSession&lt;/strong&gt; cmdlet, it seems that it is possible to have the trace output sent to a remote host via RPC and then captured directly by Network Analyzer. I haven’t been able to get this to work as yet. Figuring out how this works and getting it going is going to be my next project (between studying for the next exam).&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Your account settings are out of date in Windows 10 Universal Mail App</title>
      <link href="https://danielscottraynsford.com/blog/your-account-settings-are-out-of-date-in-windows-10-universal-mail-app/" />
      <updated>2015-07-31T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/your-account-settings-are-out-of-date-in-windows-10-universal-mail-app/</id>
      <content type="html">
				&lt;h3 id=&quot;the-problem&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/your-account-settings-are-out-of-date-in-windows-10-universal-mail-app/#the-problem&quot; class=&quot;heading-anchor&quot;&gt;The Problem&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: This problem is a very long running issue that I’m surprised hasn’t been resolved by Microsoft yet. The fix below &lt;em&gt;does work for some people&lt;/em&gt;, but based on the comments on this post and those on the &lt;a href=&quot;http://answers.microsoft.com/en-us/insider/forum/insider_apps-insider_mail/universal-mail-app-your-account-settings-are-out/562f4fad-c60a-4204-a8c3-94fa1d05bf65&quot; rel=&quot;noopener&quot;&gt;Microsoft Forums&lt;/a&gt; this solution doesn’t always work and many other fixes have been suggested (see the forum for a large number of other suggested solutions). Some folk have reported that the fix below can prevent the Mail and Calendar apps from working at all, so I’d recommend that you use this process with care and &lt;strong&gt;recommend strongly that you back up the &lt;em&gt;comms&lt;/em&gt; folder before deleting the content&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Recently I noticed on my Windows 10 desktop that I have been using to test all the Windows 10 Insider Preview releases the &lt;strong&gt;Windows 10 Universal Mail and Calendar&lt;/strong&gt; app is was no longer working with my Outlook mail account. Every time the app loaded or I clicked on the Outlook account it would show a message at the top of the mail list stating “&lt;em&gt;Your account settings are out of date&lt;/em&gt;” and giving me the option to &lt;strong&gt;fix&lt;/strong&gt; or &lt;strong&gt;dismiss&lt;/strong&gt; the problem. Clicking &lt;strong&gt;fix&lt;/strong&gt; just flashed up a black window for a brief moment (too fast to see any details) and the message remained. I messed about with the account settings and everything I could find for the account in Mail settings but couldn’t figure out how to fix it. I tried various suggested “fixes” I found online, but none of them worked for me.&lt;/p&gt;&lt;p&gt;My Gmail account worked fine in the Mail app as well and my outlook account also worked fine in the &lt;strong&gt;Windows 10 Universal Mail and Calendar&lt;/strong&gt; app on my laptop. Unfortunately I didn’t think to screenshot the problem before I managed to resolve it so I can’t post an image of the error message here. After a lot of investigation and lots of messing around I found out where the Windows Universal Mail app stores its account data - as I thought this is probably where the problem probably was. The &lt;strong&gt;Windows 10 Universal Mail and Calendar&lt;/strong&gt; seems to store all information in the folder &lt;strong&gt;%LOCALAPPDATA%&#92;Comms&#92;&lt;/strong&gt; I thought perhaps if I could get rid of (back it up just in case) this folder the app might recreate it.&lt;/p&gt;&lt;h3 id=&quot;the-solution&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/your-account-settings-are-out-of-date-in-windows-10-universal-mail-app/#the-solution&quot; class=&quot;heading-anchor&quot;&gt;The Solution&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Open an &lt;strong&gt;Administrator PowerShell&lt;/strong&gt; prompt (enter “powershell” in the start menu search and then right click the&amp;nbsp;&lt;strong&gt;Windows PowerShell&lt;/strong&gt; icon and select&amp;nbsp;&lt;strong&gt;Run as Administrator,&lt;/strong&gt; you might need to confirm a UAC prompt).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Uninstall the &lt;strong&gt;Windows 10 Universal Mail and Calendar&lt;/strong&gt; app:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-AppxPackage&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Where-Object&lt;/span&gt; Name &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;microsoft.windowscommunicationsapps&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Remove-AppxPackage&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/DBcBEF3W5n-650.png 650w, https://danielscottraynsford.com/img/DBcBEF3W5n-960.png 960w, https://danielscottraynsford.com/img/DBcBEF3W5n-1215.png 1215w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/DBcBEF3W5n-650.webp 650w, https://danielscottraynsford.com/img/DBcBEF3W5n-960.webp 960w, https://danielscottraynsford.com/img/DBcBEF3W5n-1215.webp 1215w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/DBcBEF3W5n-650.jpeg&quot; alt=&quot;Uninstall the Windows 10 Universal Mail and Calendar App&quot; width=&quot;1215&quot; height=&quot;218&quot; srcset=&quot;https://danielscottraynsford.com/img/DBcBEF3W5n-650.jpeg 650w, https://danielscottraynsford.com/img/DBcBEF3W5n-960.jpeg 960w, https://danielscottraynsford.com/img/DBcBEF3W5n-1215.jpeg 1215w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;@Stephen&lt;/strong&gt; suggests restarting your computer at this point.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Delete&lt;/strong&gt; the &lt;strong&gt;%LOCALAPPDATA%&#92;Comms&#92;&lt;/strong&gt; folder (back it up first if you want):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Remove-Item&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$Home&lt;/span&gt;&#92;AppData&#92;Local&#92;Comms&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Recurse &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/ZNwSBeaU6A-650.png 650w, https://danielscottraynsford.com/img/ZNwSBeaU6A-960.png 960w, https://danielscottraynsford.com/img/ZNwSBeaU6A-1215.png 1215w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ZNwSBeaU6A-650.webp 650w, https://danielscottraynsford.com/img/ZNwSBeaU6A-960.webp 960w, https://danielscottraynsford.com/img/ZNwSBeaU6A-1215.webp 1215w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ZNwSBeaU6A-650.jpeg&quot; alt=&quot;Deleting the Comms folder – some files can&#39;t be deleted; this is OK&quot; width=&quot;1215&quot; height=&quot;821&quot; srcset=&quot;https://danielscottraynsford.com/img/ZNwSBeaU6A-650.jpeg 650w, https://danielscottraynsford.com/img/ZNwSBeaU6A-960.jpeg 960w, https://danielscottraynsford.com/img/ZNwSBeaU6A-1215.jpeg 1215w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: you’ll probably find that some files are in use and can’t be deleted – this is OK.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Reinstall the &lt;a href=&quot;http://apps.microsoft.com/webpdp/app/64a79953-cf0b-44f9-b5c4-ee5df3a15c63&quot; rel=&quot;noopener&quot;&gt;&lt;strong&gt;Windows 10 Universal Mail and Calendar app&lt;/strong&gt;&lt;/a&gt; from the Microsoft Store.&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/PnjwFdhp4C-650.png 650w, https://danielscottraynsford.com/img/PnjwFdhp4C-960.png 960w, https://danielscottraynsford.com/img/PnjwFdhp4C-1202.png 1202w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/PnjwFdhp4C-650.webp 650w, https://danielscottraynsford.com/img/PnjwFdhp4C-960.webp 960w, https://danielscottraynsford.com/img/PnjwFdhp4C-1202.webp 1202w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/PnjwFdhp4C-650.jpeg&quot; alt=&quot;Reinstall the Windows 10 Universal Mail and Calendar app from the Windows Store&quot; width=&quot;1202&quot; height=&quot;437&quot; srcset=&quot;https://danielscottraynsford.com/img/PnjwFdhp4C-650.jpeg 650w, https://danielscottraynsford.com/img/PnjwFdhp4C-960.jpeg 960w, https://danielscottraynsford.com/img/PnjwFdhp4C-1202.jpeg 1202w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Once this had all been done I loaded the Mail up and it asked me to configure all my mail accounts again (and authorize them with the providers). After this the error message had gone away and all my accounts worked normally. I did also however have to re-enter my credentials for both Google Drive and One Drive.&lt;/p&gt;&lt;p&gt;For more information, suggestions and discussion about this issue, take a look at &lt;a href=&quot;http://answers.microsoft.com/en-us/insider/forum/insider_apps-insider_mail/universal-mail-app-your-account-settings-are-out/562f4fad-c60a-4204-a8c3-94fa1d05bf65&quot; rel=&quot;noopener&quot;&gt;this&lt;/a&gt; discussion on the Microsoft Community boards.&lt;/p&gt;&lt;p&gt;I hope this works for someone else as well.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Enable Device Naming on all Virtual Net Adapters on a VM Host</title>
      <link href="https://danielscottraynsford.com/blog/enable-device-naming-on-all-virtual-net-adapters-on-a-vm-host/" />
      <updated>2015-07-29T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/enable-device-naming-on-all-virtual-net-adapters-on-a-vm-host/</id>
      <content type="html">
				&lt;p&gt;After a couple of bumps upgrading my development laptop to Windows 10, I finally got to update all my Hyper-V lab VMs to the new version of Hyper-V. This included updating the &lt;strong&gt;Virtual Machine Configuration&lt;/strong&gt; version and enabling virtual network adapter &lt;strong&gt;Device Naming&lt;/strong&gt; - see &lt;a href=&quot;https://technet.microsoft.com/en-nz/library/dn765471.aspx&quot; rel=&quot;noopener&quot;&gt;What’s new in Hyper-V in Technical Preview&lt;/a&gt; for more information.&lt;/p&gt;&lt;p&gt;But having a number of VM’s running on this Hyper-V host I couldn’t be bothered updating them all by hand. So as usual, PowerShell to the rescue.&lt;/p&gt;&lt;p&gt;First up, to upgrade the configuration of all the VMs on this host so the new features can be used I ran the following command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-VM&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Update-VMVersion&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once that was completed (it took about 10 seconds) I could enable the &lt;strong&gt;Device Naming&lt;/strong&gt; feature of all the Virtual Network Adapters on all &lt;em&gt;Generation 2&lt;/em&gt; VMs (this feature isn’t supported on &lt;em&gt;Generation 1&lt;/em&gt; VMs). The feature labels the NIC inside the guest OS (when supported) with the name you assign in the host.&lt;/p&gt;&lt;p&gt;To enable &lt;strong&gt;Device Naming&lt;/strong&gt; on all &lt;em&gt;Generation 2&lt;/em&gt; virtual NICs:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-VM&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Where-Object&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Property VirtualMachineSubType &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Generation2&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Get-VMNetworkAdapter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Set-VMNetworkAdapter&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DeviceNaming On&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;All in all this was much easier than the endless clicking I would have had to do in the UI. You can even combine the two steps into a single command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-VM&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Update-VMVersion&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Passthru &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Where-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Property VirtualMachineSubType &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Generation2&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Get-VMNetworkAdapter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Set-VMNetworkAdapter&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DeviceNaming On&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So now it’s off to try some of the other new Hyper-V features.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>PowerShell Modules Available in Nano Server</title>
      <link href="https://danielscottraynsford.com/blog/powershell-modules-available-in-nano-server/" />
      <updated>2015-07-23T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/powershell-modules-available-in-nano-server/</id>
      <content type="html">
				&lt;p&gt;I have been spending a bit of time experimenting with loading Nano Server into WDS (using capture images, VHDX files and the like) and while doing this I decided to dig around inside Server Nano to see what is missing. The thing that is missing that makes me grumble the most is that lots of PowerShell modules are missing. This of course is because Server Nano doesn’t have the full .NET Framework available, which most PowerShell modules depend.&lt;/p&gt;&lt;h3 id=&quot;what-modules-are-included&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/powershell-modules-available-in-nano-server/#what-modules-are-included&quot; class=&quot;heading-anchor&quot;&gt;What Modules are Included?&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This to some degree depends on the packages that are installed, but with the OEM-Drivers, Storage and Guest packages installed the following modules are available:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_nano_listofpowershellmodules.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/QhwSvTFG0y-650.png 650w, https://danielscottraynsford.com/img/QhwSvTFG0y-960.png 960w, https://danielscottraynsford.com/img/QhwSvTFG0y-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/QhwSvTFG0y-650.webp 650w, https://danielscottraynsford.com/img/QhwSvTFG0y-960.webp 960w, https://danielscottraynsford.com/img/QhwSvTFG0y-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/QhwSvTFG0y-650.jpeg&quot; alt=&quot;PowerShell Modules in Nano&quot; width=&quot;1400&quot; height=&quot;523&quot; srcset=&quot;https://danielscottraynsford.com/img/QhwSvTFG0y-650.jpeg 650w, https://danielscottraynsford.com/img/QhwSvTFG0y-960.jpeg 960w, https://danielscottraynsford.com/img/QhwSvTFG0y-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;As the screenshot above shows, there are a lot of useful modules missing. Even within some of the modules, many of the CmdLets are not available.&lt;/p&gt;&lt;p&gt;For example, in the &lt;strong&gt;Microsoft.PowerShell.Management&lt;/strong&gt; module on Windows Server 2012 R2, there are &lt;strong&gt;86&lt;/strong&gt; cmdlets available. In Nano Server there are only &lt;strong&gt;38&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_nano_listofmanagmentcmdlets.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/hQXRb-jrPy-650.png 650w, https://danielscottraynsford.com/img/hQXRb-jrPy-960.png 960w, https://danielscottraynsford.com/img/hQXRb-jrPy-1400.png 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/hQXRb-jrPy-650.webp 650w, https://danielscottraynsford.com/img/hQXRb-jrPy-960.webp 960w, https://danielscottraynsford.com/img/hQXRb-jrPy-1400.webp 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/hQXRb-jrPy-650.jpeg&quot; alt=&quot;List of Cmdlets in Management Module in TP2&quot; width=&quot;1400&quot; height=&quot;672&quot; srcset=&quot;https://danielscottraynsford.com/img/hQXRb-jrPy-650.jpeg 650w, https://danielscottraynsford.com/img/hQXRb-jrPy-960.jpeg 960w, https://danielscottraynsford.com/img/hQXRb-jrPy-1400.jpeg 1400w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Obviously, this is only Tech Preview 2, and so will likely change, but it certainly might be the case that some PowerShell scripts won’t work on Nano Server and will need to be re-written.&lt;/p&gt;&lt;h3 id=&quot;sysprep-is-missing&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/powershell-modules-available-in-nano-server/#sysprep-is-missing&quot; class=&quot;heading-anchor&quot;&gt;SysPrep is Missing&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;One other element that is missing from Nano Server is the SYSPREP tool. The folder&lt;br&gt;&lt;code&gt;C:&#92;Windows&#92;System32&#92;Sysprep&lt;/code&gt; is there, but it is empty. So &lt;em&gt;sysprepping&lt;/em&gt; a Nano Server at the moment doesn’t seem to be possible.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Force WSUS to Synchronize Now from PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/force-wsus-to-synchronize-now-from-powershell/" />
      <updated>2015-07-22T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/force-wsus-to-synchronize-now-from-powershell/</id>
      <content type="html">
				&lt;p&gt;After passing my MS 70.410 exam I had a little bit of free time on my hands, so I thought I’d clean up my WSUS servers and prepare them for Windows 10 and VS 2015. So I thought I’d force myself to do the whole thing via PowerShell. The problem is that the &lt;strong&gt;UpdateServices&lt;/strong&gt; PowerShell module doesn’t have cmdlets for some things I wanted to do – &lt;strong&gt;force a synchronization&lt;/strong&gt; was among them.&lt;br&gt;So I needed to use the &lt;a href=&quot;http://Microsoft.UpdateServices.NET&quot; rel=&quot;noopener&quot;&gt;Microsoft.UpdateServices.NET&lt;/a&gt; components to perform these functions.&lt;/p&gt;&lt;h3 id=&quot;useful-commands&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/force-wsus-to-synchronize-now-from-powershell/#useful-commands&quot; class=&quot;heading-anchor&quot;&gt;Useful Commands&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;To force a WSUS server to synchronise now:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-WsusServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetSubscription&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StartSynchronization&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To get the result of the last synchronisation:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-WsusServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetSubscription&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetLastSynchronizationInfo&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pretty simple! I’m sure additional functions will crop up and I’ll try to post any useful ones here as well.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>A Minor Irritation with VHDs and Dynamic Disks</title>
      <link href="https://danielscottraynsford.com/blog/a-minor-irritation-with-vhds-and-dynamic-disks/" />
      <updated>2015-07-14T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/a-minor-irritation-with-vhds-and-dynamic-disks/</id>
      <content type="html">
				&lt;p&gt;As part of my recent studies (and because I’m a bit OCD) I’ve been writing some notes on &lt;strong&gt;how to perform&lt;/strong&gt; various DISKPART commands in &lt;strong&gt;PowerShell&lt;/strong&gt;. You might also need to do this if you’re converting old DISKPART scripts into &lt;strong&gt;PowerShell&lt;/strong&gt; for automation purposes.&lt;/p&gt;&lt;p&gt;In most cases it’s straight-forward to map DISKPART commands over to &lt;strong&gt;PowerShell&lt;/strong&gt;. For example, to use DISKPART to initialise a disk and set the partition format to GPT on disk 6:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;SELECT&lt;/span&gt; DISK=6
ONLINE
CONVERT GPT&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The PowerShell equivalent would be:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Set-Disk&lt;/span&gt;      &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Number 6 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;IsOffline &lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Initialize-Disk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Number 6 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PartitionStyle GPT&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;the-problems&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/a-minor-irritation-with-vhds-and-dynamic-disks/#the-problems&quot; class=&quot;heading-anchor&quot;&gt;The Problems&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;However, I ran into two situations where PowerShell can’t currently replace DISKPART:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dynamic disks&lt;/strong&gt; can’t be created using &lt;strong&gt;PowerShell&lt;/strong&gt;. Therefore spanned, striped, mirrored or parity volumes must still be created with DISKPART. (If you’re on Windows Server 2012/Windows 8 or later, consider using &lt;strong&gt;Storage Spaces&lt;/strong&gt; instead.)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The &lt;strong&gt;PowerShell&lt;/strong&gt; cmdlets to create or mount &lt;strong&gt;Virtual Hard Disk&lt;/strong&gt; files (VHD/VHDx) are unavailable if Hyper-V is not installed.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/KylAk2qFm1-650.png 650w, https://danielscottraynsford.com/img/KylAk2qFm1-877.png 877w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/KylAk2qFm1-650.webp 650w, https://danielscottraynsford.com/img/KylAk2qFm1-877.webp 877w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/KylAk2qFm1-650.jpeg&quot; alt=&quot;VHD cmdlets without Hyper-V&quot; width=&quot;877&quot; height=&quot;132&quot; srcset=&quot;https://danielscottraynsford.com/img/KylAk2qFm1-650.jpeg 650w, https://danielscottraynsford.com/img/KylAk2qFm1-877.jpeg 877w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This is a little annoying because the &lt;strong&gt;Hyper-V&lt;/strong&gt; role can’t always be installed—e.g. inside a guest VM. While it’s unusual to work with VHD/VHDx files inside a guest, the rise of cloud-hosted dev machines means you might hit this limitation.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;That’s it for tonight!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>New-NanoServerVHD Updated to support changes in Convert-WindowsImage</title>
      <link href="https://danielscottraynsford.com/blog/new-nanoservervhd-updated-to-support-changes-in-convert-windowsimage/" />
      <updated>2015-06-19T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/new-nanoservervhd-updated-to-support-changes-in-convert-windowsimage/</id>
      <content type="html">
				&lt;p&gt;A new version of &lt;strong&gt;Convert-WindowsImage.ps1&lt;/strong&gt; script that me&amp;nbsp;&lt;strong&gt;New-NanoServerVHD.ps1&lt;/strong&gt; script uses was released a few days ago. It fixes the issue with running on Windows 10 and Windows Server 2016. However it was also changed in other ways that caused my &lt;strong&gt;New-NanoServerVHD.ps1&lt;/strong&gt; script to no longer function.&lt;/p&gt;&lt;p&gt;So I’ve updated the &lt;strong&gt;New-NanoServerVHD.ps1&lt;/strong&gt; script to support the newer &lt;strong&gt;ConvertWindowsImage.ps1&lt;/strong&gt; script. I also added support for creating a VHDx (with a GPT partition table format) so that it can be used with Generation 2 VMs.&lt;/p&gt;&lt;p&gt;You can control the format of the VHD that is created by passing in a &lt;strong&gt;VHDFormat&lt;/strong&gt; parameter. This parameter defaults to &lt;strong&gt;VHD&lt;/strong&gt;. If &lt;strong&gt;VHD&lt;/strong&gt; is created it will automatically have a partition format of MBR set. When a VHDx is created it will have a GPT partition format.&lt;/p&gt;&lt;p&gt;Here is an example showing how to create a Nano Server VHDx for a Gen 2 VM:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;&lt;span class=&quot;token function&quot;&gt;New-NanoServerVHD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1 `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServerISO &lt;span class=&quot;token string&quot;&gt;&#39;c:&#92;nano&#92;10074.0.150424-1350.fbl_impressive_SERVER_OEMRET_X64FRE_EN-US.ISO&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DestVHD   &lt;span class=&quot;token string&quot;&gt;&#39;c:&#92;nano&#92;NanoServer02.vhdx&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VHDFormat VHDx `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ComputerName &lt;span class=&quot;token string&quot;&gt;&#39;NANOTEST02&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AdministratorPassword &lt;span class=&quot;token string&quot;&gt;&#39;P@ssword!1&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Packages &lt;span class=&quot;token string&quot;&gt;&#39;Storage&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;OEM-Drivers&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Guest&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;IPAddress &lt;span class=&quot;token string&quot;&gt;&#39;192.168.1.66&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The updated &lt;strong&gt;New-NanoServerVHD.ps1&lt;/strong&gt; script can be downloaded &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/Create-a-New-Nano-Server-61f674f1&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Setting the Computer Name installing Nano Server bug resolved</title>
      <link href="https://danielscottraynsford.com/blog/setting-the-computer-name-installing-nano-server-bug-resolved/" />
      <updated>2015-06-16T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/setting-the-computer-name-installing-nano-server-bug-resolved/</id>
      <content type="html">
				&lt;p&gt;When I &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/05/08/install-windows-server-nano-the-easy-way/&quot; rel=&quot;noopener&quot;&gt;originally wrote the script&lt;/a&gt; to help install Nano Server I ran into a problem where I couldn’t get the Computer Name of the Nano Server to set during the &lt;em&gt;OfflineServicing&lt;/em&gt; phase. This was supposed to be a new feature of Windows Server 2016 where the computer name could now be set in this phase rather than having to wait for the &lt;em&gt;Specialize&lt;/em&gt; phase - which meant one less reboot during installation of the OS - saving precious seconds. And when installing Nano, saving an extra few seconds actually matter. I spent some time trying to get this new feature to work but nothing I tried worked, so I resorted to setting it in the &lt;em&gt;Specialize&lt;/em&gt; phase like installations of old.&lt;/p&gt;&lt;p&gt;But thanks to&amp;nbsp;&lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/site/profile?userName=Michael%20Birtwistle&quot; rel=&quot;noopener&quot;&gt;Michael Birtwistle&lt;/a&gt; for pointing out &lt;a href=&quot;https://social.technet.microsoft.com/Forums/en-US/bb6ea8b9-7390-4461-8f0e-a70b0dcc83c6/error-at-applyunattend-to-nano-server-image?forum=WinServerPreview&quot; rel=&quot;noopener&quot;&gt;this forum post&lt;/a&gt;. It points out that the Unattend.xml file in the original Nano Server instructions from Microsoft was incorrect. So I have corrected the &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/Create-a-New-Nano-Server-61f674f1&quot; rel=&quot;noopener&quot;&gt;Install-NanoServerVHD.ps1&lt;/a&gt; script to reflect this as well. So even faster Nano Server provisioning should be available now.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Installing Windows Management Framework 5.0 with a GPO</title>
      <link href="https://danielscottraynsford.com/blog/installing-windows-management-framework-50-with-a-gpo/" />
      <updated>2015-06-09T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/installing-windows-management-framework-50-with-a-gpo/</id>
      <content type="html">
				&lt;h2 id=&quot;the-need&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-windows-management-framework-50-with-a-gpo/#the-need&quot; class=&quot;heading-anchor&quot;&gt;The Need&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Last week I decided I needed to get to know the new features that come with DSC in the new Windows Management Framework 5.0 (aka PowerShell 5.0) April Preview release (available to download &lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=46889&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;). I figured I’d also need to look at updating my &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/05/02/dsc-tools-hopefully-making-dsc-easier/&quot; rel=&quot;noopener&quot;&gt;DSCTools module&lt;/a&gt; to use the new features as well. But first up I’d need to update all my lab machines with the WMF 5.0 update. Being a proper lazy nerd I thought I’d just automate it.&lt;/p&gt;&lt;h2 id=&quot;the-problem&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-windows-management-framework-50-with-a-gpo/#the-problem&quot; class=&quot;heading-anchor&quot;&gt;The Problem&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;After downloading the .MSU file for installing WMF 5.0 from Microsoft I decided that WSUS would be the proper way to get this out to all my machines. Nope. Wrong! WSUS only supports pushing updates that appear in the &lt;a href=&quot;http://catalog.update.microsoft.com/v7/site/Search.aspx?q=Windows%20Management%20Framework&quot; rel=&quot;noopener&quot;&gt;Microsoft Update Catalogue (looking for Windows Management Framework)&lt;/a&gt; - but no update packages greater than Windows Management Frameworks 2.0 are available in the catalogue:&lt;/p&gt;&lt;p&gt;[caption id=“attachment_205” align=“alignnone” width=“660”]&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_microsoft_update_catalogue_windows_management_framework.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/I2mbfLT798-650.png 650w, https://danielscottraynsford.com/img/I2mbfLT798-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/I2mbfLT798-650.webp 650w, https://danielscottraynsford.com/img/I2mbfLT798-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/I2mbfLT798-650.jpeg&quot; alt=&quot;Where is PowerShell 5.0? Or 4.0? Or 3.0?&quot; width=&quot;960&quot; height=&quot;449&quot; srcset=&quot;https://danielscottraynsford.com/img/I2mbfLT798-650.jpeg 650w, https://danielscottraynsford.com/img/I2mbfLT798-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;Where is PowerShell 5.0? Or 4.0? Or 3.0?&lt;/p&gt;&lt;p&gt;As an aside after doing a bit of reading on the reason for this it appears that updating to later versions of PS can cause problems with some applications and so it is kept as a manual process.&lt;/p&gt;&lt;p&gt;Next on the drawing board was push the update out using GPO’s software installation policies - except it only supports MSI files. So that won’t work (although converting an MSU to an MSI might be possible according to some sources).&lt;/p&gt;&lt;h2 id=&quot;the-solution&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-windows-management-framework-50-with-a-gpo/#the-solution&quot; class=&quot;heading-anchor&quot;&gt;The Solution&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Finally I settled on pushing the MSU onto the machines by using a GPO Startup Script - of the PowerShell flavour of course. It seemed like I could just adapt the PS script I wrote for &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/04/06/using-powershell-to-installuninstall-microsoft-office-products-by-group-policy/&quot; rel=&quot;noopener&quot;&gt;installing Office 2013 via GPO&lt;/a&gt;. After a bit of coding I had the update installing (in silent mode of course).&lt;/p&gt;&lt;p&gt;The next problem was that I needed some sort of registry key that could be checked to see if the update was already installed so it didn’t try to repeatedly reinstall every time the computer started up. I spent a few hours hunting around for information on where in the registry a record of installed updates was kept and couldn’t seemed to find any.&lt;/p&gt;&lt;p&gt;So instead I just used a simple WMI query to find out if the update was installed:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-WmiObject&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Class&lt;/span&gt; Win32_QuickFixEngineering &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Filter&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;HotfixID = &#39;KB2908075&#39;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above command will return a list of KBs installed with a matching &lt;strong&gt;HotfixID&lt;/strong&gt;.&lt;br&gt;There will be zero results if the KB is not present, so to use it I needed to count the objects returned:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-WmiObject&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Class&lt;/span&gt; Win32_QuickFixEngineering &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Filter&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;HotfixID = &#39;KB2908075&#39;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Measure-Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Count &lt;span class=&quot;token operator&quot;&gt;-gt&lt;/span&gt; 0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If the MSU or EXE (did I mention this script will work with hot-fixes in EXE form) does indeed need installing, the script just builds a command line and calls it:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;[IO.Path]&lt;/span&gt;::GetExtension&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$InstallerPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;.msu&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$Command&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&quot;WUSA.EXE &lt;span class=&quot;token variable&quot;&gt;$InstallerPath&lt;/span&gt; /quiet /norestart&quot;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$Type&lt;/span&gt;    = &lt;span class=&quot;token string&quot;&gt;&quot;MSU &lt;span class=&quot;token variable&quot;&gt;$KBID&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$Command&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$InstallerPath&lt;/span&gt; /quiet /norestart&quot;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$Type&lt;/span&gt;    = &lt;span class=&quot;token string&quot;&gt;&quot;EXE &lt;span class=&quot;token variable&quot;&gt;$KBID&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Call the installer&lt;/span&gt;
&amp;amp; cmd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exe &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;c &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$Command&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$ErrorCode&lt;/span&gt; = &lt;span class=&quot;token variable&quot;&gt;$LASTEXITCODE&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;the-script&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-windows-management-framework-50-with-a-gpo/#the-script&quot; class=&quot;heading-anchor&quot;&gt;The Script&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;So after putting all the pieces together I had a finished script that seemed to do exactly what I needed. It can be downloaded from the Microsoft Script Center &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/PowerShell-to-Install-70009e38&quot; rel=&quot;noopener&quot;&gt;PowerShell Scripts to Install Application (EXE) or Update (MSU) using GPO&lt;/a&gt;. It is also available in &lt;a href=&quot;https://github.com/PlagueHO/InstallUsingGPOTools&quot; rel=&quot;noopener&quot;&gt;my GitHub repo&lt;/a&gt; (edit: I moved this out of my generic PowerShell tools repo and into it’s own repo).&lt;/p&gt;&lt;h3 id=&quot;warning&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-windows-management-framework-50-with-a-gpo/#warning&quot; class=&quot;heading-anchor&quot;&gt;Warning&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Before getting started, I strongly suggest you read my post about the problems encountered using PowerShell parameters in GPO &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/06/03/powershell-paramters-in-gpo-scripts/&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;. There are some really annoying issues with PowerShell script parameters in GPO that will have you tearing your hair out - luckily I tore my hair out for you.&lt;/p&gt;&lt;h3 id=&quot;using-it&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-windows-management-framework-50-with-a-gpo/#using-it&quot; class=&quot;heading-anchor&quot;&gt;Using It&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Download the &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/PowerShell-to-Install-70009e38&quot; rel=&quot;noopener&quot;&gt;PowerShell Scripts to Install Application (EXE) or Update (MSU) using GPO&lt;/a&gt; from Microsoft Script Center.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Download the MSU/EXE update file and put it in a shared folder all of your machines can get to. I used &lt;em&gt;&#92;&#92;plague-pdc&#92;Software$&#92;Updates&#92;WindowsBlue-KB3055381-x64.msu&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Create a new GPO that will be used to install the update - I called mine &lt;em&gt;Install WMF 5.0 Policy&lt;/em&gt;:&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_gpo_installwmf5.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/gGcOFv0fBg-650.png 650w, https://danielscottraynsford.com/img/gGcOFv0fBg-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/gGcOFv0fBg-650.webp 650w, https://danielscottraynsford.com/img/gGcOFv0fBg-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/gGcOFv0fBg-650.jpeg&quot; alt=&quot;New Policy&quot; width=&quot;960&quot; height=&quot;616&quot; srcset=&quot;https://danielscottraynsford.com/img/gGcOFv0fBg-650.jpeg 650w, https://danielscottraynsford.com/img/gGcOFv0fBg-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Locate the &lt;strong&gt;Startup&lt;/strong&gt; setting under &lt;em&gt;Computer Configuration/Policies/Windows Settings/Scripts&lt;/em&gt;:&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_gpo_installwmf5_startup.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/1Y4XRWu5ij-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/1Y4XRWu5ij-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/1Y4XRWu5ij-650.jpeg&quot; alt=&quot;The Startup Script of a Computer GPO.&quot; width=&quot;650&quot; height=&quot;464&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Double click the &lt;strong&gt;Startup&lt;/strong&gt; item to edit the scripts and select the &lt;strong&gt;PowerShell&lt;/strong&gt; &lt;strong&gt;Scripts&lt;/strong&gt; tab: &lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_gpo_installwmf5_startuppsscripts.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/POXAOmVz_7-414.png 414w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/POXAOmVz_7-414.webp 414w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/POXAOmVz_7-414.jpeg&quot; alt=&quot;Setting the Startup PowerShell Scripts&quot; width=&quot;414&quot; height=&quot;462&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click the &lt;strong&gt;Show Files&lt;/strong&gt; button and copy the &lt;strong&gt;Install-Update.ps1&lt;/strong&gt; file that was in the zip file downloaded from Microsoft Script Center into this location:&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_gpo_installwmf5_startuppsscripts_folder.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/HiFU-o7Mb--650.png 650w, https://danielscottraynsford.com/img/HiFU-o7Mb--960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/HiFU-o7Mb--650.webp 650w, https://danielscottraynsford.com/img/HiFU-o7Mb--960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/HiFU-o7Mb--650.jpeg&quot; alt=&quot;GPO Folder containing script&quot; width=&quot;960&quot; height=&quot;458&quot; srcset=&quot;https://danielscottraynsford.com/img/HiFU-o7Mb--650.jpeg 650w, https://danielscottraynsford.com/img/HiFU-o7Mb--960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Close the folder and go back to the &lt;strong&gt;Startup Properties&lt;/strong&gt; window and click the &lt;strong&gt;Add&lt;/strong&gt; button.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Set the &lt;strong&gt;Script Name&lt;/strong&gt; to &lt;em&gt;Install-Update.ps1&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Set the &lt;strong&gt;Script Parameters&lt;/strong&gt; to be the following (customized the &lt;strong&gt;bold&lt;/strong&gt; sections to your environment):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;InstallerPath &lt;span class=&quot;token string&quot;&gt;&quot;**&#92;&#92;&#92;&#92;plague-pdc&#92;&#92;Software$&#92;&#92;Updates&#92;&#92;WindowsBlue-KB3055381-x64.msu**&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;KBID &lt;span class=&quot;token string&quot;&gt;&quot;**KB3055381**&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;LogPath &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&#92;&#92;&#92;&#92;plague-pdc&#92;&#92;LogFiles$&#92;&#92;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;blockquote data-callout=&quot;note&quot; class=&quot;blockquote--note&quot;&gt;&lt;p&gt;You can leave off the -LogPath parameter if you don’t want to create logs stating if the update was installed correctly on each machine.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_gpo_installwmf5_startuppsscripts_details.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/3sdFgOexwx-394.png 394w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/3sdFgOexwx-394.webp 394w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/3sdFgOexwx-394.jpeg&quot; alt=&quot;Add the script and parameters&quot; width=&quot;394&quot; height=&quot;202&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;OK&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;OK&lt;/strong&gt; on the &lt;strong&gt;Startup Properties&lt;/strong&gt; window.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Close the &lt;strong&gt;GPME&lt;/strong&gt; window and assign the policy to whichever OUs you want to try WMF 5.0 out on.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;That was probably a lot more detail than most people needed, but I thought I throw it in there just in case.&lt;/p&gt;&lt;p&gt;If you require more details on the parameters available in the script, use the PowerShell cmdlet Get-Help &lt;code&gt;.&#92;Install-Update.ps1&lt;/code&gt; in the folder that Install-Update.ps1 is installed into - or just look at the &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/PowerShell-to-Install-70009e38&quot; rel=&quot;noopener&quot;&gt;PowerShell Scripts to Install Application (EXE) or Update (MSU) using GPO&lt;/a&gt; Microsoft Script Center page.&lt;/p&gt;&lt;h3 id=&quot;installing-an-application-script&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-windows-management-framework-50-with-a-gpo/#installing-an-application-script&quot; class=&quot;heading-anchor&quot;&gt;Installing an Application Script&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;While I was at it I also decided that I wanted to install applications contained in an EXE by this method too (&lt;a href=&quot;https://notepad-plus-plus.org/&quot; rel=&quot;noopener&quot;&gt;Notepad++&lt;/a&gt; was my excuse). This required a slightly different approach to detect if the application was already installed. Basically depending on the application a registry key and/or value needs to be checked to see if the application needs installation. These registry entries differ for every application being installed so which keys to check for an app need to be passed in via parameters to the PowerShell script.&lt;/p&gt;&lt;p&gt;For example, for Notepad++ version 6.7.8.2 the registry key &lt;em&gt;HKLM:&#92;SOFTWARE&#92;Wow6432Node&#92;Microsoft&#92;Windows&#92;CurrentVersion&#92;Uninstall&#92;Notepad++&lt;/em&gt; must exist and requires a string value called &lt;em&gt;DisplayVersion&lt;/em&gt; to be equal to “6.7.8.2”.&lt;/p&gt;&lt;p&gt;I also wrote a script, &lt;code&gt;Install-Application.ps1&lt;/code&gt; to do all this as well, and it is available in the same package as the &lt;code&gt;Install-Update.ps1&lt;/code&gt; script. I will write a separate blog post on using this one as it can be a little bit trickier thanks to the limitations with passing parameters to PS scripts in GPOs. So I’ll leave this post till next week.&lt;/p&gt;&lt;p&gt;Thanks for reading!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>PowerShell Paramters in GPO Scripts</title>
      <link href="https://danielscottraynsford.com/blog/powershell-paramters-in-gpo-scripts/" />
      <updated>2015-06-03T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/powershell-paramters-in-gpo-scripts/</id>
      <content type="html">
				&lt;h2 id=&quot;introduction&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/powershell-paramters-in-gpo-scripts/#introduction&quot; class=&quot;heading-anchor&quot;&gt;Introduction&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This morning I decided I wanted to update all my lab servers to Windows Management Framework 5.0 so I could do some work on the new DSC features that come with it. To do this, I thought I’d use a GPO with a startup PowerShell script that would perform the installation of the WMF 5.0 April hotfix (available &lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=46889&quot; title=&quot;Windows Management Framework 5.0 Preview April 2015&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_gpo_startuppowershellscriptparametersexample.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/_Awkq1LhQX-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/_Awkq1LhQX-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/_Awkq1LhQX-650.jpeg&quot; alt=&quot;A GPO Startup PowerShell script with parameters.&quot; width=&quot;650&quot; height=&quot;464&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;A GPO Startup PowerShell script with parameters.&lt;/p&gt;&lt;p&gt;On thinking about this I decided it might also be a good idea to modify the PowerShell script designed to install Microsoft Office 2013 products via GPO (see the post &lt;a href=&quot;https://dscottraynsford.wordpress.com/2015/04/06/using-powershell-to-installuninstall-microsoft-office-products-by-group-policy/&quot; title=&quot;Using PowerShell to Install/Uninstall Microsoft Office Products by Group Policy&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;). After producing the new scripts and testing them by manually running them to ensure they worked correctly, I put them into some GPOs.&amp;nbsp; And that is when things started to go wrong!&lt;/p&gt;&lt;h2 id=&quot;parameter-problems&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/powershell-paramters-in-gpo-scripts/#parameter-problems&quot; class=&quot;heading-anchor&quot;&gt;Parameter Problems&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The issue I ran into was that the parameters set in the GPO PowerShell script parameters seemed to misbehave in several ways. After about 6 hours of investigating and testing I’ve found the following things cause problems when you do them.&lt;/p&gt;&lt;h3 id=&quot;parameter-length-limit&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/powershell-paramters-in-gpo-scripts/#parameter-length-limit&quot; class=&quot;heading-anchor&quot;&gt;Parameter Length Limit&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;There seems to be a maximum number of characters that will be used in the Script Parameters setting in a GPO Startup/Shutdown/Logon/Logoff PowerShell script. The limit appears to be 207 but I can’t find official documentation of this. If script parameters longer than this limit is entered the additional characters will simply be ignored, leading to the script either failing to run or running incorrectly.&lt;/p&gt;&lt;p&gt;If you do run into this issue, one way around it is to edit the script and wrap all the code in a function definition and then add a call with the parameters to the end of the script after the end of the function definition. For Example:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;Function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Install-Application&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# ... Existing script code here ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;Install-Application&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;InstallerPath &lt;span class=&quot;token string&quot;&gt;&quot;&#92;&#92;Server&#92;Software$&#92;Notepad++&#92;npp.6.7.8.2.Installer.exe&quot;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;InstallerParameters &lt;span class=&quot;token string&quot;&gt;&quot;/S&quot;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;RegistryKey &lt;span class=&quot;token string&quot;&gt;&quot;HKLM:&#92;SOFTWARE&#92;Wow6432Node&#92;Microsoft&#92;Windows&#92;CurrentVersion&#92;Uninstall&#92;Notepad++&quot;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;RegistryName &lt;span class=&quot;token string&quot;&gt;&quot;DisplayVersion&quot;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;RegistryValue &lt;span class=&quot;token string&quot;&gt;&quot;6.7.8.2&quot;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;LogPath &lt;span class=&quot;token string&quot;&gt;&quot;&#92;&#92;Server&#92;Logfiles$&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The PowerShell script parameters in the GPO can then be dropped as they are contained in the script itself – this is not ideal, but unless Microsoft lifts this limitation it may be required.&lt;/p&gt;&lt;h3 id=&quot;parameter-quotes&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/powershell-paramters-in-gpo-scripts/#parameter-quotes&quot; class=&quot;heading-anchor&quot;&gt;Parameter Quotes&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;There is also some odd behaviour passing parameters with quotes (single or double) to the PowerShell scripts in a GPO. I have run into several situations where the use of quotes causes the parameters to either not be passed to the script or passed with additional quotes in them. I recommend the following:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;DO NOT use single quotes around any parameters - use double quotes around string parameters only.&lt;/li&gt;&lt;li&gt;DO NOT end the parameter list with a quoted parameter - this seems to cause the last parameter content to contain an extra quote.&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;summary&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/powershell-paramters-in-gpo-scripts/#summary&quot; class=&quot;heading-anchor&quot;&gt;Summary&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In short, if you stick to the above when calling PowerShell scripts with parameters from GPO then you might save yourself a lot of time scratching your head.&lt;/p&gt;&lt;p&gt;As a quick aside, the scripts I wrote as part of this (for installing Windows QFE Hotfixes and Applications via GPO) are available on Microsoft Script Center &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/PowerShell-to-Install-70009e38&quot; title=&quot;PowerShell Scripts to Install Application (EXE) or Update (MSU) using GPO&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;. I will be writing a full post on these scripts later in the week.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>WSUS - Declining all Superceded Updates - NOW!</title>
      <link href="https://danielscottraynsford.com/blog/wsus-declining-all-superceded-updates-now/" />
      <updated>2015-05-13T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/wsus-declining-all-superceded-updates-now/</id>
      <content type="html">
				&lt;p&gt;Just a quick snippet today. I wrote this because I was didn’t want to have to wait for &lt;strong&gt;30&lt;/strong&gt; days before unusused superceded updates in my &lt;strong&gt;WSUS&lt;/strong&gt; server were automatically &lt;em&gt;declined&lt;/em&gt; - especially those daily “Definition Update for Windows Defender”.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_wsus_definitionupdates.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/O025A43skW-650.png 650w, https://danielscottraynsford.com/img/O025A43skW-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/O025A43skW-650.webp 650w, https://danielscottraynsford.com/img/O025A43skW-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/O025A43skW-650.jpeg&quot; alt=&quot;ss_wsus_definitionupdates&quot; width=&quot;960&quot; height=&quot;63&quot; srcset=&quot;https://danielscottraynsford.com/img/O025A43skW-650.jpeg 650w, https://danielscottraynsford.com/img/O025A43skW-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;If you’re happy waiting for these &lt;em&gt;unused superceded updates&lt;/em&gt; to be &lt;em&gt;declined&lt;/em&gt; after &lt;strong&gt;30&lt;/strong&gt; days then you can just use the following cmdlet:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Invoke-WsusServerCleanup&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DeclineSupersededUpdates&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, if you don’t want to wait you can fire off this little PowerShell script. It is just a single line of PowerShell code that will automatically &lt;em&gt;decline&lt;/em&gt; all updates with a status of anything except for declined and has at least one &lt;em&gt;superceding&lt;/em&gt; update:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-WSUSUpdate&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Classification All &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Status Any &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Approval AnyExceptDeclined &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Where-Object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Update&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetRelatedUpdates&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token namespace&quot;&gt;[Microsoft.UpdateServices.Administration.UpdateRelationship]&lt;/span&gt;::UpdatesThatSupersedeThisUpdate
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Count &lt;span class=&quot;token operator&quot;&gt;-gt&lt;/span&gt; 0
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Deny-WsusUpdate&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The command will take a few minutes to run (depending on how many updates your WSUS Server has) - on my WSUS server it took about 5 minutes. Once the process has completed you could then trigger the cmdlet to perform a &lt;em&gt;WSUS Server cleanup&lt;/em&gt; (to get rid of any obsolete content files):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Invoke-WsusServerCleanup&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;CleanupObsoleteUpdates &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;CleanupUnneededContentFiles&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That is about it for today!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Reporting on File, Folder and Share Permissions and how they Change</title>
      <link href="https://danielscottraynsford.com/blog/reporting-on-file-folder-and-share-permissions-and-how-they-change/" />
      <updated>2015-05-13T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/reporting-on-file-folder-and-share-permissions-and-how-they-change/</id>
      <content type="html">
				&lt;h2 id=&quot;introduction&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/reporting-on-file-folder-and-share-permissions-and-how-they-change/#introduction&quot; class=&quot;heading-anchor&quot;&gt;Introduction&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Late last year I was asked by a friend if I could write a program that could look at the ACL’s defined within a folder structure and report back how they differed from some previously recorded state. This was basically so the administrators could report back what ACL’s had change and verify that the &quot;security Elves’ hadn’t been messing about.&lt;/p&gt;&lt;p&gt;So after several almost complete re-writes, the end result is the&amp;nbsp;&lt;strong&gt;ACLReportTools&lt;/strong&gt; PowerShell module.&lt;/p&gt;&lt;h2 id=&quot;overview&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/reporting-on-file-folder-and-share-permissions-and-how-they-change/#overview&quot; class=&quot;heading-anchor&quot;&gt;Overview&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The intended purpose of this module is to allow an administrator to report on how ACL’s for a set of path or shares have changed since a baseline was last created.&lt;/p&gt;&lt;p&gt;Basically it allows administrators to easily see what ACL changes are being made so they keep an eye on any security issues arising. If performing SMB share comparisons, the report generation can be performed remotely (from a desktop PC for example) and can also be run against shares on multiple computers.&lt;/p&gt;&lt;p&gt;The process that is normally followed using this module is:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Produce a &lt;em&gt;baseline&lt;/em&gt; &lt;strong&gt;ACL Report&lt;/strong&gt; from a set of Folders or Shares (even on multiple computers).&lt;/li&gt;&lt;li&gt;Export the &lt;em&gt;baseline&lt;/em&gt; &lt;strong&gt;ACL Report&lt;/strong&gt; as a file.&lt;/li&gt;&lt;li&gt;… Sometime later …&lt;/li&gt;&lt;li&gt;Import the &lt;em&gt;baseline&lt;/em&gt; &lt;strong&gt;ACL Report&lt;/strong&gt; from a stored file.&lt;/li&gt;&lt;li&gt;Produce an &lt;strong&gt;ACL Difference&lt;/strong&gt; report comparing the imported &lt;em&gt;baseline&lt;/em&gt; &lt;strong&gt;ACL Report&lt;/strong&gt; with the &lt;em&gt;current&lt;/em&gt; &lt;strong&gt;ACL state&lt;/strong&gt; of the Folders or Shares&lt;/li&gt;&lt;li&gt;Optionally, export the &lt;strong&gt;ACL Difference&lt;/strong&gt; report as &lt;em&gt;HTML&lt;/em&gt;.&lt;/li&gt;&lt;li&gt;Repeat from step 1.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The comparison is always performed recursively scanning a specified set of folders or SMB shares. All files and folders within these locations will be scanned, but only non-inherited ACL’s will be added to the ACL Reports.&lt;/p&gt;&lt;h2 id=&quot;report-details&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/reporting-on-file-folder-and-share-permissions-and-how-they-change/#report-details&quot; class=&quot;heading-anchor&quot;&gt;Report Details&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;An &lt;strong&gt;ACL Report&lt;/strong&gt; is a list of non-inherited ACLs for a set of &lt;em&gt;Shares&lt;/em&gt; or &lt;em&gt;Folders&lt;/em&gt;. It is stored as a &lt;em&gt;serialized&lt;/em&gt; array of &lt;em&gt;[ACLReportTools.Permission]&lt;/em&gt; objects.&amp;nbsp;&lt;strong&gt;ACL Reports&lt;/strong&gt; are returned by the &lt;em&gt;New-ACLShareReport&lt;/em&gt;, &lt;em&gt;New-ACLPathFileReport&lt;/em&gt; and &lt;em&gt;Import-ACLReport&lt;/em&gt; cmdlets.&lt;/p&gt;&lt;p&gt;An &lt;strong&gt;ACL Difference Report&lt;/strong&gt; is a list of all ACL differences between two ACL reports. It is stored as &lt;em&gt;serialized&lt;/em&gt; array of &lt;em&gt;[ACLReportTools.PermissionDiff]&lt;/em&gt; objects that are returned by the &lt;em&gt;Compare-ACLReports&lt;/em&gt; and &lt;em&gt;Import-ACLDiffReport&lt;/em&gt; cmdlet.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;ACL Reports&lt;/strong&gt; produced for &lt;em&gt;shares&lt;/em&gt; rather than &lt;em&gt;folders&lt;/em&gt; differ in that the &lt;em&gt;share name&lt;/em&gt; is provided in each &lt;em&gt;[ACLReportTools.Permission]&lt;/em&gt; object and that the &lt;em&gt;SMB Share ACL&lt;/em&gt; is also provided in the &lt;em&gt;[ACLReportTools.Permission]&lt;/em&gt; array.&lt;/p&gt;&lt;h2 id=&quot;important-notes&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/reporting-on-file-folder-and-share-permissions-and-how-they-change/#important-notes&quot; class=&quot;heading-anchor&quot;&gt;Important Notes&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;When performing a &lt;em&gt;comparison&lt;/em&gt;, make sure the baseline report used covers the &lt;em&gt;same set&lt;/em&gt; of folders/shares you want to compare now. For example, don’t try to compare ACL’s for c:&#92;windows and c:&#92;wwwroot - that would make no sense and result in non-sensical output.&lt;/p&gt;&lt;p&gt;If shares or folders that are being compared have large numbers of non-inherited ACL’s (perhaps because some junior admin doesn’t understand inheritance) then a &lt;em&gt;comparison&lt;/em&gt; can take a &lt;strong&gt;long&lt;/strong&gt; time (hours) and really hog your CPU. If this is the case, run the comparison from another machine using &lt;em&gt;share mode&lt;/em&gt; or run it after hours - or better yet, teach junior admins about inheritance! 😃&lt;/p&gt;&lt;p&gt;You should also ensure that the account that is being used to generate any reports has read access to all paths and all content (including &lt;em&gt;recursive content&lt;/em&gt;) that will be reported on and can also read the ACL’s. If it can’t access them then you may get access denied warnings (although the process will continue).&lt;/p&gt;&lt;h3 id=&quot;ntfs-security-module&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/reporting-on-file-folder-and-share-permissions-and-how-they-change/#ntfs-security-module&quot; class=&quot;heading-anchor&quot;&gt;NTFS Security Module&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This Module uses the awesome &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/1abd77a5-9c0b-4a2b-acef-90dbb2b84e85&quot; title=&quot;Download the NTFS Security PS Module&quot; rel=&quot;noopener&quot;&gt;NTFS Security Module&lt;/a&gt; to be installed in your PowerShell Modules path.&lt;/p&gt;&lt;p&gt;Ensure that you &lt;strong&gt;unblock&lt;/strong&gt; all files in the &lt;em&gt;NTFS Security Module&lt;/em&gt; folder before attempting to Import Module &lt;em&gt;ACLReportTools&lt;/em&gt;. The ACLReportTools module automatically looks for and Imports the &lt;em&gt;NTFS Security Module&lt;/em&gt; if present. If it is missing, an error will be returned stating that the module is missing. If you receive any other errors importing ACL Report tools, it is usually because some of the &lt;em&gt;NTFS Security Module&lt;/em&gt; files are &lt;strong&gt;blocked&lt;/strong&gt; and need to be &lt;strong&gt;unblocked&lt;/strong&gt; manually or with Unblock-File. You can confirm this by calling &lt;em&gt;Import-Module NTFSSecurity&lt;/em&gt; - if any errors appear then it is most likely the caused by &lt;strong&gt;blocked&lt;/strong&gt; files. After &lt;strong&gt;unblocking&lt;/strong&gt; the module files you may need to restart PowerShell.&lt;/p&gt;&lt;h2 id=&quot;installing-aclreporttools&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/reporting-on-file-folder-and-share-permissions-and-how-they-change/#installing-aclreporttools&quot; class=&quot;heading-anchor&quot;&gt;Installing ACLReportTools&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;Unzip the archive containing the &lt;strong&gt;ACLReportTools&lt;/strong&gt; module into the one of the &lt;em&gt;PowerShell Modules&lt;/em&gt; folders (E.g. $Home&#92;documents&#92;windowspowershell&#92;modules).&lt;/li&gt;&lt;li&gt;This will create a folder called &lt;strong&gt;ACLReportTools&lt;/strong&gt; containing all the files required for this module.&lt;/li&gt;&lt;li&gt;In PowerShell execute:&lt;/li&gt;&lt;/ol&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Import-Module&lt;/span&gt; ACLReportTools&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;how-to-use-it&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/reporting-on-file-folder-and-share-permissions-and-how-they-change/#how-to-use-it&quot; class=&quot;heading-anchor&quot;&gt;How to Use It&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The basic steps for using this module is as follows:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Create a &lt;strong&gt;Baseline ACL Report&lt;/strong&gt; file on a set of Folders.&lt;/li&gt;&lt;li&gt;Compare the &lt;strong&gt;Baseline ACL Report&lt;/strong&gt; file with the &lt;em&gt;current ACL’s&lt;/em&gt; for the same set of Folders.&lt;/li&gt;&lt;li&gt;Optionally, convert the &lt;strong&gt;ACL Comparisson Report&lt;/strong&gt; into an HTML report file.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;In the for following examples, the &lt;strong&gt;e:&#92;work&lt;/strong&gt; and &lt;strong&gt;d:&#92;profiles&lt;/strong&gt; are being used to produce an &lt;em&gt;ACL Difference&lt;/em&gt; report for. The &lt;em&gt;Baseline ACL Report&lt;/em&gt; and the &lt;em&gt;ACL Difference Report&lt;/em&gt; will be saved into the current users &lt;strong&gt;documents&lt;/strong&gt; folder.&lt;/p&gt;&lt;h3 id=&quot;step-1-create-a-baseline-acl-report-file-on-a-set-of-folders&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/reporting-on-file-folder-and-share-permissions-and-how-they-change/#step-1-create-a-baseline-acl-report-file-on-a-set-of-folders&quot; class=&quot;heading-anchor&quot;&gt;Step 1: Create a Baseline ACL Report file on a set of Folders&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The first step is to create &lt;em&gt;Baseline ACL Report&lt;/em&gt; on the folders &lt;strong&gt;e:&#92;work&lt;/strong&gt; and &lt;strong&gt;d:&#92;profiles&lt;/strong&gt; and store it in the b_aseline.acl_ file in the current users &lt;strong&gt;documents&lt;/strong&gt; folder:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Import-Module&lt;/span&gt; ACLReportTools
&lt;span class=&quot;token function&quot;&gt;New-ACLPathFileReport&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;e:&#92;Work&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;d:&#92;Profiles&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Export-ACLReport&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$HOME&lt;/span&gt;&#92;Documents&#92;Baseline.acl&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;step-2-compare-the-baseline-acl-report-file-with-the-current-acls-for-the-same-set-of-folders&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/reporting-on-file-folder-and-share-permissions-and-how-they-change/#step-2-compare-the-baseline-acl-report-file-with-the-current-acls-for-the-same-set-of-folders&quot; class=&quot;heading-anchor&quot;&gt;Step 2: Compare the Baseline ACL Report file with the current ACLs for the same set of Folders&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This step is usually performed a few days or weeks after step 1. In this step the &lt;em&gt;Baseline ACL Report&lt;/em&gt; created in step 1 is compared with the &lt;em&gt;current ACL’s&lt;/em&gt; for the same set of folders used in step 1. The output is put into the variable &lt;strong&gt;$DiffReport&lt;/strong&gt; which can then be exported as a file using the &lt;strong&gt;Export-ACLDiffReport&lt;/strong&gt; cmdlet or saved as HTML using &lt;strong&gt;Export-ACLPermissionDiffHTML&lt;/strong&gt; for easier review.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Import-Module&lt;/span&gt; ACLReportTools
&lt;span class=&quot;token variable&quot;&gt;$DiffReport&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Compare-ACLReports&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Baseline &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Import-ACLReport&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$HOME&lt;/span&gt;&#92;Documents&#92;Baseline.acl&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;e:&#92;Work&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;d:&#92;Profiles&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;step-3-convert-the-acl-comparison-report-into-an-html-report-file&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/reporting-on-file-folder-and-share-permissions-and-how-they-change/#step-3-convert-the-acl-comparison-report-into-an-html-report-file&quot; class=&quot;heading-anchor&quot;&gt;Step 3: Convert the ACL Comparison Report into an HTML Report File&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Once the &lt;em&gt;ACL Difference Report&lt;/em&gt; has been produced, it could be simply dumped straight into the pipeline or converted into an HTML using the &lt;strong&gt;Export-ACLPermissionDiffHTML&lt;/strong&gt; cmdlet. The title that will appear on the HTML page is also provided.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$DiffReport&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Export-ACLPermissionDiffHtml&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Title &lt;span class=&quot;token string&quot;&gt;&#39;ACL Diff Report for e:&#92;work and d:&#92;profile&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;reporting-on-shares-instead-of-folders&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/reporting-on-file-folder-and-share-permissions-and-how-they-change/#reporting-on-shares-instead-of-folders&quot; class=&quot;heading-anchor&quot;&gt;Reporting on Shares Instead of Folders&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Instead of specifying a set of folders it is also possible to specify a list of computers and/or SMB shares to pull the &lt;em&gt;ACL Reports&lt;/em&gt; from. For example if we wanted to report on the shares&amp;nbsp;&lt;strong&gt;Share1&lt;/strong&gt; and&amp;nbsp;&lt;strong&gt;Share2&lt;/strong&gt; on computer&amp;nbsp;&lt;strong&gt;Client&lt;/strong&gt; the following commands could be used for step 1:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Baseline for Share1 and Share2 on CLIENT&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Import-Module&lt;/span&gt; ACLReportTools
&lt;span class=&quot;token function&quot;&gt;Compare-ACLReports&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Baseline &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Import-ACLReport&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$HOME&lt;/span&gt;&#92;Documents&#92;Baseline.acl&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ComputerName Client `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Include Share1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;Share2&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then for step 2 we would use:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Later, create the difference report&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Import-Module&lt;/span&gt; ACLReportTools
&lt;span class=&quot;token variable&quot;&gt;$DiffReport&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Compare-ACLReports&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Baseline &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Import-ACLReport&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$HOME&lt;/span&gt;&#92;Documents&#92;Baseline.acl&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ComputerName Client `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Include Share1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;Share2&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Step 3 in would be exactly the same as in the Folder scenario.&lt;/p&gt;&lt;h2 id=&quot;final-word&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/reporting-on-file-folder-and-share-permissions-and-how-they-change/#final-word&quot; class=&quot;heading-anchor&quot;&gt;Final Word&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;What started as a simple script actually ended up turning into quite a large module that taught me a huge amount about PowerShell. So I hope someone else out there is also able to find a use for this and it helps track down some of those ‘Permission Elves’.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Install Windows Server Nano the Easy Way</title>
      <link href="https://danielscottraynsford.com/blog/install-windows-server-nano-the-easy-way/" />
      <updated>2015-05-08T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/install-windows-server-nano-the-easy-way/</id>
      <content type="html">
				&lt;p&gt;All the recent talk about the new Windows Server Nano (Windows Server Core on diet pills) that is available for installation on the Windows Server 2016 Technical Preview 2 ISO got me quite interested. So, I thought I’d give it a whirl to see what all the fuss was about. Well, first up, it really is as small (568Mb with my chosen packages) and fast as Microsoft says. Second, however, it is most definitely a Tech Preview and is missing lots of stuff and has some obvious issues.&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Edit - 14 May 2016&lt;/strong&gt;: The scripts and process on this page have now been updated to support &lt;strong&gt;Windows Server 2016 Technical Preview 5&lt;/strong&gt;. The process will not work for versions earlier than Windows Server 2016 Technical Preview 5. I removed a some parts of this document as they were not correct for TP5. I decided to update this blog post with TP5 information because it is still getting a lot of traffic.&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;manual-install&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-windows-server-nano-the-easy-way/#manual-install&quot; class=&quot;heading-anchor&quot;&gt;Manual Install&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Installing a copy of Server Nano is not quite as straight forward as mounting the ISO into a VM and booting it up - at least not yet. But if you’re still keen to give it a try, Microsoft provides some instructions on how you can install Nano into a VHDx which you can then mount as the boot disk in a Gen-1 or Gen-2 Hyper-V Machine.&lt;/p&gt;&lt;p&gt;The manual instructions to create a VHD with Nano Server on it can be found here:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://technet.microsoft.com/en-us/library/mt126167.aspx&quot; title=&quot;Getting Started with Nano Server&quot; rel=&quot;noopener&quot;&gt;Getting Started With Nano Server&lt;/a&gt;&lt;/p&gt;&lt;p&gt;It is well worth reading this to get an idea of how Nano Server is different from regular Core Server.&lt;/p&gt;&lt;h2 id=&quot;easy-install&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-windows-server-nano-the-easy-way/#easy-install&quot; class=&quot;heading-anchor&quot;&gt;Easy Install&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As you can see, installing Nano Server requires quite a few steps. None of them are difficult, but I wouldn’t be much of a nerd if I didn’t convert it into a script. So after a quiet Friday night I managed to cobble something together. You can find it here:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/Create-a-New-Nano-Server-61f674f1&quot; title=&quot;Create a New Nano Server VHD&quot; rel=&quot;noopener&quot;&gt;Create a New Nano Server VHD&lt;/a&gt;&lt;/p&gt;&lt;p&gt;It is fairly straight forward to install and use:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Create a &lt;strong&gt;Working Folder&lt;/strong&gt; on your computer in the case of this example I used c:&#92;Nano.&lt;/li&gt;&lt;li&gt;Download the &lt;em&gt;New-NanoServerVHD.ps1&lt;/em&gt; to the &lt;strong&gt;Working Folder&lt;/strong&gt;. Download the Windows Server 2016 Technical Preview ISO (&lt;a href=&quot;https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-technical-preview&quot; rel=&quot;noopener&quot;&gt;download here&lt;/a&gt;) to the &lt;strong&gt;Working Folder&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;Open an Administrative PowerShell window.&lt;/li&gt;&lt;li&gt;Change directory to the &lt;strong&gt;Working Folder&lt;/strong&gt; (cd c:&#92;nano).&lt;/li&gt;&lt;li&gt;Execute the following command (customizing the parameters to your needs):&lt;/li&gt;&lt;/ol&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&#92;&lt;span class=&quot;token function&quot;&gt;New-NanoServerVHD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ps1 `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ServerISO &lt;span class=&quot;token string&quot;&gt;&#39;c:&#92;nano&#92;14300.1000.160324-1723.RS1_RELEASE_SVC_SERVER_OEMRET_X64FRE_EN-US.ISO&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DestVHD   &lt;span class=&quot;token string&quot;&gt;&#39;c:&#92;nano&#92;NanoServer01.vhdx&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VHDFormat &lt;span class=&quot;token string&quot;&gt;&#39;VHDx&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ComputerName &lt;span class=&quot;token string&quot;&gt;&#39;NANOTEST01&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AdministratorPassword &lt;span class=&quot;token string&quot;&gt;&#39;P@ssword!1&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Packages &lt;span class=&quot;token string&quot;&gt;&#39;Storage&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;OEM-Drivers&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Guest&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;IPAddress &lt;span class=&quot;token string&quot;&gt;&#39;192.168.1.65&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: If you run this in the PowerShell ISE, a pop-up message appears during execution:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_nano_building_error_wrong_volume.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/xbcNf3QVuF-410.png 410w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/xbcNf3QVuF-410.webp 410w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/xbcNf3QVuF-410.jpeg&quot; alt=&quot;This error can be ignored without it causing a problem.&quot; width=&quot;410&quot; height=&quot;172&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;This error can be ignored without it causing a problem.&lt;/p&gt;&lt;p&gt;If this happens, just click &lt;strong&gt;Continue&lt;/strong&gt;. I’m not sure why this happens in the ISE, but the script still functions fine if it occurs. I tried to get a screenshot of this but I couldn’t get it to happen.&lt;/p&gt;&lt;h2 id=&quot;booting-it-up&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-windows-server-nano-the-easy-way/#booting-it-up&quot; class=&quot;heading-anchor&quot;&gt;Booting it up&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Once you’ve the VHD has been created, just create a new or Gen-2 (or Gen-1) Hyper-V Machine and assign this VHDx as the boot disk. Start up the VM and after about a minute (for me anyway) you should be able to use PS remoting (Enter-PSSession) to connect to it and begin playing.&lt;/p&gt;&lt;p&gt;Remember, Server Nano is completely headless (which just sounds cool), so if you try to connect to it using the Hyper-V Console you will see the recovery console:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/JSoQidFygP-650.png 650w, https://danielscottraynsford.com/img/JSoQidFygP-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/JSoQidFygP-650.webp 650w, https://danielscottraynsford.com/img/JSoQidFygP-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/JSoQidFygP-650.jpeg&quot; alt=&quot;ss_nano_login&quot; width=&quot;960&quot; height=&quot;720&quot; srcset=&quot;https://danielscottraynsford.com/img/JSoQidFygP-650.jpeg 650w, https://danielscottraynsford.com/img/JSoQidFygP-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h2 id=&quot;observations&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/install-windows-server-nano-the-easy-way/#observations&quot; class=&quot;heading-anchor&quot;&gt;Observations&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Edit - 14 May 2016:&lt;/strong&gt; These “issues” have been resolved in more recent versions of Windows Server 2016 Nano Server.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;One thing I have noted though, is that if you watch the machine with the Hyper-V Console while it is booting it will show the nice little Windows Start up screen for up to 5 minutes - even though the machine appears to be completely booted and can be connected to. I’m not sure why this is, but I’m sure MS will sort it out.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_hyperv_nano_booting.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/V1kxzGmyLd-650.png 650w, https://danielscottraynsford.com/img/V1kxzGmyLd-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/V1kxzGmyLd-650.webp 650w, https://danielscottraynsford.com/img/V1kxzGmyLd-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/V1kxzGmyLd-650.jpeg&quot; alt=&quot;Nano booting up - on my system it can be connected to and used even while the boot screen is showing.&quot; width=&quot;960&quot; height=&quot;814&quot; srcset=&quot;https://danielscottraynsford.com/img/V1kxzGmyLd-650.jpeg 650w, https://danielscottraynsford.com/img/V1kxzGmyLd-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;Nano booting up - on my system it can be connected to and used even while the boot screen is showing.&lt;/p&gt;&lt;p&gt;A second thing I found while writing this script was that in the &lt;em&gt;Unattend.xml&lt;/em&gt; file the &lt;strong&gt;ComputerName&lt;/strong&gt; is supposed to be set in the &lt;em&gt;offlineServicing&lt;/em&gt; phase (according to the MS instructions). But this didn’t seem to work for me so my script sets it in both the &lt;em&gt;offlineServicing&lt;/em&gt; phase and the &lt;em&gt;Specialize&lt;/em&gt; phase. This actually doubles the first boot time from 5 seconds to 10 seconds because it needs to reboot to apply the &lt;strong&gt;ComputerName&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;If anyone reads this and has any ideas on how to improve the process (or if I’ve gone wrong somewhere), please let me know!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>DSC Tools- Hopefully Making DSC Easier</title>
      <link href="https://danielscottraynsford.com/blog/dsc-tools-hopefully-making-dsc-easier/" />
      <updated>2015-05-02T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/dsc-tools-hopefully-making-dsc-easier/</id>
      <content type="html">
				&lt;h3 id=&quot;introduction&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/dsc-tools-hopefully-making-dsc-easier/#introduction&quot; class=&quot;heading-anchor&quot;&gt;Introduction&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Desired State Configuration (DSC) is definitely one of the coolest features of WMF 4.0. This article however is not about what DSC is or how to implement it - there are already many great introductions to DSC out there. If you’re new to DSC I’d suggest you take a look at the great free e-book from &lt;a href=&quot;http://PowerShell.org&quot; rel=&quot;noopener&quot;&gt;PowerShell.org&lt;/a&gt;: &lt;a href=&quot;https://www.penflip.com/powershellorg/the-dsc-book&quot; title=&quot;The DSC Book&quot; rel=&quot;noopener&quot;&gt;The DSC Book&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I’ve been working with DSC for several months now in my lab environments and I really love it. But it can be a little bit tricky to set up, with lots of steps and things to remember (or forget in my case). It is very easy to miss a step and spend many hours trying to figure out what went wrong - I know this from experience.&lt;/p&gt;&lt;p&gt;I’m certain in the future that Microsoft and/or other tool providers will provide easier methods of implementing DSC, but at the moment it is still a fairly manual process. There are products like Chef (see &lt;a href=&quot;https://www.chef.io/solutions/windows/&quot; title=&quot;Chef for Windows&quot; rel=&quot;noopener&quot;&gt;Chef for Windows&lt;/a&gt;) that are going to use DSC to provide configuration of Windows operating systems (and perhaps non-Windows as well), but as of writing this these tools aren’t yet available.&lt;/p&gt;&lt;p&gt;So, after about the 20th time of setting up DSC, I figured that it might be an idea to write some simple tools that could make my life a little bit easier. With that in mind I created a PowerShell module (supporting WMF 4.0 and above) that provides some helper functions and configurations that make setting up DSC Pull Servers and configuring Local Configuration Manager (LCM) on the nodes slightly less painful.&lt;/p&gt;&lt;h3 id=&quot;the-module&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/dsc-tools-hopefully-making-dsc-easier/#the-module&quot; class=&quot;heading-anchor&quot;&gt;The Module&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;With all of the above in mind I set about creating a module that would combine all of these steps into simple PS cmdlets that could be used without having to remember exactly how to do things such as creating an LCM configuration file for a Pull Server or something like that. This in theory would leave more time for the really fun part about DSC: creating node configuration files and resources.&lt;/p&gt;&lt;p&gt;For example, to set up a DSC Pull Server (in HTTP mode) there are several steps required:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Download and Install the DSC Resources your configurations will use to the PS modules folder.&lt;/li&gt;&lt;li&gt;Publish the DSC Resources to a folder in your Pull Server and create checksum files for them.&lt;/li&gt;&lt;li&gt;Create a DSC Configuration file for your Pull Server(s).&lt;/li&gt;&lt;li&gt;Run the DSC Configuration file for your Pull Server(s) into to create MOF files.&lt;/li&gt;&lt;li&gt;Push the DSC Configuration MOF files to the LCM on the Pull Servers.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;These steps are easy to understand and perform, but if you don’t do them often you will quickly forget exactly how to do it. I found myself referring back to long sets of instructions (found many places online) every time I did it.&lt;/p&gt;&lt;p&gt;To perform the above steps using the &lt;strong&gt;DSCTools&lt;/strong&gt; module simply requires the following cmdlets to be executed on the computer that will become the DSC Pull Server.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Download the DSC Resource Kit and install it to the local DSC Pull Server&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Install-DSCResourceKit&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Copy all the resources up to the local DSC Pull Server (zipped and with a checksum file)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Publish-DSCPullResources&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Install a DSC Pull Server to the local machine&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Enable-DSCPullServer&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The cmdlets can be executed on a computer other than the Pull Server by providing &lt;code&gt;-ComputerName&lt;/code&gt; and &lt;code&gt;-Credential&lt;/code&gt; parameters.&lt;/p&gt;&lt;h3 id=&quot;dsctool-cmdlet-parameters&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/dsc-tools-hopefully-making-dsc-easier/#dsctool-cmdlet-parameters&quot; class=&quot;heading-anchor&quot;&gt;DSCTool CmdLet Parameters&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Like most PS cmdlets, you can also provide additional configuration options to them as well. For example, you might want your DSC Pull Server resources folder to be placed in a different location, in which case you could provide the commands with the PullServerResourcePath parameter:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Copy the resources to a non-default folder on the Pull Server&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Publish-DSCPullResources&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PullServerResourcePath e:&#92;DSC&#92;Resources

&lt;span class=&quot;token comment&quot;&gt;# Install a DSC Pull Server that uses the same custom folder&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Enable-DSCPullServer&lt;/span&gt;    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PullServerResourcePath e:&#92;DSC&#92;Resources&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Most &lt;strong&gt;DSCTools&lt;/strong&gt; cmdlets have many other parameters for controlling most aspects of the functions such as the type of Pull Server to install (HTTP, HTTPS or SMB), the location where files should be stored. If these parameters aren’t passed then the default values will be used.&lt;/p&gt;&lt;h3 id=&quot;default-dsctools-module-settings&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/dsc-tools-hopefully-making-dsc-easier/#default-dsctools-module-settings&quot; class=&quot;heading-anchor&quot;&gt;Default DSCTools Module Settings&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;If you’re lazy (like me) and don’t want to have to pass the same parameters in to every cmdlet, you can change the default values by overriding script variables once the module has been loaded. For example you might always want your Pull Servers to use a configuration path of &lt;em&gt;e:&#92;DSC&#92;Configuration&lt;/em&gt;. You could pass the &lt;em&gt;PullServerConfigurationPath&lt;/em&gt; parameter to each cmdlet that needs it (which could be many), or you could just change the value of the &lt;em&gt;$Script:DSCTools_DefaultPullServerConfigurationPath&lt;/em&gt; variable:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Change the default location where all Pull-Server cmdlets put configuration files&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$Script&lt;/span&gt;:DSCTools_DefaultPullServerConfigurationPath = &lt;span class=&quot;token string&quot;&gt;&#39;e:&#92;DSC&#92;Configuration&#39;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Now the standard commands pick up that new default&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Publish-DSCPullResources&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Enable-DSCPullServer&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;configuring-a-node&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/dsc-tools-hopefully-making-dsc-easier/#configuring-a-node&quot; class=&quot;heading-anchor&quot;&gt;Configuring a Node&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Configuring a node to use the Pull Server also used to be a more complicated process (not counting the actual creation of the node configuration file):&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Copy the configuration MOF file to the Pull Server.&lt;/li&gt;&lt;li&gt;Generate a checksum file for the configuration.&lt;/li&gt;&lt;li&gt;Configure the LCM on the node to pull its configuration from the Pull Server.&lt;/li&gt;&lt;li&gt;Trigger the node to immediately pull it’s DSC configuration from the Pull Server (rather than wait 30 minutes).&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;This process can now be performed by just two cmdlets:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Set up NODE01 to pull from the server MYDSCSERVER&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# The MOF file is searched for in $Home&#92;Documents&#92;NODE01.MOF (configurable)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Start-DSCPullMode&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ComputerName &lt;span class=&quot;token string&quot;&gt;&#39;NODE01&#39;&lt;/span&gt; `
    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PullServerURL &lt;span class=&quot;token string&quot;&gt;&#39;http://MYDSCSERVER:8080/PSDSCPullServer.svc&#39;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Force an immediate configuration pull (optional)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Invoke-DSCCheck&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ComputerName NODE01&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;configuring-lots-of-nodes-at-once&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/dsc-tools-hopefully-making-dsc-easier/#configuring-lots-of-nodes-at-once&quot; class=&quot;heading-anchor&quot;&gt;Configuring Lots of Nodes at Once&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Once again, this module is all about laziness. So, why configure one node at a time when you can configure lots of them all at once. Many of the cmdlets in this module support a &lt;em&gt;Nodes&lt;/em&gt; parameter, which take an array of hash tables. This array of hash tables will contain the definitions of the nodes that need to be set up or have other procedures performed on them (e.g. invoke a configuration check).&lt;/p&gt;&lt;p&gt;For example, to configure seven different nodes for using a DSC Pull Server (with different configurations even) would require the following code:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$Nodes&lt;/span&gt; = @&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Name=&lt;span class=&quot;token string&quot;&gt;&#39;NODE01&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; Guid=&lt;span class=&quot;token string&quot;&gt;&#39;115929a0-61e2-41fb-a9ad-0cdcd66fc2e1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; RebootIfNeeded=&lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; MofFile=&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PSScriptRoot&lt;/span&gt;&#92;Config&#92;NODE01.MOF&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Name=&lt;span class=&quot;token string&quot;&gt;&#39;NODE02&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; Guid=&lt;span class=&quot;token string&quot;&gt;&#39;115929a0-61e2-41fb-a9ad-0cdcd66fc2e2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; RebootIfNeeded=&lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; MofFile=&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PSScriptRoot&lt;/span&gt;&#92;Config&#92;NODE02.MOF&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Name=&lt;span class=&quot;token string&quot;&gt;&#39;NODE03&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; Guid=&lt;span class=&quot;token string&quot;&gt;&#39;115929a0-61e2-41fb-a9ad-0cdcd66fc2e3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; RebootIfNeeded=&lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; MofFile=&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PSScriptRoot&lt;/span&gt;&#92;Config&#92;NODE03.MOF&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Name=&lt;span class=&quot;token string&quot;&gt;&#39;NODE04&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; Guid=&lt;span class=&quot;token string&quot;&gt;&#39;115929a0-61e2-41fb-a9ad-0cdcd66fc2e4&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; RebootIfNeeded=&lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; MofFile=&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PSScriptRoot&lt;/span&gt;&#92;Config&#92;NODE04.MOF&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Name=&lt;span class=&quot;token string&quot;&gt;&#39;NODE05&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; Guid=&lt;span class=&quot;token string&quot;&gt;&#39;115929a0-61e2-41fb-a9ad-0cdcd66fc2e5&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; RebootIfNeeded=&lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; MofFile=&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PSScriptRoot&lt;/span&gt;&#92;Config&#92;NODE05.MOF&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Name=&lt;span class=&quot;token string&quot;&gt;&#39;NODE06&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; Guid=&lt;span class=&quot;token string&quot;&gt;&#39;115929a0-61e2-41fb-a9ad-0cdcd66fc2e6&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; RebootIfNeeded=&lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; MofFile=&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PSScriptRoot&lt;/span&gt;&#92;Config&#92;NODE06.MOF&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    @&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Name=&lt;span class=&quot;token string&quot;&gt;&#39;NODE07&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; Guid=&lt;span class=&quot;token string&quot;&gt;&#39;115929a0-61e2-41fb-a9ad-0cdcd66fc2e7&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; RebootIfNeeded=&lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; MofFile=&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$PSScriptRoot&lt;/span&gt;&#92;Config&#92;NODE07.MOF&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;Start-DSCPullMode&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Nodes &lt;span class=&quot;token variable&quot;&gt;$Nodes&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PullServerURL &lt;span class=&quot;token string&quot;&gt;&#39;http://MYDSCSERVER:8080/PSDSCPullServer.svc&#39;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Invoke-DSCCheck&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Nodes &lt;span class=&quot;token variable&quot;&gt;$Nodes&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The nodes array could even be populated from a CSV file to make it even easier.&lt;/p&gt;&lt;h3 id=&quot;where-to-get-it&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/dsc-tools-hopefully-making-dsc-easier/#where-to-get-it&quot; class=&quot;heading-anchor&quot;&gt;Where to get It&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Once the new &lt;a href=&quot;http://www.powershellgallery.com/&quot; rel=&quot;noopener&quot;&gt;PowerShell Gallery&lt;/a&gt; is available (for public consumption) I’ll upload the module there. But in the mean time it is available on the Microsoft Script Center here:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/DSC-Tools-c96e2c53&quot; title=&quot;DSC Tools on Script Center&quot; rel=&quot;noopener&quot;&gt;DSCTools on Script Center&lt;/a&gt;&lt;/p&gt;&lt;p&gt;If you want to see some more example files (as well as sample node config files I’ve used in testing the module) you can check out the GitHub repository for the module:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/PlagueHO/Powershell/tree/master/DSCTools&quot; title=&quot;DSCTools on GitHub&quot; rel=&quot;noopener&quot;&gt;DSCTools on GitHub&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The GitHub repository contains additional files not available in the download from the script center including details examples and test configuration files. It is also contained within a Visual Studio 2013 solution so if you’re using VS2013 and the PowerShell VS add-in you can easily load it up.&lt;/p&gt;&lt;h3 id=&quot;future-versions&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/dsc-tools-hopefully-making-dsc-easier/#future-versions&quot; class=&quot;heading-anchor&quot;&gt;Future Versions&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Over the next few weeks I am planning to update the module so that some of the functions will create the node configuration MOF files by running the actual configuration PS1 files (provided in a nodes array or as parameters). This will eliminate another step when updating any node configuration files.&lt;/p&gt;&lt;p&gt;After creating this module it occurred to me that there might be an even better way to implement a DSC set up: using a basic DSC system XML configuration file that could define all the pull servers and nodes. This XML file could be passed to a cmdlet that could configure all the applicable servers and nodes from the content. So this is the next long-term goal I have for this module.&lt;/p&gt;&lt;p&gt;If anyone out there finds this module useful and has a request for additional features or finds a (shudder) bug, please let me know!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Best New Feature of Windows 10?</title>
      <link href="https://danielscottraynsford.com/blog/best-new-feature-of-windows-10/" />
      <updated>2015-04-26T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/best-new-feature-of-windows-10/</id>
      <content type="html">
				&lt;p&gt;Well for me, it has to be the proper standard support for text selection, copy (CTRL+C), cut (CTRL+X) and paste (CTRL+V) in the Command and PowerShell windows. Finally, Microsoft has gotten rid of the completely unintuitive and non-standard method employed for the last 20 or so years. What took them so long?&lt;/p&gt;&lt;p&gt;Although I still don’t like the fact that copying text in a console window de-selects it. This is not standard behaviour anywhere else. Perhaps they’ll fix this too?&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Get DSC Configuration from a Remote Host using an SSL Connection</title>
      <link href="https://danielscottraynsford.com/blog/get-dsc-configuration-from-a-remote-host-using-an-ssl-connection/" />
      <updated>2015-04-23T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/get-dsc-configuration-from-a-remote-host-using-an-ssl-connection/</id>
      <content type="html">
				&lt;p&gt;I’ve spent the last day or so working on a module to help with managing DSC Pull Servers and other functions to help making DSC a little bit easier to get up and running. This module isn’t quite finished yet, but I thought I’d share a quick code snippet that I’ve been using a lot to get the DSC configuration from a remote machine when credentials and special port details are required.&lt;/p&gt;&lt;p&gt;Normally, if you want to pull the DSC configuration for a remote computer that won’t require credentials or SSL WSMan then you can just execute:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-DSCConfiguration&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;CimSession &lt;span class=&quot;token string&quot;&gt;&#39;DSCSVR01&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This cmdlet just pulls the DSC configuration from the remote host using any existing credentials and using HTTP instead of HTTPS.&lt;/p&gt;&lt;p&gt;But if you want to use any alternative connection information – such as forcing the use of SSL WSMan – you need to add some CIM options:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$cimOption&lt;/span&gt;  = &lt;span class=&quot;token function&quot;&gt;New-CimSessionOption&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;UseSsl
&lt;span class=&quot;token variable&quot;&gt;$cimSession&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;New-CimSession&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ComputerName &lt;span class=&quot;token string&quot;&gt;&#39;DSCSVR01&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Credential &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Credential&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SessionOption &lt;span class=&quot;token variable&quot;&gt;$cimOption&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;Get-DSCConfiguration&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;CimSession &lt;span class=&quot;token variable&quot;&gt;$cimSession&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Remove-CimSession&lt;/span&gt;    &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;CimSession &lt;span class=&quot;token variable&quot;&gt;$cimSession&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That is about all I’ve got for today. Hope this helps someone.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Clear All Windows Event Logs</title>
      <link href="https://danielscottraynsford.com/blog/clear-all-windows-event-logs/" />
      <updated>2015-04-09T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/clear-all-windows-event-logs/</id>
      <content type="html">
				&lt;p&gt;Just a quick one this time.&lt;/p&gt;&lt;p&gt;One thing I often like to do on my lab machines (servers and clients) is clear out all event logs. Not just the older style Windows Logs, but the newer Applications and Services Logs as well: &lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_eventviewer.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/kJTuYpKI8K-650.png 650w, https://danielscottraynsford.com/img/kJTuYpKI8K-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/kJTuYpKI8K-650.webp 650w, https://danielscottraynsford.com/img/kJTuYpKI8K-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/kJTuYpKI8K-650.jpeg&quot; alt=&quot;Event Viewer Logs&quot; width=&quot;960&quot; height=&quot;628&quot; srcset=&quot;https://danielscottraynsford.com/img/kJTuYpKI8K-650.jpeg 650w, https://danielscottraynsford.com/img/kJTuYpKI8K-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The easiest way I’ve found to do this is just run the following PowerShell command in an Administrator PowerShell console:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-WinEvent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ListLog &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ForEach-Object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.Diagnostics.Eventing.Reader.EventLogSession]&lt;/span&gt;::GlobalSession&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ClearLog&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LogName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will dump the content of every Windows Log and Applications and Services log in one go.&lt;/p&gt;&lt;p&gt;Be aware, this is a one-way ticket - you can’t recover the content of these logs after they’ve been deleted!&lt;/p&gt;&lt;p&gt;So if you’re a bit concerned and want to archive the content before it gets deleted use this command instead:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-WinEvent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ListLog &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ForEach-Object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;[System.Diagnostics.Eventing.Reader.EventLogSession]&lt;/span&gt;::GlobalSession&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ClearLog&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LogName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;d:&#92;LogArchive&#92;&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LogName &lt;span class=&quot;token operator&quot;&gt;-replace&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.evtx&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You’ll want to configure the &lt;strong&gt;d:&#92;ArchiveLog&lt;/strong&gt; to set the path you want the old events saved to. All the events will be saved into this folder with one file for each event log:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_events_archived.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/zn_O9fHmnl-650.png 650w, https://danielscottraynsford.com/img/zn_O9fHmnl-827.png 827w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/zn_O9fHmnl-650.webp 650w, https://danielscottraynsford.com/img/zn_O9fHmnl-827.webp 827w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/zn_O9fHmnl-650.jpeg&quot; alt=&quot;Events after they&#39;ve been archived&quot; width=&quot;827&quot; height=&quot;680&quot; srcset=&quot;https://danielscottraynsford.com/img/zn_O9fHmnl-650.jpeg 650w, https://danielscottraynsford.com/img/zn_O9fHmnl-827.jpeg 827w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Simple as that!&lt;/p&gt;&lt;p&gt;&#92;m/&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Multiple VHD/VHDx Optimization using PowerShell Workflows</title>
      <link href="https://danielscottraynsford.com/blog/multiple-vhd/vhdx-optimization-using-powershell-workflows/" />
      <updated>2015-04-07T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/multiple-vhd/vhdx-optimization-using-powershell-workflows/</id>
      <content type="html">
				&lt;p&gt;Like most tech people I have lots of Hyper-V VM’s scattered across various computers at home. Some of these VM’s are running on Server OS hosts (Server 2012 R2) and some running on client OS hosts (Windows 8.1) on my desktop or laptop. These VMs also get varying amount of use - lab and dev machines getting used most of the time while “experimentation” machines getting booted only rarely.&lt;/p&gt;&lt;p&gt;I also like to run my heavily used VMs on fast SSD drives to keep them “snappy”. But like most people I have only a limited amount of SSD space. I’m also quite an obsessive neat freak. These two things combined means I like to keep the VHD/VHDx files used by my VMs as small as possible.&lt;/p&gt;&lt;p&gt;Keeping VHD/VHDx files trim is can easily be performed inside the Hyper-V management tool by clicking the &lt;strong&gt;Edit Disk…&lt;/strong&gt; button and using the &lt;strong&gt;Edit Virtual Hard Disk Wizard&lt;/strong&gt; to &lt;strong&gt;Compact&lt;/strong&gt; the desired VHD/VHDx file:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_hyperv_compactvhd.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/APW7c869jo-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/APW7c869jo-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/APW7c869jo-650.jpeg&quot; alt=&quot;Compact a VHD using the Edit Virtual Hard Disk wizard&quot; width=&quot;650&quot; height=&quot;486&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;This of course is all a bit manual and can be really time-consuming when performing this on lots of virtual hard disks. This sounds like it could be performed by a PowerShell command.&lt;/p&gt;&lt;p&gt;After about 5 minutes of investigation I came up with this simple PowerShell command:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-VM&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; Where &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; $&#92;_&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;State &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Off&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Get-VMHardDiskDrive&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Optimize-VHD&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Mode Full&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It basically performs a full optimization on all VHD/VHDx files attached to all Virtual Machines that are in the &lt;strong&gt;Off&lt;/strong&gt; state on the host the command is run on. This does the job quite well but has a few annoyances:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The optimization is performed in series.&lt;/li&gt;&lt;li&gt;Running guests won’t be optimized.&lt;/li&gt;&lt;li&gt;The command only works on VMs on the host the command is run on.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Looking at the list of annoyances it seems a PowerShell workflow might be a good solution. So, after a few hours coding and testing (I can’t tell the number of times my VMs were rebooted and optimized over the day) I managed to complete a module containing a PS Workflow that was up to the task (documentation contained in the link):&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/Optimize-Hyper-V-VHDs-20e24fb7http://&quot; title=&quot;Optimize Hyper-V VHDs using PowerShell Workflow&quot; rel=&quot;noopener&quot;&gt;Optimize Hyper-V VHDs using PowerShell Workflow&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The module can be installed into a PowerShell modules folder (or imported from any location) and the workflow called like in the same way a normal cmdlet would be called:&lt;/p&gt;&lt;p&gt;Optimize-VHDsWorkflow -ComputerName HV-01,HV-02 -VMName NTB01,NTB02,NTB03 -Mode Quick&lt;/p&gt;&lt;p&gt;The above command would optimize all VHD/VHDx files attached to VMs called NTB01, NTB02 or NTB03 on hosts HV-01 and HV-02. It would perform this optimization in parallel meaning all VHDs would be optimized at the same time. Care obviously needs to be taken here, because optimizing too many VHDs running off the same data store could saturate the IO leading to performance of the data store being crippled.&lt;/p&gt;&lt;p&gt;When this workflow is run, any VMs that are running will not have their VHDs optimized. This is probably a good thing for production guests, but for my test lab guest, I want them to be shutdown automatically, have their VHDs optimized and have them automatically started back up. So I implemented a switch called &lt;strong&gt;AllowRestart&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;Optimize-VHDsWorkflow -AllowRestart -Mode Full -Verbose&lt;/p&gt;&lt;p&gt;The &lt;strong&gt;AllowRestart&lt;/strong&gt; switch will allow a guests that are in a running state to be shut down (not turned off or forced), an optimization performed and then started back up. If a guest is not in a running state it will just be optimized. If the guest can’t be shut down using a normal shut down (because the guest doesn’t have Hyper-V tools running or installed or isn’t running a compatible OS) then it won’t be optimized.&lt;/p&gt;&lt;p&gt;You can also use the &lt;strong&gt;Verbose&lt;/strong&gt; switch to show more information about the workflow process:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_hyperv_optimize-vhdsworkflow_process.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/NC6DP7ZuVi-650.png 650w, https://danielscottraynsford.com/img/NC6DP7ZuVi-877.png 877w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/NC6DP7ZuVi-650.webp 650w, https://danielscottraynsford.com/img/NC6DP7ZuVi-877.webp 877w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/NC6DP7ZuVi-650.jpeg&quot; alt=&quot;The Optimize Verbose Process&quot; width=&quot;877&quot; height=&quot;643&quot; srcset=&quot;https://danielscottraynsford.com/img/NC6DP7ZuVi-650.jpeg 650w, https://danielscottraynsford.com/img/NC6DP7ZuVi-877.jpeg 877w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;There really isn’t much to the process and it could even be scheduled via Task Manager to be performed automatically. If anyone has any comments or feature requests, please let me know - I’m always enjoy a challenge!&lt;/p&gt;&lt;p&gt;&#92;m/ &#92;m/&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Using PowerShell to Install/Uninstall Microsoft Office Products by Group Policy</title>
      <link href="https://danielscottraynsford.com/blog/using-powershell-to-install/uninstall-microsoft-office-products-by-group-policy/" />
      <updated>2015-04-06T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/using-powershell-to-install/uninstall-microsoft-office-products-by-group-policy/</id>
      <content type="html">
				&lt;p&gt;I’ve been recently doing some experimentation with AD RMS templates and AD FS integration in my lab environment. Clearly, I lead a very exciting life. Of course, to test AD RMS templates, one needs a copy of Office installed into the lab environment. This, I thought, would be a good opportunity to configure Office (Office 2013 Pro Plus to be precise) to be installed via Group Policy.&lt;/p&gt;&lt;p&gt;I, of course, read the &lt;a href=&quot;https://technet.microsoft.com/en-us/library/ff602181.aspx&quot; rel=&quot;noopener&quot;&gt;Deploy Office 2013 by using Group Policy computer startup scripts&lt;/a&gt; documentation on TechNet, which directed me to use GPOs that called batch files on computer start-up. This is all simple enough and works well, but being a bit of a PowerShell fiend, I’d prefer to use it whenever I can. Since Windows Server 2008 R2 and above supports PowerShell scripts for GPO startup and shutdown, I thought I’d write some general-purpose PowerShell scripts that could be used in place of the old batch file scripts:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/PowerShell-to-InstallUninst-0536b17b&quot; title=&quot;PowerShell Scripts to Install/Uninstall Office 2013 products using GPO&quot; rel=&quot;noopener&quot;&gt;PowerShell Scripts to Install/Uninstall Office 2013 products using GPO&lt;/a&gt;&lt;/p&gt;&lt;p&gt;As a side note - I wonder why Microsoft doesn’t support installing Office 2013 products using the standard Software Deployment policies in GPO (assigning, publishing, etc.). But that is a question only Microsoft can answer.&lt;/p&gt;&lt;p&gt;These PowerShell scripts accept parameters that allow key elements of the installation process (product, source folder, config file, log file location) to be specified in the GPO Script parameters themselves:&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/ew0ybIEa9W-394.png 394w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ew0ybIEa9W-394.webp 394w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ew0ybIEa9W-394.jpeg&quot; alt=&quot;GPO PowerShell Startup Script&quot; width=&quot;394&quot; height=&quot;202&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;For example, to install a copy of Microsoft Office 2013 Pro Plus from the &lt;code&gt;&#92;&#92;server&#92;software$&#92;Office15.ProPlus&#92;&lt;/code&gt; share using the file &lt;code&gt;&#92;&#92;server&#92;software$&#92;Office15.ProPlus&#92;ProPlus.ww&#92;SilentInstall.xml&lt;/code&gt; to control the configuration, the following parameters could be used:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ProductId &lt;span class=&quot;token string&quot;&gt;&quot;Office15.ProPlus&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SourcePath &lt;span class=&quot;token string&quot;&gt;&quot;&#92;&#92;server&#92;software$&#92;Office15.ProPlus&#92;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ConfigFile &lt;span class=&quot;token string&quot;&gt;&quot;&#92;&#92;server&#92;software$&#92;Office15.ProPlus&#92;ProPlus.ww&#92;SilentInstall.xml&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The full script parameters are documented in the PowerShell scripts as well as on the &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/PowerShell-to-InstallUninst-0536b17b&quot; title=&quot;PowerShell Scripts to Install/Uninstall Office 2013 products using GPO&quot; rel=&quot;noopener&quot;&gt;Microsoft Script Repository page&lt;/a&gt; along with additional examples. The scripts can also accept a parameter for controlling where a brief log file can be written to so that it is easy to see if the product has been successfully installed on each machine that has been assigned the GPO.&lt;/p&gt;&lt;h2 id=&quot;creating-a-gpo-using-the-powershell-scripts&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/using-powershell-to-install/uninstall-microsoft-office-products-by-group-policy/#creating-a-gpo-using-the-powershell-scripts&quot; class=&quot;heading-anchor&quot;&gt;Creating a GPO using the PowerShell Scripts&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;In Group Policy Management Console, create a new GPO:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/wCIyOcqW2z-436.png 436w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/wCIyOcqW2z-436.webp 436w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/wCIyOcqW2z-436.jpeg&quot; alt=&quot;Create a new GPO&quot; width=&quot;436&quot; height=&quot;236&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Enter a name for the installation policy and click &lt;strong&gt;OK&lt;/strong&gt;:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/MyICVaNZ5h-400.png 400w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/MyICVaNZ5h-400.webp 400w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/MyICVaNZ5h-400.jpeg&quot; alt=&quot;Enter GPO name&quot; width=&quot;400&quot; height=&quot;187&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Right-click on the new policy and select &lt;strong&gt;Edit&lt;/strong&gt;:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/EsRYZDiKLn-350.png 350w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/EsRYZDiKLn-350.webp 350w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/EsRYZDiKLn-350.jpeg&quot; alt=&quot;Edit GPO&quot; width=&quot;350&quot; height=&quot;79&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Select the &lt;strong&gt;Computer Configuration&#92;Policies&#92;Windows Settings&#92;Scripts (Startup/Shutdown)&lt;/strong&gt; node:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/KBANKjYDhB-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/KBANKjYDhB-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/KBANKjYDhB-650.jpeg&quot; alt=&quot;Edit Startup Script&quot; width=&quot;650&quot; height=&quot;464&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Double-click the &lt;strong&gt;Startup&lt;/strong&gt; item:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/CCzGQ_NoQE-414.png 414w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/CCzGQ_NoQE-414.webp 414w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/CCzGQ_NoQE-414.jpeg&quot; alt=&quot;Startup Properties&quot; width=&quot;414&quot; height=&quot;462&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Select the &lt;strong&gt;PowerShell Scripts&lt;/strong&gt; tab:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/qZRkbl9ka2-414.png 414w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/qZRkbl9ka2-414.webp 414w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/qZRkbl9ka2-414.jpeg&quot; alt=&quot;PowerShell Scripts Tab&quot; width=&quot;414&quot; height=&quot;462&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Add&lt;/strong&gt; to add a new startup script:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/QJXRtymITb-394.png 394w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/QJXRtymITb-394.webp 394w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/QJXRtymITb-394.jpeg&quot; alt=&quot;Add PowerShell Script&quot; width=&quot;394&quot; height=&quot;202&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;Browse&lt;/strong&gt; button to locate the folder where the policy scripts should be stored:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/ZtAGYa6UZa-650.png 650w, https://danielscottraynsford.com/img/ZtAGYa6UZa-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/ZtAGYa6UZa-650.webp 650w, https://danielscottraynsford.com/img/ZtAGYa6UZa-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/ZtAGYa6UZa-650.jpeg&quot; alt=&quot;Script Location&quot; width=&quot;960&quot; height=&quot;540&quot; srcset=&quot;https://danielscottraynsford.com/img/ZtAGYa6UZa-650.jpeg 650w, https://danielscottraynsford.com/img/ZtAGYa6UZa-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Copy the PowerShell scripts downloaded from the &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/PowerShell-to-InstallUninst-0536b17b&quot; title=&quot;PowerShell Scripts to Install/Uninstall Office 2013 products using GPO&quot; rel=&quot;noopener&quot;&gt;Microsoft Script Repository&lt;/a&gt; into this folder and select the one that should be used with this policy.&lt;/li&gt;&lt;li&gt;Enter the PowerShell script parameters into the &lt;strong&gt;Script Parameters&lt;/strong&gt; box:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/TjeJ2BXLG1-394.png 394w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/TjeJ2BXLG1-394.webp 394w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/TjeJ2BXLG1-394.jpeg&quot; alt=&quot;Script Parameters&quot; width=&quot;394&quot; height=&quot;202&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt; to save the PowerShell startup script:&lt;br&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/AjOyOkoMIL-414.png 414w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/AjOyOkoMIL-414.webp 414w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/AjOyOkoMIL-414.jpeg&quot; alt=&quot;Configured Scripts&quot; width=&quot;414&quot; height=&quot;462&quot;&gt;&lt;/picture&gt;&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt; to save the Startup Properties.&lt;/li&gt;&lt;li&gt;Close the GPME and apply the new GPO.&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;important-note-about-powershell-script-parameters&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/using-powershell-to-install/uninstall-microsoft-office-products-by-group-policy/#important-note-about-powershell-script-parameters&quot; class=&quot;heading-anchor&quot;&gt;Important Note about PowerShell Script Parameters&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;There appears to be a limit to the maximum number of characters supported in the GPO Startup/Shutdown PowerShell script parameters. This limit seems to be 207 characters, but I haven’t been able to confirm this anywhere. Although more than this number of characters can be entered into the Parameter text box, anything above 207 does not get passed through to the script, which either causes the script to run incorrectly or not at all.&lt;/p&gt;&lt;p&gt;If you do encounter this limit but still need additional parameters passed, you could use positional parameters to reduce the overhead or create another script that calls these scripts with the defined parameters.&lt;/p&gt;&lt;p&gt;Hopefully, someone will find this useful. If you have any comments or requests for improvements to the scripts, don’t hesitate to let me know.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Certificate Web Enrollment on a Server and a Misleading Error Message</title>
      <link href="https://danielscottraynsford.com/blog/certificate-web-enrollment-on-a-server-and-a-misleading-error-message/" />
      <updated>2015-03-13T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/certificate-web-enrollment-on-a-server-and-a-misleading-error-message/</id>
      <content type="html">
				&lt;p&gt;The Certificate Web Enrollment component of Certificate Services is fairly helpful for allowing easy certificate request and enrollment from any computer.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_cs_advancedcertificaterequest_ok.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/yqUUnQUgWB-650.png 650w, https://danielscottraynsford.com/img/yqUUnQUgWB-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/yqUUnQUgWB-650.webp 650w, https://danielscottraynsford.com/img/yqUUnQUgWB-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/yqUUnQUgWB-650.jpeg&quot; alt=&quot;Requesting a certificate via the Web Enrollment service web page.&quot; width=&quot;960&quot; height=&quot;540&quot; srcset=&quot;https://danielscottraynsford.com/img/yqUUnQUgWB-650.jpeg 650w, https://danielscottraynsford.com/img/yqUUnQUgWB-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;Requesting a certificate via the Web Enrollment service web page.&lt;/p&gt;&lt;p&gt;It does require Internet Explorer because of an Active X control that runs on the page, but this is acceptable. It also needs to be connected to using HTTPS - which is also fine. Except when it isn’t. Or more accurately, reports that you are not connected via HTTPS when you in fact are.&lt;/p&gt;&lt;p&gt;The following error message appears when connecting to this page from Internet Explorer 11 on a Windows Server 2012 R2 member server or DC:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_cs_advancedcertificateqrequest_sslerror.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/AyOeLeq5IH-650.png 650w, https://danielscottraynsford.com/img/AyOeLeq5IH-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/AyOeLeq5IH-650.webp 650w, https://danielscottraynsford.com/img/AyOeLeq5IH-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/AyOeLeq5IH-650.jpeg&quot; alt=&quot;The page requires an HTTPS connection - but it is connected via HTTPS!&quot; width=&quot;960&quot; height=&quot;568&quot; srcset=&quot;https://danielscottraynsford.com/img/AyOeLeq5IH-650.jpeg 650w, https://danielscottraynsford.com/img/AyOeLeq5IH-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;The page requires an HTTPS connection - but it is connected via HTTPS!&lt;/p&gt;&lt;p&gt;“In order to complete certificate enrollment, the Web site for the CA must be configured to use HTTPS authentication.”&lt;/p&gt;&lt;p&gt;Clearly, Internet Explorer was using HTTPS to communicate with this web site. Clicking OK on the error message and hoping to just ignore it wasn’t possible because most of the drop down boxes on the form were not being populated - preventing it from being submitted:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_cs_advancedcertificateqrequest_aftererror.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/5LWbXUJnXY-650.png 650w, https://danielscottraynsford.com/img/5LWbXUJnXY-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/5LWbXUJnXY-650.webp 650w, https://danielscottraynsford.com/img/5LWbXUJnXY-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/5LWbXUJnXY-650.jpeg&quot; alt=&quot;After clicking OK on the error message the page is broken.&quot; width=&quot;960&quot; height=&quot;540&quot; srcset=&quot;https://danielscottraynsford.com/img/5LWbXUJnXY-650.jpeg 650w, https://danielscottraynsford.com/img/5LWbXUJnXY-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;After clicking OK on the error message the page is broken.&lt;/p&gt;&lt;p&gt;I spent quite some time investigating the cause of this error, including checking the certificate chain that the client was using:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_cs_advancedcertificateqrequest_certificatechain.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/eH8pKxsBr4-346.png 346w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/eH8pKxsBr4-346.webp 346w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/eH8pKxsBr4-346.jpeg&quot; alt=&quot;The certificate and certificate chain - nothing wrong here.&quot; width=&quot;346&quot; height=&quot;281&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;The certificate and certificate chain - nothing wrong here.&lt;/p&gt;&lt;p&gt;Of course if the certificate or the chain was bad then SSL wouldn’t be being used - which it clearly was.&lt;/p&gt;&lt;p&gt;Eventually I identified the cause of the problem. The Active X was being prevented from being run properly because of IE security. This is probably a good thing on a Server operating system, but the error message being presented in this case was very misleading.&lt;/p&gt;&lt;p&gt;The way to fix this is to add the web site to the trusted sites list in Internet Explorer:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Select &lt;strong&gt;Internet Options&lt;/strong&gt; from the Internet Explorer Settings menu:&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_cs_advancedcertificateqrequest_internetexplorersettings.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/aPf1BShyhc-285.png 285w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/aPf1BShyhc-285.webp 285w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/aPf1BShyhc-285.jpeg&quot; alt=&quot;ss_cs_advancedcertificateqrequest_internetexplorersettings&quot; width=&quot;285&quot; height=&quot;355&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Select the &lt;strong&gt;Security&lt;/strong&gt; tab and click &lt;strong&gt;Trusted Sites&lt;/strong&gt;:&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_cs_advancedcertificateqrequest_ietrustedsites.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/j96EaMkJeX-423.png 423w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/j96EaMkJeX-423.webp 423w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/j96EaMkJeX-423.jpeg&quot; alt=&quot;ss_cs_advancedcertificateqrequest_ietrustedsites&quot; width=&quot;423&quot; height=&quot;542&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Click the &lt;strong&gt;Sites&lt;/strong&gt; button.&lt;/li&gt;&lt;li&gt;Enter the &lt;strong&gt;https URL&lt;/strong&gt; of the Certificate Web Enrollment site and click &lt;strong&gt;Add&lt;/strong&gt;:&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_cs_advancedcertificateqrequest_trustthissite.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/iktumNEB54-394.png 394w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/iktumNEB54-394.webp 394w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/iktumNEB54-394.jpeg&quot; alt=&quot;ss_cs_advancedcertificateqrequest_trustthissite&quot; width=&quot;394&quot; height=&quot;349&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Close&lt;/strong&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The web site can now be refreshed and should work correctly at last:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_cs_advancedcertificateqrequest_workingcorrectly.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/gpgDHFY0vg-650.png 650w, https://danielscottraynsford.com/img/gpgDHFY0vg-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/gpgDHFY0vg-650.webp 650w, https://danielscottraynsford.com/img/gpgDHFY0vg-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/gpgDHFY0vg-650.jpeg&quot; alt=&quot;Certificate Web Enrollment form working correctly with SSL. &quot; width=&quot;960&quot; height=&quot;540&quot; srcset=&quot;https://danielscottraynsford.com/img/gpgDHFY0vg-650.jpeg 650w, https://danielscottraynsford.com/img/gpgDHFY0vg-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;Certificate Web Enrollment form working correctly with SSL.&lt;/p&gt;&lt;p&gt;Hopefully this helps someone out there avoid this annoying problem.&lt;/p&gt;&lt;p&gt;B&#92;M/D&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Disk Cleanup and the joys of Features-on-demand</title>
      <link href="https://danielscottraynsford.com/blog/disk-cleanup-and-the-joys-of-features-on-demand/" />
      <updated>2015-01-28T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/disk-cleanup-and-the-joys-of-features-on-demand/</id>
      <content type="html">
				&lt;p&gt;&lt;em&gt;Features-on-demand&lt;/em&gt; - it’s a great new “feature” - when it works. However, the rest of the time it is a real headache.&lt;/p&gt;&lt;p&gt;A couple of months ago I decided I wanted to trim down the size of my Windows Server 2012 R2 VM’s. &lt;em&gt;Disk Cleanup&lt;/em&gt; (cleanmgr.exe) is one tool that I’ve often found really useful to have on a server install, especially when preparing OS VM images to ensure the install is as lean and clean as possible.&lt;/p&gt;&lt;p&gt;However, by default the tool isn’t installed on Windows Server 2012. To get access to &lt;em&gt;Disk Cleanup&lt;/em&gt; on a server OS you need to install the &lt;em&gt;Desktop Experience&lt;/em&gt; feature:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_installfeature_desktopexperience.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/Gob4MQAvYp-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Gob4MQAvYp-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Gob4MQAvYp-650.jpeg&quot; alt=&quot;Install Desktop Experience using Add Roles and Features Wizard&quot; width=&quot;650&quot; height=&quot;460&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;Install Desktop Experience using Add Roles and Features Wizard&lt;/p&gt;&lt;p&gt;Because I had used &lt;em&gt;features-on-demand&lt;/em&gt; to remove any disabled packages from the system I received a message telling me I might need to specify an alternate location for the source files:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_installfeature_specifyanalternatesourcepath.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/YOoUI6DDQQ-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/YOoUI6DDQQ-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/YOoUI6DDQQ-650.jpeg&quot; alt=&quot;Specify an Alternate Source path&quot; width=&quot;650&quot; height=&quot;460&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;Specify an Alternate Source path&lt;/p&gt;&lt;p&gt;Anyone who has used &lt;em&gt;features-on-demand&lt;/em&gt; should be familiar with this:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_windowsfeature_desktopexperienceremoved.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/EYiS8vRTho-650.png 650w, https://danielscottraynsford.com/img/EYiS8vRTho-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/EYiS8vRTho-650.webp 650w, https://danielscottraynsford.com/img/EYiS8vRTho-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/EYiS8vRTho-650.jpeg&quot; alt=&quot;Desktop Experience Feature-on-demand removed&quot; width=&quot;960&quot; height=&quot;145&quot; srcset=&quot;https://danielscottraynsford.com/img/EYiS8vRTho-650.jpeg 650w, https://danielscottraynsford.com/img/EYiS8vRTho-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;Desktop Experience Feature-on-demand removed&lt;/p&gt;&lt;p&gt;Because I haven’t got a GPO restricting my servers from downloading updates and packages from &lt;em&gt;Windows Update&lt;/em&gt; I thought I wouldn’t have a problem and didn’t need to specify a source.&lt;/p&gt;&lt;p&gt;I was wrong:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_installfeature_failed.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/7wbxiQS5O--650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/7wbxiQS5O--650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/7wbxiQS5O--650.jpeg&quot; alt=&quot;Failed to install the Desktop Exprience&quot; width=&quot;650&quot; height=&quot;460&quot;&gt;&lt;/picture&gt;&lt;/a&gt;Failed to install the Desktop Experience&lt;/p&gt;&lt;p&gt;That is a bit odd - the server has access to &lt;em&gt;Windows Update&lt;/em&gt; - it had downloaded updates earlier that day. Other &lt;strong&gt;removed&lt;/strong&gt; &lt;em&gt;features-on-demand&lt;/em&gt; features had been installed on this server without an issue, downloading the source files directly from &lt;em&gt;Windows Update&lt;/em&gt;. So I was a little puzzled as to why this was different.&lt;/p&gt;&lt;p&gt;No problem, I thought! All that needs to be done is specify a &lt;em&gt;source&lt;/em&gt;. In case this is useful, the following TechNet article covers the different ways of specifying a &lt;em&gt;source&lt;/em&gt; when installing features that have been removed:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://technet.microsoft.com/en-us/library/jj127275.aspx&quot; title=&quot;Configure Features on Demand in Windows Server&quot; rel=&quot;noopener&quot;&gt;Configure Features on Demand in Windows Server&lt;/a&gt;&lt;/p&gt;&lt;p&gt;There are several different sources that can be provided to the &lt;em&gt;Add Roles and Features Wizard&lt;/em&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Specify a WIM file (and index) containing the windows installation files for the OS version that was installed on the server. This is usually a file called &lt;em&gt;install.wim&lt;/em&gt; that can be found in the &lt;em&gt;Sources&lt;/em&gt; folder of the Windows Server2012R2 installation media.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_installfeature_alternatesourcewim.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/RXzJO-cUBE-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/RXzJO-cUBE-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/RXzJO-cUBE-650.jpeg&quot; alt=&quot;Install Feature using a WIM source&quot; width=&quot;650&quot; height=&quot;516&quot;&gt;&lt;/picture&gt;&lt;/a&gt;Install Feature using a WIM source&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Specify the windows folder of a working OS install containing the files for this feature. This is usually done by mapping a drive to a share or by mountingaVHD/VHDx file as a drive to the OS.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_installfeature_alternatesourceshare.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/mA_socSKmN-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/mA_socSKmN-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/mA_socSKmN-650.jpeg&quot; alt=&quot;Install Feature using a shared Windows Folder on a machine with the Desktop Experience feature installed&quot; width=&quot;650&quot; height=&quot;516&quot;&gt;&lt;/picture&gt;&lt;/a&gt;Install Feature using a shared Windows Folder on a machine with the Desktop Experience feature installed&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I tried both of the above methods but neither of them seemed to work for the &lt;em&gt;Desktop Experience&lt;/em&gt; feature. The same error occurred every time:&lt;/p&gt;&lt;p&gt;Install-Windowsfeature: The request to add or remove features on the specified server failed.&lt;br&gt;Installation of one or more roles, role services, or features failed.&lt;br&gt;The source files could not be downloaded.&lt;br&gt;Use the “source” option to specify the location of the files that are required to restore the feature. For more&lt;br&gt;information on specifying a source location, see &lt;a href=&quot;http://go.microsoft.com/fwlink/?LinkId=243077&quot; rel=&quot;noopener&quot;&gt;http://go.microsoft.com/fwlink/?LinkId=243077&lt;/a&gt;. Error: 0x800f0906&lt;/p&gt;&lt;p&gt;I also tried installing the feature using &lt;em&gt;PowerShell,&lt;/em&gt; using no alternative source, using a WIM source and using a Windows folder source:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Install-WindowsFeature&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;name Desktop-Experience &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;IncludeAllSubfeature &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Restart &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Source z:&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But each time I received the same error message:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_installfeature_powershellfailed.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/LxIch9eHGe-650.png 650w, https://danielscottraynsford.com/img/LxIch9eHGe-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/LxIch9eHGe-650.webp 650w, https://danielscottraynsford.com/img/LxIch9eHGe-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/LxIch9eHGe-650.jpeg&quot; alt=&quot;Install Feature with PowerShell and specifing a valid source - failure.&quot; width=&quot;960&quot; height=&quot;372&quot; srcset=&quot;https://danielscottraynsford.com/img/LxIch9eHGe-650.jpeg 650w, https://danielscottraynsford.com/img/LxIch9eHGe-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;Install Feature with PowerShell and specifying a valid source - failure.&lt;/p&gt;&lt;p&gt;At this point I had all but given up. Luckily I didn’t. I thought I’d give it one last try - but this time instead of using commands from the &lt;em&gt;PowerShell DISM module&lt;/em&gt; I’d use &lt;em&gt;DISM.EXE&lt;/em&gt; directly:&lt;/p&gt;&lt;pre class=&quot;language-plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;DISM /online /enable-feature /featurename:DesktopExperience /all /source:z:&#92;&#92;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Success! DISM worked!&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_installfeature_dismsuccess.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/6CqQX50jY2-650.png 650w, https://danielscottraynsford.com/img/6CqQX50jY2-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/6CqQX50jY2-650.webp 650w, https://danielscottraynsford.com/img/6CqQX50jY2-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/6CqQX50jY2-650.jpeg&quot; alt=&quot;DISM for the WIN! Back of the net!&quot; width=&quot;960&quot; height=&quot;202&quot; srcset=&quot;https://danielscottraynsford.com/img/6CqQX50jY2-650.jpeg 650w, https://danielscottraynsford.com/img/6CqQX50jY2-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;DISM for the WIN! Back of the net!&lt;/p&gt;&lt;p&gt;This screenshot and the one above of the &lt;em&gt;PowerShell&lt;/em&gt; &lt;em&gt;install-windowsfeature&lt;/em&gt; failing to install the feature are from the same machine with the same source mapped.&lt;/p&gt;&lt;p&gt;Z: drive here was mapped to a share of the &lt;em&gt;c:&#92;windows&lt;/em&gt; folder of a server that has the &lt;em&gt;Desktop Experience&lt;/em&gt; feature correctly installed.&lt;/p&gt;&lt;p&gt;It looks like &lt;em&gt;DISM&lt;/em&gt; may operate in a slightly different way to &lt;em&gt;PowerShell DISM Module&lt;/em&gt; and the &lt;em&gt;Add Roles and Features Wizard&lt;/em&gt; when it comes to installing features.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;So, if &lt;em&gt;Add Roles and Features Wizard&lt;/em&gt; and &lt;em&gt;PowerShell Install-WindowsFeature&lt;/em&gt; fail, try DISM - it might work!&lt;/strong&gt;&lt;/p&gt;&lt;h3 id=&quot;additional-notes&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/disk-cleanup-and-the-joys-of-features-on-demand/#additional-notes&quot; class=&quot;heading-anchor&quot;&gt;Additional Notes&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I have also run into the same problem when installing the &lt;em&gt;AD DS&lt;/em&gt; feature on a different server - so I don’t think this is specific to the machine or the feature. It has also occurred on machines that I want to convert from a &lt;em&gt;core&lt;/em&gt; install to a &lt;em&gt;gui&lt;/em&gt; install.&lt;/p&gt;&lt;p&gt;I have tried installing the feature on a clean install of the OS and it works fine - but as soon as all the latest windows hotfixes for the OS are installed from &lt;em&gt;Windows Update&lt;/em&gt; the feature can no longer be installed (if it has been removed).&lt;/p&gt;&lt;p&gt;From my investigation, many other people have experienced this problem with varying degrees of success in solving it. Some have said that patching the WIM file with all the latest hotfixes worked for them - but it didn’t for me (but it did inspire me to write a &lt;em&gt;PowerShell&lt;/em&gt; module to ease the WIM patch process - more on this in another post). I was certainly in the “tearing my hair out” group until I randomly tried this.&lt;/p&gt;&lt;p&gt;Also, I did try &lt;em&gt;DISM&lt;/em&gt; without specifying a source as well, and it failed with the same error code as the &lt;em&gt;PowerShell&lt;/em&gt; did:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://danielscottraynsford.com/assets/images/screenshots/ss_installfeature_dismfailure2.png&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/Fam_srjwEU-650.png 650w, https://danielscottraynsford.com/img/Fam_srjwEU-960.png 960w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Fam_srjwEU-650.webp 650w, https://danielscottraynsford.com/img/Fam_srjwEU-960.webp 960w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Fam_srjwEU-650.jpeg&quot; alt=&quot;Install Feature using DISM fails with no Source specified.&quot; width=&quot;960&quot; height=&quot;293&quot; srcset=&quot;https://danielscottraynsford.com/img/Fam_srjwEU-650.jpeg 650w, https://danielscottraynsford.com/img/Fam_srjwEU-960.jpeg 960w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;br&gt;Install Feature using DISM fails with no Source specified.&lt;/p&gt;&lt;p&gt;At first I actually thought the soution was using &lt;em&gt;DISM&lt;/em&gt; with the &lt;em&gt;/LimitAccess&lt;/em&gt; switch to prevent &lt;em&gt;DISM&lt;/em&gt; from using the internet to download the packages, but after further tests it doesn’t seem to make any difference - &lt;em&gt;DISM&lt;/em&gt; works with and without this switch. The equivelent to the &lt;em&gt;/LimitAccess&lt;/em&gt; switch also doesn’t appear to be available in the &lt;em&gt;PowerShell Install-WindowsFeature&lt;/em&gt; cmdlet.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Tiered Storage Spaces Experimentation with PowerShell</title>
      <link href="https://danielscottraynsford.com/blog/tiered-storage-spaces-experimentation-with-powershell/" />
      <updated>2015-01-11T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/tiered-storage-spaces-experimentation-with-powershell/</id>
      <content type="html">
				&lt;p&gt;I’ve been recently experimenting with the interesting Tiered Storage Spaces features in Windows Server 2012 R2. As part of this, I’ve been going through some of the excellent MSFT File Server team blogs on TechNet.&lt;/p&gt;&lt;p&gt;This &lt;a href=&quot;http://blogs.technet.com/b/josebda/archive/2013/08/28/step-by-step-for-storage-spaces-tiering-in-windows-server-2012-r2.aspx&quot; rel=&quot;noopener&quot;&gt;one&lt;/a&gt; in particular by Jose Barreto was supremely useful, and I highly recommend it. It contains some excellent PowerShell code for setting up a VM for experimenting with Tiered Storage Spaces as well as configuring them within the VM.&lt;/p&gt;&lt;p&gt;I did find the PowerShell code fragmented throughout the long article, which made it a little bit hard to use (lots of ALT+TAB, CTRL+C, and CTRL+V). So I’ve assembled the code into two code snippets that you should be able to paste into a PowerShell ISE script window and then step through—it should save a lot of repetitive keypressing.&lt;/p&gt;&lt;p&gt;I also modified it slightly to have some of the configuration items, such as where to put the VM HDD and SSD VHDX files, in variables at the beginning of the script to make it easier to manage.&lt;/p&gt;&lt;h3 id=&quot;script-to-run-on-the-host-os&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/tiered-storage-spaces-experimentation-with-powershell/#script-to-run-on-the-host-os&quot; class=&quot;heading-anchor&quot;&gt;Script to Run on the Host OS&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This script will install Hyper-V, create the VM, create the test VHDXs, and start it up:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# --------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Execute on HOST OS&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# --------------------------------------------------------------------------&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Configure these paths and names&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$SSD_VHD_Path&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&quot;F:&#92;VM&#92;VHD&quot;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# Path to store the VM SSDs&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$HDD_VHD_Path&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&quot;E:&#92;VM&#92;VHD&quot;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# Path to store the VM HDDs&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$VMName&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&#39;Windows Server 2012&#39;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# Name of the VM to create/use&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$VM_OS_Path&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&quot;F:&#92;VM&#92;OS&#92;Windows Server 2012.VHDX&quot;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# Path to the VM OS disk (if creating a VM)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Install required roles and features, restart at the end&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# If Hyper-V is already installed, comment out this line:&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Install-WindowsFeature&lt;/span&gt; Hyper-V &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;IncludeManagementTools &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Restart

&lt;span class=&quot;token comment&quot;&gt;# Create 4 VHDX files on the SSD with 10GB each&lt;/span&gt;
1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;4 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ForEach-Object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;New-VHD&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$SSD_VHD_Path&lt;/span&gt;&#92;VMA_SSD_&lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;.VHDX&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Dynamic &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SizeBytes 10GB &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Create 8 VHDX files on the HDD with 30GB each&lt;/span&gt;
1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;8 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ForEach-Object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;New-VHD&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$HDD_VHD_Path&lt;/span&gt;&#92;VMA_HDD_&lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;.VHDX&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Dynamic &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;SizeBytes 30GB &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Create a new VM. Assumes you have a Windows Server 2012 R2 OS VHDX in place&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# If you already have a VM, comment out this line:&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;New-VM&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token variable&quot;&gt;$VMName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;D:&#92;VMS&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VHDPath &lt;span class=&quot;token variable&quot;&gt;$VM_OS_Path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MemoryStartupBytes 2GB

&lt;span class=&quot;token comment&quot;&gt;# Add all data disks to the VM&lt;/span&gt;
1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;4 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ForEach-Object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Add-VMHardDiskDrive&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VMName &lt;span class=&quot;token variable&quot;&gt;$VMName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ControllerType SCSI &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$SSD_VHD_Path&lt;/span&gt;&#92;VMA_SSD_&lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;.VHDX&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;8 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ForEach-Object&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Add-VMHardDiskDrive&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;VMName &lt;span class=&quot;token variable&quot;&gt;$VMName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ControllerType SCSI &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$HDD_VHD_Path&lt;/span&gt;&#92;VMA_HDD_&lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;.VHDX&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Start the VM&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Start-VM&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$VMName&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Get-VM&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$VMName&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Get-VMHardDiskDrive&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;script-to-run-on-the-guest-os&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/tiered-storage-spaces-experimentation-with-powershell/#script-to-run-on-the-guest-os&quot; class=&quot;heading-anchor&quot;&gt;Script to Run on the Guest OS&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Once you’ve got your VM up and running, you can execute the following script on it to experiment with the actual Tiered Storage Spaces:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# --------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Execute on GUEST OS&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# --------------------------------------------------------------------------&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Display physical disks&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Get-PhysicalDisk&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Sort-Object&lt;/span&gt; Size &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Format-Table&lt;/span&gt; DeviceId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; FriendlyName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; CanPool&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Size&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; MediaType &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;AutoSize

&lt;span class=&quot;token comment&quot;&gt;# Create a storage pool&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$s&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-StorageSubSystem&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;New-StoragePool&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;StorageSubSystemId &lt;span class=&quot;token variable&quot;&gt;$s&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UniqueId &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FriendlyName Pool1 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PhysicalDisks &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-PhysicalDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;CanPool &lt;span class=&quot;token boolean&quot;&gt;$true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Configure media type for virtual SAS disks&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Get-StoragePool&lt;/span&gt; Pool1 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Get-PhysicalDisk&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Where-Object&lt;/span&gt; Size &lt;span class=&quot;token operator&quot;&gt;-lt&lt;/span&gt; 20GB &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Set-PhysicalDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MediaType SSD
&lt;span class=&quot;token function&quot;&gt;Get-StoragePool&lt;/span&gt; Pool1 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Get-PhysicalDisk&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Where-Object&lt;/span&gt; Size &lt;span class=&quot;token operator&quot;&gt;-gt&lt;/span&gt; 20GB &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Set-PhysicalDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MediaType HDD

&lt;span class=&quot;token comment&quot;&gt;# Create storage tiers&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Get-StoragePool&lt;/span&gt; Pool1 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;New-StorageTier&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FriendlyName SSDTier &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MediaType SSD
&lt;span class=&quot;token function&quot;&gt;Get-StoragePool&lt;/span&gt; Pool1 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;New-StorageTier&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FriendlyName HDDTier &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;MediaType HDD

&lt;span class=&quot;token comment&quot;&gt;# Create virtual disks with tiering&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$SSD&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-StorageTier&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FriendlyName SSDTier
&lt;span class=&quot;token variable&quot;&gt;$HDD&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Get-StorageTier&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FriendlyName HDDTier
&lt;span class=&quot;token function&quot;&gt;Get-StoragePool&lt;/span&gt; Pool1 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;New-VirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FriendlyName Space1 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResiliencySettingName Simple &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;StorageTiers &lt;span class=&quot;token variable&quot;&gt;$SSD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$HDD&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;StorageTierSizes 8GB&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 32GB
&lt;span class=&quot;token function&quot;&gt;Get-StoragePool&lt;/span&gt; Pool1 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;New-VirtualDisk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FriendlyName Space2 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ResiliencySettingName Mirror &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;StorageTiers &lt;span class=&quot;token variable&quot;&gt;$SSD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$HDD&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;StorageTierSizes 8GB&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 32GB

&lt;span class=&quot;token comment&quot;&gt;# Initialize and format the virtual disks&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Get-VirtualDisk&lt;/span&gt; Space1 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Get-Disk&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Initialize-Disk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PartitionStyle GPT
&lt;span class=&quot;token function&quot;&gt;Get-VirtualDisk&lt;/span&gt; Space1 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Get-Disk&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;New-Partition&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DriveLetter F &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;UseMaximumSize
&lt;span class=&quot;token function&quot;&gt;Initialize-Volume&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DriveLetter F &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FileSystem NTFS &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Confirm:&lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;Get-VirtualDisk&lt;/span&gt; Space2 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Get-Disk&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Initialize-Disk&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;PartitionStyle GPT
&lt;span class=&quot;token function&quot;&gt;Get-VirtualDisk&lt;/span&gt; Space2 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Get-Disk&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;New-Partition&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DriveLetter G &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;UseMaximumSize
&lt;span class=&quot;token function&quot;&gt;Initialize-Volume&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;DriveLetter G &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FileSystem NTFS &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Confirm:&lt;span class=&quot;token boolean&quot;&gt;$false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Hopefully, this saves someone out there a little time copying and pasting!&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Cleaning Up Windows SxS Folder in Preparation for Imaging</title>
      <link href="https://danielscottraynsford.com/blog/cleaning-up-windows-sxs-folder-in-preparation-for-imaging/" />
      <updated>2014-12-08T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/cleaning-up-windows-sxs-folder-in-preparation-for-imaging/</id>
      <content type="html">
				&lt;p&gt;In preparation for releasing an operating system image to be used as a VM template, I usually like to perform some “shrinking commands” to make sure the image has as small a footprint as possible.&lt;/p&gt;&lt;p&gt;All the “shrinking commands” are command-line or PowerShell-based because they must also be able to be performed on a Windows Server Core installation.&lt;/p&gt;&lt;h2 id=&quot;remove-all-uninstalled-feature-binaries&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/cleaning-up-windows-sxs-folder-in-preparation-for-imaging/#remove-all-uninstalled-feature-binaries&quot; class=&quot;heading-anchor&quot;&gt;Remove all Uninstalled Feature Binaries&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The first thing I usually do is remove any uninstalled feature binaries (as part of Windows Features on Demand). I’ve covered this in an earlier article &lt;a href=&quot;https://danielscottraynsford.com/blog/remove-all-uninstalled-feature-binaries/&quot; title=&quot;Remove all Uninstalled Feature Binaries&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;remove-old-service-pack-backup-files&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/cleaning-up-windows-sxs-folder-in-preparation-for-imaging/#remove-old-service-pack-backup-files&quot; class=&quot;heading-anchor&quot;&gt;Remove Old Service Pack Backup Files&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The following command removes any files that were backed up as part of installing a service pack. If you execute this command, you won’t be able to uninstall any service packs.&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;DISM &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;online &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;cleanup-image &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;SPSuperseded&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/Hhpmz37Q8d-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Hhpmz37Q8d-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Hhpmz37Q8d-650.jpeg&quot; alt=&quot;DISM /online /cleanup-image /SPSuperseded&quot; width=&quot;650&quot; height=&quot;329&quot;&gt;&lt;/picture&gt;&lt;br&gt;DISM /online /cleanup-image /SPSuperseded&lt;/p&gt;&lt;h2 id=&quot;remove-superseded-components&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/cleaning-up-windows-sxs-folder-in-preparation-for-imaging/#remove-superseded-components&quot; class=&quot;heading-anchor&quot;&gt;Remove Superseded Components&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This command removes any components in the Windows SxS folder that have been superseded by newer versions.&lt;/p&gt;&lt;p&gt;For &lt;strong&gt;Windows Server 2008/2008 R2/2012&lt;/strong&gt; and &lt;strong&gt;Windows 7/8&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;DISM &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;online &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;cleanup-image &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;StartComponentCleanup&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For &lt;strong&gt;Windows Server 2012 R2&lt;/strong&gt; and &lt;strong&gt;Windows 8.1&lt;/strong&gt; (performs some additional optimization):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;DISM &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;online &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;cleanup-image &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;StartComponentCleanup &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ResetBase&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/TR34YaiaaA-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/TR34YaiaaA-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/TR34YaiaaA-650.jpeg&quot; alt=&quot;DISM /online /cleanup-image /StartComponentCleanup /ResetBase&quot; width=&quot;650&quot; height=&quot;329&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;DISM &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;online &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;cleanup-image &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;StartComponentCleanup &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ResetBase&lt;/code&gt;&lt;/pre&gt;
 			</content>
    </entry><entry>
      <title>Remove all Uninstalled Feature Binaries</title>
      <link href="https://danielscottraynsford.com/blog/remove-all-uninstalled-feature-binaries/" />
      <updated>2014-12-05T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/remove-all-uninstalled-feature-binaries/</id>
      <content type="html">
				&lt;p&gt;I’m still getting my head around this whole blog thing, but I’m going to write a quick article about trimming down a Windows Server 2012 install while I wait for my partner to recover from a small bout of vertigo.&lt;/p&gt;&lt;p&gt;The “Features on Demand” feature of Windows Server 2012 is really great at trimming down a Windows Server 2012 installation.&lt;/p&gt;&lt;p&gt;To use the “Features on Demand,” you need to “remove” the binaries required by any features that aren’t currently installed by your OS. You can remove them one by one, but this is a real drag.&lt;/p&gt;&lt;p&gt;The following command will remove the binaries for any non-installed features on your system:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# filepath: d:&#92;source&#92;GitHub&#92;PlagueHO&#92;plagueho.github.io&#92;src&#92;posts&#92;2014&#92;12&#92;2014-12-05-remove-all-uninstalled-feature-binaries.md&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Get-WindowsFeature&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Where-Object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;FilterScript &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;InstallState &lt;span class=&quot;token operator&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Available&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Remove-WindowsFeature&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Remove&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/J2Dzbi26DO-650.png 650w, https://danielscottraynsford.com/img/J2Dzbi26DO-877.png 877w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/J2Dzbi26DO-650.webp 650w, https://danielscottraynsford.com/img/J2Dzbi26DO-877.webp 877w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/J2Dzbi26DO-650.jpeg&quot; alt=&quot;Features on Demand Removal Progress&quot; width=&quot;877&quot; height=&quot;140&quot; srcset=&quot;https://danielscottraynsford.com/img/J2Dzbi26DO-650.jpeg 650w, https://danielscottraynsford.com/img/J2Dzbi26DO-877.jpeg 877w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;br&gt;Features on Demand Removal Progress&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/h_2l5Y1rx9-650.png 650w, https://danielscottraynsford.com/img/h_2l5Y1rx9-877.png 877w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/h_2l5Y1rx9-650.webp 650w, https://danielscottraynsford.com/img/h_2l5Y1rx9-877.webp 877w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/h_2l5Y1rx9-650.jpeg&quot; alt=&quot;Features on Demand Removal Complete&quot; width=&quot;877&quot; height=&quot;199&quot; srcset=&quot;https://danielscottraynsford.com/img/h_2l5Y1rx9-650.jpeg 650w, https://danielscottraynsford.com/img/h_2l5Y1rx9-877.jpeg 877w&quot; sizes=&quot;auto&quot;&gt;&lt;/picture&gt;&lt;br&gt;Features on Demand Removal Complete&lt;/p&gt;&lt;p&gt;After you’ve run this command, you’ll need to have your Windows install source available if you want to install any additional features. You might also need to specify an alternate source location when installing any future features. For more information on installing Windows features from an alternate source, see this article on TechNet:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://technet.microsoft.com/en-us/library/hh831809.aspx&quot; title=&quot;Install or Uninstall Roles, Role Services, or Features&quot; rel=&quot;noopener&quot;&gt;Install or Uninstall Roles, Role Services, or Features&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I usually like to run this command when I’m preparing a new VM template after I’ve installed all the features I think will be needed for this VM. This results in a smaller deployment footprint.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>Installing Windows Server 2012 R2 Core - Additional Tools and Scripts</title>
      <link href="https://danielscottraynsford.com/blog/installing-windows-server-2012-r2-core-additional-tools-and-scripts/" />
      <updated>2014-12-03T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/installing-windows-server-2012-r2-core-additional-tools-and-scripts/</id>
      <content type="html">
				&lt;p&gt;Although Windows Server Core is a great way of ending up with a slim and trim diet server, it can be a little bit tricky when first getting started configuring it. During my experiments running Server Core VMs, I found that there were a few tools either built into Server Core or available separately that can help get over this configuration “hump.”&lt;/p&gt;&lt;h2 id=&quot;built-in-tools&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-windows-server-2012-r2-core-additional-tools-and-scripts/#built-in-tools&quot; class=&quot;heading-anchor&quot;&gt;Built-in Tools&lt;/a&gt;&lt;/h2&gt;&lt;h3 id=&quot;sconfigexe&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-windows-server-2012-r2-core-additional-tools-and-scripts/#sconfigexe&quot; class=&quot;heading-anchor&quot;&gt;SConfig.exe&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;SConfig.exe&lt;/code&gt; is a built-in command-line tool (with a simple command-line GUI) that allows you to perform some simple configuration tasks on your core installation.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/Z1mzM4I8Jt-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/Z1mzM4I8Jt-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/Z1mzM4I8Jt-650.jpeg&quot; alt=&quot;SConfig.exe&quot; width=&quot;650&quot; height=&quot;329&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Some of the functions include:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Renaming the computer&lt;/li&gt;&lt;li&gt;Joining a domain or workgroup&lt;/li&gt;&lt;li&gt;Enabling remote management (Win-RM)&lt;/li&gt;&lt;li&gt;Enabling remote desktop&lt;/li&gt;&lt;li&gt;Installing Windows updates&lt;/li&gt;&lt;li&gt;Configuring network settings&lt;/li&gt;&lt;li&gt;Changing system date/time&lt;/li&gt;&lt;li&gt;Shutting down/rebooting the server&lt;/li&gt;&lt;/ol&gt;&lt;h2 id=&quot;other-tools&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-windows-server-2012-r2-core-additional-tools-and-scripts/#other-tools&quot; class=&quot;heading-anchor&quot;&gt;Other Tools&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;When installing a new copy of one of the Windows Server Core versions, it’s quite useful to install several additional tools that will help manage the server from a &lt;em&gt;command prompt&lt;/em&gt; and/or &lt;em&gt;PowerShell&lt;/em&gt; console.&lt;/p&gt;&lt;h3 id=&quot;corefig-for-windows-server-2012-core-and-hyper-v-server-2012&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-windows-server-2012-r2-core-additional-tools-and-scripts/#corefig-for-windows-server-2012-core-and-hyper-v-server-2012&quot; class=&quot;heading-anchor&quot;&gt;Corefig for Windows Server 2012 Core and Hyper-V Server 2012&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;https://corefig.codeplex.com/&quot; title=&quot;Corefig for Windows Server 2012 Core and Hyper-V Server 2012&quot; rel=&quot;noopener&quot;&gt;Corefig&lt;/a&gt; allows you to configure some of the main settings of a Windows Server Core installation as well as installing updates and Windows features and roles.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://danielscottraynsford.com/img/A-oNoQgzc3-650.png 650w&quot; sizes=&quot;auto&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://danielscottraynsford.com/img/A-oNoQgzc3-650.webp 650w&quot; sizes=&quot;auto&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://danielscottraynsford.com/img/A-oNoQgzc3-650.jpeg&quot; alt=&quot;Corefig PowerShell Application&quot; width=&quot;650&quot; height=&quot;512&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h3 id=&quot;windows-update-powershell-module&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-windows-server-2012-r2-core-additional-tools-and-scripts/#windows-update-powershell-module&quot; class=&quot;heading-anchor&quot;&gt;Windows Update PowerShell Module&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/2d191bcd-3308-4edd-9de2-88dff796b0bc&quot; title=&quot;Windows Update PowerShell Module&quot; rel=&quot;noopener&quot;&gt;Windows Update PowerShell module&lt;/a&gt; allows you to install Windows updates from a PowerShell command line by executing (after installing the module onto the server):&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Get-WUInstall&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;remote-server-administration-tools-powershell-module&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-windows-server-2012-r2-core-additional-tools-and-scripts/#remote-server-administration-tools-powershell-module&quot; class=&quot;heading-anchor&quot;&gt;Remote Server Administration Tools PowerShell Module&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The Remote Server Administration Tools PowerShell module is available as an installable feature on Windows Server Core edition. It needs to be first installed by executing the following command at a PowerShell prompt:&lt;/p&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Add-WindowsFeature&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Name &lt;span class=&quot;token string&quot;&gt;&#39;RSAT-AD-PowerShell&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;IncludeAllSubFeature&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;powershell-community-extensions&quot;&gt;&lt;a href=&quot;https://danielscottraynsford.com/blog/installing-windows-server-2012-r2-core-additional-tools-and-scripts/#powershell-community-extensions&quot; class=&quot;heading-anchor&quot;&gt;PowerShell Community Extensions&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The &lt;a href=&quot;https://pscx.codeplex.com/&quot; title=&quot;PowerShell Community Extensions&quot; rel=&quot;noopener&quot;&gt;PowerShell Community Extensions&lt;/a&gt; project provides various useful PowerShell cmdlets. I always install this onto the server and copy the entire PSCX modules folder:&lt;/p&gt;&lt;pre class=&quot;language-plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;C:&#92;Program Files (x86)&#92;PowerShell Community Extensions&#92;Pscx3&#92;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;…into the system modules folder:&lt;/p&gt;&lt;pre class=&quot;language-plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;C:&#92;Program Files&#92;WindowsPowerShell&#92;Modules&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This makes the PSCX module available via the &lt;code&gt;Import-Module&lt;/code&gt; command in PowerShell.&lt;/p&gt;
 			</content>
    </entry><entry>
      <title>What&#39;s All This Then?</title>
      <link href="https://danielscottraynsford.com/blog/whats-all-this-then/" />
      <updated>2014-11-21T00:00:00Z</updated>
      <id>https://danielscottraynsford.com/blog/whats-all-this-then/</id>
      <content type="html">
				&lt;p&gt;This blog is where I will attempt to document my adventures in coding.&lt;/p&gt;
 			</content>
    </entry></feed>
