<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>PowerShell on Andrew Wilson's Blog</title><link>https://andrewilson.co.uk/tags/powershell/</link><description>Recent content in PowerShell on Andrew Wilson's Blog</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Thu, 04 Jul 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://andrewilson.co.uk/tags/powershell/index.xml" rel="self" type="application/rss+xml"/><item><title>Azure Role Based Access Control (RBAC) | Removing Orphaned Role Assignments</title><link>https://andrewilson.co.uk/post/2024/07/removing-orphaned-rbac-role-assignments/</link><pubDate>Thu, 04 Jul 2024 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2024/07/removing-orphaned-rbac-role-assignments/</guid><description>Problem Space Deploying solutions into Azure that rely on Role Based Access often involve us creating IaC automation for the assignment of roles, such as:
A services access to Key Vault A services access to a Key Vault specific secret A services access to a storage account A services access to a Service Bus Queue or Topic In many of these instances we may wish to leverage the source resource identity (System Assigned Managed Identity) for the assigned access.</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>Deploying solutions into Azure that rely on Role Based Access often involve us creating IaC automation for the assignment of roles, such as:</p>
<ul>
<li>A services access to Key Vault</li>
<li>A services access to a Key Vault specific secret</li>
<li>A services access to a storage account</li>
<li>A services access to a Service Bus Queue or Topic</li>
</ul>
<p>In many of these instances we may wish to leverage the source resource identity (<em>System Assigned Managed Identity</em>) for the assigned access.</p>
<p>But what happens when we delete the source resource, are the role assignments applied on the target resources removed?</p>
<p>The answer&hellip; No they are not.</p>
<p>Your target resources are left with orphaned role assignments. In many cases the use of User Assigned Managed Identity is used to avoid this particular issue as the identity exists regardless of the resource life cycle, at which point there is no orphaning.</p>
<p>But whats the big problem, just redeploy and everything should line up again right?</p>
<p>Unfortunately this is not the case. If you follow through with this sentiment, you will receive the following error from the Azure Resource Manager:</p>
<p><code>Tenant ID, application ID, principal ID, and scope are not allowed to be updated. (code: RoleAssignmentUpdateNotPermitted)</code></p>
<p>The reasoning behind is role assignments require a globally unique identifier (GUID) of which out of <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/scenarios-rbac#name">good practices you have made deterministic</a> for solution deployments. So when you have deleted and recreated the resource the underlying principal ID has now changed, but the role assignment name has not. This infers the problem, you cannot create two role assignments with the same name, even in different Subscriptions, followed with properties of an existing role assignment cannot be changed.</p>
<p>Surly there is a automated process in Azure that will remove these for me? Sadly this is not the case and it has pushed many in resulting to manually searching for and removing these orphaned assignments. This is tedious and time consuming, not to mention requiring the user to have <a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#general"><code>User Access Administrator</code></a> role access which you may not wish to grant to everyone.</p>
<h2 id="solution">Solution</h2>
<p>The solution I came up with uses PowerShell and the az cli to retrieve all role assignments for a given subscription. This is then filtered down to any orphaned assignments scoped to resources in a specific resource group (<em>scope can be changed to what you need</em>). Given that there are orphaned assignments these are then removed  through the az cli.</p>
<p>The script looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-PowerShell" data-lang="PowerShell"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$ResourceGroupName</span> = <span style="color:#f1fa8c">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$roleAssignments</span> = az role assignment list --all | <span style="color:#8be9fd;font-style:italic">ConvertFrom-Json</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$orphaned</span> = <span style="color:#8be9fd;font-style:italic">$roleAssignments</span> | <span style="color:#8be9fd;font-style:italic">Where-Object</span> { (<span style="color:#8be9fd;font-style:italic">$_</span>.principalName <span style="color:#ff79c6">-eq</span> <span style="color:#f1fa8c">&#34;&#34;</span>) <span style="color:#ff79c6">-and</span> (<span style="color:#8be9fd;font-style:italic">$_</span>.scope <span style="color:#ff79c6">-match</span> <span style="color:#f1fa8c">&#34;resourcegroups/</span><span style="color:#8be9fd;font-style:italic">$ResourceGroupName</span><span style="color:#f1fa8c">&#34;</span>) }
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$orphaned</span>.Count
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$orphaned</span> | <span style="color:#8be9fd;font-style:italic">ForEach-Object</span> { az role assignment delete --ids <span style="color:#8be9fd;font-style:italic">$_</span>.id }
</span></span></code></pre></div><p>To run this script you will need the following Roles:</p>
<ul>
<li>Directory.Read.All</li>
<li>User Access Administrator (<em>At the respective Scope</em>)</li>
</ul>
<p>To avoid assigning <code>User Access Administrator</code> to anyone who may need to run this script, I placed this script into a CI/CD pipeline of which the Pipeline Identity has the roles applied as shown above. This way, the development team can remove orphaned role assignments with ease without the need for elevation of privilege.</p>
<blockquote>
<p>⚠️ Best Practice of Least Privilege.</p>
</blockquote>
<p>Hope this helps, and have fun</p>
]]></content:encoded></item><item><title>Azure API Management | Logic App (Standard) Backend Using a Swagger Definition</title><link>https://andrewilson.co.uk/post/2024/02/standard-logic-app-apim-backend-swagger/</link><pubDate>Thu, 01 Feb 2024 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2024/02/standard-logic-app-apim-backend-swagger/</guid><description>Overview After setting up a Logic App (Standard) Backend in Azure API Management (APIM) in my last post, I wanted to try and see if I could create a Swagger definition from a Standard Logic App which could then be used to simplify the API authoring process in APIM. This post shows my methods of doing so.
If you haven&amp;rsquo;t already I would recommend reading my previous post as this one will be working off of the building blocks of the last.</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p><a href="https://github.com/Andrew-D-Wilson/Standard-Logic-App-APIM-Backend">
  <img src="https://img.shields.io/badge/Repo-Standard--Logic--App--APIM--Backend-blue?logo=github&amp;style=for-the-badge" alt="GitHub Repository">

