<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Olivier Miossec</title>
    <description>The latest articles on DEV Community by Olivier Miossec (@omiossec).</description>
    <link>https://dev.to/omiossec</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F160244%2Ff2d80283-eb50-4e19-a699-1cd6ca166e49.jpg</url>
      <title>DEV Community: Olivier Miossec</title>
      <link>https://dev.to/omiossec</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/omiossec"/>
    <language>en</language>
    <item>
      <title>Decision model in action, Jev in Cloud operation with PowerShell</title>
      <dc:creator>Olivier Miossec</dc:creator>
      <pubDate>Thu, 01 Oct 2026 15:21:34 +0000</pubDate>
      <link>https://dev.to/omiossec/decision-model-in-action-jev-in-cloud-operation-with-powershell-367c</link>
      <guid>https://dev.to/omiossec/decision-model-in-action-jev-in-cloud-operation-with-powershell-367c</guid>
      <description>&lt;p&gt;In &lt;em&gt;Thinking, Fast and Slow&lt;/em&gt;, Daniel Kahneman describes two modes of thinking. System 1 is fast and intuitive: you know that 2 + 2 = 4 without any effort. System 2 is slow and deliberate: the result of 17 × 24 takes time and real cognitive effort.&lt;/p&gt;

&lt;p&gt;That distinction was on the Typesafe.ai team's mind when they built Jev.&lt;/p&gt;

&lt;p&gt;Jev isn't a model for writing text, generating code, or creating images. It's a decision model. You give it a context (plain text, JSON,  email…) and predefined questions, then, it returns a predefined answer with a probability. Jev supports three question types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choice picks an option from a list.&lt;/li&gt;
&lt;li&gt;Yes/No says whether a statement is true, with a probability between 0 and 1 (returned as noul)&lt;/li&gt;
&lt;li&gt;Score rates the state against a rubric.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, Jev gives you an answer you can act on directly: "yes, 87%" or "category B". An LLM, by contrast, writes a full response. The first is built for chaining automated actions; the second shines at explanations and documentation.&lt;/p&gt;

&lt;p&gt;Jev is also fast and cheap. Answers are tiny, and only input tokens are billed; output is free.&lt;/p&gt;

&lt;p&gt;The best use case are: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The possible answers are known in advance. Is this email a scam? How urgent is this request? Which team should handle it? If you can't enumerate the answers, or the task needs deep reasoning, a decision model probably isn't the right tool.&lt;/li&gt;
&lt;li&gt;It happens a lot. Logs, metrics, tickets, emails: at that volume, speed and cost make all the difference.&lt;/li&gt;
&lt;li&gt;It needs judgment, not deep thinking. Think of Jev as a human making a quick call on something too fuzzy for a regex or a script.&lt;/li&gt;
&lt;li&gt;Something happens next. The answer feeds a workflow: route, alert, block, approve.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In one sentence: a decision model is worth it when you have a large pile of things that each need a quick human-like judgment, and that judgment should trigger an automatic next step.&lt;/p&gt;

&lt;p&gt;There are a lot of example on Youtube and elsewhere, but they are very basic. To better understand Jev, I will use it in every day tasks of a cloud engineer. &lt;/p&gt;

&lt;p&gt;To see what Jev can really do, I'll apply it to the everyday work of a cloud engineer, automated with PowerShell and the JEV PowerShell module from &lt;a href="https://github.com/dfinke/Jev" rel="noopener noreferrer"&gt;Doug Finke&lt;/a&gt;.&lt;br&gt;
I will create another post later to explain who to work with Jev in detail with PowerShell.&lt;/p&gt;

&lt;p&gt;Before starting, the TYPESAFE_API_KEY environment variable should be setup with the API key from &lt;a href="https://typesafe.ai/" rel="noopener noreferrer"&gt;TypeSafe.ai&lt;/a&gt; and store it in the TYPESAFE_API_KEY environment variable. Outside a quick test, don't hardcode it: pull it from a vault instead.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$env:TYPESAFE_API_KEY = "apikey_XXXXX"&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  First example, how to work with Jev in PowerShell
&lt;/h2&gt;

&lt;p&gt;Now the first test to understand how Jev work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# import module &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Import-Module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="nx"&gt;/Jev/Jev.psd1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# create the state, the context for the question&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$feedback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pscustomobject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'The customer is blocked by a connection issue to the database.'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# define the criteria for the question&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$criteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;support&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'The issue needs technical support.'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;sales&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'The issue concerns sales.'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# create the question based on the criteria&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;New-JevQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TeamRouting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Choice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Instructions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Which team should handle this?'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Criteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$criteria&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# invoke the question with the current state&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Invoke-Jev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$feedback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$question&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# check the result of the question&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;answers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TeamRouting&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result look like &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;type   choice  confidence probabilities&lt;/p&gt;



&lt;p&gt;choice support       1.00 @{support=1; sales=0}&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The decision model pickup “support” with confidence, but it was just a test, and this example says nothing on how Jev can help in day to day task off a Cloud Engineer.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to handle resource change and catch risky operation with Bicep -whatif
&lt;/h2&gt;

&lt;p&gt;One of the first use case I have in mind is Infrastructure as Code change. In Terraform or Bicep it you have a complex infrastructure, the Terraform Plan or the Bicep -WhatIf will be dense. An you will have often several questions: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do the change after a part of critical resource?&lt;/li&gt;
&lt;li&gt;Do it weaken security on resources?&lt;/li&gt;
&lt;li&gt;What will happen if something go wrong&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If for example you use Bicep to manage a complex Azure Network Infrastructure, with Express Route Gateway, Circuit, VPN connection, you probably review the -whatif output before executing the change in a pipeline. &lt;/p&gt;

&lt;p&gt;Here, Jev can help, the WhatIf output will be our state, or context. &lt;br&gt;
Bicep WhatIf output is verbose and need to be cleaned and shorted for Jev. The output I used here is a 55Kb file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ResourceId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ChangeType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Before"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"After"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;XXXX&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some parts of the file are useless. When the “changeType” property is “noChange” or “ignore” should be striped before sending to Jev.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$whatIf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$WhatIfFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Raw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertFrom-Json&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$changes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$whatIf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Changes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Property&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ChangeType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-NotIn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="s1"&gt;'NoChange'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Ignore'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the data still have the full Before and After payload of the resource, so the few properties that change are buried in ids. We only need to have the change type, the resource and the changed property paths.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$summary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$changes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;After&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;After&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Before&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ChangeType&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$resource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$resource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ChangeType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Create'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s1"&gt;'  properties: '&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$resource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;properties&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertTo-Json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Compress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Depth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"  &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PropertyChangeType&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Path&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can create an array of question.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$questions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;New-JevYesNoQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;security&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Do the changes in `whatif` weaken the security of the environment?'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-TrueCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Password login enabled on a VM, no NSG on a NIC or subnet, a security setting removed or relaxed'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-FalseCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Security settings are unchanged or stronger'&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="err"&gt;New-JevYesNoQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;public_ip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Do the changes in `whatif` expose a resource to the internet with a public IP?'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-TrueCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'A public IP address is created or attached to a NIC, a VM or a load balancer'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-FalseCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'No public IP is created or attached'&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="err"&gt;New-JevQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;risk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Score&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-Instructions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'How risky is it to deploy the changes in `whatif`?'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-Criteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s1"&gt;'No risk: cosmetic or read-only property changes'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s1"&gt;'Low: new isolated resources'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s1"&gt;'Medium: changes to shared network resources (VNets, peerings, route tables)'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s1"&gt;'High: an outage or a security incident is likely'&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And get the answer. Here you can see that the decision stays in the code and can be used later&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;whatif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$summary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;`n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$answers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Invoke-Jev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$questions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Mock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$Mock&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;answers&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$reasons&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$answers&lt;/span&gt;&lt;span class="err"&gt;.security.noul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Weakens security'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$answers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;public_ip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;noul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Exposes a public IP'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$answers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;risk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;score&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-ge&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Risk level &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$answers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;risk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;score&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pscustomobject&lt;/span&gt;&lt;span class="p"&gt;]@{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;Decision&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$reasons&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Review'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Pass'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;Reasons&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$reasons&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'; '&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;Changes&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="nv"&gt;$changes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Count&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;Security&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;Round&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$answers&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;security&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;noul&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;PublicIp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;Round&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$answers&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ip&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;noul&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;Risk&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$answers&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;risk&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Decision : Review&lt;br&gt;
Reasons  : Weakens security; Exposes a public IP; Risk level 2.72&lt;br&gt;
Changes  : 16&lt;br&gt;
Security : 0.9&lt;br&gt;
PublicIp : 0.97&lt;br&gt;
Risk     : 2.72&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Mixing PowerShell result and Jev insights.
&lt;/h2&gt;

&lt;p&gt;Another use case for a decision model is reviewing NSG configurations, especially when a simple PowerShell query is insufficient. A decision model can provide insights beyond PowerShell’s capabilities.&lt;/p&gt;

&lt;p&gt;In this example, PowerShell is used for deterministic checks (like a management port open to the Internet) and Jev to check the rule to NSG best practices. &lt;/p&gt;

