<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Cloud & Governance]]></title><description><![CDATA[Cloud & Governance]]></description><link>https://cloud.qbits.no</link><generator>RSS for Node</generator><lastBuildDate>Fri, 17 Apr 2026 12:07:27 GMT</lastBuildDate><atom:link href="https://cloud.qbits.no/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Access Management shouldn't be an afterthought in your Internal Developer Platform]]></title><description><![CDATA[Introduction
One common challenge that all Internal Developer Platforms (IDP) will reach at some point is how to handle access management. Who should be able to request self-service resources? Who sho]]></description><link>https://cloud.qbits.no/access-management-shouldn-t-be-an-afterthought-in-your-internal-developer-platform</link><guid isPermaLink="true">https://cloud.qbits.no/access-management-shouldn-t-be-an-afterthought-in-your-internal-developer-platform</guid><category><![CDATA[idp]]></category><dc:creator><![CDATA[Øystein]]></dc:creator><pubDate>Tue, 17 Mar 2026 20:12:50 GMT</pubDate><content:encoded><![CDATA[<h2>Introduction</h2>
<p>One common challenge that all Internal Developer Platforms (IDP) will reach at some point is how to handle access management. Who should be able to request self-service resources? Who should own the solutions that are requested? How should members be onboarded and offboarded? In this blog post I will introduce the concept of Team Boundary as a first-class citizen as a way to solve these challenges, with examples from how we have done it at our client.</p>
<h2>The Challenge</h2>
<p>At our client we have self-service solutions for many different types of resources: GitHub repositories, Kubernetes namespaces, databases, Azure Subscriptions, among others. These solutions exist so developers can quickly get ready-made environments and start doing what matters: writing code. As a platform team we want to avoid being blockers and ensure that new projects and features can be delivered quickly.</p>
<p>However, we do not want to tie these resources to individual users. That would make it time-consuming for developers to onboard and offboard members to each individual solution — wasted time from both a developer and business perspective. We also don't want the platform team to micromanage members. We do not have full insight into who should have access to what, and at some point managing access would become at least a full-time job. The security risk of improperly offboarded members compounds over time. And of course, granting everyone access to everything is never a solution.</p>
<img src="https://cdn.hashnode.com/uploads/covers/679df419082f7d723f8a8f28/a83a40b8-6ae5-4dc4-9570-f9968ff71adb.png" alt="" style="display:block;margin:0 auto" />

<h2>What is a Team Boundary?</h2>
<p>A Team Boundary is a named team identity that serves as a prerequisite for provisioning any platform resource. It is a first-class citizen — built into the core of the platform, not an afterthought. It is the first resource a developer must have before they can request anything else.</p>
<h2>How it works in practice</h2>
<p>The Team Boundary is where it all starts. Before a developer can create anything, they need to either be a member of an existing Team Boundary or create a new one. At our client we use Azure Entra ID as the identity provider, so this was a natural place to start. When a developer requests a new Team Boundary the automation does the following:</p>
<ul>
<li><p>Creates an Entra ID group: The members of this group will automatically get access to all resources related to the Team Boundary</p>
</li>
<li><p>Adds the requestor as a member of the group: It's assumed that the requestor will need access to the resources, so the requestor is automatically added as the first member</p>
</li>
<li><p>Adds the requestor as an owner of the group: The requestor will be responsible for onboarding and offboarding members to the group</p>
</li>
<li><p>Adds a scheduled Entra ID access review for the group: This acts as a reminder for the owner to evaluate who should continue to have access</p>
</li>
</ul>
<p>Now the developer has a Team Boundary. This serves as an input for all other resources the team will need. To request a Kubernetes namespace, the developer provides their Team Boundary as input. The automation takes the Team Boundary (the Entra ID group) and grants it the necessary access to operate resources inside the namespace and all its supporting resources. The same applies to databases, additional repositories, and any other resource — the automation wires up access automatically.</p>
<img src="https://cdn.hashnode.com/uploads/covers/679df419082f7d723f8a8f28/515ca9d9-c2b9-4dae-9a84-53a9fa4ded90.png" alt="" style="display:block;margin:0 auto" />