</a></p>
<p>After setting up a <a href="/post/2024/01/standard-logic-app-apim-backend/">Logic App (Standard) Backend in Azure API Management (APIM)</a> in my last post, I wanted to try and see if I could create a Swagger definition from a Standard Logic App which could then be used to simplify the API authoring process in APIM. This post shows my methods of doing so.</p>
<blockquote>
<p>If you haven&rsquo;t already I would recommend reading my previous <a href="/post/2024/01/standard-logic-app-apim-backend/"><strong>post</strong></a> as this one will be working off of the building blocks of the last.</p>
</blockquote>
<p>As with my previous post, the high-level architecture has not changed and neither has the overall aim as shown below.

  <img src="/images/posts/2024/01/Overview.png" alt="Overview">

</p>
<p>The design aims to abstract the backend from the api operations, i.e. the backend points to the Logic App and the individual operations point to the respective workflows. The design also specifies granular access to the workflow Shared-Access-Signature (sig) held in the applications specific KeyVault (<em>to see further details on this, see <a href="/post/2023/11/rbac-key-vault-specific-secret/">Azure RBAC Key Vault | Role Assignment for Specific Secret</a></em>). Furthermore, the additional required parameters that are necessary to call a workflow have been implemented through APIM policies to remove the need for callers to understand backend implementation.</p>
<p>I have opted for Infrastructure as Code (IaC) as my method of implementation, specifically Bicep. I have broken down the implementation of the diagram above into two parts, Application Deployment, and API Deployment.</p>
<h3 id="problem-space">Problem Space</h3>
<p>Having come across the ARM REST API to generate a Swagger definition for a <a href="https://learn.microsoft.com/en-us/rest/api/logic/workflows/list-swagger?view=rest-logic-2016-06-01&amp;tabs=HTTP">consumption Logic App</a>, I went searching for the same functionality for Standard Logic Apps. Given a short time searching, I found the <a href="https://learn.microsoft.com/en-us/rest/api/appservice/workflows?view=rest-appservice-2022-03-01">REST APIs</a> situated under App Services with the Swagger generation API no where to be seen.</p>
<p>But would this stop me&hellip; No.</p>
<p>Knowing that I can retrieve a detailed definition of a Standard Logic App through the Azure Resource Manager, I started looking at methods of stripping out the detail that I would need to construct a Swagger Definition.</p>
<p>My chosen method to do this is through a PowerShell script using both ARM REST APIs and the az cli. This choice will allow me to run the script as non-interactive in future DevOps CI/CD pipelines.</p>
<h2 id="application-deployment">Application Deployment</h2>
<p>Our first step toward generating the Swagger definition for our Logic App is to deploy an instance of our Logic App to Azure. The process has not changed from the last post apart from the addition of step 4. As shown in the diagram below, step 4 is where we will use the PowerShell script that I have written to extract the core details of our Logic App and workflows to construct a Swagger Definition.</p>
<p>
  <img src="/images/posts/2024/01/Application-Deployment-Swagger.png" alt="Application Deployment">

