<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Rahiel Akhtar]]></title><description><![CDATA[A field guide to making PIM eligible role assignments permanent with Terraform. Covers the role management policy lever, prerequisites, and gotchas.]]></description><link>https://rahielakhtar.no</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Rahiel Akhtar</title><link>https://rahielakhtar.no</link></image><generator>RSS for Node</generator><lastBuildDate>Mon, 14 Sep 2026 18:25:37 GMT</lastBuildDate><atom:link href="https://rahielakhtar.no/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Rolling out VNet flow logs and traffic analytics with EPAC across landing zones]]></title><description><![CDATA[Why look beyond firewall logs
Network monitoring beyond what flows through your firewall or NVA gives you a more holistic view of your traffic — patterns, trends, and what never touches the firewall i]]></description><link>https://rahielakhtar.no/rolling-out-vnet-flow-logs-and-traffic-analytics-with-epac-across-landing-zones</link><guid isPermaLink="true">https://rahielakhtar.no/rolling-out-vnet-flow-logs-and-traffic-analytics-with-epac-across-landing-zones</guid><category><![CDATA[VNet flow logs]]></category><category><![CDATA[Traffic analytics]]></category><category><![CDATA[Azure]]></category><category><![CDATA[Azure Policy]]></category><category><![CDATA[epac]]></category><category><![CDATA[enterprise policy as code]]></category><category><![CDATA[AzureNetworking ]]></category><dc:creator><![CDATA[Rahiel Akhtar]]></dc:creator><pubDate>Fri, 28 Aug 2026 13:32:53 GMT</pubDate><content:encoded><![CDATA[<h2>Why look beyond firewall logs</h2>
<p>Network monitoring beyond what flows through your firewall or NVA gives you a more holistic view of your traffic — patterns, trends, and what never touches the firewall in the first place. That's where VNet flow logs and traffic analytics come in.</p>
<h2>Why VNet flow logs and Traffic Analytics</h2>
<p>VNet flow logs is a Network Watcher feature that captures network traffic for a Virtual Network and writes the raw output to a blob container in a designated storage account. This feature can be enabled at a VNet, subnet, or NIC level. The logs on their own aren't very useful, but paired with Traffic Analytics, the picture is clearer.</p>
<p>Traffic Analytics is what turns them into something you can work with, it ingests the raw logs, enriches them, and lands them in a Log Analytics workspace where you can query and visualise the data. A built-in dashboard covers the common views out of the box. Information such as allowed vs blocked, top talkers, and threat hits.</p>
<h2>why bother enabling them</h2>
<p>Each platform team should weigh the monitoring value setting this solution up against the cost — storage and Log Analytics ingestion aren't free, so decide before enabling broadly. A few questions that can help out:</p>
<ol>
<li><p>Do we need visibility into Azure traffic that does not traverse our firewall?</p>
</li>
<li><p>Do we need to troubleshoot workload connections — for example, from an Azure workload to a private endpoint?</p>
</li>
<li><p>Do we need visibility into inter-VNet traffic?</p>
</li>
</ol>
<h2>Automating deployment with Azure Policy</h2>
<p>In an enterprise environment with several spoke networks that each need a Network Watcher, you'll want to automate the deployment. Azure Policy has a built-in policy definition that does exactly that: <a href="https://www.azadvertizer.net/azpolicyadvertizer/3e9965dc-cc13-47ca-8259-a4252fd0cf7b.html">Configure virtual network to enable Flow Log and Traffic Analytics</a>.</p>
<p>Configuring this with Enterprise Policy as Code (EPAC) requires a policy assignment at your target scope that references the built-in policy.</p>
<p>A few things worth knowing about the assignment:</p>
<ol>
<li><p><code>policyId</code> hooks up the assignment with the correct policy definition</p>
</li>
<li><p>Flow logs are enabled per region, so each region needs its own assignment.</p>
</li>
<li><p><code>networkWatcherRG</code> and <code>networkWatcherName</code> values are hardcoded (explained below)</p>
</li>
<li><p>Retention can be 30 or 60 days.</p>
</li>
<li><p>You can centralise both: raw logs to a shared storage account, Traffic Analytics to a shared Log Analytics workspace.</p>
</li>
</ol>
<pre><code class="language-json">{
  "$schema": "https://raw.githubusercontent.com/Azure/enterprise-azure-policy-as-code/main/Schemas/policy-assignment-schema.json",
  "nodeName": "/root",
  "definitionEntry": {
    "policyId": "/providers/Microsoft.Authorization/policyDefinitions/3e9965dc-cc13-47ca-8259-a4252fd0cf7b",
    "displayName": "Configure virtual network to enable Flow Log and Traffic Analytics"
  },
  "assignment": {
    "name": "Configure VNet Flow Logs and Traffic Analytics - Norway East",
    "displayName": "Configure VNet Flow Logs and Traffic Analytics - &lt;REGION&gt;",
    "description": "Deploys and enables VNet flow logs with Traffic Analytics for virtual networks in the specified region. Flow logs are written to a central storage account and Traffic Analytics data is sent to a Log Analytics workspace. Each subscription hosts its own Network Watcher flow log resource."
  },
  "metadata": {
    "assignedBy": "epac/&lt;ID&gt;/prod"
  },
  "parameters": {
    "effect": "DeployIfNotExists",
    "vnetRegion": "norwayeast",
    "storageId": "/subscriptions/&lt;SUBSCRIPTION_ID&gt;/resourceGroups/&lt;RESOURCE_GROUP&gt;/providers/Microsoft.Storage/storageAccounts/&lt;STORAGE_ACCOUNT&gt;",
    "timeInterval": "60",
    "workspaceResourceId": "/subscriptions/&lt;SUBSCRIPTION_ID&gt;/resourceGroups/&lt;RESOURCE_GROUP&gt;/providers/Microsoft.OperationalInsights/workspaces/&lt;LOG_ANALYTICS_WORKSPACE&gt;",
    "workspaceRegion": "&lt;REGION&gt;",
    "networkWatcherRG": "NetworkWatcherRG",
    "networkWatcherName": "NetworkWatcher_norwayeast",
    "retentionDays": "30"
  },
  "scope": {
    "prod": [
      "/providers/Microsoft.Management/managementGroups/&lt;MANAGEMENT_GROUP&gt;"
    ]
  }
}
</code></pre>
<h2>The naming-consistency dependency</h2>
<p>At one VNet, most of these are <em>quirks</em>. Across dozens of subscriptions and Virtual Networks, they're the difference between a working rollout and a broken one.</p>
<p>For this to scale, the values <code>networkWatcherRG</code>and <code>networkWatcherName</code> must match across subscriptions where there is a VNet you want to set up flow logs and Traffic Analytics. The assignment explicitly checks for Network Watchers in a Resource Group following this naming standard and if conditions are met the <code>DeployIfNotExists</code> effect will trigger. The compliance page will help you narrow down whether a subscription is missing the Resource Group entirely, or has the Resource Group but is missing the Network Watcher.</p>
<h3>A couple of smaller things worth knowing</h3>
<p><strong>Storage accounts can be private</strong></p>
<p>You might expect flow logs to require public network access on the storage account. They don't — you can lock the storage account down and put the blob service behind a private endpoint. Flow logs will still write.</p>
<p><strong>Data Collection Rule (DCR) and Data Collection Endpoint (DCE)</strong></p>
<p>Traffic Analytics deploys a Data Collection Rule and Data Collection Endpoint into the resource group containing the Log Analytics workspace. If you tightly control which resource types are allowed at that scope, make sure both are permitted. I had some issues debugging this part, because it does not show up in Activity Logs.</p>
<h2>References</h2>
<ul>
<li><p><a href="https://learn.microsoft.com/en-us/azure/network-watcher/vnet-flow-logs-overview">VNet flow logs overview</a></p>
</li>
<li><p><a href="https://www.azadvertizer.net/azpolicyadvertizer/3e9965dc-cc13-47ca-8259-a4252fd0cf7b.html">Built-in policy definition</a></p>
</li>
<li><p><a href="https://aidanfinn.com/?p=24580">Aidan Finn MVP - VNet Flow Logs</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Permanent PIM Eligible Role Assignment with Infrastructure as Code
]]></title><description><![CDATA[Permanent PIM Eligible Role Assignment with Infrastructure as Code
Introduction
This article will go over how to enable a PIM role assignment permanently without any expiration date for Azure resource]]></description><link>https://rahielakhtar.no/permanent-pim-eligible-role-assignment-with-infrastructure-as-code</link><guid isPermaLink="true">https://rahielakhtar.no/permanent-pim-eligible-role-assignment-with-infrastructure-as-code</guid><category><![CDATA[Terraform]]></category><category><![CDATA[#IaC]]></category><category><![CDATA[IAM]]></category><category><![CDATA[pim]]></category><dc:creator><![CDATA[Rahiel Akhtar]]></dc:creator><pubDate>Mon, 06 Jul 2026 14:08:53 GMT</pubDate><content:encoded><![CDATA[<h1>Permanent PIM Eligible Role Assignment with Infrastructure as Code</h1>
<h2>Introduction</h2>
<p>This article will go over how to enable a PIM role assignment permanently without any expiration date for Azure resources with Terraform. You might encounter a 365-day limitation on PIM eligible role assignments. This article will show how to work around that.</p>
<h3>What I was trying to achieve</h3>
<p>While improving our security posture, we were switching from permanent normal role assignments to PIM eligible role assignments for our Entra ID groups. While enabling PIM on these, we ran into a constraint on the maximum eligible assignment duration: The management policy did not allow more than 365 days, so we couldn't set the assignment to have no expiration date. Our goal was to keep administrative overhead low and keep the PIM eligibility permanent on the assigned groups. Without a fix, we'd be renewing role assignments every 365 days, which at scale is untenable</p>
<h3>Prerequisites</h3>
<table>
<thead>
<tr>
<th>Requirement</th>
<th>Details</th>
</tr>
</thead>
<tbody><tr>
<td>Licensing</td>
<td>Entra ID P2 / Governance</td>
</tr>
<tr>
<td>Graph API permissions</td>
<td><code>Group.Read.All</code>, <code>Group.Create</code> (if also creating groups), <code>User.Read.All</code></td>
</tr>
<tr>
<td>Azure RBAC</td>
<td>User Access Administrator is sufficient at the resource group, subscription, or management group level for the identity running Terraform</td>
</tr>
<tr>
<td>Terraform provider version</td>
<td><code>azurerm</code> 4.79.0 or later</td>
</tr>
</tbody></table>
<h2>Understanding the components</h2>
<h3>PIM for Azure resources vs. Entra roles</h3>
<p>PIM can be enabled for Entra roles such as Global Administrator, Security Reader, etc., and the scope is tenant-wide.</p>
<p>But PIM can also be enabled for Azure resources that handle Azure RBAC roles such as Owner, Contributor, etc., and the scope can be set to RG, subscription, or management group level.</p>
<p>My scenario covers PIM eligibility for Azure resources.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a4bb49901674f58c01ace43/1bbc5890-f3c5-4b33-921a-f6e327224c99.png" alt="" style="display:block;margin:0 auto" />