<p>The Entra ID group is the single source of truth. Everything else is a projection of it: the GitHub Team is synced from the group, Grafana directories are provisioned automatically, Azure resources grant access to the group directly. Developers can't create orphaned access — they cannot manually create a GitHub team and add whoever they want. All access flows through the Team Boundary, enforced by automation. When the team needs to onboard a new member, the only thing they do is add that person to the Entra ID group.</p>
<img src="https://cdn.hashnode.com/uploads/covers/679df419082f7d723f8a8f28/60a55416-1db4-4b19-9c5f-513292f865df.png" alt="" style="display:block;margin:0 auto" />

<h2>What this unlocks</h2>
<p>This model follows a key design principle: coarse-grained control, fine-grained autonomy. The platform is strict about who belongs to a team, but relaxed about what team members can do within their space. Once inside, teams have meaningful autonomy — free rein in their Grafana directory, and the access they need to operate within their GitHub repositories and namespace.</p>
<img src="https://cdn.hashnode.com/uploads/covers/679df419082f7d723f8a8f28/22074bba-3ac7-4991-ae9d-027675355819.png" alt="" style="display:block;margin:0 auto" />

<p>This is fundamentally different from traditional IT access management, where both membership and permissions are tightly controlled centrally. It also means that even if a team goes off the golden path for some resources, their identity and access still flows through the same boundary.</p>
<p>Yes, some developers may not need all these resources — that is an accepted trade-off. The alternative is micromanaging access control, which becomes more complex and fragile than simply managing members of one group.</p>
<p>While our example uses Azure Entra ID, the concept is platform-agnostic. In AWS you would map this to IAM Identity Center groups; in GCP to Google Groups. The implementation differs, but the principle — one team identity primitive that everything else flows from — holds regardless.</p>
<h2>Key decisions</h2>
<p>Every organization will have different requirements around the Team Boundary. Key questions to ask:</p>
<ul>
<li><p>Who should be able to be owners of the Team Boundary? Should it be project leaders, only internal employees, any developers?</p>
</li>
<li><p>Should we allow multiple owners of the Team Boundary? Or should we have a process for handling onboarding / offboarding if the owner is not available?</p>
</li>
<li><p>How often should an access review be scheduled? What should the default action of an access review be?</p>
</li>
<li><p>How do we communicate the responsibilities to owners of the Team Boundary?</p>
</li>
<li><p>How do we grant the Team Boundary access to what they need and not more?</p>
</li>
</ul>
<h2>Conclusion</h2>
<p>Access management is one of those things that feels easy to defer — until it isn't. By treating the Team Boundary as a first-class citizen in your IDP, you give your platform a stable identity primitive that scales with your organization. Onboarding new members becomes a single operation. Offboarding is equally simple, and the security benefits compound over time. The platform team stays out of the business of micromanaging access, and developers retain the autonomy they need to move fast.</p>
<p>The right time to introduce this concept is at the start, before self-service resources multiply and access management becomes a tangled mess to untangle. But even if you're further along, the Team Boundary is worth introducing — the earlier the better.</p>
]]></content:encoded></item><item><title><![CDATA[Unlocking the Power of MCP Servers on Linux: A Quickstart Guide]]></title><description><![CDATA[What is MCP and why should I use it?
I spent this weekend exploring MCP (Model Context Protocol), a protocol that allows LLMs (Large Language Models) to interact with other systems. This means you can connect your LLM, like Claude, to your file syste...]]></description><link>https://cloud.qbits.no/unlocking-the-power-of-mcp-servers-on-linux-a-quickstart-guide</link><guid isPermaLink="true">https://cloud.qbits.no/unlocking-the-power-of-mcp-servers-on-linux-a-quickstart-guide</guid><category><![CDATA[AI]]></category><category><![CDATA[mcp]]></category><category><![CDATA[llm]]></category><dc:creator><![CDATA[Øystein]]></dc:creator><pubDate>Sun, 06 Apr 2025 12:46:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/_0iV9LmPDn0/upload/8a1f7673bdbd53a208e332d41f0a4aae.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-what-is-mcp-and-why-should-i-use-it">What is MCP and why should I use it?</h1>
<p>I spent this weekend exploring <a target="_blank" href="https://modelcontextprotocol.io/introduction">MCP</a> (Model Context Protocol), a protocol that allows LLMs (Large Language Models) to interact with other systems. This means you can connect your LLM, like Claude, to your file system, for example. You can then instruct your LLM to "change all my file names with whitespace to underscores," and it will execute the commands on your system, making the actual changes. This enables you to use natural language to interact with the systems you're working with, potentially transforming how we engage with our systems.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743944051890/7160d5af-438e-4a59-b232-c7ffce39347c.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-how-do-i-interact-with-mcp">How do I interact with MCP?</h1>
<p>To interact with an MCP, you need a client that can communicate with an MCP server. See <a target="_blank" href="https://modelcontextprotocol.io/clients#feature-support-matrix">here</a> for a matrix of supported clients. In this post, I will use Claude Desktop. Since I’m running Linux and there’s no officially supported version of Claude, I’m using the <a target="_blank" href="https://github.com/aaddrick/claude-desktop-debian">unofficial desktop version</a> of Claude Desktop. <em>NB: Read and evaluate the code before you run it!</em></p>
<h1 id="heading-adding-an-mcp-server">Adding an MCP server</h1>
<p>After installing the desktop version, you can add your first MCP server. Our MCP server in this example will require npx. Check if you have it installed:</p>
<pre><code class="lang-plaintext">npx --version
</code></pre>
<p>If not, install it.</p>
<p>We will then add an <a target="_blank" href="https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem">MCP server</a> to your Claude desktop config that interacts with your local file system:</p>
<pre><code class="lang-bash">vim .config/Claude/claude_desktop_config.json
</code></pre>
<p>Add the following:</p>
<pre><code class="lang-bash">{
  <span class="hljs-string">"mcpServers"</span>: {
    <span class="hljs-string">"filesystem"</span>: {
      <span class="hljs-string">"command"</span>: <span class="hljs-string">"npx"</span>,
      <span class="hljs-string">"args"</span>: [
        <span class="hljs-string">"-y"</span>,
        <span class="hljs-string">"@modelcontextprotocol/server-filesystem"</span>,
        <span class="hljs-string">"/home/oystein/Desktop"</span>,
        <span class="hljs-string">"/home/oystein/Downloads"</span>
      ]
    }
  }
}
</code></pre>
<p>This list will specify the directories in which the LLM can operate. Replace <code>&lt;myhomedir&gt;</code> with the name of your home directory.</p>
<p>Restart Claude Desktop. You will now see a hammer with a number, indicating that you have MCP tools available:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743941625175/946cf60e-79cc-4621-9025-955aebb37411.png" alt class="image--center mx-auto" /></p>
<p>You can also select the “attach” button to the right of the hammer to see connected MCP servers:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743941662817/9bbc38d0-bc95-4a76-af91-36d8978e840a.png" alt class="image--center mx-auto" /></p>
<p>You can see that I have two file servers installed.</p>
<h1 id="heading-make-changes-to-your-local-filesystem">Make changes to your local filesystem</h1>
<p>You can now start interacting with Claude to make changes to your file system. Here, I ask it to create a script in my Desktop directory:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743941924210/aa5a22ee-3842-45e3-af97-6d7c39e3aa46.png" alt class="image--center mx-auto" /></p>
<p>You will have to confirm when it wants to execute commands on your system:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743941773313/7ecd7b7e-c7ae-4262-b9fd-2449562665e8.png" alt class="image--center mx-auto" /></p>
<p>Claude does its work:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743941961783/d6c50e96-5852-439f-b804-ebbd4f19af11.png" alt class="image--center mx-auto" /></p>
<p>And to confirm that it actually exists:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743942050135/6023e03d-cd49-4b50-8f0a-d0fa776faa08.png" alt class="image--center mx-auto" /></p>
<p>As you can see, the file now exists locally in my file system.</p>
<p>A very basic example, but full of potential. Let’s take it a bit further and see how we can interact with Azure DevOps.</p>
<h1 id="heading-interacting-with-azure-devops">Interacting with Azure DevOps</h1>
<p>In this example, I will use the <a target="_blank" href="https://github.com/Tiberriver256/mcp-server-azure-devops">mcp-server-azure-devops</a> for interacting with Azure DevOps. Note: I recommend using this only in your personal lab Azure DevOps organization.</p>
<p>Start by adding the Azure DevOps MCP server to your Claude Desktop config:</p>
<pre><code class="lang-bash">{
  <span class="hljs-string">"mcpServers"</span>: {
    <span class="hljs-string">"filesystem"</span>: {
      <span class="hljs-string">"command"</span>: <span class="hljs-string">"npx"</span>,
      <span class="hljs-string">"args"</span>: [
        <span class="hljs-string">"-y"</span>,
        <span class="hljs-string">"@modelcontextprotocol/server-filesystem"</span>,
        <span class="hljs-string">"/home/oystein/Desktop"</span>,
        <span class="hljs-string">"/home/oystein/Downloads"</span>
      ]
    },
    <span class="hljs-string">"azureDevOps"</span>: {
      <span class="hljs-string">"command"</span>: <span class="hljs-string">"npx"</span>,
      <span class="hljs-string">"args"</span>: [<span class="hljs-string">"-y"</span>, <span class="hljs-string">"@tiberriver256/mcp-server-azure-devops"</span>],
      <span class="hljs-string">"env"</span>: {
        <span class="hljs-string">"AZURE_DEVOPS_ORG_URL"</span>: <span class="hljs-string">"https://dev.azure.com/&lt;myorg&gt;"</span>,
        <span class="hljs-string">"AZURE_DEVOPS_AUTH_METHOD"</span>: <span class="hljs-string">"pat"</span>,
        <span class="hljs-string">"AZURE_DEVOPS_PAT"</span>: <span class="hljs-string">"&lt;redacted&gt;"</span>,
        <span class="hljs-string">"AZURE_DEVOPS_DEFAULT_PROJECT"</span>: <span class="hljs-string">"Demo"</span>
      }
    }
  }
}
</code></pre>
<p>Add your organization in <code>&lt;myorg&gt;</code> and your Azure DevOps PAT to <code>&lt;redacted&gt;</code>. I granted the PAT read access with write access to tasks.</p>
<p>Restart Claude and ask it to:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743942453114/9f947815-9d07-4930-9cd3-ba36e604a051.png" alt class="image--center mx-auto" /></p>
<p>It will start listing all tasks assigned to me and their states:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743942494407/05e09bf2-a651-4a88-becb-34d56ad6f9e0.png" alt class="image--center mx-auto" /></p>
<p>I can even add comments and close tasks:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743943033419/dd3e03bd-8e75-4d98-9c87-81b891922d8a.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>This was a quick introduction to the how and why of the MCP server. Even though it’s early, I believe this will significantly change our daily workflow, and it will be interesting to see what MCP server capabilities people will develop. However powerful it is, security departments will have a lot to monitor going forward, and the chance of user mistakes can increase since one can quickly select “yes” to all prompts asked by the LLM.</p>
]]></content:encoded></item><item><title><![CDATA[Want to Save Money, Ship Faster, and Improve Security? Automate IT Processes, Not Just Tasks]]></title><description><![CDATA[The Cost of missing automated processes
Many platform engineers focus on automating individual tasks that run from their local machines. While these scripts are helpful, the real value comes when automation is embedded into a structured process. The ...]]></description><link>https://cloud.qbits.no/want-to-save-money-ship-faster-and-improve-security-automate-it-processes-not-just-tasks</link><guid isPermaLink="true">https://cloud.qbits.no/want-to-save-money-ship-faster-and-improve-security-automate-it-processes-not-just-tasks</guid><category><![CDATA[Azure]]></category><category><![CDATA[automation]]></category><category><![CDATA[business]]></category><dc:creator><![CDATA[Øystein]]></dc:creator><pubDate>Mon, 10 Feb 2025 23:00:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/w0H4K148PGw/upload/3b3fbc2817f7068cf7f22b9bedc379d8.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h4 id="heading-the-cost-of-missing-automated-processes"><strong>The Cost of missing automated processes</strong></h4>
<p>Many platform engineers focus on automating individual tasks that run from their local machines. While these scripts are helpful, the real value comes when automation is embedded into a structured process. The comic below illustrates a common scenario in many IT departments where an automated process is missing:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738690219009/ea98cbc7-dc2d-4d39-bd41-1474408a6ff2.jpeg" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738690226732/ad994e6e-1460-4ede-ae8d-f0d9c1835fc0.jpeg" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738690235190/48b10554-2ecd-4028-90a2-190853fa14d9.jpeg" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738690240610/e2cb992a-7744-40b2-af4b-51cbd48df809.jpeg" alt class="image--center mx-auto" /></p>
<p>For many developers and platform engineers, this is an all-too-familiar day. These interruptions are costly—not just in time, but in focus. Constant context switching slows development, delays progress, and frustrates everyone involved. Developers waste time waiting for what they need, while platform engineers get stuck handling repetitive requests instead of improving the system.</p>
<h4 id="heading-the-problem-scripting-tasks-doesnt-solve-the-root-issue"><strong>The Problem: Scripting Tasks Doesn’t Solve the Root Issue</strong></h4>
<p>A typical reaction from platform engineers is to write a script that helps automate these tasks. While this reduces some manual effort, it doesn’t eliminate the costly back-and-forth between developers and platform teams. The interruptions continue, context switching remains, and the fundamental inefficiencies persist.</p>
<p>Now, imagine taking that script and embedding it into a <strong>fully automated process</strong> that considers the entire developer experience.</p>
<h4 id="heading-the-solution-automate-the-process-not-just-the-task"><strong>The Solution: Automate the Process, Not Just the Task</strong></h4>
<p>We can simplify it like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738692125231/6eebe20a-01eb-4d67-81ad-2e0177126de5.png" alt class="image--center mx-auto" /></p>
<p>By providing a <strong>self-service interface</strong>, developers can request common services without interrupting anyone. If necessary, approval steps can be added, and validation checks can ensure input accuracy. By automating the entire process, we eliminate all unnecessary communication between teams.</p>
<p>In the early stages, simplicity is key. For instance, you might start with a GitHub Actions pipeline that developers can trigger on demand, serving as a straightforward self-service interface. As your team gains experience and your processes mature, you can explore more advanced interface options to further streamline operations.</p>
<p>Even if the initial process isn’t perfect, having a process in place is always better than having none at all—it creates a foundation that you can continuously improve.</p>
<p>The benefits extend beyond just saving time:</p>
<ul>
<li><p>✅ <strong>Clear traceability</strong> – Every request and approval is logged.</p>
</li>
<li><p>✅ <strong>Improved security</strong> – Compliance can be built into the process itself.</p>
</li>
<li><p>✅ <strong>Defined business processes</strong> – Security teams and stakeholders can refine automation rather than fight ad-hoc scripts.</p>
</li>
</ul>
<p>If security is unhappy with how something is automated? No problem—the process can be improved. If the user experience is lacking? Refine the workflow. <strong>This is where the real value of automation begins to take effect.</strong></p>
<h4 id="heading-automation-as-a-business-value-driver"><strong>Automation as a Business Value Driver</strong></h4>
<p>One of the biggest challenges platform engineers face is <strong>communicating the value of their work to stakeholders</strong>. Automating IT processes makes this easier by providing <strong>clear, measurable benefits</strong>.</p>
<p>Without a structured, automated workflow, <strong>each request costs nearly 10,000 NOK in lost productivity</strong>. Multiply that by an estimated <strong>500 requests per year</strong>, and the total yearly cost <strong>approaches 5 million NOK</strong>—just for a single process!</p>
<p>Let’s break down the numbers using the <strong>app registration process</strong> as an example:</p>
<h4 id="heading-cost-breakdown-for-one-app-registration-request"><strong>Cost Breakdown for One App Registration Request</strong></h4>
<h4 id="heading-1-developer-time"><strong>1️⃣ Developer Time</strong></h4>
<ul>
<li><p><strong>Total Process Time:</strong> 5 hours</p>
</li>
<li><p><strong>Hourly Rate:</strong> 1500 NOK/hour</p>
</li>
<li><p><strong>Total Cost:</strong> <code>5 hours * 1500 NOK = 7,500 NOK</code></p>
</li>
</ul>
<h4 id="heading-2-interruption-costs-context-switching-amp-actual-work"><strong>2️⃣ Interruption Costs (Context Switching &amp; Actual Work)</strong></h4>
<ul>
<li><p><strong>Tech Lead:</strong> 23 minutes ≈ <strong>0.38 hours</strong></p>
</li>
<li><p><strong>Platform Engineer One:</strong> 23 minutes ≈ <strong>0.38 hours</strong></p>
</li>
<li><p><strong>Platform Engineer Two:</strong> 23 minutes (interruptions) + 30 minutes (actual work) ≈ <strong>0.88 hours</strong></p>
</li>
</ul>
<p><strong>Total Interruption Time:</strong><br /><code>0.38 + 0.38 + 0.88 = 1.64 hours</code></p>
<ul>
<li><strong>Total Cost:</strong> <code>1.64 hours * 1500 NOK = 2,460 NOK</code></li>
</ul>
<h4 id="heading-3-total-cost-for-one-request"><strong>3️⃣ Total Cost for One Request</strong></h4>
<ul>
<li><strong>Total Cost:</strong> <code>7,500 NOK + 2,460 NOK = 9,960 NOK</code></li>
</ul>
<hr />
<h3 id="heading-yearly-cost-impact"><strong>Yearly Cost Impact</strong></h3>
<ul>
<li><p><strong>Number of App Registrations per Year:</strong> <strong>500</strong></p>
</li>
<li><p><strong>Total Yearly Cost:</strong><br />  <code>500 * 9,960 NOK = 4,980,000 NOK</code></p>
</li>
</ul>
<hr />
<p><strong>Take a moment to consider this:</strong><br />Each app registration request costs nearly 10,000 NOK. Multiply that by 500 requests a year, and you're facing almost 5 million NOK in annual expenses. This stark figure shows how even minor inefficiencies—like repeated context switching and manual interventions—will result in massive, unnecessary expenses. Streamlining these processes through automation isn’t just about saving time; it’s about saving a substantial amount of money.</p>
<h3 id="heading-the-hidden-benefit-platform-engineers-win-too"><strong>The Hidden Benefit: Platform Engineers Win Too</strong></h3>
<p>Beyond the cost savings, platform engineers <strong>benefit directly</strong>:<br />✅ <strong>Less context switching</strong><br />✅ <strong>Fewer repetitive tasks</strong><br />✅ <strong>More time to focus on higher-impact automation</strong></p>
<p>By shifting from <strong>task automation to process automation</strong>, organizations can <strong>save money, improve security, and accelerate development</strong>—all while making life easier for <strong>everyone involved</strong>.</p>
<h3 id="heading-the-best-part-the-cost-can-be-reduced-to-practically-zero"><strong>The Best Part? The Cost Can Be Reduced to Practically Zero</strong></h3>
<p>By <strong>investing in process automation</strong>, this <strong>5 million NOK cost can be reduced to nearly zero</strong>. The solution isn’t just about automation—it’s about <strong>building scalable, efficient processes</strong> that eliminate waste at its core.</p>
]]></content:encoded></item><item><title><![CDATA[Automating Azure PIM Role Elevation with PowerShell]]></title><description><![CDATA[Azure Privileged Identity Management (PIM) is a crucial security feature that enables just-in-time role elevation for both Azure resources and Azure Entra roles. While this significantly enhances platform security by eliminating permanent active assi...]]></description><link>https://cloud.qbits.no/automating-azure-pim-role-elevation-with-powershell</link><guid isPermaLink="true">https://cloud.qbits.no/automating-azure-pim-role-elevation-with-powershell</guid><category><![CDATA[Azure]]></category><category><![CDATA[automation]]></category><category><![CDATA[Powershell]]></category><category><![CDATA[Security]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Øystein]]></dc:creator><pubDate>Sat, 01 Feb 2025 10:31:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/vpOeXr5wmR4/upload/bab129a20c6dd0533f8d7d9d7d1d0402.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Azure Privileged Identity Management (PIM) is a crucial security feature that enables just-in-time role elevation for both Azure resources and Azure Entra roles. While this significantly enhances platform security by eliminating permanent active assignments, the traditional portal-based elevation process can introduce inefficiencies into development workflows.</p>
<h2 id="heading-the-challenge-with-portal-based-elevation">The Challenge with Portal-Based Elevation</h2>
<p>The Azure Portal interface for PIM, while functional, presents several operational friction points:</p>
<ul>
<li><p>Time-consuming role selection, especially with multiple assignments</p>
</li>
<li><p>Significant context switching for developers working primarily in PowerShell</p>
</li>
<li><p>Interrupted development flow when managing multiple role elevations</p>
</li>
</ul>
<h2 id="heading-a-powershell-based-solution">A PowerShell-Based Solution</h2>
<p>For developers who primarily work in PowerShell, here's a solution that simplifies the role elevation process:</p>
<pre><code class="lang-powershell"><span class="hljs-keyword">param</span>(
   [<span class="hljs-type">Parameter</span>()]
   [<span class="hljs-built_in">string</span>]<span class="hljs-variable">$SubscriptionId</span>,

   [<span class="hljs-type">Parameter</span>()]
   [<span class="hljs-built_in">string</span>[]]<span class="hljs-variable">$RoleDefinitionName</span> = <span class="hljs-selector-tag">@</span>(<span class="hljs-string">"Contributor"</span>, <span class="hljs-string">"User Access Administrator"</span>),

   [<span class="hljs-type">Parameter</span>()]
   [<span class="hljs-built_in">string</span>]<span class="hljs-variable">$Justification</span> = <span class="hljs-string">"Local development"</span>,

   [<span class="hljs-type">Parameter</span>()]
   [<span class="hljs-built_in">int</span>]<span class="hljs-variable">$DurationInHours</span> = <span class="hljs-number">8</span>
)