</p>
<p>The script has been designed to take a ControlFile.json that you update with one or more HTTP triggered workflows and API details to assist in the retrieval and generation of the definition. There is an example control file in my <a href="https://github.com/Andrew-D-Wilson/Standard-Logic-App-APIM-Backend/blob/main/SwaggerGenerator/ControlFile.json">GitHub repository</a>.</p>
<p>The <a href="https://github.com/Andrew-D-Wilson/Standard-Logic-App-APIM-Backend/blob/main/SwaggerGenerator/SpecCreator.ps1">SpecCreator.ps1</a> takes the following parameters:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-PowerShell" data-lang="PowerShell"><span style="display:flex;"><span><span style="color:#6272a4">&lt;#
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .</span><span style="color:#f1fa8c">SYNOPSIS</span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">	Creates a Swagger definition for a select Standard Logic App and its specified Workflows.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .</span><span style="color:#f1fa8c">DESCRIPTION</span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">	Uses a series of parameters to derive the Standard Logic App and the Workflows to include in the base Swagger definition template. 
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .PARAMETER SubscriptionId
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    The ID of the Subscription that the Standard Logic App is Hosted in.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .PARAMETER ResourceGroupName
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    The Resource Group Name that the Standard Logic App is Hosted in.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .PARAMETER LogicAppName
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    The Name of the Standard Logic App to use for the Swagger Definition.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .PARAMETER APITitle
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    API Name used to define the set of operations.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .PARAMETER APIDescription
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    Description of the API and its set of operations.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .PARAMETER APIVersion
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    None-Mandatory - Specified version of the API. Default is 1.0.0.0
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .PARAMETER SpecTemplatePath
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    None-Mandatory - Base path to the swagger definition template, doesn&#39;t include the name of the file.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    .PARAMETER ControlFile
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    None-Mandatory - Base path to the json control file used to identify the set of Workflows to extract as operations for the swagger definition, doesn&#39;t include the name of the file.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">	.PARAMETER InteractiveMode
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">	If specified will request the user to interactively login into Azure for az tooling.
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">    #&gt;</span>
</span></span></code></pre></div><p>The script then uses a series of ARM REST API calls to obtain the following detail:</p>
<ul>
<li>Request Schema (if one is provided).</li>
<li>Workflow Definition.</li>
</ul>
<p>Using the Workflow Definition, we can interrogate the Workflow Trigger for details such as:</p>
<ul>
<li>HTTP Method.</li>
<li>Relative Paths.
<ul>
<li><strong>Note:</strong> Path Parameters are expected to be wrapped with curly braces, for example:
<ul>
<li>/users/{id}</li>
<li>/cars/{carId}/drivers/{driverId}</li>
<li>cars/{carId}/drivers/{driverId}</li>
<li>/{carId}/{driverId}</li>
<li>{carId}</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Once all the Workflow details have been obtained, the script uses an imported tokenised Swagger Definition to generate the respective Definition for your Standard Logic App and Workflows.</p>
<h2 id="api-deployment">API Deployment</h2>
<p>For the most part, the API Deployment has not changed. We are using the same Bicep to create the APIM Instance, APIM Backends/Named Values, and Policies. The main change to our Bicep Deployment occurs in step 2.1 as shown in the diagram below.</p>
<p>
  <img src="/images/posts/2024/01/API-Deployment-with-Swagger.png" alt="API Deployment">