&lt;p&gt;First part PowerShell need to test rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$managementPorts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3389&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5985&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5986&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$internetSources&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Internet'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'0.0.0.0/0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Any'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# merge singular and plural rules &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nf"&gt;Get-RuleValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$Single&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$Plural&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;@(@(&lt;/span&gt;&lt;span class="nv"&gt;$Single&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="nv"&gt;$Plural&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-FilterScript&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# Then a function to test the port&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Test-PortMatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]]&lt;/span&gt;&lt;span class="nv"&gt;$Range&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[]]&lt;/span&gt;&lt;span class="nv"&gt;$Port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Range&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'^(\d+)-(\d+)$'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nv"&gt;$low&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$high&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="bp"&gt;$Matches&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="bp"&gt;$Matches&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilterScript&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-ge&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$low&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-le&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$high&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;elseif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="bp"&gt;$false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Get-DeterministicFinding&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mandatory&lt;/span&gt;&lt;span class="p"&gt;)][&lt;/span&gt;&lt;span class="n"&gt;pscustomobject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$Rule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Rule&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Direction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-ne&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Inbound'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Rule&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Access&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-ne&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Allow'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Rule&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilterScript&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$internetSources&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Rule&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DestinationPort&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'All ports open inbound from the internet'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;elseif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-PortMatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Rule&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DestinationPort&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$managementPorts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Management port open inbound from the internet'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Raw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertFrom-Json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-AsHashtable&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$rules&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$nsg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;networkSecurityGroups&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$subnets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="nv"&gt;$nsg&lt;/span&gt;&lt;span class="err"&gt;.properties.subnets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ForEach-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-split&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$nics&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="nv"&gt;$nsg&lt;/span&gt;&lt;span class="err"&gt;.properties.networkInterfaces&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ForEach-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-split&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$nsg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;properties&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;securityRules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;properties&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pscustomobject&lt;/span&gt;&lt;span class="p"&gt;]@{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;Nsg&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$nsg&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;Rule&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;Priority&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;priority&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;Access&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;Protocol&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;Source&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="err"&gt;Get-RuleValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Single&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="err"&gt;.sourceAddressPrefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Plural&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="err"&gt;.sourceAddressPrefixes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;Destination&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="err"&gt;Get-RuleValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Single&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="err"&gt;.destinationAddressPrefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Plural&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="err"&gt;.destinationAddressPrefixes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;DestinationPort&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="err"&gt;Get-RuleValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Single&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="err"&gt;.destinationPortRange&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Plural&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="err"&gt;.destinationPortRanges&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;Description&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Add-Member&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-NotePropertyName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HardFindings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-NotePropertyValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="err"&gt;Get-DeterministicFinding&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Rule&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Add-Member&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-NotePropertyName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-NotePropertyValue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;ordered&lt;/span&gt;&lt;span class="p"&gt;]@{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nx"&gt;nsg&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ordered&lt;/span&gt;&lt;span class="p"&gt;]@{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$nsg&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;associated_subnets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="nv"&gt;$subnets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ForEach-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ordered&lt;/span&gt;&lt;span class="p"&gt;]@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;purpose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnetPurpose&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'unknown'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;associated_nics&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$nics&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ordered&lt;/span&gt;&lt;span class="p"&gt;]@{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Rule&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;priority&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Priority&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;access&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Access&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Protocol&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Source&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Destination&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;destination_ports&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DestinationPort&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Description&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;??&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'(none)'&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nx"&gt;nsg_standard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$standard&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the Jev part. We need to setup the standard for NSG rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$standard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sh"&gt;@'
NSG STANDARD (sample)
- Management ports (22, 3389, 5985, 5986) are only allowed from the Azure Bastion subnet or the snet-mgmt subnet.
- No inbound allow rule may use source * or Internet, except on subnets explicitly marked as public-facing, and only for 80/443.
- Data subnets accept traffic only from the application subnets of the same landing zone, on the data ports they need.
- Use specific ports; port ranges wider than 10 ports need a documented reason in the rule description.
- Prefer Application Security Groups or specific subnet prefixes over the VirtualNetwork service tag.
- Every custom rule must have a description stating its purpose and owner.
- Temporary rules must include an expiry date in the description.
'@&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$questions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;New-JevQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;intent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Choice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-Instructions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'What is `rule` most likely for, given the purpose of the subnets in `nsg`?'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-Criteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ordered&lt;/span&gt;&lt;span class="p"&gt;]@{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;app_traffic&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Application traffic between tiers or from clients'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;management_access&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Administrative access (SSH, RDP, WinRM, Bastion)'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;platform_dependency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Platform needs: DNS, identity, monitoring, backup, load balancer probes'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;vendor_or_partner&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Access for a third party, vendor or partner network'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;temporary_or_test&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Troubleshooting, test, migration or other temporary access'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;unclear&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Purpose cannot be determined from the rule and its context'&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;New-JevYesNoQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compliant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Does `rule` comply with every applicable point of `nsg_standard`?'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-TrueCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'No point of the standard is violated by the rule as configured'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-FalseCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'At least one point of the standard is violated'&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;New-JevYesNoQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;broader_than_needed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Is the source, destination or port scope of `rule` broader than its apparent purpose requires?'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-TrueCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'All ports for a web tier, VirtualNetwork tag where one subnet would do, large CIDR, * source'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-FalseCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Specific ports, specific subnets or ASGs matching the purpose'&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;New-JevYesNoQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;looks_temporary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Does the name or description of `rule` suggest a temporary, test or troubleshooting rule?'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-TrueCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Words like temp, test, debug, troubleshoot, migration, POC, a ticket number, or a past date'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-FalseCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Name and description describe a permanent, owned purpose'&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;New-JevYesNoQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;description_mismatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Is the description of `rule` missing, or does it contradict what the rule actually allows?'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-TrueCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'No description, or the description names other ports, sources or purposes than configured'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-FalseCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'The description accurately states what the rule allows and why'&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;New-JevQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;severity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Score&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-Instructions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'What is the security severity of `rule` as currently configured?'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-Criteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s1"&gt;'None: correct and tightly scoped'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s1"&gt;'Low: hygiene or documentation issue'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s1"&gt;'Moderate: broader than needed, no internet exposure'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s1"&gt;'High: exposes a sensitive subnet or breaks segmentation'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s1"&gt;'Critical: direct internet exposure of sensitive services'&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can invoke Jev for each rules&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Invoke-Jev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$questions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Mock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$Mock&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;answers&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$action&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HardFindings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'security-ticket-p1'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c"&gt;# deterministic, never overridden&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;elseif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;severity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;score&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-ge&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'security-review'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;elseif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;confidence&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-lt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'human-review'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;elseif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;looks_temporary&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;noul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'owner-confirm-or-remove'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;elseif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;broader_than_needed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;noul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'tighten-rule'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;elseif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compliant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;noul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-lt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'non-compliant-owner-ticket'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;elseif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;description_mismatch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;noul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'fix-description'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'ok'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$findings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="err"&gt;.HardFindings&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="err"&gt;.compliant.noul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-lt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Not compliant with NSG standard'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;broader_than_needed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;noul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Scope broader than needed'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;looks_temporary&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;noul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Looks temporary'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;description_mismatch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;noul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Description missing or misleading'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pscustomobject&lt;/span&gt;&lt;span class="p"&gt;]@{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Nsg&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Nsg&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Rule&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Rule&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$action&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Severity&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HardFindings&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Count&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;Round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;severity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Findings&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$findings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'; '&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Intent&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choice&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Compliant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;Round&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compliant&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;noul&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Ports&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DestinationPort&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Source&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$rule&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nsg       : nsg-vm-legacy-01&lt;br&gt;
Rule      : allow-winrm&lt;br&gt;
Action    : security-review&lt;br&gt;
Severity  : 3.08&lt;br&gt;
Findings  : Not compliant with NSG standard&lt;br&gt;
Intent    : vendor_or_partner&lt;br&gt;
Compliant : 0.06&lt;br&gt;
Ports     : 5985&lt;br&gt;
Source    : 203.0.113.0/24&lt;br&gt;
Nsg       : nsg-vnet-spoke&lt;br&gt;
Rule      : allow-ssh-any&lt;br&gt;
Action    : security-ticket-p1&lt;br&gt;
Severity  : 4&lt;br&gt;
Findings  : Management port open inbound from the internet; Not compliant with NSG standard; Scope broader than needed&lt;br&gt;
Intent    : management_access&lt;br&gt;
Compliant : 0.02&lt;br&gt;
Ports     : 22&lt;br&gt;
Source    : *&lt;br&gt;
Nsg       : nsg-vnet-spoke&lt;br&gt;
Rule      : allow-port-range&lt;br&gt;
Action    : tighten-rule&lt;br&gt;
Severity  : 2.1&lt;br&gt;
Findings  : Not compliant with NSG standard; Scope broader than needed&lt;br&gt;
Intent    : app_traffic&lt;br&gt;
Compliant : 0.06&lt;br&gt;
Ports     : 1024-65535&lt;br&gt;
Source    : 10.2.1.0/24&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Using Azure Policy Linter to control policy definition
&lt;/h2&gt;

&lt;p&gt;Last example, working with Azure Policy definition. For testing definition, we first need a &lt;a href="https://github.com/Azure/azure-policy-linter" rel="noopener noreferrer"&gt;linter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The result.json file contains the finding of the linter on specific definition part with a severity (error/warning/informational). &lt;/p&gt;

&lt;p&gt;The rule for a decision model is to turn these finding into actionable items. &lt;br&gt;
First step, get the content for each policy and run the linter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$resultFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Join-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ChildPath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'PolicyResult.json'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-ChildItem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'*.json'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Property&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-NE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'PolicyResult.json'&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="bp"&gt;$null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;policylinter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$files&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FullName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$resultFile&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$LASTEXITCODE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-ne&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"policylinter failed with exit code &lt;/span&gt;&lt;span class="nv"&gt;$LASTEXITCODE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$lint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$resultFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Raw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertFrom-Json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-AsHashtable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnumerator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ForEach-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$lint&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;Split&lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Leaf&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="err"&gt;.Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the Jev part.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$questions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;New-JevYesNoQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;findings_matter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Does at least one item in `linter_findings` mean `policy` may not evaluate or enforce as intended?'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-TrueCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'A finding changes which resources match: wrong array logic, missing alias, broken condition'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-FalseCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Findings are style, old API versions, optional properties or best-practice hints, or there are no findings'&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="err"&gt;New-JevYesNoQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;description_matches&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Do displayName and description of `policy` match what policyRule really evaluates and its default effect?'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-TrueCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Same resource types, same condition, same effect as the rule'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-FalseCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'The text promises other resource types, another condition or a stronger effect than the rule'&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="err"&gt;New-JevYesNoQuestion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;weakens_security&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Could assigning `policy` weaken security?'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-TrueCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'The remediation opens network access, uses hard-coded public IP ranges, or grants a broader role than needed (Owner, Contributor)'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;-FalseCriteria&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'The policy only audits or denies, or remediates with a least-privilege role'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;For&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Jev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;called&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$definition&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Get-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FullName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Raw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertFrom-Json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;properties&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$findings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$lint&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ordered&lt;/span&gt;&lt;span class="p"&gt;]@{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ordered&lt;/span&gt;&lt;span class="p"&gt;]@{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;displayName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$definition&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;displayName&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$definition&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$definition&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;policyRule&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$definition&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;policyRule&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;linter_findings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="nv"&gt;$findings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ForEach-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;severity&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ruleIdentifier&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; at &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Invoke-Jev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-State&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Question&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$questions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Mock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$Mock&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;answers&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$action&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;weakens_security&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;noul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Security review'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;elseif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findings_matter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;noul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Fix the rule'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;elseif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;description_matches&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;noul&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-lt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Fix the description'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;elseif&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$findings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Backlog'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'OK'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pscustomobject&lt;/span&gt;&lt;span class="p"&gt;]@{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Policy&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Warnings&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="nv"&gt;$findings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Property&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;severity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-EQ&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Warning'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Count&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Informational&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="nv"&gt;$findings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Property&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;severity&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-EQ&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Informational'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Count&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;FindingsMatter&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;Round&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findings_matter&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;noul&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;DescriptionMatches&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;Round&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description_matches&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;noul&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;WeakensSecurity&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;Round&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$a&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;weakens_security&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;noul&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Action&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$action&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Policy             : policy4.json&lt;br&gt;
Warnings           : 1&lt;br&gt;
Informational      : 0&lt;br&gt;
FindingsMatter     : 0.28&lt;br&gt;
DescriptionMatches : 0.68&lt;br&gt;
WeakensSecurity    : 0.87&lt;br&gt;
Action             : Security review&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These are a few examples of what a decision model can do in cloud engineering. Decision model can do more. Our daily work generates a constant stream of data: metrics, logs, IaC plans, traces, network routes. Most of it contains noise, and we already rely on scripts and observability tools to turn it into tasks. Jev fits right into that chain. It applies judgment to your data, against your rules, and leaves the final decision in your code.&lt;/p&gt;

&lt;p&gt;Volume is also why a general-purpose LLM is often too expensive for this kind job. Jev isn't: everything in this article used 103,525 tokens, for a total of $0.0039.&lt;/p&gt;

&lt;p&gt;You can find the code of this post &lt;a href="https://github.com/omiossec/Jev-PowerShell-Cloud-Eng" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>azure</category>
      <category>cloud</category>
      <category>powershell</category>
    </item>
    <item>
      <title>Azure Virtual Network Routing Appliance: peering performance, hub-and-spoke control</title>
      <dc:creator>Olivier Miossec</dc:creator>
      <pubDate>Mon, 21 Sep 2026 20:06:37 +0000</pubDate>
      <link>https://dev.to/omiossec/azure-vnra-peering-performance-hub-and-spoke-control-5oa</link>
      <guid>https://dev.to/omiossec/azure-vnra-peering-performance-hub-and-spoke-control-5oa</guid>
      <description>&lt;p&gt;Yet another routing appliance? The name of this new Azure service is confusing. Is it a sort of NVA? A new kind of routing engine? No, the name tells too little, you need to look at the problem it solves.&lt;/p&gt;

&lt;p&gt;When two applications in different VNETs need to talk (the East-West pattern), you have two options.&lt;/p&gt;

&lt;p&gt;The first is VNET peering. Throughput and latency are excellent: traffic stays on the Azure backbone, and VMs behave as if they were on the same network. But peering isn't transitive. With more VNETs that need to talk to each other means peerings. Difficult to control and manage at scale.&lt;/p&gt;

&lt;p&gt;The second is to route through a central device in a hub: Azure Firewall, a third-party NVA, or even your on-premises network. You get central control and visibility, but every flow now depends on the capacity of that device. It becomes the bottleneck. A NVA will be blocked by  the VMs capacity, Azure Firewall can manage up to 100 Gbps but you will have to pay the price. &lt;/p&gt;

&lt;p&gt;Most companies chose the second option and accepted the performance hit in exchange for more governance. But this trade-off become harder to defend when workloads move large volumes of data between VNETs: data pipelines, centralized databases, AI workloads reading from storage in another landing zone, agents calling APIs across the Azure tenant.&lt;/p&gt;

&lt;p&gt;The Azure Virtual Network Routing Appliance is between the two options. It's an Azure-managed forwarding layer that you deploy in your hub (or any central VNETs, you don't need a gateway to make it work). It runs on dedicated networking hardware, directly inside the Azure Backbone, and comes in 10, 50, 100 or 200 Gbps tiers. VNETs route through it the way they would through an NVA, without the NVA bottleneck.&lt;/p&gt;

&lt;p&gt;It is not a firewall. It doesn't inspect traffic. Control comes from the Azure-native tools around it: UDRs, NSGsand Azure Monitor metrics. If controlling flows is needed, keep your firewall in the path for those flows.&lt;/p&gt;