<span class="hljs-variable">$ErrorActionPreference</span> = <span class="hljs-string">"Stop"</span>

<span class="hljs-variable">$currentContext</span> = <span class="hljs-built_in">Get-AzContext</span>

<span class="hljs-keyword">ForEach</span> (<span class="hljs-variable">$role</span> <span class="hljs-keyword">in</span> <span class="hljs-variable">$RoleDefinitionName</span>) {
   <span class="hljs-built_in">Write-Verbose</span> <span class="hljs-string">"Elevating to role <span class="hljs-variable">$role</span> on subscription <span class="hljs-variable">$SubscriptionId</span> for <span class="hljs-variable">$DurationInHours</span> hours"</span> <span class="hljs-literal">-Verbose</span>:<span class="hljs-variable">$true</span>
   <span class="hljs-variable">$roleDefinitionId</span> = (<span class="hljs-built_in">Get-AzRoleDefinition</span> <span class="hljs-literal">-Name</span> <span class="hljs-variable">$role</span>).Id
   <span class="hljs-variable">$inputObject</span> = <span class="hljs-selector-tag">@</span>{
       Name = [<span class="hljs-type">guid</span>]::NewGuid().ToString()
       Scope = <span class="hljs-string">"/subscriptions/<span class="hljs-variable">$subscriptionId</span>/"</span>
       ExpirationDuration = <span class="hljs-string">"PT<span class="hljs-variable">$</span>{DurationInHours}H"</span>
       ExpirationType = <span class="hljs-string">"AfterDuration"</span>
       PrincipalId = (<span class="hljs-built_in">Get-AzAdUser</span> <span class="hljs-literal">-Mail</span> <span class="hljs-variable">$currentContext</span>.Account.Id).Id
       RequestType = <span class="hljs-string">"SelfActivate"</span>
       RoleDefinitionId = <span class="hljs-string">"/subscriptions/<span class="hljs-variable">$SubscriptionId</span>/providers/Microsoft.Authorization/roleDefinitions/<span class="hljs-variable">$roleDefinitionId</span>"</span>
       ScheduleInfoStartDateTime = <span class="hljs-built_in">Get-Date</span> <span class="hljs-literal">-Format</span> o
       Justification = <span class="hljs-variable">$Justification</span>
       Verbose = <span class="hljs-variable">$false</span>
   }

   <span class="hljs-built_in">New-AzRoleAssignmentScheduleRequest</span> @inputObject
}
</code></pre>
<h2 id="heading-implementation-guide">Implementation Guide</h2>
<ol>
<li><p>Save the script as <a target="_blank" href="http://Elevate-PimRole.ps"><code>Elevate-PimRole.ps1</code></a></p>
</li>
<li><p>Execute with your subscription ID:</p>
</li>
</ol>
<pre><code class="lang-plaintext">./Elevate-PimRole.ps1 -SubscriptionId xxxxxxxx-xxxx-xxxx-xxxxxxxx
</code></pre>
<h2 id="heading-important-considerations">Important Considerations</h2>
<ul>
<li><p>The default configuration elevates both Contributor and User Access Administrator roles</p>
</li>
<li><p>The script assumes subscription-level scope; modify the scope parameter for resource group-level operations</p>
</li>
<li><p>Default elevation duration is set to 8 hours</p>
</li>
<li><p>Custom justification messages can be provided via the <code>-Justification</code> parameter</p>
</li>
</ul>
<p>This approach maintains security while significantly improving developer productivity by eliminating unnecessary context switching and portal navigation.</p>
]]></content:encoded></item></channel></rss>