</p>
<p>Rather than constructing the API and API Operations individually through Bicep Resources as done in the previous post, we are going to define the APIM API along with the generated Swagger Definition from earlier steps. APIM validates the Swagger Definition and uses it to construct the API and Operations on our behalf.</p>
<p>The three main differences to our API deployment are:</p>
<ol>
<li>We need to make use of the <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-files#loadtextcontent">loadTextContent</a> Bicep function to load the generated Swagger Definition into our Bicep Template.
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">var</span> <span style="color:#8be9fd;font-style:italic">swaggerDefinition</span> = <span style="color:#50fa7b">loadTextContent</span>(<span style="color:#f1fa8c">&#39;../../SwaggerGenerator/GeneratedSpec.json&#39;</span>)
</span></span></code></pre></div></li>
<li>We need to change the <code>Microsoft.ApiManagement/service/apis</code> resource to accept our generated Swagger Definition:
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Create the Logic App API in APIM - Loading in Swagger Definition&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">resource</span> <span style="color:#8be9fd;font-style:italic">logicAppAPI</span> <span style="color:#f1fa8c">&#39;Microsoft.ApiManagement/service/apis@2022-08-01&#39;</span> = {
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#8be9fd;font-style:italic">apiName</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">parent</span>: <span style="color:#8be9fd;font-style:italic">apimInstance</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">properties</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">displayName</span>: <span style="color:#8be9fd;font-style:italic">apimAPIDisplayName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">subscriptionRequired</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">path</span>: <span style="color:#8be9fd;font-style:italic">apimAPIPath</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">protocols</span>: [
</span></span><span style="display:flex;"><span>      <span style="color:#f1fa8c">&#39;https&#39;</span>
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">format</span>: <span style="color:#f1fa8c">&#39;swagger-json&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">value</span>: <span style="color:#8be9fd;font-style:italic">swaggerDefinition</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></li>
<li>We need to remove the following Bicep Module call to create the API Operations as this will now be done for us.
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Bicep" data-lang="Bicep"><span style="display:flex;"><span>@<span style="color:#50fa7b">description</span>(<span style="color:#f1fa8c">&#39;Deploy logic App API operation&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">module</span> <span style="color:#8be9fd;font-style:italic">logicAppAPIOperation</span> <span style="color:#f1fa8c">&#39;Modules/apimOperation.azuredeploy.bicep&#39;</span> = [<span style="color:#8be9fd;font-style:italic">for</span> <span style="color:#8be9fd;font-style:italic">operation</span> <span style="color:#8be9fd;font-style:italic">in</span> <span style="color:#8be9fd;font-style:italic">apimAPIOperations</span> :{
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">name</span>: <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">-deploy&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#8be9fd;font-style:italic">params</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">parentName</span>: <span style="color:#f1fa8c">&#39;</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">apimInstance</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">/</span><span style="color:#f1fa8c">${</span><span style="color:#8be9fd;font-style:italic">logicAppAPI</span>.<span style="color:#8be9fd;font-style:italic">name</span><span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">lgCallBackObject</span>: <span style="color:#50fa7b">listCallbackUrl</span>(<span style="color:#50fa7b">resourceId</span>(<span style="color:#f1fa8c">&#39;Microsoft.Web/sites/hostruntime/webhooks/api/workflows/triggers&#39;</span>, <span style="color:#8be9fd;font-style:italic">logicAppName</span>, <span style="color:#f1fa8c">&#39;runtime&#39;</span>, <span style="color:#f1fa8c">&#39;workflow&#39;</span>, <span style="color:#f1fa8c">&#39;management&#39;</span>, <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">lgWorkflowName</span>, <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">lgWorkflowTrigger</span>), <span style="color:#f1fa8c">&#39;2022-09-01&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">operationDisplayName</span>: <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">displayName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">operationMethod</span>: <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">method</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">operationName</span>: <span style="color:#8be9fd;font-style:italic">operation</span>.<span style="color:#8be9fd;font-style:italic">name</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}]
</span></span></code></pre></div></li>
</ol>
<h2 id="summary">Summary</h2>
<p>The main benefit of using this method over constructing the API and Operations through Bicep is that we only need to define the backing API once.</p>
<p>If you would like to spin up a demo of this implementation or would like to use it as the basis of something you are working on, the source to all my work is in my <a href="https://github.com/Andrew-D-Wilson/Standard-Logic-App-APIM-Backend">GitHub repository</a> along with a README that explains how to get started.</p>
<p>Have a go and see if this simplifies your Standard Logic App APIM Configurations.</p>
]]></content:encoded></item><item><title>Windows Terminal | Azure Customisation for PowerShell</title><link>https://andrewilson.co.uk/post/2023/11/windows-terminal-customisation-powershell/</link><pubDate>Thu, 30 Nov 2023 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2023/11/windows-terminal-customisation-powershell/</guid><description>If you haven&amp;rsquo;t already, placing a customisation on your command prompt for PowerShell is a great way to gain extra context in given activities. For example, if you would like to have a comprehensive overview of your Git status summary information, then posh-git is for you.
Be Careful: Adding customisations will bring latency to start-up
One customisation that I came across that I really like is the cloud-native-azure theme using Oh My Posh.</description><content:encoded><![CDATA[<p>If you haven&rsquo;t already, placing a customisation on your command prompt for PowerShell is a great way to gain extra context in given activities. For example, if you would like to have a comprehensive overview of your Git status summary information, then <a href="https://github.com/dahlbyk/posh-git">posh-git</a> is for you.</p>
<p>
  <img src="/images/posts/2023/11/posh-git.png" alt="posh-git">

</p>
<blockquote>
<p><strong>Be Careful:</strong> Adding customisations will bring latency to start-up</p>
</blockquote>
<p>One customisation that I came across that I really like is the <a href="https://ohmyposh.dev/docs/themes#cloud-native-azure">cloud-native-azure</a> theme using <a href="https://ohmyposh.dev/">Oh My Posh</a>.
The theme gives context to the following:</p>
<ul>
<li>Path.</li>
<li>Git status summary with changing colours and glyphs depending on status.</li>
<li>Command execution status.</li>
<li>Terminal Type (pwsh, shell, etc.)</li>
<li>Battery Status.</li>
<li>Date and Time.</li>
<li>Kubernetes Context.</li>
<li>The part I really like | <strong>Azure Subscription Context</strong>.</li>
</ul>
<p>
  <img src="/images/posts/2023/11/cloud-native-azure.png" alt="cloud-native-azure">

</p>
<p>For me, this theme really ticks the box, having both context of git status information and the Azure context in which I may be working is a real productivity assist.</p>
<h2 id="oh-my-posh-theme-install">Oh My Posh Theme Install</h2>
<p>To install and enable the theme for your command prompt for PowerShell, you will need to do the following:</p>
<ol>
<li>Install a font.
<ul>
<li>Often these customisations will make use of glyphs (graphic symbols) to assist in conveying information quickly.</li>
<li>I made use of <a href="https://www.nerdfonts.com/font-downloads">Nerd Font - 0xProto Nerd Font</a></li>
<li>Simply:
<ul>
<li>Install the font.</li>
<li>Unzip the download.</li>
<li>Right Click and Install the .ttf files.</li>
</ul>
</li>
</ul>
</li>
<li>Enable the font in Windows Terminal.
<ul>
<li>Open the Windows Terminal.</li>
<li>Press <code>ctrl+,</code> to get to Windows Terminal Settings.</li>
<li>Under Profiles, select Windows PowerShell.</li>
<li>Under Additional Settings, select Appearance.</li>
<li>Select the drop-down for Font Face and select your downloaded font. For me this is 0xProto Nerd Font.</li>
</ul>
</li>
<li>Install <a href="https://ohmyposh.dev/docs/installation/windows">Oh My Posh</a>.</li>
<li>Apply your theme of choice, or in this case <a href="https://ohmyposh.dev/docs/themes#cloud-native-azure">cloud-native-azure</a>.
<ul>
<li>Simply:
<ul>
<li>Load your PowerShell profile through your editor of choice:
<ul>
<li><code>&gt; code $PROFILE</code> / <code>&gt; notepad $PROFILE</code></li>
<li>If you receive a path error, a profile may not have been setup for you yet. To sort this, use the following PowerShell command:
<ul>
<li><code>&gt; new-item -type file -path $profile -force</code></li>
<li>Then run the previous command to open the profile for editing.</li>
</ul>
</li>
</ul>
</li>
<li>Add the following initiation line for oh-my-posh for your given choice of theme
<ul>
<li><code>oh-my-posh init pwsh --config &quot;$env:POSH_THEMES_PATH\cloud-native-azure.omp.json&quot; | Invoke-Expression</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<p>Now the next time you load up your PowerShell terminal, you can enjoy the awesome customisation and hopefully get some productivity benefits too.</p>
]]></content:encoded></item><item><title>Azure API Management | Purge Soft-Deleted Instance</title><link>https://andrewilson.co.uk/post/2022/09/apim-purge-soft-deleted-instance/</link><pubDate>Wed, 07 Sep 2022 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2022/09/apim-purge-soft-deleted-instance/</guid><description>Problem Space: Around June 2020 a change was made to Azure API Management whereby any deletion of the instance via the Azure portal, Azure PowerShell, Azure CLI, and REST API version 2020-06-01-preview or later will result in the instance being soft-deleted.
This is to allow for recoverability of a recently deleted API Management instance, and therefore protecting against accidental deletion of the instance.
The problem with this is that not all the Azure Resource Management tooling currently supports the management of soft deleted API Management Instances.</description><content:encoded><![CDATA[<h3 id="problem-space">Problem Space:</h3>
<p>Around June 2020 a change was made to Azure API Management whereby any deletion of the instance via the Azure portal, Azure PowerShell, Azure CLI, and REST API version <code>2020-06-01-preview</code> or later will result in the instance being <em><strong>soft-deleted</strong></em>.</p>
<p>This is to allow for recoverability of a recently deleted API Management instance, and therefore protecting against accidental deletion of the instance.</p>
<p>The problem with this is that not all the Azure Resource Management tooling currently supports the management of soft deleted API Management Instances. Currently the only management tooling that supports this feature are the REST API and Azure CLI. You cannot at this point in time list, show, or purge a soft deleted instance in the Management Portal or Azure PowerShell.</p>
<h3 id="solutions">Solutions</h3>
<h4 id="azure-cli">Azure CLI</h4>
<p>As of the 5th of July 2022, you can use the Azure CLI to manage soft-deleted APIM instances. <a href="https://github.com/MicrosoftDocs/azure-docs-cli/blob/main/docs-ref-conceptual/release-notes-azure-cli.md#july-05-2022">Specifically CLI version 2.38.0 and above</a>.</p>
<p>Documentation on the relative management actions that can be applied are shown <a href="https://learn.microsoft.com/en-us/cli/azure/apim/deletedservice?view=azure-cli-latest">here</a>.</p>
<p>To purge a deleted instance, you can use the following commands:</p>
<pre tabindex="0"><code class="language-Azure" data-lang="Azure">az login

az account set --subscription &#34;{SubscriptionId}&#34;

// Using the list command and query argument you can obtain the name and location of your soft deleted APIM Instances

az apim deletedservice list --query &#34;[].[name, location]&#34;

// Since you now know the name and location of all the potential instances you wish to purge, you can now issue the purge command for each instance.

az apim deletedservice purge --service-name &#34;{APIMInstanceName}&#34; --location &#34;{RegionDeployedTo}&#34;
</code></pre><h4 id="rest-api">Rest API</h4>
<p>In-order to purge a soft-deleted APIM instance, you will need to execute the <em>Delete</em> REST API for API Management as per <a href="https://docs.microsoft.com/en-us/rest/api/apimanagement/current-ga/deleted-services/purge?tabs=HTTP">Microsoft Documentation</a>.</p>
<p>To make this simpler, here is some PowerShell using the <code>2021-08-01</code> REST API version and az tooling:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-PowerShell" data-lang="PowerShell"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$SubscriptionId</span> = <span style="color:#f1fa8c">&#39;{SubscriptionId}&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$Region</span> = <span style="color:#f1fa8c">&#39;{Region}&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$APIMInstanceName</span> = <span style="color:#f1fa8c">&#39;{APIMInstanceName}&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Connect-AzAccount</span> -Subscription <span style="color:#8be9fd;font-style:italic">$SubscriptionId</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$accessToken</span> = <span style="color:#8be9fd;font-style:italic">Get-AzAccessToken</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">$request</span> = @{
</span></span><span style="display:flex;"><span>    Method = <span style="color:#f1fa8c">&#39;DELETE&#39;</span>
</span></span><span style="display:flex;"><span>    Uri = <span style="color:#f1fa8c">&#34;https://management.azure.com/subscriptions/</span>$(<span style="color:#8be9fd;font-style:italic">$SubscriptionId</span>)<span style="color:#f1fa8c">/providers/Microsoft.ApiManagement/locations/</span>$(<span style="color:#8be9fd;font-style:italic">$Region</span>)<span style="color:#f1fa8c">/deletedservices/</span>$(<span style="color:#8be9fd;font-style:italic">$APIMInstanceName</span>)<span style="color:#f1fa8c">?api-version=2021-08-01&#34;</span>
</span></span><span style="display:flex;"><span>    Headers = @{
</span></span><span style="display:flex;"><span>        Authorization = <span style="color:#f1fa8c">&#34;Bearer </span>$(<span style="color:#8be9fd;font-style:italic">$accessToken</span>.Token)<span style="color:#f1fa8c">&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">Invoke-RestMethod</span> <span style="color:#8be9fd;font-style:italic">@request</span>
</span></span></code></pre></div>]]></content:encoded></item></channel></rss>