&lt;p&gt;Deploying it takes three steps. First, register the feature on your subscription:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Register-AzProviderFeature&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FeatureName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AllowVirtualNetworkAppliance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;–&lt;/span&gt;&lt;span class="nx"&gt;ProviderNamespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Microsoft.Network&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second step is to create a virtual network (or use an existing one) with a dedicated subnet, named VirtualNetworkApplianceSubnet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource hubVnet 'Microsoft.Network/virtualNetworks@2024-05-01' = {
&amp;nbsp; name: 'hub-vnet'
&amp;nbsp; location: location
&amp;nbsp; properties: {
&amp;nbsp; &amp;nbsp; addressSpace: {
&amp;nbsp; &amp;nbsp; &amp;nbsp; addressPrefixes: [
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 10.0.0.0/21
&amp;nbsp; &amp;nbsp; &amp;nbsp; ]
&amp;nbsp; &amp;nbsp; }
&amp;nbsp; &amp;nbsp; subnets: [
&amp;nbsp; &amp;nbsp; &amp;nbsp; {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; name:VirtualNetworkApplianceSubnet
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; properties: {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; addressPrefix: 10.0.0.0/24
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }
&amp;nbsp; &amp;nbsp; &amp;nbsp; }
&amp;nbsp; &amp;nbsp; ]
&amp;nbsp; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can deploy the Virtual Network Routing Appliance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource routingAppliance 'Microsoft.Network/virtualNetworkAppliances@2025-07-01' = {
&amp;nbsp; name: 'vnra-test'
&amp;nbsp; location: location
&amp;nbsp; properties: {
&amp;nbsp; &amp;nbsp; bandwidthInGbps: 10
&amp;nbsp; &amp;nbsp; privateIPAddressVersion: 'IPv4'
&amp;nbsp; &amp;nbsp; subnet: {
&amp;nbsp; &amp;nbsp; &amp;nbsp; id: '${hubVnet.id}/subnets/VirtualNetworkApplianceSubnet'
&amp;nbsp; &amp;nbsp; }
&amp;nbsp; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once it's deployed, the portal view is nearly empty. There's nothing to configure beyond the properties.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxs3yunjvyoa70zpstxrg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxs3yunjvyoa70zpstxrg.png" alt=" " width="799" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The interesting part is the Metrics blade. Bytes, packets and flows, inbound and outbound, are available out of the box, with no diagnostic settings to configure. That's your visibility on east-west traffic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Furtze2cblmd9dhyew4ng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Furtze2cblmd9dhyew4ng.png" alt=" " width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With PowerShell, &lt;code&gt;Get-AzVirtualNetworkAppliance&lt;/code&gt; gives you a bit more detail. In the IP configuration you'll see several &lt;code&gt;ipconfig_4_xxxx&lt;/code&gt; entries, a hint that more than one instance sits behind the single private IP you route to&lt;/p&gt;

&lt;p&gt;So how do spokes VNETs can use it? First, each spoke need a peering with the hub VNET. And, make sure that spoke VNETs are not peered with each other.&lt;/p&gt;

&lt;p&gt;Peering alone isn't enough. Every spoke subnet that should use the appliance needs a UDR pointing to the appliance's private IP as next hop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource spokeRouteTables 'Microsoft.Network/routeTables@2024-05-01' = 
  name: 'vnra-to-other-spoke-rt'
  location: location
  properties: {
    disableBgpRoutePropagation: false
    routes: {
      name: 'to-appliance-10-2-0-0_16'
      properties: {
        addressPrefix: 10.2.0.0/16
        nextHopType: 'VirtualAppliance'
        nextHopIpAddress: 10.0.0.4
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply the route tables on both sides. With that in place, workloads in two spokes that aren't peered together can talk through the hub, at close to peering performance.&lt;/p&gt;

&lt;p&gt;After these operations you should have this architecture. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2l15yr1d666545873yow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2l15yr1d666545873yow.png" alt=" " width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also send &lt;code&gt;0.0.0.0/0&lt;/code&gt; to the appliance so every spoke gets the same route table. The appliance doesn't NAT, so internet traffic still needs a next hop that does, configured on the appliance subnet. And check the return path, especially for Private Endpoint traffic. The simplest design is still RFC1918 to the appliance and the default route to your firewall.&lt;/p&gt;

&lt;p&gt;The service went GA in August 2026, so several preview restriction are gone: IPv6 and dual-stack are supported, and the region list has grown. A few limits remain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two appliances per region per subscription.&lt;/li&gt;
&lt;li&gt;Bandwidth is fixed at creation. To change it, you need to delete and redeploy the appliance.&lt;/li&gt;
&lt;li&gt;VNET flow logs is not supported yet.&lt;/li&gt;
&lt;li&gt;Terraform needs the AzAPI provider; AzureRM doesn't support it.&lt;/li&gt;
&lt;li&gt;Traffic through the appliance isn't covered by VNET encryption.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Virtual Network Routing Appliance doesn't replace your firewall. It removes the reason your firewall was doing routing in the first place. If your hub firewall today spends most of its time forwarding east-west traffic, this is worth a look: send private traffic through VNRA, and keep the firewall for the flows that actually need inspection.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>networking</category>
    </item>
    <item>
      <title>Prompting Anthropic v5 for Cloud Engineers: Practical Rules for Sonnet 5, Open 5, and Fable 5.1</title>
      <dc:creator>Olivier Miossec</dc:creator>
      <pubDate>Mon, 14 Sep 2026 21:22:18 +0000</pubDate>
      <link>https://dev.to/omiossec/prompting-anthropic-v5-for-cloud-engineers-practical-rules-for-sonnet-5-open-5-and-fable-51-5hbe</link>
      <guid>https://dev.to/omiossec/prompting-anthropic-v5-for-cloud-engineers-practical-rules-for-sonnet-5-open-5-and-fable-51-5hbe</guid>
      <description>&lt;p&gt;With each new model generation, Anthropic updates its prompting best practices, and the 5th generation is no exception. &lt;br&gt;
This post distils the practical guidance from Anthropic’s documentation into actionable rules for Sonnet 5, Open 5, and Fable 5.1, focused on cloud engineering workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rules for every current model
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Give the reason, not just the rule&lt;/strong&gt;&lt;br&gt;
The model will use the why or the reason to generalise and find an answer on what wasn’t present in the prompt. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Don’t do&lt;/em&gt;: Don't use Write-Host in the script.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Do&lt;/em&gt;: Don't use Write-Host: this script runs as an Azure DevOps pipeline task where only the streams are captured, and Write-Host output is invisible in the job log.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write the output contract explicitly&lt;/strong&gt;&lt;br&gt;
The model has no idea of your convention and format. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Don’t do&lt;/em&gt;: Write me a PowerShell function to get ExpressRoute circuit status.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Do&lt;/em&gt;: Write a PowerShell 7 function Get-ErCircuitHealth. Contract: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[CmdletBinding()], approved Verb-Noun, singular noun, comment-based help with at least one .EXAMPLE &lt;/li&gt;
&lt;li&gt;Accepts -SubscriptionId (mandatory, ValidateNotNullOrEmpty) and -ResourceGroupName (optional); pipeline input by property name - Emits one [PSCustomObject] per circuit: CircuitName, ServiceProviderProvisioningState, CircuitProvisioningState, PeeringLocation, BandwidthInMbps. 
No formatted text, no Write-Host &lt;/li&gt;
&lt;li&gt;try/catch with -ErrorAction Stop inside the try; rethrow with context, never an empty catch &lt;/li&gt;
&lt;li&gt;Filter server-side via Az cmdlet parameters, not | Where-Object after the fact&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Show examples instead of describing style&lt;/strong&gt;&lt;br&gt;
Using 3-5 examples help the model to get what you want. Use &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Don’t do&lt;/em&gt;: Write Azure Policy definitions in our usual style.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Do&lt;/em&gt;: Write an Azure Policy definition that denies public network access on Storage accounts. Match the conventions in these examples &lt;br&gt;
— note that they cover deny, audit and deployIfNotExists, and both a single-condition and an allOf policyRule: &lt;br&gt;
 { ...our existing deny-public-ip policy JSON... }   { ...our existing audit-diagnostic-settings policy JSON... }  &lt;br&gt;
 { ...our existing deployIfNotExists policy JSON... } &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State what to do, not what to avoid&lt;/strong&gt;&lt;br&gt;
a prohibition describes a space to stay out of; an instruction describes where to go.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Don’t do&lt;/em&gt;: Don't use hardcoded values.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Do&lt;/em&gt;: Every environment-varying value comes from a variable with a type constraint and a description; defaults only where a sane default genuinely exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In long context, documents first, question after&lt;/strong&gt;&lt;br&gt;
Anthropic measured up to a 30% quality improvement from putting the query after multi-document inputs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Don’t do&lt;/em&gt;: Which of our landing zone modules still create NSG rules with source *? &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Do&lt;/em&gt;:  XXX . &lt;br&gt;
Before answering, quote the specific resource blocks that are relevant to the question. Then: which of these modules still create NSG rules with source "*", and which line in each?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cap the complexity explicitly&lt;/strong&gt;&lt;br&gt;
The instruction that works is a ceiling, not a vague plea for simplicity&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Don’t do&lt;/em&gt;: Keep it simple. Add a retry to this Az module call.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Do&lt;/em&gt;: Add a retry with exponential backoff to this Az module call. Scope: this call only. Don't refactor the surrounding function, don't extract a generic retry helper, don't add a config file for the retry count, and don't add validation for states that can't occur&lt;/p&gt;

&lt;h2&gt;
  
  
  Specific rules for Opus 5
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Delete your verification scaffolding&lt;/strong&gt;&lt;br&gt;
Opus 5 verifies its own work unprompted. Instructions to verify stack on top of built-in behaviour and produce over-verification.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Don’t do&lt;/em&gt;: After writing the Terraform module, include a final verification step. Re-check your work before responding.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Do&lt;/em&gt;: Write the Terraform module&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Soften the tool language you inherited&lt;/strong&gt;&lt;br&gt;
older models were lazy when calling tools, this is not the case with Opus 5&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Don’t do&lt;/em&gt;: CRITICAL: You MUST call get_policy_assignments whenever the user mentions policy. NEVER answer policy questions from memory. ALWAYS verify with the tool first.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Do&lt;/em&gt;: Use get_policy_assignments when you need the current assignment state for a specific scope. Answer conceptual questions about how Azure Policy works directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Specific rules for Sonnet 5
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Fix shallow reasoning with effort&lt;/strong&gt;&lt;br&gt;
Sonnet 5 respects effort strictly, especially at the low end. At low and medium it scopes work to exactly what was asked and does not go above and beyond&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Don’t do&lt;/em&gt;: [effort: low] Think step by step. Be extremely thorough. Consider all edge cases. Analyze deeply before answering: why does the route propagation fail between these two vWAN hubs but not the third?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Do&lt;/em&gt;: [effort: high — or xhigh for the hardest agentic and coding work] Why does route propagation fail between these two vWAN hubs but not the third?&lt;/p&gt;

&lt;h2&gt;
  
  
  Specific rules for Fable 5
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Draw the line between assessment and action&lt;/strong&gt;&lt;br&gt;
Fable 5 can take unrequested actions&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Don’t do&lt;/em&gt;: The ExpressRoute peering keeps dropping on the secondary connection. Route table looks wrong to me.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Do&lt;/em&gt;: The ExpressRoute peering keeps dropping on the secondary connection. Route table looks wrong to me. &lt;br&gt;
I'm describing a problem, not requesting a change: the deliverable is your assessment. Investigate and report findings, then stop. Don't apply a fix until I ask. &lt;br&gt;
Before proposing any command that changes state; resetting a peering, editing a route table, restarting a gateway, check that the evidence actually supports that specific action rather than a failure mode it resembles.&lt;/p&gt;

&lt;p&gt;never instruct it to echo, transcribe or explain its internal reasoning&lt;br&gt;
That will trigger the reasoning_extraction refusal category and may stop the session. &lt;/p&gt;

&lt;p&gt;You can get the full reference &lt;a href="https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/overview" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>cloud</category>
      <category>azure</category>
    </item>
    <item>
      <title>Azure Route-Map: Route and traffic engineering in Azure</title>
      <dc:creator>Olivier Miossec</dc:creator>
      <pubDate>Fri, 07 Aug 2026 19:23:22 +0000</pubDate>
      <link>https://dev.to/omiossec/route-and-traffic-engineering-in-azure-39pj</link>
      <guid>https://dev.to/omiossec/route-and-traffic-engineering-in-azure-39pj</guid>
      <description>&lt;p&gt;Everything in Azure networking is easy until you connect something to it. It's the joke I open with every time I run a training session on how Azure networking actually behaves behind the scenes, and routing is no exception.&lt;/p&gt;

&lt;p&gt;I've already written about routing in Azure with UDRs and the risk of building asymmetric paths (&lt;a href="https://dev.to/omiossec/asymmetrical-routing-in-azure-network-3h3o"&gt;Asymmetrical routing in Azure network&lt;/a&gt;). This post goes one level deeper: modifying the BGP advertisements themselves.&lt;/p&gt;

&lt;p&gt;That's traffic engineering. It's routine practice in on-premises networking and a new one in Azure. Two features carry most of the weight today: &lt;strong&gt;advertised gateway prefixes&lt;/strong&gt; on a hub VNET, and &lt;strong&gt;Route-maps&lt;/strong&gt; in Virtual WAN and Azure Route Server (in public preview).&lt;/p&gt;

&lt;h2&gt;
  
  
  Advertised gateway prefixes
&lt;/h2&gt;

&lt;p&gt;Azure virtual networks expose a property called &lt;code&gt;summarizedGatewayPrefixes&lt;/code&gt; (GA since &lt;a href="https://azure.microsoft.com/en-us/updates/?id=569743" rel="noopener noreferrer"&gt;2026-08-20&lt;/a&gt;). It lets you hand the gateway a list of CIDR blocks to advertise &lt;strong&gt;instead of&lt;/strong&gt; its default advertisement.&lt;/p&gt;

&lt;p&gt;The default behaviour is straightforward: an ExpressRoute Gateway or VPN Gateway advertises every address space of its own VNet, plus every address space of each peered VNet using gateway transit. In a hub-and-spoke topology, that means every spoke prefixes are advertised to the gateway.&lt;/p&gt;

&lt;p&gt;So instead of advertising &lt;code&gt;10.0.0.0/24&lt;/code&gt;, &lt;code&gt;10.0.1.0/24&lt;/code&gt; and &lt;code&gt;10.0.2.0/24&lt;/code&gt; separately, you configure &lt;code&gt;10.0.0.0/22&lt;/code&gt; on the hub VNet and the gateway advertises that single prefix. For each peered spoke, Azure checks whether the spoke's address space is covered by a summary — if it is, the spoke prefix is suppressed. If it isn't, it keeps being advertised individually.&lt;/p&gt;

&lt;p&gt;ExpressRoute private peering caps the number of prefixes that can be advertised on a single connection to 1,000 IPv4 and 100 IPv6. Cross the ceiling and the session drops. That is not a graceful degradation; it's an outage. In a Hub VNet with a few hundred spokes and multiple address spaces per spoke, you get there faster than you'd like.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The property only takes effect on a VNet that has a &lt;code&gt;GatewaySubnet&lt;/code&gt; &lt;strong&gt;and&lt;/strong&gt; a deployed gateway. You can set it earlier; it just does nothing.&lt;/li&gt;
&lt;li&gt;It applies to both VPN Gateway and ExpressRoute Gateway.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Route Maps
&lt;/h2&gt;

&lt;p&gt;This used to be the only BGP manipulation available outside Virtual WAN. That changed: &lt;a href="https://azure.microsoft.com/en-us/updates/?id=568631" rel="noopener noreferrer"&gt;Route Maps for Azure Route Server&lt;/a&gt; entered public preview recently.&lt;/p&gt;

&lt;p&gt;Route-maps is a policy engine on the virtual hub. It inspects, modifies and filters the routing information exchanged between the hub and a connection, in the inbound direction, the outbound direction, or both.&lt;/p&gt;

&lt;p&gt;It applies to &lt;strong&gt;ExpressRoute connections, site-to-site VPN connections, point-to-site connections, and VNet connections&lt;/strong&gt;. One route-map per direction, per connection.&lt;/p&gt;

&lt;p&gt;Worth knowing up front: you cannot apply a route-map to the connection between on-premises and an SD-WAN or firewall NVA deployed in the hub, other connections in that hub still work normally. &lt;/p&gt;

&lt;p&gt;A route-map is an ordered list of rules. Each rule has match conditions and actions, plus a &lt;strong&gt;Next step&lt;/strong&gt; setting that decides whether a matched route continues to the following rules or terminates there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Match conditions&lt;/strong&gt; — &lt;code&gt;Route-prefix&lt;/code&gt;, &lt;code&gt;AS-Path&lt;/code&gt;, &lt;code&gt;Community&lt;/code&gt;, each with an &lt;code&gt;equals&lt;/code&gt; or &lt;code&gt;contains&lt;/code&gt; criterion:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Criterion&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Route-prefix&lt;/td&gt;
&lt;td&gt;equals&lt;/td&gt;
&lt;td&gt;Matches those exact routes only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Route-prefix&lt;/td&gt;
&lt;td&gt;contains&lt;/td&gt;
&lt;td&gt;Matches those routes and everything more specific underneath&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS-Path&lt;/td&gt;
&lt;td&gt;equals&lt;/td&gt;
&lt;td&gt;AS-PATH must contain the listed ASNs in that order&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS-Path&lt;/td&gt;
&lt;td&gt;contains&lt;/td&gt;
&lt;td&gt;AS-PATH contains one or more of the listed ASNs, order irrelevant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Community&lt;/td&gt;
&lt;td&gt;equals&lt;/td&gt;
&lt;td&gt;Route must carry all the listed communities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Community&lt;/td&gt;
&lt;td&gt;contains&lt;/td&gt;
&lt;td&gt;Route carries one or more of them&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Multiple match conditions in a rule are evaluated as a logical &lt;strong&gt;AND&lt;/strong&gt;, all of them have to be satisfied. A rule with no match condition matches every route on the connection. If no rule matches, the default is &lt;strong&gt;allow&lt;/strong&gt;, not deny.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actions&lt;/strong&gt; — drop, or modify:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Route-prefix&lt;/td&gt;
&lt;td&gt;Drop&lt;/td&gt;
&lt;td&gt;Matched routes are filtered out of the advertisement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Route-prefix&lt;/td&gt;
&lt;td&gt;Replace&lt;/td&gt;
&lt;td&gt;Matched routes replaced by the prefix(es) in the rule (this is summarization)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS-Path&lt;/td&gt;
&lt;td&gt;Add&lt;/td&gt;
&lt;td&gt;Prepend the listed ASNs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS-Path&lt;/td&gt;
&lt;td&gt;Replace&lt;/td&gt;
&lt;td&gt;Set AS-PATH to the listed ASNs — with no value, clear it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Community&lt;/td&gt;
&lt;td&gt;Add / Replace / Remove&lt;/td&gt;
&lt;td&gt;Add, overwrite, or remove specific communities&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A single rule can carry several modifications; the order in which they're written doesn't matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use cases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Summarization
&lt;/h3&gt;

&lt;p&gt;Same problem as Part 1, this time on a Virtual WAN hub. You're heading toward the ExpressRoute advertised-prefix ceiling and you need to cut the count.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Match:&lt;/strong&gt; Route-prefix, &lt;code&gt;contains&lt;/code&gt;, &lt;code&gt;10.2.0.0/16&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action:&lt;/strong&gt; Route-prefix, &lt;code&gt;Replace&lt;/code&gt;, &lt;code&gt;10.2.0.0/16&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Applied:&lt;/strong&gt; Outbound, on the ExpressRoute connection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of a pile of /24s, the circuit sees one /16.&lt;/p&gt;

&lt;p&gt;Two caveats. Summarization &lt;strong&gt;strips the BGP community and AS-PATH&lt;/strong&gt; from the resulting route. &lt;/p&gt;

&lt;h3&gt;
  
  
  Filtering
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Match:&lt;/strong&gt; Route-prefix, &lt;code&gt;contains&lt;/code&gt;, &lt;code&gt;10.2.0.0/24&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action:&lt;/strong&gt; Drop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The prefix is no longer advertised to that peer. &lt;strong&gt;This is not a firewall.&lt;/strong&gt; You've removed a route, not blocked traffic. If the peer has another path to that destination, a default route, a summary from elsewhere, a User Defined Route, the packets still flow. Filtering is a routing tool. Use Azure Firewall or NSGs for security.&lt;/p&gt;

&lt;h3&gt;
  
  
  BGP communities
&lt;/h3&gt;

&lt;p&gt;A community is a tag attached to a route so that the receiving side can make policy decisions on it. Virtual WAN preserves communities generated by BGP peers, within a hub and across inter-hub connections.&lt;/p&gt;

&lt;p&gt;The natural use is classification. Say you tag by workload tier:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prefix&lt;/th&gt;
&lt;th&gt;Community&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;10.0.0.0/21&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;65001:100&lt;/code&gt;  database services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;10.0.8.0/21&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;65001:200&lt;/code&gt;  production applications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;10.0.16.0/21&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;65001:300&lt;/code&gt;  test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;10.0.24.0/21&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;65001:400&lt;/code&gt;  dev&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Match:&lt;/strong&gt; Route-prefix, &lt;code&gt;contains&lt;/code&gt;, &lt;code&gt;10.0.0.0/21&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action:&lt;/strong&gt; Community, &lt;code&gt;Add&lt;/code&gt;, &lt;code&gt;65001:100&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The connected network can then apply local preference, filtering or QoS policy on the tag rather than maintaining a prefix list that goes stale every time someone deploys a spoke.&lt;/p&gt;

&lt;p&gt;Do not remove the Azure-reserved communities in the &lt;code&gt;65515:*&lt;/code&gt;, &lt;code&gt;65517:*&lt;/code&gt; and &lt;code&gt;65518:*&lt;/code&gt; ranges. They are used internally and stripping them breaks routing in a way that is unpleasant to debug.&lt;/p&gt;

&lt;h2&gt;
  
  
  AS-PATH manipulation
&lt;/h2&gt;

&lt;p&gt;This is the part where most designs go wrong, so it's worth being precise.&lt;/p&gt;

&lt;h3&gt;
  
  
  The scenario
&lt;/h3&gt;

&lt;p&gt;A Virtual WAN with two hubs: &lt;strong&gt;Hub-Paris&lt;/strong&gt; (France Central) and &lt;strong&gt;Hub-London&lt;/strong&gt; (UK South). A branch office runs a site-to-site VPN with BGP to &lt;strong&gt;both&lt;/strong&gt; hubs, so it learns Azure routes from both. An application VNet attached to Hub-Paris advertises &lt;strong&gt;10.2.100.0/24&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Every Virtual WAN hub and gateway uses ASN &lt;strong&gt;65515&lt;/strong&gt;, and that is not configurable. Inter-hub propagation doesn't add an ASN either. So the branch receives &lt;code&gt;10.2.100.0/24&lt;/code&gt; from two neighbours with an &lt;strong&gt;identical AS-PATH&lt;/strong&gt;. Equal length, equal everything, you have no say in which hub it picks.&lt;/p&gt;

&lt;p&gt;Goal: the branch should prefer Paris and keep London as a warm backup.&lt;/p&gt;

&lt;h3&gt;
  
  
  The fix
&lt;/h3&gt;

&lt;p&gt;Apply the route-map &lt;strong&gt;outbound, on the branch's VPN connection in Hub-London&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Match:&lt;/strong&gt; Route-prefix, &lt;code&gt;equals&lt;/code&gt;, &lt;code&gt;10.2.100.0/24&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action:&lt;/strong&gt; AS-Path, &lt;code&gt;Add&lt;/code&gt;, your ASN (repeat it two or three times for margin)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direction:&lt;/strong&gt; Outbound&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection:&lt;/strong&gt; the site-to-site VPN connection to the branch, &lt;strong&gt;in Hub-London only&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The branch now receives the prefix with a one-ASN path from Paris and a longer path from London. Paris wins on AS-PATH length; London stays in the RIB as a backup and takes over if Paris drops.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not inbound at Hub-Paris
&lt;/h3&gt;

&lt;p&gt;Because inbound route-maps are applied &lt;strong&gt;before the route enters the hub's &lt;code&gt;defaultRouteTable&lt;/code&gt;&lt;/strong&gt; — and that table feeds &lt;em&gt;every&lt;/em&gt; propagating connection, including the inter-hub link to London. Prepend inbound at Paris and both hubs advertise the prepended route to the branch. You've made the prefix uniformly less attractive and changed nothing about which hub gets chosen.&lt;/p&gt;

&lt;p&gt;Inbound is the wrong direction for this problem. It's the right direction for a different one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing an ASN
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;2-byte ASNs only.&lt;/strong&gt; If your network runs 4-byte ASNs, you can't use them in a route-map action.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't use reserved numbers:&lt;/strong&gt; public &lt;code&gt;8074&lt;/code&gt;, &lt;code&gt;8075&lt;/code&gt;, &lt;code&gt;12076&lt;/code&gt;; private &lt;code&gt;65515&lt;/code&gt;, &lt;code&gt;65517&lt;/code&gt;, &lt;code&gt;65518&lt;/code&gt;, &lt;code&gt;65519&lt;/code&gt;, &lt;code&gt;65520&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For Express Route only &lt;strong&gt;Don't use private ASNs for prepending.&lt;/strong&gt;  the Microsoft Enterprise Edge router that connect to your network locally removes private ASNs from the AS-PATH, so your prepend never reaches the your routers and you spend an afternoon wondering why nothing changed. This stripping doesn't apply to a VPN-connected branch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never prepend the receiving router's own ASN.&lt;/strong&gt; It will reject the route as a loop. Same reason you can't prepend &lt;code&gt;12076&lt;/code&gt; over ExpressRoute.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;64496&lt;/code&gt;–&lt;code&gt;64511&lt;/code&gt; are RFC 5398 documentation ASNs and are explicitly not permitted in the path over ExpressRoute.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The distinction that matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inbound route-maps can influence Azure's own best-path selection.&lt;/strong&gt; The modified AS-PATH is what lands in &lt;code&gt;defaultRouteTable&lt;/code&gt;, and with hub routing preference set to &lt;em&gt;AS Path&lt;/em&gt;, the hub compares those lengths. This is how you make Azure prefer ExpressRoute over VPN, or one hub over another, for return traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outbound route-maps cannot.&lt;/strong&gt; Best-path selection happens &lt;em&gt;before&lt;/em&gt; outbound route-maps run. Outbound only changes what the peer sees.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  things to remind
&lt;/h3&gt;

&lt;p&gt;One line to remember: &lt;strong&gt;inbound influences Azure's choice; outbound influences your peer's choice.&lt;/strong&gt; Pick the direction that matches whose decision you're trying to change.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Route-maps aren't applied to the virtual hub address space.&lt;/li&gt;
&lt;li&gt;A prefix can be modified by Route-maps &lt;strong&gt;or&lt;/strong&gt; by NAT, not both.&lt;/li&gt;
&lt;li&gt;Your spoke VNet connections must propagate to the Default route table for their prefixes to reach on-premises at all.&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;Terminate&lt;/strong&gt; on the last rule, or you'll spend time debugging a rule you thought had stopped processing.&lt;/li&gt;
&lt;li&gt;Use the Route Map dashboard to verify. It shows the route before and after the map, with AS-PATH and communities — which turns "I think this worked" into "this worked."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Azure has quietly grown a real traffic-engineering toolbox. &lt;code&gt;summarizedGatewayPrefixes&lt;/code&gt; handles route scale on classic hub-and-spoke; Route-maps gives Virtual WAN the prefix, AS-PATH and community control that on-premises networks have had for twenty years; Route Server is catching up.&lt;/p&gt;

&lt;p&gt;But the tools don't change the fundamentals. A summary is a promise about reachability, and a promise your address plan can't keep becomes a blackhole. A dropped route is not a blocked flow. And an AS-PATH prepend only does something if it's applied in the direction of the decision you're trying to influence, which is why the single most useful thing to internalise is &lt;em&gt;inbound changes Azure routes, outbound changes your peer routes&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Route Map can be difficult to master, it is a good advise to confirm your design on a test environment before going in production.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I would like to thank Jose Moreno; his &lt;a href="https://blog.cloudtrooper.net/2026/06/28/optimal-routing-with-expressroute-revisited/" rel="noopener noreferrer"&gt;blog&lt;/a&gt; helped me understand Route Maps. I recommend reading it.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>azure</category>
      <category>networking</category>
    </item>
    <item>
      <title>Local AI with Foundry Local and PowerShell: chat, memory, tools; building a harness.</title>
      <dc:creator>Olivier Miossec</dc:creator>
      <pubDate>Fri, 17 Jul 2026 17:53:05 +0000</pubDate>
      <link>https://dev.to/omiossec/local-ai-with-foundry-local-and-powershell-chat-memory-tools-building-a-harness-3m4g</link>
      <guid>https://dev.to/omiossec/local-ai-with-foundry-local-and-powershell-chat-memory-tools-building-a-harness-3m4g</guid>
      <description>&lt;p&gt;Since June 2026, the frontier labs have not stopped shipping: Fable, Sonnet 5, GPT-5.6 (Sol, Terra and Luna), Grok 4.5 trained on Cursor metrics, without talking about Chinese models DeepSeek R4, Kimi K3 and GLM 5.2. The race for the next frontier model is wide open.&lt;/p&gt;

&lt;p&gt;A quieter signal runs alongside it: the move to open-weight models. Microsoft is testing DeepSeek, Uber Eats and Airbnb are running Qwen, Cursor uses Kimi. These models run on hardware you control, and they do real work for a fraction of the cost of a frontier API call.&lt;/p&gt;

&lt;p&gt;Three reasons push in the same direction: API cost, privacy and data residency (GDPR and the Cloud Act discussion in the EU), and the fact that the gap between open-weight and frontier models has narrowed sharply in the last few months.&lt;/p&gt;

&lt;p&gt;This is where Foundry Local fits. It ships SDKs (Python, C#, Rust, JS, C++) and a CLI exposing a REST API, in under 100 MB. It handles model acquisition and hardware acceleration for you: pick a model from the curated catalogue — Mistral, Phi, Qwen and others — and Foundry Local selects the right backend for your machine (CPU, GPU, NPU).&lt;/p&gt;

&lt;p&gt;What Foundry Local doesn’t give you is the harness: the small program between the model and the real world that holds the state, executes the tools, and decides what comes back. This post is about writing it in PowerShell.&lt;/p&gt;

&lt;p&gt;To install Foundry Local CLI, use the latest release on the GitHub page of the &lt;a href="https://github.com/microsoft/Foundry-Local/releases" rel="noopener noreferrer"&gt;project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Everything below is in preview. This post uses the Foundry Local CLI 0.1.1 and has been tested on CLI 0.1.2.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Scripts used in this post can be find on my &lt;a href="https://github.com/omiossec/demo-foundry-local-powershell" rel="noopener noreferrer"&gt;GitHub repos&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can find a YouTube Video with all the demo &lt;a href="https://www.youtube.com/watch?v=xZI6GLl0S24" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The CLI runs a local service. Everything else in this post talks about it, so start here.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;gt;Foundry server status&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should have something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;StateReady
PID60900
Started2026-07-13 18:58:02Z
Uptime1d 2h 13m 39s
Web URLs http://127.0.0.1:56172
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If not, you can start the server. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;gt;foundry server start&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can get a list of models available for your platform via this command. &lt;br&gt;
&lt;code&gt;&amp;gt;foundry model list&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqeomnp7w1gzrp7h9sfrw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqeomnp7w1gzrp7h9sfrw.png" alt=" " width="799" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To get more information about a particular model, use this command:&lt;br&gt;
&lt;code&gt;&amp;gt;foundry model info mistral-7b-v0.2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsjnbrl5udmbxhtttz2xc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsjnbrl5udmbxhtttz2xc.png" alt=" " width="800" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Models are identified by an alias, and each alias can resolve to several variants. A variant is a build of the model for one acceleration backend, TensorRT or CUDA (NVIDIA), QNN (Qualcomm), or generic, and one device type (CPU, GPU, NPU).&lt;/p&gt;

&lt;p&gt;Foundry Local picks a variant for you, but the pick is only as good as your hardware; a machine with no suitable GPU falls back to NPU.&lt;/p&gt;

&lt;p&gt;The alias usually encodes the parameter count; for mistral-7b-v0.2, it is 7 billion. One exception is the Phi models, which use size tiers (mini, small…). More parameters mean a bigger memory footprint and generally more capability, though architecture and training quality matter at least as much.&lt;/p&gt;

&lt;p&gt;The “model info” command also reports the context window, in tokens. This matters more than it looks: as you approach the limit, older turns get dropped, and the model starts answering confidently from a history it no longer has.&lt;/p&gt;

&lt;p&gt;One last property: whether the model supports tool calling. The model never executes anything itself; it emits a structured request that your code executes and feeds back. In practice, below roughly 3B parameters, tool call reliability drops off fast.&lt;/p&gt;

&lt;p&gt;To be able to use a model, it must be available in the local cache and loaded. &lt;/p&gt;

&lt;p&gt;You can download a model:&lt;br&gt;
&lt;code&gt;&amp;gt;foundry model download qwen3.5-4b&lt;/code&gt;&lt;br&gt;
And then load the model. &lt;br&gt;
&lt;code&gt;&amp;gt;foundry model load qwen3.5-4b&lt;/code&gt;&lt;br&gt;
The latest command will download the model if it is not in the cache and load the model. &lt;br&gt;
You can run the model by typing.&lt;br&gt;
&lt;code&gt;&amp;gt;foundry run qwen3.5-4b&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzu0w7guj0vlkby7e4509.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzu0w7guj0vlkby7e4509.png" alt=" " width="797" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You get a chat with the model; you can type /help to see available commands.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgvivjep2wfnau296ud53.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgvivjep2wfnau296ud53.png" alt=" " width="800" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But this is just a chat. It is limited; there is no context, no memory, no tools, no harness.&lt;br&gt;
To add more control, we can start building a solution in PowerShell using the included REST API. &lt;/p&gt;

&lt;p&gt;The port is assigned at startup and changes when the service restarts. Don't hardcode it; read it back from the foundry server status command, or the SDK, before each session.&lt;br&gt;
&lt;code&gt;&amp;gt;foundry server status&lt;/code&gt;&lt;br&gt;
If the service is not started, you need to use this command:&lt;br&gt;
&lt;code&gt;&amp;gt;foundry server start&lt;/code&gt;&lt;br&gt;
To use the foundry CLI in PowerShell, we need to save the URI given by the command in a variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$restApiUri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:56172"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the first script, we will do a simple chat, a simple question to the model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"qwen3.5-4b"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Give the name of the capital of Belgium"&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

 &lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Method&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Uri&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$restApiUri&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/v1/chat/completions"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ConvertTo&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Depth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;ContentType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Invoke-RestMethod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Foundry Local's REST API is OpenAI-compatible, which is the whole point: the body you build here is the same body you'd send to api.openai.com. Swapping a local model for a hosted one becomes a URL change.&lt;/p&gt;

&lt;p&gt;The result contain an object with different property, among them the model, choices which is an array of HashMap containing the conversation with the model and total_tokens, a map listing used tokens, prompt_tokens, completion_tokens and total_tokens: &lt;br&gt;
&lt;code&gt;usage : @{prompt_tokens=11; completion_tokens=7; total_tokens=18}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It also contains the property Choices, an array that contains the interaction with the model. Normally the Model’s response is in the first (index 0). It contains the message, a map with a role (assistant), a content, the model response and the tool_call. &lt;/p&gt;

&lt;p&gt;To get the message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script so far does nothing the CLI chat didn't. The first real gain is the system prompt: instructions that shape behaviour before the user ever speaks. It's the cheapest control surface you have.&lt;/p&gt;

&lt;p&gt;To do so we need to modify the $message variable and add a hash table with the system role:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$messages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are a PowerShell coding assistant. Only give code if requested."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Give me a PowerShell script to list all files in a given directory."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works, but the model has no memory. Every call is a cold start; nothing carries over. Memory here isn't a feature of the model; it's you resending the transcript on each interaction.&lt;/p&gt;

&lt;p&gt;Which means the transcript needs to preserve insertion order and grow cheaply. A PowerShell array += reallocates on every append, so use a generic list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System.Collections.Generic.List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;hashtable&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Messages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are a science popularizer specializing in physics."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Explain quantum computing in plain English in less than 200 words"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first interaction is similar to the two first examples.  But for the next interaction with the model, the result is added to the message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$assistantResponse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'assistant'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$assistantResponse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can create a new message&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Now explain it to a 5 year old"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And start the conversation with the history.&lt;/p&gt;

&lt;p&gt;Everything above sets up the last piece. Tool calling only works because of the transcript, the model's request, your result, and its final answer are three turns in the same history.&lt;/p&gt;

&lt;p&gt;A model doesn’t execute tool, instead the model send back a request for the tool to be executed externally and return the result. &lt;/p&gt;

&lt;p&gt;The function is a stub, it returns a canned answer so we can focus on the protocol, not on Azure stuff.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Get-VnetPeeringStatus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$VnetName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$ResourceGroup&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="c"&gt;# Simulate a response from Azure&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;VnetName&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$VnetName&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;ResourceGroup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$ResourceGroup&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;PeeringStatus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Connected'&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use a tool calling this function, the model needs to know its definition, which is a JSON array. You can define one or several tool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$tools&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'function'&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Get-VnetPeeringStatus'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Returns the peering status of an Azure VNet'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'object'&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;VnetName&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Name of the virtual network'&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;ResourceGroup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Resource group containing the VNet'&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="s1"&gt;'VnetName'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'ResourceGroup'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the message is needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System.Collections.Generic.List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;hashtable&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Messages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are an Azure infrastructure assistant. You have access to tools that query live Azure state. When a user asks about the status of a resource, call the appropriate tool instead of guessing. Only answer from tool results, never fabricate resource state."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Is the hub-vnet in rg-network-prod peered correctly?"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note how much heavier the system prompt is here. It tells the model to call the tool rather than guess, and to answer only from tool results. With small models this isn't optional, a 4B model asked about live infrastructure will happily invent a plausible peering status. The prompt is your first line of defence against this kind of hallucination.&lt;/p&gt;

&lt;p&gt;Then we need to create the body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$modelID&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$messages&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$tools&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;tool_choice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'function'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Get-VnetPeeringStatus'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertTo-Json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Depth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;10&lt;/span&gt;&lt;span class="w"&gt;   
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;“tool_choice” pins which tool the model may call. Here I force Get-VnetPeeringStatus, useful for a demo, since it removes one source of nondeterminism. In real use you'd set auto and let the model decide, which is also where small models start to disappoint.&lt;/p&gt;

&lt;p&gt;After send the request to the model, we need to scan the response to see if a “tool_calls” is returned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$choice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$choice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finish_reason&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'tool_calls'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c"&gt;# Append the assistant turn (with its tool_calls) to history&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'assistant'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$choice&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;


    &lt;/span&gt;&lt;span class="kr"&gt;foreach&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$call&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$choice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="bp"&gt;$args&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$call&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;function&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arguments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertFrom-Json&lt;/span&gt;&lt;span class="w"&gt;

        &lt;/span&gt;&lt;span class="c"&gt;# Dispatch — never Invoke-Expression on model output&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$call&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;function&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s1"&gt;'Get-VnetPeeringStatus'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;Get-VnetPeeringStatus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-VnetName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;VnetName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ResourceGroup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ResourceGroup&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Unknown tool: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$call&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;function&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

        &lt;/span&gt;&lt;span class="nv"&gt;$messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'tool'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tool_call_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$call&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ConvertTo&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Depth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Compress&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c"&gt;# Re-send with updated $messages to get the final answer&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$modelID&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$messages&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertTo-Json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Depth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;10&lt;/span&gt;&lt;span class="w"&gt;        

     &lt;/span&gt;&lt;span class="nv"&gt;$params&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Method&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Uri&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$restApiUri&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/v1/chat/completions"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;ContentType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Invoke-RestMethod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assistant content is added to the $message variable to keep the history. &lt;/p&gt;

&lt;p&gt;Then for each call in the model message, the tool (the powershell function) is executed, and we add a new hashmap with the role tool, the tool_call_id and the result of the function. &lt;/p&gt;

&lt;p&gt;Finaly, we resend the updated message to the model and this time it will reply to the question sent in the first interaction. &lt;/p&gt;

&lt;p&gt;These are the basic interactions: chat, system prompt, memory, tools. Nothing here is PowerShell-specific, the same steps work the same way in Python or C#.&lt;/p&gt;

&lt;p&gt;But look back at what got built. The model didn't remember anything; we resent the transcript. The model didn't call a function; it emitted a name and we decided whether to honour it. The model didn't check its own output; the switch did. Everything that made this useful happened outside the model, in a hundred lines of PowerShell sitting between it and the rest of the world.&lt;/p&gt;

&lt;p&gt;That's the foundation of a harness. It's the small program that holds the state, owns the allowlist, executes the calls, and decides what goes back in. When you use a hosted product, the harness ships with it and you never see it. Foundry Local gives you a model and a port. The harness is yours to write, which is the cost, and the entire point: nothing reaches your infrastructure unless your code let it through.&lt;/p&gt;

&lt;p&gt;One practical note before you build on this. Everything above talks to the REST API, which means you own the plumbing, discovering the port, resolving aliases to variants, catching a service that isn't up. I will post something about what you can do with the SDK.&lt;/p&gt;

&lt;p&gt;Experiment with it; try it on some workflows but remember Foundry Local CLI is in preview, and you may encounter bugs and malfunctions.&lt;/p&gt;

&lt;p&gt;I'm wrapping all of this into a PowerShell module; alias resolution, streaming, the tool dispatch loop. More on that in a follow-up.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Scripts used in this post can be find on my &lt;a href="https://github.com/omiossec/demo-foundry-local-powershell" rel="noopener noreferrer"&gt;GitHub repos&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>powershell</category>
      <category>agents</category>
      <category>azure</category>
    </item>
    <item>
      <title>The price of AI: The end of free AI or how model rationing creates hidden technical debt</title>
      <dc:creator>Olivier Miossec</dc:creator>
      <pubDate>Mon, 20 Apr 2026 14:39:17 +0000</pubDate>
      <link>https://dev.to/omiossec/from-hype-to-risk-how-model-rationing-creates-hidden-technical-debt-4m7a</link>
      <guid>https://dev.to/omiossec/from-hype-to-risk-how-model-rationing-creates-hidden-technical-debt-4m7a</guid>
      <description>&lt;p&gt;Anthropic kept its new model, Mythos, out of public release, only a douzaine of enterprises get limited access. The stated reason: security.&lt;br&gt;
Yes, Mythos might be powerful enough to worry security teams, but part of the secrecy reads like a smart marketing move and not as a pure risk mitigation.&lt;/p&gt;

&lt;p&gt;GitHub is certainly starting doing the same, there are rumours about tighten rate limits, just check &lt;a href="https://github.blog/news-insights/company-news/changes-to-github-copilot-individual-plans/" rel="noopener noreferrer"&gt;this&lt;/a&gt;. They Stopped signup for new Copilot Pro subscription &lt;a href="https://github.blog/news-insights/company-news/changes-to-github-copilot-individual-plans/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Until then, the use of AI was largely subsidized; subscription low prices do not actually compensate for the compute cost.&lt;/p&gt;

&lt;p&gt;The bigger point is simple: models are getting stronger and adoption is exploding. Pew finds 62% of US adults use AI weekly, and McKinsey’s 2025 survey shows 88% of organizations use AI in at least one function.&lt;/p&gt;

&lt;p&gt;Stronger models + surging usage = far more compute demand. Demand can spike exponentially; datacentre capacity grows far more slowly. You can’t plug power, add cooling, set up generator, connect network and racks servers overnight, datacentre projects take 6–24 months.&lt;/p&gt;

&lt;p&gt;More, estimates put GPU demand in the millions of H100 equivalents, well beyond what the industry can produce today.&lt;/p&gt;

&lt;p&gt;When demand outstrips supply, vendors ration access. The best models and largest number of tokens will go to the highest bidders; everyone else gets older models, throttled contexts, higher latency, or no access during peak times. Free tiers will, also, erode.&lt;/p&gt;

&lt;p&gt;That creates a new business risk: workflows tuned to a specific model may only run reliably behind a paywall in the next months, sometimes an expensive one. The sponsored run we enjoy today will be over soon. Either you pay a premium that kills margins, or accept degraded service when vendors throttle subscriptions.&lt;/p&gt;

&lt;p&gt;Don’t lock yourself to a single vendor. Choosing models by public benchmarks is risky; what matters is how a model performs on your data and in your workflows, not leader board scores.&lt;/p&gt;

&lt;p&gt;If you are only using Claude Code, you should start working also with GitHub Copilot CLI, or/and OpenAI Codex or OpenClaude. &lt;/p&gt;

&lt;p&gt;You need to run your workflows on several model, not as an experiment, but as a routing. There are to possible outcome.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The output is similar, with or without a few adjustments, great you have a low lock-in risk&lt;/li&gt;
&lt;li&gt;The output is degraded; you have a decency you must mitigate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Adopt a model agnostic mindset. Prompts and context that rely on vendor quirks (Claude’s long context, GPT’s tool syntax, Gemini’s multimodal shortcuts) create hidden technical debt, and fragile, costly systems.&lt;/p&gt;

&lt;p&gt;Plan for scarcity: design for multiple models, measure outputs on your real data, and treat model access as a strategic dependency.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>githubcopilot</category>
      <category>claude</category>
    </item>
    <item>
      <title>You're Already in AI control Debt. You Just Haven't Seen the Bill Yet</title>
      <dc:creator>Olivier Miossec</dc:creator>
      <pubDate>Thu, 09 Apr 2026 21:21:27 +0000</pubDate>
      <link>https://dev.to/omiossec/youre-already-in-ai-control-debt-you-just-havent-seen-the-bill-yet-36kl</link>
      <guid>https://dev.to/omiossec/youre-already-in-ai-control-debt-you-just-havent-seen-the-bill-yet-36kl</guid>
      <description>&lt;p&gt;Poor deadlines, unrealistic timelines, pressure to finish a project on time, or lack of knowledge, often result as of what we call technical debt. The concept of future cost, a debt that must be repay by refactoring or redeploying a system, and where interests are pay by extra maintenance actions and reduced reliability.&lt;/p&gt;

&lt;p&gt;This concept was coined in 1992 by Ward Cunningham and has since become a widely used framework to argue for software quality, system sustainability, and long-term organizational efficiency.&lt;/p&gt;

&lt;p&gt;Looking at AI adoption today in cloud and software engineering, the parallel is hard to miss. The developer shipping features without review, the office worker using OpenClaw to speed up their tasks, the cloud engineer letting an agent generate deployment documentation, the architect wiring up an unknown MCP server to get a job done. Worse still, many employees use AI with personal accounts and no validation from their IT department; enter the new world of shadow AI.&lt;/p&gt;

&lt;p&gt;These situations are commonplace today, and sometimes the results go well beyond what was intended. We're not just delegating tasks only, we're delegating decisions.&lt;/p&gt;

&lt;p&gt;Think of a critical system failing because agent-generated code wasn't production-grade, a presentation riddled with hallucinated references, secrets or API tokens pushed to a public repository, personal data sent to an LLM, or a rogue MCP plugin taking control of a machine. The thread connecting all these examples is a lack of control, diligence, and discernment.&lt;/p&gt;

&lt;p&gt;This raises a real question: is agentic AI as safe as we assume? There are three systemic risks to be aware of: security risk, compliance risk, and quality risk.&lt;/p&gt;

&lt;p&gt;In the early days of the cloud, people started using file transfer services without authorization. Strategic files, client data, and confidential information were sent outside company networks with no visibility. It wasn't because people were reckless, they knew it wasn't allowed, but the value proposition was compelling enough: instant file sharing.&lt;/p&gt;

&lt;p&gt;The value proposition of AI is well known to anyone in a corporate role. People are already sending files and data to AI systems for proofreading, analysis, and document drafting. That alone is a breach. Agentic AI is simply the next step.&lt;/p&gt;

&lt;p&gt;When the OpenClaw project launched under the name Clawbot, it quickly became one of the most popular repositories on GitHub. It's an open-source AI agent that connects to LLMs. It can run workflows, connect to your calendar, CRM, and other applications, a genuine game-changer for anyone who had been waiting for a real personal assistant.&lt;/p&gt;

&lt;p&gt;But OpenClaw has a dark side. Between the project's rename on GitHub and the explosion of third-party plugins, malicious elements have polluted the ecosystem. You think you've deployed a plugin to manage your CRM — and you have, but it's also leaking your data to a third party. That email-reading plugin? It may be handing access to your mailbox to a threat actor. Check what the CVE database says about &lt;a href="https://www.cve.org/CVERecord/SearchResults?query=openclaw" rel="noopener noreferrer"&gt;OpenClaw&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;OpenClaw isn't the only risk vector here — think about MCP servers. Almost nobody had heard of MCP a few months ago; today there are more than 15,000 available. How many are misconfigured or outright malicious? Far from just helping you to complete tasks, they drastically expand your attack surface. You can check this &lt;a href="https://mcppedia.org/" rel="noopener noreferrer"&gt;website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The same applies to plugins and skills: they can contain malicious scripts or prompt injection payloads. Not vetting what you add to your AI agent can turn it into a liability.&lt;/p&gt;

&lt;p&gt;AI agents are also vulnerable to prompt injection when processing external documents, and the broader the permissions granted, the larger the blast radius.&lt;/p&gt;

&lt;p&gt;This is the essence of shadow AI, analogous to shadow IT, where employees used unauthorized Dropbox accounts to share data. Here they're using AI agents with the same mindset, but with a far greater potential impact on the company's infrastructure&lt;/p&gt;

&lt;p&gt;But shadow AI isn't limited to agents running on local machines. Anyone in a company can open an AI chatbot, it's just a webpage. People paste in Excel tables, internal documents, or email threads without a second thought. It's nearly invisible from a monitoring perspective, but company data is out the door.&lt;/p&gt;

&lt;p&gt;Company data is now flowing to servers outside your control and may be used to train models. Code, medical, insurance, and legal data are already inside these systems. Data ingested by an LLM cannot be truly erased, it should be treated as a permanent data leak. Shadow AI makes this worse: by definition, none of it will ever be audited.&lt;/p&gt;

&lt;p&gt;Data leaked to an LLM can include sensitive coding assets: tokens, API keys, and sometimes passwords. But there's a bigger problem. LLMs are incredibly effective at generating content that looks correct. We've recently seen multiple examples of consulting firms and law practices submitting documents filled with entirely fabricated citations.&lt;/p&gt;

&lt;p&gt;Code generation isn't exempt from hallucinations either; non-existent APIs, invented function parameters, bogus CLI commands, non-functional logic. At least in this case, the verdict is fast and unambiguous: the code doesn't run.&lt;/p&gt;

&lt;p&gt;More insidious is the sub-optimal code that does run. Security gaps, misconfigurations, systems that can't scale under load, all pushed to production without a second look. The result: incidents at scale.&lt;/p&gt;

&lt;p&gt;The root cause is a loss of control over pace. With traditional software engineering, writing and reviewing code operated on a human timeline. With AI, the same output is generated in minutes instead of hours. A single coding agent can produce more in a day than a team can meaningfully review, forcing engineers to ship code they don't fully understand.&lt;/p&gt;

&lt;p&gt;Even worse, AI agents will take the most optimal path to complete a task, which is a problem when the intent or context is malformed. The result can be quietly catastrophic. Cases of agents dropping databases or silently erasing data are more common than most teams realize.&lt;/p&gt;

&lt;p&gt;AI is relatively new, and agentic AI even more so. The evolution is rapid, and adoption is outpacing it. Too fast for enterprises to put a governance skeleton in place. If even the largest tech players have suffered AI failures, it tells us this governance still needs to be invented — and that the AI control debt is already accumulating.   &lt;/p&gt;

</description>
      <category>ai</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Using Claude Code in a PowerShell Project: An Introduction</title>
      <dc:creator>Olivier Miossec</dc:creator>
      <pubDate>Sat, 14 Mar 2026 18:47:01 +0000</pubDate>
      <link>https://dev.to/omiossec/using-claude-code-in-a-powershell-project-an-introduction-56j9</link>
      <guid>https://dev.to/omiossec/using-claude-code-in-a-powershell-project-an-introduction-56j9</guid>
      <description>&lt;p&gt;Generative and agentic AI have reshaped software development in a few short months. This is more than asking a chatbot for help; it’s a mindset shift and a cultural transformation in how we design, generate and maintain code.&lt;/p&gt;

&lt;p&gt;Claude Code is one of the most advanced agentic tools for code generation, but like any generative system it can produce incorrect or low quality output. See the sloop content on Instagram, AI used carelessly, it can create the same "surface level" results you see on social platforms, attractive but unreliable.&lt;/p&gt;

&lt;p&gt;In this post I’ll show how to use Claude Code in a PowerShell project: we’ll build a module to automate GitHub issue management.&lt;br&gt;
First, a quick intro: Claude Code is part of Anthropic’s ecosystem (alongside Claude.ai, the Claude API and Claude CoWork). Claude Code is designed as an agentic coding assistant rather than a simple chatbot.&lt;br&gt;
You can download it &lt;a href="https://claude.com/product/claude-code" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Claude Code is not a Chatbot, it is an AI Agent; it follow a workflow, gets the context, analyzes, plan the work and creates the code.  In Claude Code, you don’t ask a question, you specify the result you want and the agent executes. &lt;/p&gt;

&lt;p&gt;There is one important notion in Claude Code (and other AI tools), the Context. Context is the agent’s working memory, measured in tokens.  It’s critical to manage. Claude Code sessions typically support large contexts (hundreds of thousands of tokens, depending on your plan). Monitor usage with the /context command; if you approach ~60% capacity, run /compact to reduce context size and avoid degraded behaviours.&lt;/p&gt;

&lt;p&gt;The first best practice with Claude Code, is to keep a CLAUDE.md at the project root: treat it like a brief for the agent. Describe the project goals, constraints, runtime targets and coding conventions. That file becomes the agent’s persistent context, so you manage an agentic collaborator rather than a stateless LLM chat.&lt;/p&gt;

&lt;p&gt;You can generate CLAUDE.md with the /init command: when run against an existing codebase, the agent analyses the repository and drafts the brief based on discovered files and patterns.&lt;/p&gt;

&lt;p&gt;In this tutorial we start from an empty folder; you can author CLAUDE.md manually or ask Claude Code to create it for you. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create a new CLAUDE.md file in this folder with these instructions, PowerShell module for GitHub Issue Automation, named psGTIssue, the module will use classes, private function and public function, it run with PwSh 7 on Linux, MacOS and Windows. The project structure include, src, tests and build folder, the code should respect the camel case notation for variable and authorized verb for function (get-something), Pester will be used for unit testing" &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You should have something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# CLAUDE.md — psGTIssue Project Instructions&lt;/span&gt;

&lt;span class="gu"&gt;## Project Overview&lt;/span&gt;

&lt;span class="gs"&gt;**Module name:**&lt;/span&gt; &lt;span class="sb"&gt;`psGTIssue`&lt;/span&gt;
&lt;span class="gs"&gt;**Purpose:**&lt;/span&gt; PowerShell module for GitHub Issue Automation
&lt;span class="gs"&gt;**Runtime:**&lt;/span&gt; PowerShell 7 (pwsh) — cross-platform: Linux, macOS, Windows
&lt;span class="p"&gt;
---
&lt;/span&gt;
&lt;span class="gu"&gt;## Project Structure&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;psGTIssue/&lt;br&gt;
├── src/&lt;br&gt;
│   ├── Classes/          # PowerShell classes&lt;br&gt;
│   ├── Private/          # Private (internal) functions&lt;br&gt;
│   ├── Public/           # Public (exported) functions&lt;br&gt;
│   └── psGTIssue.psm1    # Module root — dot-sources all classes, private, public&lt;br&gt;
├── tests/&lt;br&gt;
│   ├── Unit/             # Pester unit tests mirroring src/ structure&lt;br&gt;
│   └── Integration/      # Integration tests (optional)&lt;br&gt;
├── build/                # Build output and scripts&lt;br&gt;
│   └── psGTIssue.psd1    # Module manifest (generated or maintained here)&lt;br&gt;
└── CLAUDE.md&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;
---
&lt;/span&gt;
&lt;span class="gu"&gt;## Coding Conventions&lt;/span&gt;

&lt;span class="gu"&gt;### Naming&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="gs"&gt;**Variables:**&lt;/span&gt; camelCase — &lt;span class="sb"&gt;`$issueTitle`&lt;/span&gt;, &lt;span class="sb"&gt;`$repoOwner`&lt;/span&gt;, &lt;span class="sb"&gt;`$pageNumber`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Parameters:**&lt;/span&gt; camelCase — &lt;span class="sb"&gt;`param([string]$issueTitle)`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Classes:**&lt;/span&gt; PascalCase — &lt;span class="sb"&gt;`class GitHubIssue`&lt;/span&gt;, &lt;span class="sb"&gt;`class IssueFilter`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Functions:**&lt;/span&gt; Approved PowerShell verb + PascalCase noun — &lt;span class="sb"&gt;`Get-GtIssue`&lt;/span&gt;, &lt;span class="sb"&gt;`New-GtIssue`&lt;/span&gt;, &lt;span class="sb"&gt;`Set-GtIssue`&lt;/span&gt;, &lt;span class="sb"&gt;`Remove-GtIssue`&lt;/span&gt;, &lt;span class="sb"&gt;`Invoke-GtIssueAction`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Private functions:**&lt;/span&gt; same verb-noun convention, not exported — &lt;span class="sb"&gt;`Get-GtApiHeader`&lt;/span&gt;, &lt;span class="sb"&gt;`ConvertTo-GtIssueObject`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Constants / enum values:**&lt;/span&gt; camelCase

&lt;span class="gu"&gt;### Approved Verbs&lt;/span&gt;

&lt;span class="gh"&gt;Always use verbs from `Get-Verb`. Common ones for this module:
---
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can create the project structure. First, create the project skeleton. Use a prompt such as Create the project structure and Claude will scaffold folders, a .psd1 manifest, a .psm1 module file, a build.ps1 script and Pester test stubs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create a public function that list all issue for a given repository, param are repository, organization name and a switch all, open, close" &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we can create a function that list all issue for a repository&lt;/p&gt;

&lt;p&gt;The agent will create several files, a function to call the GitHub API, a function to retrieve all issues in the repository and test files. &lt;/p&gt;

&lt;p&gt;You see that the prompt used are optimised to create a function, the prompt should be optimised to get the result.  If you want a modification, you should be precise on the result you want. &lt;/p&gt;

&lt;p&gt;For example, using a switch for the parameter $State is not a great idead&lt;br&gt;
We can ask Claude to replace the switch parameter in the Get-GtIssueList to a simple validateSet parameter with All, Open, Closed. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"replace the switch parameter in the Get-GtIssueList to a simple validateSet parameter with All, Open, Closed in the&lt;br&gt;
  @psGTIssue\src\Public\Get-GtIssueList.ps1"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can work directly on a file work by using @  here @psGTIssue\src\Public\Get-GtIssueList.ps1 it will cost less in token as Claude will not have to search for the file. &lt;/p&gt;

&lt;p&gt;Now it is time to check the status of the session with /context. The command will show the percentage used by the session. If the number is higher than 60% you should run the command /compact &lt;br&gt;
It is a good practice to name the session (and its context) so you can retrieve it later.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/rename buildGitHubIssuePwSh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To restart the session &lt;/p&gt;

&lt;p&gt;&lt;code&gt;claude --resume "buildGitHubIssuePwSh"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Review generated code carefully. Common issues: the agent may prefer Write-Verbose over Write-Debug, and it might scaffold a .psm1 that dynamically dot sources files at runtime. For best practice a static .psm1 that explicitly imports and exports functions and classes is better; this improves readability, testability and module loading behaviours.&lt;/p&gt;

&lt;p&gt;You can encode these conventions in CLAUDE.md, but they’re generic PowerShell best practices you’ll want in every project.&lt;/p&gt;

&lt;p&gt;Claude Code supports skills; reusable instruction sets that tell the agent how to behave in specific contexts. Create a PowerShell module skill to enforce coding conventions, file layout and test patterns across projects.&lt;br&gt;
All you need to do is to create a folder in .claude/skills/ and create the file SKILL.md. &lt;/p&gt;

&lt;p&gt;You can ask Claude for that &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create the folder ./claude/skills/powershell-module at the root and add the file SKILL.md"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude will create the file for you with some elements&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;  -&lt;/span&gt; Trigger conditions for when the skill applies
&lt;span class="p"&gt;  -&lt;/span&gt; Naming conventions summary
&lt;span class="p"&gt;  -&lt;/span&gt; Function templates (public &amp;amp; private)
&lt;span class="p"&gt;  -&lt;/span&gt; Class template
&lt;span class="p"&gt;  -&lt;/span&gt; Module dot-source order
&lt;span class="p"&gt;  -&lt;/span&gt; API call rules
&lt;span class="p"&gt;  -&lt;/span&gt; Pester test patterns
&lt;span class="p"&gt;  -&lt;/span&gt; File placement checklist

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can add an header&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt; 
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;powershell-module&lt;/span&gt; 
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;conventions and guidelines for building PowerShell modules&lt;/span&gt;
&lt;span class="na"&gt;argument-hint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[check|fix]"&lt;/span&gt; 
&lt;span class="nn"&gt;---&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And information about write-verbose and write-debug and psm1 generation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### write-verbose?/write-debug?
- Use `Write-Verbose` for informational messages that may be helpful for debugging but are not critical to the user, only visible when the `-Verbose` switch is used. 
- Use `Write-Debug` for detailed debugging information that is typically only relevant when troubleshooting specific issues. This can be enabled with the `-Debug` switch when running the function.
- Avoid using `Write-Host` for regular output; reserve it for special cases where you want to display colored or formatted output directly to the console. For standard output, return objects or use `Write-Output`.

### psm1 file 
- The `.psm1` file should contain all individual functions and class files, and the `Export-ModuleMember` call to specify which public functions are exported. The build script will handle the creation of the `.psm1` manifest file, so you do not need to create that manually.
- All actual code (functions, classes) should live in separate `.ps1` files under the `src/` directory, organized into `Public`, `Private`, and `Classes` subdirectories.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once conventions live in a skill, you can simplify CLAUDE.md and rely on the skill for enforcement. Skills are shareable, store them in a repo so your teams can reuse the same conventions and templates.&lt;/p&gt;

&lt;p&gt;Another way to extend Claude Code is to use sub-agent. Sub-agent is a separate agent that use its own context (it doesn’t impact your session) specialised in one task. The task must be specific and should not be related to any other task in your current session. &lt;/p&gt;

&lt;p&gt;Agents are located inside ./claude/agents folder. &lt;/p&gt;

&lt;p&gt;We can ask Claude code to create the agent &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Create the folder ./claude/agents/ at the root and add the file security-check.md&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude will create the markdown file with some security check; you can review them and add your own check. &lt;/p&gt;

&lt;p&gt;You can run the check&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Start security check with the sub agent security-check&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude will produce a report in the terminal you can review and then act on it. &lt;/p&gt;

&lt;p&gt;Claude Code is a powerful agentic coding tool; it lets you manage a team of coding agents as if you had human collaborators. That power demands discipline: be precise in your briefs, test outputs, and maintain human oversight. Agents can produce useful code quickly, but they also make mistakes; you must validate, adapt, and own the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  In short
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core commands and session hygiene
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;/init&lt;/strong&gt; — generate CLAUDE.md from an existing repo.&lt;br&gt;
&lt;strong&gt;/context&lt;/strong&gt; — show token usage (monitor session memory).&lt;br&gt;
&lt;strong&gt;/compact&lt;/strong&gt; — reduce context when usage is high.&lt;br&gt;
&lt;strong&gt;/rename &lt;/strong&gt; — name the session for later resume.&lt;br&gt;
&lt;strong&gt;claude --resume ""&lt;/strong&gt; — resume a named session.&lt;br&gt;
&lt;strong&gt;1st rule of Claude&lt;/strong&gt;: compact when usage approaches 60–80% of context capacity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Project files
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;CLAUDE.md&lt;/strong&gt; — project brief (goals, constraints, runtime, conventions).&lt;br&gt;
&lt;strong&gt;.claude/skills/powershell-module/SKILL.md&lt;/strong&gt; — reusable PowerShell conventions and templates.&lt;br&gt;
&lt;strong&gt;.claude/agents/&lt;/strong&gt; — sub‑agents for isolated tasks (security, linting).&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>aiops</category>
      <category>powershell</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Will AI Replace Your Job? A Practical Guide for Devs and Cloud Engineers</title>
      <dc:creator>Olivier Miossec</dc:creator>
      <pubDate>Fri, 06 Mar 2026 18:28:55 +0000</pubDate>
      <link>https://dev.to/omiossec/will-ai-replace-your-job-a-practical-guide-for-devs-and-cloud-engineers-3ehl</link>
      <guid>https://dev.to/omiossec/will-ai-replace-your-job-a-practical-guide-for-devs-and-cloud-engineers-3ehl</guid>
      <description>&lt;p&gt;The hot question today is: Will AI take my job? Headlines about mass layoffs and viral posts claiming an entire team can be replaced with a single prompt make the fear feel immediate. Scrolling LinkedIn or Twitter, you see dramatic claims and quick takes.&lt;/p&gt;

&lt;p&gt;Many of those claims are exaggerated, for now. Companies cut headcount for many reasons: cost, restructuring, strategic shifts, and sometimes, "AI" is just a convenient narrative. Dismissing AI as a simple chatbot hype is also a mistake. The reality is that AI, mostly agentic AI, will change our relationship with work and how enterprises create value.&lt;/p&gt;

&lt;p&gt;Agentic AI is real, and some roles will be replaced, just as roles shifted during the Internet, SaaS, and cloud revolutions. In this post, I’ll try to identify which roles are most at risk, suggest ways to protect and reskill yourself, and show how AI can create new opportunities. Like past technological revolutions, Agentic AI will destroy some roles, like steam trains removed the need for diligence, but it will create others.&lt;/p&gt;

&lt;p&gt;Generative AI is a powerful tool that can process texts based on general knowledge. It excels at producing and transforming text-based artifacts: contracts, job descriptions, support tickets, documentation, and code. Agentic AI goes further by orchestrating workflows and subtasks autonomously. If your role is primarily about following a strict, repeatable process to produce standardized outputs, an agent can often do it faster and cheaper.&lt;/p&gt;

&lt;p&gt;Examples include analysts, consultants, HR processes, and other roles where a defined input yields a standardized output. Developers face partial automation too: AI can generate boilerplate code, tests, and scaffolding quickly. Accounting and routine financial reporting are vulnerable. Many managerial tasks, such as synthesizing and distributing information from up and down, can be automated or augmented.&lt;/p&gt;

&lt;p&gt;We already see concrete examples of agentic systems accelerating decision cycles. In high-stakes contexts, automated analysis of multiple data sources can reduce days of work to minutes. Similar acceleration is appearing in HR workflows and financial analysis, where agents aggregate, filter, and summarize large datasets. In the current war in the Middle East, the Pentagon is using agentic AI to analyze multiple data sources to make decisions.&lt;/p&gt;

&lt;p&gt;If your role is primarily execution, producing outputs from well-defined and static processes without having to make any design or judgment, it’s at high risk of automation. Execution can be delegated to agents cheaply. It scales much more than humans can do.  Does that mean you’ll lose your job? Not necessarily.&lt;/p&gt;

&lt;p&gt;Generative AI is powerful but limited: it doesn’t make value judgments, exercise domain judgment, or possess taste. Those human skills, judgment, critical thinking, navigating ambiguity, domain expertise, and taste, become stronger differentiators in an AI-augmented workplace.&lt;/p&gt;

&lt;p&gt;Be proactive: learn how AI works beyond the surface of simple prompts. You don’t need to be an expert or LLM engineer, but you should understand core concepts, how models generate text or images, what tokens and context windows are, and how parameters like temperature affect output. It will help you to have a better vision of what generative AI can do, cannot do, and why hallucinations happen.&lt;/p&gt;

&lt;p&gt;Get hands-on with agentic AI platforms and tooling. Explore multiple ecosystems (Anthropic, OpenAI, GitHub Copilot, and others) and experiment with APIs, MCP, agent orchestration, and observability. Use vendor resources and community tutorials to build practical experience.&lt;/p&gt;

&lt;p&gt;To learn more about the Anthropic ecosystem, you can use these courses published &lt;a href="https://claude.com/resources/courses" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also learn GitHub Copilot CLI, it is for developers, but you can try to learn it with this beginner &lt;a href="https://github.com/github/copilot-cli-for-beginners" rel="noopener noreferrer"&gt;course&lt;/a&gt; (if you want to learn more, this &lt;a href="https://docs.github.com/en/copilot/how-tos/copilot-cli" rel="noopener noreferrer"&gt;page will be useful&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;You may also try on local AI tool, where you can use a model on your local device. A good start is to set up Foundry local (&lt;a href="https://learn.microsoft.com/en-us/azure/foundry-local/get-started?view=foundry-classic" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/azure/foundry-local/get-started?view=foundry-classic&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;You can also try any other alternative AI tools, for example, Mistral AI (&lt;a href="https://mistral.ai/products/studio" rel="noopener noreferrer"&gt;https://mistral.ai/products/studio&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;Don’t wait for corporate training. Start learning on your own and build demonstrable projects. Early adopters will have an advantage over peers who delay. &lt;/p&gt;

&lt;p&gt;Don’t be desperate, AI will also create new roles. Here are practical examples of emerging job categories where human expertise will remain essential:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent governance and compliance, to add policies, audit data access, and permissions for agents. &lt;/li&gt;
&lt;li&gt;AI debt management, to identify, track, and remediate risks from unsupervised agent usage. &lt;/li&gt;
&lt;li&gt;Agent developer/integrator, for designing, building, and maintaining task-specific agents and orchestrations. &lt;/li&gt;
&lt;li&gt;Data discoverability and indexing, to ensure agents can reliably find and use the right data (the “AI SEO” problem). &lt;/li&gt;
&lt;li&gt;AI-assisted code reviewer/verifier, for validating code produced by agents and ensuring security, correctness, and maintainability.&lt;/li&gt;
&lt;li&gt;Token and cost optimization engineer for minimizing inference costs and optimizing model usage patterns.&lt;/li&gt;
&lt;li&gt;AI infrastructure engineer, to deploy and operate compute resources, networking, and observability stacks for agentic workloads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are predictions,s and I may be wrong. But like past revolutions, agentic AI will eliminate some roles and create new ones, often in places we don’t yet fully anticipate. Things about the Steam train, it revolutionized the world of transportation and the economy. &lt;/p&gt;

&lt;p&gt;AI will reshape work, but it won’t simply “replace” people overnight. Tasks that are repeatable and execution focused are most at risk; judgment, design, domain expertise, and governance remain human strengths. The practical strategy for engineers is clear: learn how agents work, build hands-on experience, and pivot toward roles that require oversight, orchestration, and measurable impact. Those who combine technical fluency with judgment and governance skills will be best positioned in this new tech revolution.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>cloud</category>
    </item>
    <item>
      <title>When AI Agents Meet Wall Street: Why Markets Overreact and what Engineers Should Prepare For</title>
      <dc:creator>Olivier Miossec</dc:creator>
      <pubDate>Sat, 28 Feb 2026 12:28:00 +0000</pubDate>
      <link>https://dev.to/omiossec/when-ai-agents-meet-wall-street-why-markets-overreact-and-what-engineers-should-prepare-for-145e</link>
      <guid>https://dev.to/omiossec/when-ai-agents-meet-wall-street-why-markets-overreact-and-what-engineers-should-prepare-for-145e</guid>
      <description>&lt;p&gt;An economic bubble occurs when asset prices detach from long term fundamentals and are driven instead by irrational exuberance and social contagion.&lt;/p&gt;

&lt;p&gt;Since 2025 we’ve seen that pattern again: mass layoffs, elevated valuations, and extreme market volatility, all of this are tied to the AI narrative.&lt;/p&gt;

&lt;p&gt;Let’s try to analyse what happens and see if we can define a strategy for us, engineer.&lt;/p&gt;

&lt;p&gt;Last year major tech firms, Meta, Amazon, Microsoft and others, announced mass layoffs totalling roughly 200,000 roles. Publicly, the rationale was the rise of AI: many positions will be automated or restructured around AI capabilities.&lt;/p&gt;

&lt;p&gt;There is some truth in this reason, but this is not the main reason. To get the main reason, take a lot on what these companies are doing with their investments. &lt;/p&gt;

&lt;p&gt;There’s truth in that claim, but it misses the main driver. Look at where these companies are investing infrastructure, chips, datacentres and energy capacity. It is not just about chatbots.&lt;/p&gt;

&lt;p&gt;They’re not just chasing chatbots; they’re preparing for an agentic AI world. Training large models consumes massive energy, while inference, asking something, is far cheaper. The real shift is the infrastructure needed to run persistent, autonomous AI agents at scale.&lt;/p&gt;

&lt;p&gt;Agentic AI is not like Chatbot: agents such as Claude CoWork operate autonomously, executing tasks without continuous human input; managing email, CRM workflows, scheduling, and more. Once created, an agent can remain active and consume resources while completing its tasks.&lt;/p&gt;

&lt;p&gt;The capacity required is colossal: servers, accelerators, datacentre space, networking and power provisioning at scale. &lt;/p&gt;

&lt;p&gt;To build that capacity companies must invest heavily; buying accelerators (e.g., NVIDIA GPUs), networking devices and datacenter capacity, and securing power contracts. That capital commitment helps explain why some firms reallocate headcount toward these investments.&lt;/p&gt;

&lt;p&gt;It is capital reallocation, laying off people to build AI capacity, I can’t say it is fair or normal. But this is very rational.&lt;/p&gt;

&lt;p&gt;What’s less rational is the market’s reaction to agentic AI in 2026. Agents differ from chatbots: the value proposition is task execution, not prompt finesse. An agent combines an LLM, tool access and autonomy, but that doesn’t mean it instantly replaces entire software categories.&lt;/p&gt;

&lt;p&gt;In January Anthropic launched Claude CoWork, an agent that handles non technical tasks locally (initially macOS only): files, email, calendar, and deliver artifacts like spreadsheets and text summaries.&lt;/p&gt;

&lt;p&gt;Claude CoWork wasn’t the first agentic solution, but it was the first to capture broad public attention. Anthropic developed it after customers used Claude Code for non coding tasks; managing email, expense reports and file organization, demonstrating real demand for delegated AI automation.&lt;/p&gt;

&lt;p&gt;Soon after CoWork’s general availability, several SaaS and enterprise software stocks, like Salesforce, SAP or Adobe, saw a large market declines, contributing to a broader sell off in software. The MSCI Software &amp;amp; Services index fell significantly in early 2026.&lt;/p&gt;

&lt;p&gt;The market appears to be pricing a future where AI agents displace some SaaS functionality. That view is optimistic and only partially rational, because agents may complement rather than fully replace many enterprise apps in the near future.&lt;/p&gt;

&lt;p&gt;What’s coming next is surprising. Markets moved into irrational territory. On 20 February Anthropic published a blog post suggesting Claude Code could assist with static code analysis. It is just a concept, not a shipped product or roadmap. Yet cybersecurity stocks, from niche vendors to large firms like Palo Alto and CrowdStrike, tumbled in response.&lt;/p&gt;

&lt;p&gt;On 23 February another Anthropic post suggested agents could assist with modernizing COBOL code, again a concept. Consulting firms and legacy technology vendors saw market pressure as investors extrapolated far beyond the announcement. &lt;/p&gt;

&lt;p&gt;This is the irrational zone: a static analysis capability cannot replace entire cybersecurity teams, runtime scanners, or the complex processes that govern legacy banking systems. The market is reacting to buzzwords without understanding the engineering and operational realities behind the headlines. &lt;/p&gt;

&lt;p&gt;Sound familiar? It echoes the dot com bubble, when a single buzzword in a press release could inflate a stock. The mechanism is the same: narrative driven speculation divorced from technical substance. &lt;/p&gt;

&lt;p&gt;It’s easy to laugh, but boards will react. A sudden market signal can pressure executives to “do AI”, sometimes by reallocating resources toward visible AI projects at the expense of core capabilities like cybersecurity. &lt;/p&gt;

&lt;p&gt;We may be in a feedback loop where irrational market moves trigger irrational corporate responses. As engineers, remember that AI is a tool, a powerful tool, but only a probabilistic predictor that generates text, code or images. It does not make value judgments or replace human decision making. &lt;/p&gt;

&lt;p&gt;Strategy for engineers: learn agentic AI, not just prompt engineering, but how agents are built, orchestrated and monitored. Master relevant tools and workflows (e.g., agent orchestration, observability and safety patterns). AI will also create new roles; those who understand agentic systems will be better positioned. &lt;/p&gt;

&lt;p&gt;And of course, there will be soon some AI tools that could complete the cybersecurity landscape, but it will be a real product, with verifiable, measurable and concrete results.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>careerdevelopment</category>
    </item>
    <item>
      <title>What is wrong with LinkedIn in the age of AI</title>
      <dc:creator>Olivier Miossec</dc:creator>
      <pubDate>Wed, 11 Feb 2026 11:02:06 +0000</pubDate>
      <link>https://dev.to/omiossec/what-is-wrong-with-linkedin-in-the-age-of-ai-1pid</link>
      <guid>https://dev.to/omiossec/what-is-wrong-with-linkedin-in-the-age-of-ai-1pid</guid>
      <description>&lt;p&gt;Like everyone in tech, I utilize LinkedIn to manage my professional network, showcase my work, and maintain visibility to recruiters. LinkedIn is central to professional life and has no real competitor.&lt;/p&gt;

&lt;p&gt;Not long ago, I had a strange experience: a stream of posts about an Azure networking change. Nothing unusual, this kind of post floods my LinkedIn every week, but here, there was a tiny but consequential error. The update stated that the change would occur on 30 September 2025, yet the posts were dated December 2025. Everyone was warning readers to act on a change that, in reality, was scheduled for 31 March&lt;/p&gt;

&lt;p&gt;This is where we are now: a lot of noise on a professional network. That noise is amplified by conformity. People avoid friction and rarely post dissenting views. The result is a chorus of similar takes that can feel caricatural; sometimes the most viral LinkedIn posts end up as jokes on Twitter or Reddit, and some have become famous memes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2lob1t3vte65x2zf8rj.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2lob1t3vte65x2zf8rj.jpeg" alt=" " width="640" height="783"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This Mediocrity is only part of the problem on the platform. Much of LinkedIn is declarative and unverified: anyone can claim to be a Rust or Go expert simply by writing it. You can be a brain surgeon if you want; you only need to edit your profile. The platform is full of inflated titles and grandiose claims that often bear little relation to reality.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qpu2npm0z0ppbeh39u6.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qpu2npm0z0ppbeh39u6.jpeg" alt=" " width="640" height="731"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s bad news for job seekers. Recruiters must wade through noise and exaggerated titles before they even reach your profile. By the time they find you, they still don’t know whether your résumé is accurate or just inflated.&lt;/p&gt;

&lt;p&gt;There is a lot of friction before reaching your profile. A potential recruiter will need to navigate through all the noise, do some research, and maybe find you. But at this point, the recruiter will have no idea if your profile is valid or if it is just an inflated one. &lt;/p&gt;

&lt;p&gt;The recruiter needs to navigate to LinkedIn, like you do when trying to find a job. It’s often frustrating. The probability that a recruit will find you is limited.&lt;/p&gt;

&lt;p&gt;LinkedIn’s interface, like any computer interface, is designed for humans to navigate into the complexity of data, to control their behaviours, and authorize access.&lt;/p&gt;

&lt;p&gt;The interface is going to be disrupted by Artificial Intelligence. Agentic AI are systems that can autonomously plan, make decisions, and take actions to achieve goals without human direction, and can now replace some work tasks, including finding profile.&lt;/p&gt;

&lt;p&gt;AI Agents do not have time to lose scrolling a feed on the interface; they work on raw data. When a recruiter instructs an agent to find an Azure architect skilled in Bicep and Terraform, the agent won’t scroll through posts and buzzwords. It will search raw, verifiable artifacts; GitHub and GitLab repositories, blog posts, published articles, … These are concrete and verifiable artifacts to spot the perfect profile. &lt;/p&gt;

&lt;p&gt;In this agentic recruitment world, LinkedIn becomes secondary. It will be useful for correlation, but the primary sources will be your public artifacts: documented side projects, repositories, PRs and issues, and technical blog posts. Those are the things agents can verify and score.&lt;/p&gt;

&lt;p&gt;To facilitate the AI, you should group them on a single page visible from GitHub, Twitter and LinkedIn. A personal brand API that automatically lists your original blog posts, your public repos, your PR, and your contributions. This will be your new AI era visit card. &lt;/p&gt;

&lt;p&gt;LinkedIn still matters for human networking and visibility, but the rise of agentic hiring shifts the signal to verifiable, AI-readable artifacts. Start publishing concrete work and make it easy for agents to find and verify.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
    </item>
    <item>
      <title>Azure Machine Configuration, Linux, and DSC Configuration</title>
      <dc:creator>Olivier Miossec</dc:creator>
      <pubDate>Tue, 13 Jan 2026 22:03:23 +0000</pubDate>
      <link>https://dev.to/omiossec/azure-machine-configuration-linux-and-dsc-configuration-1i74</link>
      <guid>https://dev.to/omiossec/azure-machine-configuration-linux-and-dsc-configuration-1i74</guid>
      <description>&lt;p&gt;I recently made a post about Azure Machine Configuration and PowerShell DSC, and how to deploy VM configurations as infrastructure as Code, just like the rest of your infrastructure.&lt;/p&gt;

&lt;p&gt;But in this post, I focused mainly on Windows configuration and wrote almost nothing about Linux.&lt;/p&gt;

&lt;p&gt;However, Azure Machine Configuration is also available for Linux VMs, where you can use DSC or Chef InSpec.&lt;/p&gt;

&lt;p&gt;To run DSC configurations, VMs need to have the AzurePolicyForLinux extension and a managed identity.&lt;/p&gt;

&lt;p&gt;But unlike Windows, most Linux VMs don’t come with PowerShell installed. To solve this problem, the AzurePolicyForLinux extension installs PowerShell in a Folder but doesn’t modify the Path of the VM. PowerShell can only be used by the policy. &lt;/p&gt;

&lt;p&gt;The next step is to write a DSC configuration. There are many DSC resources for managing Windows, but to manage a Linux server, you will need a compatible DSC resource. The most advanced is the &lt;a href="https://github.com/azure/nxtools" rel="noopener noreferrer"&gt;NxTools module&lt;/a&gt;. It is the recommended version to use with the Azure Machine configuration. &lt;/p&gt;

&lt;p&gt;The NxTools module is a POSIX wrapper for several Linux commands and includes several DSC resources. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nxFiles, to manage files and folders&lt;/li&gt;
&lt;li&gt;nxGroup, to manage groups and group membership&lt;/li&gt;
&lt;li&gt;nxUser, to manage users&lt;/li&gt;
&lt;li&gt;nxPackages, to manage packages (support only apt)&lt;/li&gt;
&lt;li&gt;nxService, to manage service (only system)&lt;/li&gt;
&lt;li&gt;nxScript, to execute scripts in DSC configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To demonstrate DSC on Linux with Azure Machine Configuration, let's ensure the user devTo is created and added to the group publisher, and a file with specified content is created using a script resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;configuration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;demoDSCLinux&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;Import-DscResource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ModuleName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'nxtools'&lt;/span&gt;&lt;span class="w"&gt;


        &lt;/span&gt;&lt;span class="n"&gt;nxUser&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ensureDevToUser&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;

            &lt;/span&gt;&lt;span class="n"&gt;Ensure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s1"&gt;'Present'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;UserName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s1"&gt;'DevTo'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Dev To Demo user'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;HomeDirectory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/home/DevTo'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Ensure that DevTo user is present on the system'&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

        &lt;/span&gt;&lt;span class="n"&gt;nxGroup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ensurePublishersGroup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="c"&gt;# the group must be present and have root as only member&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Ensure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s1"&gt;'Present'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;GroupName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s1"&gt;'publishers'&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Members&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="s1"&gt;'DevTo'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

        &lt;/span&gt;&lt;span class="n"&gt;nxScript&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ensurePublisherfilePresent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;GetScript&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nv"&gt;$Reason&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Reason&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nv"&gt;$Reason&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Script:Script:FileMissing"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nv"&gt;$Reason&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Phrase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"File does not exist"&lt;/span&gt;&lt;span class="w"&gt;

            &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/home/DevTo/publisher.txt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nv"&gt;$Reason&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Script:Script:Success"&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nv"&gt;$Reason&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Phrase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"File exists"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

            &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nx"&gt;Reasons&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="nv"&gt;$Reason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;TestScript&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/home/DevTo/publisher.txt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="kr"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$false&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;SetScript&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="bp"&gt;$null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;New-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/home/DevTo/publisher.txt"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ItemType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"File"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first two resources create the DevTo user and create the Publishers group, and add the DevTo user. &lt;/p&gt;

&lt;p&gt;The last one ensures that the file "/home/DevTo/publisher.txt" is present on the system using a nxScript resource. The GetScript block of the resource needs to return a Hashtable with a Reason object to work. The reason.phrase will be shown in the Azure Portal in case of noncompliance.&lt;/p&gt;

&lt;p&gt;If you try to compile this configuration on your Windows or macOS machine, you will get an error; you need a Linux VM with PowerShell. &lt;/p&gt;

&lt;p&gt;You can set up a dev VM for that, but you can also use a CI/CD pipeline to compile and package the configuration for Linux. You can also compile and package the DSC configuration in GitHub Actions. &lt;/p&gt;

&lt;p&gt;First, you will need a script for compiling the configuration and creating the Azure Machine Configuration package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check if the host is a Linux system&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;$IsLinux&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This script must be run on a Linux system."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# install modules &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Install-Module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;nxtools&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;install-module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PSDesiredStateConfiguration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-RequiredVersion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;3.0.0-beta1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-AllowPrerelease&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;install-module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;GuestConfiguration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-RequiredVersion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;4.1.0&lt;/span&gt;&lt;span class="w"&gt; 

&lt;/span&gt;&lt;span class="c"&gt;# compile the demo configuration&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;/DSC-Linux/demolinux.dsc.ps1&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;demoDSCLinux&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# rename the MOF file &lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Rename-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\demoDSCLinux\localhost.mof&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-NewName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;demoDSCLinux.mof&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt; 

&lt;/span&gt;&lt;span class="c"&gt;# create the package for Azure Machine Configuration&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;New-GuestConfigurationPackage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"demoDSCLinux"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AuditAndSet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Configuration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\demoDSCLinux\demoDSCLinux.mof&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line of the script tests if the host is a Linux machine. The configuration can only be created on Linux. &lt;/p&gt;

&lt;p&gt;Then the script imports all the necessary modules, nxtools for the DSC resources, PSDesiredStateConfiguration for DSC, and GuestConfiguration to package the configuration. &lt;/p&gt;

&lt;p&gt;It then compiles the configuration and packages it. The package can then be deployed to a Linux VM with Azure Machine configuration.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>iac</category>
      <category>powershell</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