<h3>Active vs. Eligible</h3>
<p>An active assignment means the user has the role right now and can use it without any activation step. This is what a normal <code>azurerm_role_assignment</code> grants, and also what <code>azurerm_pim_active_role_assignment</code> gives but under PIM governance.</p>
<p>An eligible assignment means the user is allowed to hold the role but does not have it until they explicitly activate it in PIM, also known as the just-in-time (JIT) model. This is what my scenario covers, with <code>azurerm_pim_eligible_role_assignment</code>.</p>
<h3>Role management policy</h3>
<p>A crucial component to understand is the role management policy. This ruleset governs how an eligible assignment or activation behaves. This policy handles multiple controls, one of which is <strong>eligibility expiration</strong>. This is set at the RG, subscription, or management group level and covers all assignments under the respective scope.</p>
<h2>Key observations</h2>
<h3>Provider version in lock file</h3>
<p>We had a lot of back and forth with <code>azurerm_pim_eligible_role_assignment</code> because of an older version of <code>azurerm</code> pinned in our lock file. That version's resource block didn't support all arguments — pin at least 4.79.0 (or latest) and you won't need to think about it.</p>
<h3>Existing management policy on the target scope</h3>
<p>Before editing, check the management policy already set at your target scope. I was never able to find a dedicated portal view for the underlying policy object itself — the closest is <a href="https://learn.microsoft.com/en-us/entra/id-governance/privileged-identity-management/pim-resource-roles-configure-role-settings">Microsoft docs</a> on Azure resource role settings, which covers the same settings from the role-settings UI. A policy applied at a higher scope, like the subscription, is inherited by every resource group beneath it; it isn't scoped to just the one resource you're editing. That inheritance might make you wonder whether a permanent eligible assignment weakens protection further down. It doesn't: permanent eligibility only changes how long a principal is allowed to stay eligible, not what happens at activation — MFA, approval, and any narrower policy set at a lower scope still apply. What changes is who's responsible for cleaning up stale eligibilities, not which controls fire.</p>
<h3>Dependencies</h3>
<p>Terraform's apply order comes from resource attribute references, and here the two resources don't reference each other — they each just take <code>scope</code> and <code>role_definition_id</code> as separate input variables. Without an explicit dependency, Terraform is free to create the <code>azurerm_pim_eligible_role_assignment</code> before the <code>azurerm_role_management_policy</code> update has landed, which means the assignment gets created under whatever policy is already active on that scope. If that policy still requires an expiration, the apply fails on the same 365-day constraint from the introduction. Add <code>depends_on = [azurerm_role_management_policy.management_policy]</code> to the role assignment resource block so the policy is guaranteed to apply first.</p>
<h2>Terraform automation</h2>
<p>Below is an example of how to create:</p>
<ul>
<li><p>Entra ID group</p>
</li>
<li><p>Management policy with no expiration</p>
</li>
<li><p>PIM eligible role assignment with no expiration</p>
</li>
</ul>
<h3>Group creation</h3>
<pre><code class="language-hcl">resource "azuread_group" "group" {
  display_name     = var.group_name
  owners           = var.owners
  security_enabled = true
}
</code></pre>
<h3>Management policy creation with no expiration for PIM role assignments</h3>
<pre><code class="language-hcl">resource "azurerm_role_management_policy" "management_policy" {
  scope              = var.subscription_id
  role_definition_id = var.role_definition_id

  eligible_assignment_rules {
    expiration_required = false # Ensures expiration is not required in role assignment
  }
}
</code></pre>
<h3>PIM eligible role assignment creation without expiration</h3>
<pre><code class="language-hcl">resource "azurerm_pim_eligible_role_assignment" "pim_role_assignment" {
  scope              = var.subscription_id
  role_definition_id = var.role_definition_id
  principal_id       = var.principal_id
  depends_on         = [azurerm_role_management_policy.management_policy] # ensures assignment is compliant with new management policy
}
</code></pre>
<h3>IAM overview in portal</h3>
<p>The screenshot below shows both assignments in the Azure portal's IAM blade. One active permanent, one eligible permanent:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a4bb49901674f58c01ace43/f952e651-9b89-4d01-8f8b-0156692e1218.png" alt="" style="display:block;margin:0 auto" />

<h2>Conclusion</h2>
<p>If I did this again, I would understand and check the existing role management policy before touching anything. This is the most crucial part of the setup and requires a good understanding of the backend governance that is not visible in the Entra ID portal.</p>
<p>That upfront effort only pays off if you're repeating it: I'd recommend this approach to any team managing role assignments at scale, but not necessarily for a single role assignment — such a setup would be overkill.</p>
]]></content:encoded></item></channel></rss>