<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://mimachniak.github.io/sysopslife/feed.xml" rel="self" type="application/atom+xml" /><link href="https://mimachniak.github.io/sysopslife/" rel="alternate" type="text/html" /><updated>2026-05-08T00:57:40+02:00</updated><id>https://mimachniak.github.io/sysopslife/feed.xml</id><title type="html">AzOps Engineer</title><subtitle>Michal Machniak blog dedicated to Microsoft technologies, based on my daily experience of working as sysOps / devOps / System Architect.</subtitle><author><name>Michal Machniak</name><email>mmachniak@outlook.com</email></author><entry><title type="html">Microsoft DSCv3 build and deploy classic resources ActiveDirectoryDSC</title><link href="https://mimachniak.github.io/sysopslife/2026/05/06/dscv3-build-and-deploy-classic-resources-activedirectory-dsc/" rel="alternate" type="text/html" title="Microsoft DSCv3 build and deploy classic resources ActiveDirectoryDSC" /><published>2026-05-06T00:00:00+02:00</published><updated>2026-05-06T00:00:00+02:00</updated><id>https://mimachniak.github.io/sysopslife/2026/05/06/dscv3-build-and-deploy-classic-resources-activedirectory-dsc</id><content type="html" xml:base="https://mimachniak.github.io/sysopslife/2026/05/06/dscv3-build-and-deploy-classic-resources-activedirectory-dsc/"><![CDATA[<p><img src="/assets/images/DSC/dsc-flow.PNG" alt="" /></p>

<h1 id="deploying-active-directory-with-microsoft-dsc-v3--a-configuration-as-code-approach">Deploying Active Directory with Microsoft DSC v3 — A Configuration-as-Code Approach</h1>

<h2 id="introduction">Introduction</h2>

<p>Microsoft Desired State Configuration (DSC) v3 is a cross-platform, declarative configuration management engine built in Rust. Unlike its PowerShell-based predecessors (DSC v1 and v2), DSC v3 is a standalone binary (<code class="language-plaintext highlighter-rouge">dsc.exe</code>) that orchestrates resources defined in YAML documents. It supports the full PowerShell DSC adapter ecosystem, enabling teams to configure Windows infrastructure — including Active Directory — using pure YAML files.</p>

<p>This post walks through a complete, modular Active Directory deployment built on DSC v3, covering every configuration file, parameter file, and the commands used to apply them.</p>

<hr />

<h2 id="why-dsc-v3-for-active-directory">Why DSC v3 for Active Directory?</h2>

<p>Traditional AD deployment relies on PowerShell scripts or manual steps that are difficult to audit, version, and reproduce. DSC v3 brings:</p>

<ul>
  <li><strong>Declarative YAML</strong> — describe <em>what</em> you want, not <em>how</em> to do it</li>
  <li><strong>Idempotency</strong> — running the same configuration multiple times produces the same result</li>
  <li><strong>Modularity</strong> — split large configurations into focused, reusable include files</li>
  <li><strong>Parameter separation</strong> — keep environment-specific values (domain names, passwords) out of the configuration files</li>
  <li><strong>Built-in dependency management</strong> — <code class="language-plaintext highlighter-rouge">dependsOn</code> ensures resources are applied in the correct order</li>
</ul>

<hr />

<h2 id="prerequisites">Prerequisites</h2>

<p>Before running any configuration, ensure the following are in place on the target Domain Controller:</p>

<p><strong>Search DSC v3:</strong></p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">winget</span><span class="w"> </span><span class="nx">search</span><span class="w"> </span><span class="nx">DesiredStateConfiguration</span><span class="w">
</span><span class="n">Name</span><span class="w">                              </span><span class="nx">Id</span><span class="w">            </span><span class="nx">Version</span><span class="w"> </span><span class="nx">Match</span><span class="w">                          </span><span class="nx">Source</span><span class="w">
</span><span class="o">----------------------------------------------------------------------------------------------</span><span class="w">
</span><span class="n">DesiredStateConfiguration</span><span class="w">         </span><span class="nx">9NVTPZWRC6KQ</span><span class="w">  </span><span class="nx">Unknown</span><span class="w">                                </span><span class="nx">msstore</span><span class="w">
</span><span class="n">DesiredStateConfiguration-Preview</span><span class="w"> </span><span class="nx">9PCX3HX4HZ0Z</span><span class="w">  </span><span class="nx">Unknown</span><span class="w">                                </span><span class="nx">msstore</span><span class="w">
</span><span class="n">DSC</span><span class="w"> </span><span class="nx">v3</span><span class="w">                            </span><span class="nx">Microsoft.DSC</span><span class="w"> </span><span class="nx">3.2.0</span><span class="w">   </span><span class="nx">Tag:</span><span class="w"> </span><span class="nx">desiredstateconfiguration</span><span class="w"> </span><span class="nx">winget</span><span class="w">

</span></code></pre></div></div>

<p><strong>Install DSC v3:</strong></p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">winget</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nt">--id</span><span class="w"> </span><span class="nx">9PCX3HX4HZ0Z</span><span class="w"> </span><span class="nt">--source</span><span class="w"> </span><span class="nx">msstore</span><span class="w">
</span></code></pre></div></div>

<p><strong>Install the ActiveDirectoryDsc PowerShell module:</strong></p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Install-Module</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="nx">ActiveDirectoryDsc</span><span class="w"> </span><span class="nt">-Repository</span><span class="w"> </span><span class="nx">PSGallery</span><span class="w"> </span><span class="nt">-Force</span><span class="w">
</span></code></pre></div></div>

<p><strong>Verify DSC v3 is available and can discover resources:</strong></p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dsc</span><span class="w"> </span><span class="nx">resource</span><span class="w"> </span><span class="nx">list</span><span class="w">
</span></code></pre></div></div>

<hr />

<h2 id="project-file-structure">Project File Structure</h2>

<p>The configuration is split into a layered directory structure to separate concerns:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>config/ADS/
├── ads-root-forest.dsc.yaml          # Stage 1 – Forest/domain creation
├── ads-root.dsc.parameters.yaml      # Parameters for root forest deployment
├── ads-child-forest.dsc.yaml         # Stage 1 (optional) – Child domain creation
│
└── ADS-Include/
    ├── ads.main.dsc.yaml             # Stage 2 – Orchestrator (Include entries)
    │
    ├── config/
    │   ├── ads.ou.dsc.yaml           # OU hierarchy definition
    │   ├── ads.admin-users.dsc.yaml  # Administrative user accounts
    │   ├── ads.groups.dsc.yaml       # Delegation security groups
    │   ├── ads.groups-members.dsc.yaml    # Group membership assignments
    │   ├── ads.ou-delegation.dsc.yaml     # OU permission entries (ACLs)
    │   └── ads.finegrained-password-policy.dsc.yaml  # PSO policies
    │
    └── parameters/
        ├── ads.parameters.yaml            # Domain root path parameter
        └── ads.admin-user.parameters.yaml # Admin user credentials
</code></pre></div></div>

<hr />

<h2 id="stage-1--forest-and-domain-creation">Stage 1 — Forest and Domain Creation</h2>

<h3 id="ads-root-forestdscyaml"><code class="language-plaintext highlighter-rouge">ads-root-forest.dsc.yaml</code></h3>

<p>This is the entry point for bringing up the first domain controller. It installs the required Windows features and then creates the AD forest using <code class="language-plaintext highlighter-rouge">ActiveDirectoryDsc/ADDomain</code>.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">$schema</span><span class="pi">:</span> <span class="s">https://aka.ms/dsc/schemas/v3/bundled/config/document.json</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">ads-root-forest-configuration</span>
  <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
  <span class="na">description</span><span class="pi">:</span> <span class="s">Global Active Directory configuration for forest and domain creation</span>
  <span class="na">Microsoft.DSC</span><span class="pi">:</span>
    <span class="na">requiredSecurityContext</span><span class="pi">:</span> <span class="s">current</span>

<span class="na">parameters</span><span class="pi">:</span>
  <span class="na">SafemodeAdministratorPassword</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">secureObject</span>
  <span class="na">Credential</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">secureObject</span>
  <span class="na">DomainName</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
    <span class="na">defaultValue</span><span class="pi">:</span> <span class="s">contoso.com</span>
  <span class="na">DomainNetBiosName</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
    <span class="na">defaultValue</span><span class="pi">:</span> <span class="s">contoso</span>
  <span class="na">ForestMode</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
    <span class="na">defaultValue</span><span class="pi">:</span> <span class="s">WinThreshold</span>   <span class="c1"># Win2008, Win2008R2, Win2012, Win2012R2, WinThreshold, Win2025</span>
  <span class="na">SuppressReboot</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">bool</span>
    <span class="na">defaultValue</span><span class="pi">:</span> <span class="no">true</span>
<span class="na">resources</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Windows Features Install ADS</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">PSDesiredStateConfiguration/WindowsFeature</span>
    <span class="na">properties</span><span class="pi">:</span>
      <span class="na">ensure</span><span class="pi">:</span> <span class="s">Present</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">AD-Domain-Services</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Windows Features Install ADS RSAT</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">PSDesiredStateConfiguration/WindowsFeature</span>
    <span class="na">properties</span><span class="pi">:</span>
      <span class="na">ensure</span><span class="pi">:</span> <span class="s">Present</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">RSAT-AD-PowerShell</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Windows Features Install AD DS Tools</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">PSDesiredStateConfiguration/WindowsFeature</span>
    <span class="na">properties</span><span class="pi">:</span>
      <span class="na">ensure</span><span class="pi">:</span> <span class="s">Present</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">RSAT-ADDS</span>
      <span class="na">IncludeAllSubFeature</span><span class="pi">:</span> <span class="no">true</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory Forest</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">ActiveDirectoryDsc/ADDomain</span>
    <span class="na">properties</span><span class="pi">:</span> 
      <span class="na">DomainName</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[parameters('DomainName')]"</span>
      <span class="na">DomainNetBiosName</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[parameters('DomainNetBiosName')]"</span>
      <span class="na">SafemodeAdministratorPassword</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[parameters('SafemodeAdministratorPassword')]"</span>
      <span class="na">Credential</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[parameters('Credential')]"</span>
      <span class="na">ForestMode</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[parameters('ForestMode')]"</span>
      <span class="na">SuppressReboot</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[parameters('SuppressReboot')]"</span> <span class="c1"># no reboot rquired</span>
    <span class="na">dependsOn</span><span class="pi">:</span> 
      <span class="pi">-</span> <span class="s2">"</span><span class="s">[resourceId('PSDesiredStateConfiguration/WindowsFeature','Windows</span><span class="nv"> </span><span class="s">Features</span><span class="nv"> </span><span class="s">Install</span><span class="nv"> </span><span class="s">ADS')]"</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">[resourceId('PSDesiredStateConfiguration/WindowsFeature','Windows</span><span class="nv"> </span><span class="s">Features</span><span class="nv"> </span><span class="s">Install</span><span class="nv"> </span><span class="s">ADS</span><span class="nv"> </span><span class="s">RSAT')]"</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">[resourceId('PSDesiredStateConfiguration/WindowsFeature','Windows</span><span class="nv"> </span><span class="s">Features</span><span class="nv"> </span><span class="s">Install</span><span class="nv"> </span><span class="s">AD</span><span class="nv"> </span><span class="s">DS</span><span class="nv"> </span><span class="s">Tools')]"</span>
</code></pre></div></div>

<p><strong>Key resources:</strong></p>

<table>
  <thead>
    <tr>
      <th>Resource name</th>
      <th>DSC type</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Windows Features Install ADS</td>
      <td><code class="language-plaintext highlighter-rouge">PSDesiredStateConfiguration/WindowsFeature</code></td>
      <td>Installs <code class="language-plaintext highlighter-rouge">AD-Domain-Services</code></td>
    </tr>
    <tr>
      <td>Windows Features Install ADS RSAT</td>
      <td><code class="language-plaintext highlighter-rouge">PSDesiredStateConfiguration/WindowsFeature</code></td>
      <td>Installs <code class="language-plaintext highlighter-rouge">RSAT-AD-PowerShell</code></td>
    </tr>
    <tr>
      <td>Windows Features Install AD DS Tools</td>
      <td><code class="language-plaintext highlighter-rouge">PSDesiredStateConfiguration/WindowsFeature</code></td>
      <td>Installs <code class="language-plaintext highlighter-rouge">RSAT-ADDS</code> with all sub-features</td>
    </tr>
    <tr>
      <td>Active Directory Forest</td>
      <td><code class="language-plaintext highlighter-rouge">ActiveDirectoryDsc/ADDomain</code></td>
      <td>Promotes server to DC and creates the forest</td>
    </tr>
  </tbody>
</table>

<p>The <code class="language-plaintext highlighter-rouge">ADDomain</code> resource depends on all three <code class="language-plaintext highlighter-rouge">WindowsFeature</code> resources, so DSC applies them in sequence automatically.</p>

<p><strong><code class="language-plaintext highlighter-rouge">ads-root.dsc.parameters.yaml</code></strong> — supplies the sensitive values at runtime, keeping them out of the configuration file:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">parameters</span><span class="pi">:</span>
  <span class="na">Credential</span><span class="pi">:</span>
    <span class="na">username</span><span class="pi">:</span> <span class="s">AdminTest</span>
    <span class="na">password</span><span class="pi">:</span> <span class="s">Password</span>
  <span class="na">SafemodeAdministratorPassword</span><span class="pi">:</span>
    <span class="na">username</span><span class="pi">:</span> <span class="s">AdminTest</span>
    <span class="na">password</span><span class="pi">:</span> <span class="s">AdminTest</span>
  <span class="na">DomainName</span><span class="pi">:</span> <span class="s">contoso.com</span>
  <span class="na">DomainNetBiosName</span><span class="pi">:</span> <span class="s">contoso</span>
</code></pre></div></div>

<blockquote>
  <p><strong>Security note:</strong> In Organization, replace plain-text passwords with secrets retrieved from Azure Key Vault or a secrets manager. Never commit credential files to source control.</p>
</blockquote>

<p><strong>Deploy command:</strong></p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dsc</span><span class="w"> </span><span class="nx">config</span><span class="w"> </span><span class="nx">set</span><span class="w"> </span><span class="se">`
</span><span class="w">  </span><span class="nt">--parameters-file</span><span class="w"> </span><span class="o">.</span><span class="nx">\ads-root.dsc.parameters.yaml</span><span class="w"> </span><span class="se">`
</span><span class="w">  </span><span class="nt">--file</span><span class="w"> </span><span class="o">.</span><span class="nx">\ads-root-forest.dsc.yaml</span><span class="w">
</span></code></pre></div></div>

<hr />

<h3 id="ads-child-forestdscyaml"><code class="language-plaintext highlighter-rouge">ads-child-forest.dsc.yaml</code></h3>

<p>Used when adding a child domain to an existing forest. The structure mirrors the root forest file but adds <code class="language-plaintext highlighter-rouge">DomainType</code> and <code class="language-plaintext highlighter-rouge">DomainMode</code> parameters:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">parameters</span><span class="pi">:</span>
  <span class="na">DomainType</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
    <span class="na">defaultValue</span><span class="pi">:</span> <span class="s">ChildDomain</span>   <span class="c1"># TreeDomain or ChildDomain</span>
  <span class="na">DomainMode</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
    <span class="na">defaultValue</span><span class="pi">:</span> <span class="s">WinThreshold</span>
</code></pre></div></div>

<p>This allows the same pattern to deploy both root and child domains by varying the parameters.</p>

<hr />

<h2 id="stage-2--ad-structure-with-include-based-orchestration">Stage 2 — AD Structure with Include-Based Orchestration</h2>

<p>Once the domain exists, the second stage configures its internal structure. Rather than one monolithic YAML, the project uses the <code class="language-plaintext highlighter-rouge">Microsoft.DSC/Include</code> resource type to compose smaller, focused files.</p>

<h3 id="ads-includeadsmaindscyaml"><code class="language-plaintext highlighter-rouge">ADS-Include/ads.main.dsc.yaml</code></h3>

<p>This is the single entry point for Stage 2. It does nothing itself — it delegates to six sub-configurations via <code class="language-plaintext highlighter-rouge">Include</code>, order of deployment is base on yaml files:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">$schema</span><span class="pi">:</span> <span class="s">https://aka.ms/dsc/schemas/v3/bundled/config/document.json</span>
<span class="na">resources</span><span class="pi">:</span>

<span class="c1"># Deploy in order </span>

<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory Ou structure</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">Microsoft.DSC/Include</span>
  <span class="na">properties</span><span class="pi">:</span>
    <span class="na">configurationFile</span><span class="pi">:</span> <span class="s">config\ads.ou.dsc.yaml</span>
    <span class="na">parametersFile</span><span class="pi">:</span> <span class="s">parameters\ads.parameters.yaml</span>

<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory Administrative User</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">Microsoft.DSC/Include</span>
  <span class="na">properties</span><span class="pi">:</span>
    <span class="na">configurationFile</span><span class="pi">:</span> <span class="s">config\ads.admin-users.dsc.yaml</span>
    <span class="na">parametersFile</span><span class="pi">:</span> <span class="s">parameters\ads.admin-user.parameters.yaml</span>

<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory Groups Management</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">Microsoft.DSC/Include</span>
  <span class="na">properties</span><span class="pi">:</span>
    <span class="na">configurationFile</span><span class="pi">:</span> <span class="s">config\ads.groups.dsc.yaml</span>
    <span class="na">parametersFile</span><span class="pi">:</span> <span class="s">parameters\ads.parameters.yaml</span>

<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory Groups Membership</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">Microsoft.DSC/Include</span>
  <span class="na">properties</span><span class="pi">:</span>
    <span class="na">configurationFile</span><span class="pi">:</span> <span class="s">config\ads.groups-members.dsc.yaml</span>

<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory Groups OU Delegation</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">Microsoft.DSC/Include</span>
  <span class="na">properties</span><span class="pi">:</span>
    <span class="na">configurationFile</span><span class="pi">:</span> <span class="s">config\ads.ou-delegation.dsc.yaml</span>
    <span class="na">parametersFile</span><span class="pi">:</span> <span class="s">parameters\ads.parameters.yaml</span>

<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory Password Policy</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">Microsoft.DSC/Include</span>
  <span class="na">properties</span><span class="pi">:</span>
    <span class="na">configurationFile</span><span class="pi">:</span> <span class="s">config\ads.finegrained-password-policy.dsc.yaml</span>
    <span class="na">parametersFile</span><span class="pi">:</span> <span class="s">parameters\ads.parameters.yaml</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">Microsoft.DSC/Include</code> resource loads an external configuration file and optionally merges a parameters file into it before execution. This pattern is the key to keeping each concern in its own file without repeating boilerplate.</p>

<hr />

<h2 id="sub-configuration-files">Sub-Configuration Files</h2>

<h3 id="configadsoudscyaml--organizational-unit-hierarchy"><code class="language-plaintext highlighter-rouge">config/ads.ou.dsc.yaml</code> — Organizational Unit Hierarchy</h3>

<p>Defines the entire OU tree under the domain root. The structure follows a tiered model:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Organization (root)
├── Accounts
│   ├── Users
│   ├── Users.NoSync
├── Devices
│   └── Servers
│       ├── Tier0
│       ├── Tier1
│       └── Tier2
│   └── Computers
└── Groups
    ├── Delegation
    └── Distribution
</code></pre></div></div>

<p>Each OU uses <code class="language-plaintext highlighter-rouge">ActiveDirectoryDsc/ADOrganizationalUnit</code> and declares its parent via <code class="language-plaintext highlighter-rouge">dependsOn</code>, using the <code class="language-plaintext highlighter-rouge">resourceId()</code> function to reference sibling resources:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="na">$schema</span><span class="pi">:</span> <span class="s">https://aka.ms/dsc/schemas/v3/bundled/config/document.json</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">ads-groups-ou-configuration</span>
  <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
  <span class="na">description</span><span class="pi">:</span> <span class="s">Active Directory configuration of defualt groups</span>
  <span class="na">Microsoft.DSC</span><span class="pi">:</span>
    <span class="na">requiredSecurityContext</span><span class="pi">:</span> <span class="s">current</span> <span class="c1"># this is the default and just used as an example indicating this config works for admins and non-admins</span>

<span class="na">parameters</span><span class="pi">:</span>
  <span class="na">DomainRootPath</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
    <span class="na">defaultValue</span><span class="pi">:</span> <span class="s2">"</span><span class="s">DC=contoso,DC=com"</span>
<span class="na">resources</span><span class="pi">:</span>

<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory Root Organizational Unit</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">ActiveDirectoryDsc/ADOrganizationalUnit</span>
  <span class="na">properties</span><span class="pi">:</span> 
    <span class="na">Name</span><span class="pi">:</span> <span class="s">Organization</span>
    <span class="na">Path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[parameters('DomainRootPath')]"</span>
    <span class="na">Description</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Organization</span><span class="nv"> </span><span class="s">OU</span><span class="nv"> </span><span class="s">for</span><span class="nv"> </span><span class="s">All</span><span class="nv"> </span><span class="s">organizational</span><span class="nv"> </span><span class="s">objects"</span>
    <span class="na">ProtectedFromAccidentalDeletion</span><span class="pi">:</span> <span class="no">true</span>
    <span class="na">ensure</span><span class="pi">:</span> <span class="s">Present</span>


<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory Accounts Organizational Unit</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">ActiveDirectoryDsc/ADOrganizationalUnit</span>
  <span class="na">properties</span><span class="pi">:</span>
    <span class="na">Name</span><span class="pi">:</span> <span class="s">Accounts</span>
    <span class="na">Path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[concat('OU=Organization,',</span><span class="nv"> </span><span class="s">parameters('DomainRootPath'))]"</span>
    <span class="na">Description</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Accounts</span><span class="nv"> </span><span class="s">OU</span><span class="nv"> </span><span class="s">for</span><span class="nv"> </span><span class="s">all</span><span class="nv"> </span><span class="s">user</span><span class="nv"> </span><span class="s">and</span><span class="nv"> </span><span class="s">service</span><span class="nv"> </span><span class="s">accounts"</span>
    <span class="na">ProtectedFromAccidentalDeletion</span><span class="pi">:</span> <span class="no">true</span>
    <span class="na">ensure</span><span class="pi">:</span> <span class="s">Present</span>
    <span class="na">dependsOn</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">[resourceId('ActiveDirectoryDsc/ADOrganizationalUnit','Active</span><span class="nv"> </span><span class="s">Directory</span><span class="nv"> </span><span class="s">Root</span><span class="nv"> </span><span class="s">Organizational</span><span class="nv"> </span><span class="s">Unit')]"</span>

<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory Users Organizational Unit</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">ActiveDirectoryDsc/ADOrganizationalUnit</span>
  <span class="na">properties</span><span class="pi">:</span> 
    <span class="na">Name</span><span class="pi">:</span> <span class="s">Users</span>
    <span class="na">Path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[concat('OU=Accounts,','OU=Organization,',</span><span class="nv"> </span><span class="s">parameters('DomainRootPath'))]"</span>
    <span class="na">Description</span><span class="pi">:</span> <span class="s2">"</span><span class="s">OU</span><span class="nv"> </span><span class="s">for</span><span class="nv"> </span><span class="s">Users</span><span class="nv"> </span><span class="s">accounts</span><span class="nv"> </span><span class="s">that</span><span class="nv"> </span><span class="s">can</span><span class="nv"> </span><span class="s">be</span><span class="nv"> </span><span class="s">synchonized</span><span class="nv"> </span><span class="s">to</span><span class="nv"> </span><span class="s">Microsoft</span><span class="nv"> </span><span class="s">365</span><span class="nv"> </span><span class="s">or</span><span class="nv"> </span><span class="s">used</span><span class="nv"> </span><span class="s">for</span><span class="nv"> </span><span class="s">on-premises</span><span class="nv"> </span><span class="s">authentication"</span>
    <span class="na">ProtectedFromAccidentalDeletion</span><span class="pi">:</span> <span class="no">true</span>
    <span class="na">ensure</span><span class="pi">:</span> <span class="s">Present</span>
    <span class="na">dependsOn</span><span class="pi">:</span> 
      <span class="pi">-</span> <span class="s2">"</span><span class="s">[resourceId('ActiveDirectoryDsc/ADOrganizationalUnit','Active</span><span class="nv"> </span><span class="s">Directory</span><span class="nv"> </span><span class="s">Accounts</span><span class="nv"> </span><span class="s">Organizational</span><span class="nv"> </span><span class="s">Unit')]"</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">concat()</code> function builds the LDAP path dynamically from the <code class="language-plaintext highlighter-rouge">DomainRootPath</code> parameter, making the file fully portable across domains.</p>

<p>All OUs have <code class="language-plaintext highlighter-rouge">ProtectedFromAccidentalDeletion: true</code> set to prevent inadvertent removal.</p>

<hr />

<h3 id="configadsadmin-usersdscyaml--administrative-user-accounts"><code class="language-plaintext highlighter-rouge">config/ads.admin-users.dsc.yaml</code> — Administrative User Accounts</h3>

<p>Creates privileged user accounts and places them in the correct OU:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">parameters</span><span class="pi">:</span>
  <span class="na">DomainName</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
    <span class="na">defaultValue</span><span class="pi">:</span> <span class="s2">"</span><span class="s">contoso.com"</span>
  <span class="na">DomainRootPath</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
    <span class="na">defaultValue</span><span class="pi">:</span> <span class="s2">"</span><span class="s">DC=contoso,DC=com"</span>
  <span class="na">Password</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">secureObject</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">Password</code> parameter is typed as <code class="language-plaintext highlighter-rouge">secureObject</code>, meaning it accepts a <code class="language-plaintext highlighter-rouge">{ username, password }</code> structure without exposing the value in logs. The user is placed in <code class="language-plaintext highlighter-rouge">OU=Users.Special.NoSync</code> — the dedicated OU for privileged accounts that must not synchronize to Microsoft 365:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">$schema</span><span class="pi">:</span> <span class="s">https://aka.ms/dsc/schemas/v3/bundled/config/document.json</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">ads-admin-users-configuration</span>
  <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
  <span class="na">description</span><span class="pi">:</span> <span class="s">Active Directory configuration for administrative user</span>
  <span class="na">Microsoft.DSC</span><span class="pi">:</span>
    <span class="na">requiredSecurityContext</span><span class="pi">:</span> <span class="s">current</span> <span class="c1"># this is the default and just used as an example indicating this config works for admins and non-admins</span>

<span class="na">parameters</span><span class="pi">:</span>
  <span class="na">DomainName</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
    <span class="na">defaultValue</span><span class="pi">:</span> <span class="s2">"</span><span class="s">contoso.com"</span>
  <span class="na">DomainRootPath</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
    <span class="na">defaultValue</span><span class="pi">:</span> <span class="s2">"</span><span class="s">DC=contoso,DC=com"</span>
  <span class="na">Password</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">secureObject</span>
<span class="na">resources</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory admin - michal.mmachniak</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">ActiveDirectoryDsc/ADUser</span>
  <span class="na">properties</span><span class="pi">:</span>
    <span class="na">Ensure</span><span class="pi">:</span> <span class="s">Present</span>
    <span class="na">UserName</span><span class="pi">:</span> <span class="s">michal.mmachniak</span>
    <span class="na">CommonName</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Michal</span><span class="nv"> </span><span class="s">Machniak"</span>
    <span class="na">UserPrincipalName</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[concat('michal.mmachniak@',</span><span class="nv"> </span><span class="s">parameters('DomainName'))]"</span>
    <span class="na">Password</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[parameters('Password')]"</span>
    <span class="na">Path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[concat('OU=Users.Special.NoSync,OU=Accounts,OU=Organization,',</span><span class="nv"> </span><span class="s">parameters('DomainRootPath'))]"</span>
    <span class="na">PasswordNeverResets</span><span class="pi">:</span> <span class="no">true</span> <span class="c1"># Each deployment won't setup new password</span>
</code></pre></div></div>

<p><strong><code class="language-plaintext highlighter-rouge">parameters/ads.admin-user.parameters.yaml</code></strong> provides the runtime values for this config:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">parameters</span><span class="pi">:</span>
  <span class="na">Password</span><span class="pi">:</span>
    <span class="na">username</span><span class="pi">:</span> <span class="s">admin</span>
    <span class="na">password</span><span class="pi">:</span> <span class="s">P@SSWord!@!</span>
  <span class="na">DomainRootPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">DC=contoso,DC=com"</span>
  <span class="na">DomainName</span><span class="pi">:</span> <span class="s2">"</span><span class="s">contoso.com"</span>
</code></pre></div></div>

<hr />

<h3 id="configadsgroupsdscyaml--delegation-security-groups"><code class="language-plaintext highlighter-rouge">config/ads.groups.dsc.yaml</code> — Delegation Security Groups</h3>

<p>Creates Universal Security Groups in the <code class="language-plaintext highlighter-rouge">OU=Delegation,OU=Groups,OU=Organization</code> container. Each group corresponds to a delegation scope on a specific OU:</p>

<table>
  <thead>
    <tr>
      <th>Group name</th>
      <th>Delegation target</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">OU-Organization-FullControl</code></td>
      <td>Organization OU (full control)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">OU-Organization-Accounts-RWD</code></td>
      <td>Accounts OU (Read/Write/Delete)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">OU-Organization-Accounts-Users-RWD</code></td>
      <td>Users OU</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">OU-Organization-Accounts-Users.NoSync-RWD</code></td>
      <td>Users.NoSync OU</td>
    </tr>
  </tbody>
</table>

<p>All groups follow the same pattern:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">$schema</span><span class="pi">:</span> <span class="s">https://aka.ms/dsc/schemas/v3/bundled/config/document.json</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">ads-groups-ou-configuration</span>
  <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
  <span class="na">description</span><span class="pi">:</span> <span class="s">Active Directory configuration of defualt groups</span>
  <span class="na">Microsoft.DSC</span><span class="pi">:</span>
    <span class="na">requiredSecurityContext</span><span class="pi">:</span> <span class="s">current</span> <span class="c1"># this is the default and just used as an example indicating this config works for admins and non-admins</span>

<span class="na">parameters</span><span class="pi">:</span>
  <span class="na">DomainRootPath</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
    <span class="na">defaultValue</span><span class="pi">:</span> <span class="s2">"</span><span class="s">DC=contoso,DC=com"</span>
<span class="na">resources</span><span class="pi">:</span>

<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory Organization OU</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">ActiveDirectoryDsc/ADGroup</span>
  <span class="na">properties</span><span class="pi">:</span>
    <span class="na">GroupName</span><span class="pi">:</span> <span class="s">OU-Organization-FullControl</span>
    <span class="na">GroupScope</span><span class="pi">:</span> <span class="s">Universal</span>
    <span class="na">Category</span><span class="pi">:</span> <span class="s">Security</span>
    <span class="na">Description</span><span class="pi">:</span> <span class="s">Delegation group for OU Organization with full control permissions</span>
    <span class="na">Path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[concat('OU=Delegation,OU=Groups,OU=Organization,',</span><span class="nv"> </span><span class="s">parameters('DomainRootPath'))]"</span>
    <span class="na">Ensure</span><span class="pi">:</span> <span class="s">Present</span>
</code></pre></div></div>

<p>Using Universal scope allows these groups to work across trusts in multi-domain forest scenarios.</p>

<hr />

<h3 id="configadsgroups-membersdscyaml--group-membership"><code class="language-plaintext highlighter-rouge">config/ads.groups-members.dsc.yaml</code> — Group Membership</h3>

<p>Assigns accounts to groups using <code class="language-plaintext highlighter-rouge">MembersToInclude</code>, which is additive and non-destructive (it does not remove existing members):</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory - Domain Admins group membership</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">ActiveDirectoryDsc/ADGroup</span>
  <span class="na">properties</span><span class="pi">:</span>
    <span class="na">GroupName</span><span class="pi">:</span> <span class="s">Domain Admins</span>
    <span class="na">Ensure</span><span class="pi">:</span> <span class="s">Present</span>
    <span class="na">MembersToInclude</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">michal.mmachniak</span>
</code></pre></div></div>

<p>This config intentionally has no parameters file — the group names and member accounts are static by design for privileged group membership, reducing the risk of accidental misconfiguration.</p>

<hr />

<h3 id="configadsou-delegationdscyaml--ou-permission-entries"><code class="language-plaintext highlighter-rouge">config/ads.ou-delegation.dsc.yaml</code> — OU Permission Entries</h3>

<p>Applies ACLs to OUs using <code class="language-plaintext highlighter-rouge">ActiveDirectoryDsc/ADObjectPermissionEntry</code>. Each entry grants the corresponding delegation group the required access:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">$schema</span><span class="pi">:</span> <span class="s">https://aka.ms/dsc/schemas/v3/bundled/config/document.json</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">ads-groups-ou-configuration</span>
  <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
  <span class="na">description</span><span class="pi">:</span> <span class="s">Active Directory configuration for organizational units delegations</span>
  <span class="na">Microsoft.DSC</span><span class="pi">:</span>
    <span class="na">requiredSecurityContext</span><span class="pi">:</span> <span class="s">current</span> <span class="c1"># this is the default and just used as an example indicating this config works for admins and non-admins</span>

<span class="na">parameters</span><span class="pi">:</span>
  <span class="na">DomainRootPath</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
    <span class="na">defaultValue</span><span class="pi">:</span> <span class="s2">"</span><span class="s">DC=contoso,DC=com"</span>
<span class="na">resources</span><span class="pi">:</span>

<span class="c1"># This configuration defines a security group for delegating permissions on the Organization OU in Active Directory. The group is created with full control permissions on the OU, allowing members of the group to manage the OU and its contents effectively.</span>


  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory Organization OU Delegation Group Permissions</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">ActiveDirectoryDsc/ADObjectPermissionEntry</span>
    <span class="na">properties</span><span class="pi">:</span> 
      <span class="na">Ensure</span><span class="pi">:</span> <span class="s">Present</span>
      <span class="na">Path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[concat('OU=Organization,',</span><span class="nv"> </span><span class="s">parameters('DomainRootPath'))]"</span>
      <span class="na">IdentityReference</span><span class="pi">:</span> <span class="s">OU-Organization-FullControl</span>
      <span class="na">ActiveDirectoryRights</span><span class="pi">:</span> 
        <span class="pi">-</span> <span class="s">GenericAll</span>
      <span class="na">AccessControlType</span><span class="pi">:</span> <span class="s">Allow</span>
      <span class="na">ObjectType</span><span class="pi">:</span> <span class="s">00000000-0000-0000-0000-000000000000</span>
      <span class="na">ActiveDirectorySecurityInheritance</span><span class="pi">:</span> <span class="s">Descendents</span>
      <span class="na">InheritedObjectType</span><span class="pi">:</span> <span class="s">00000000-0000-0000-0000-000000000000</span>

<span class="c1"># This configuration defines a security group for delegating permissions on the Organization-Accounts OU in Active Directory. The group is created with full control permissions on the OU, allowing members of the group to manage user accounts and other objects within the OU effectively.</span>


  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory Organization-Accounts OU Delegation Group Permissions Descendents</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">ActiveDirectoryDsc/ADObjectPermissionEntry</span>
    <span class="na">properties</span><span class="pi">:</span> 
      <span class="na">Ensure</span><span class="pi">:</span> <span class="s">Present</span>
      <span class="na">Path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[concat('OU=Accounts,OU=Organization,',</span><span class="nv"> </span><span class="s">parameters('DomainRootPath'))]"</span>
      <span class="na">IdentityReference</span><span class="pi">:</span> <span class="s">OU-Organization-Accounts-RWD</span>
      <span class="na">ActiveDirectoryRights</span><span class="pi">:</span> 
        <span class="pi">-</span> <span class="s">GenericAll</span>
      <span class="na">AccessControlType</span><span class="pi">:</span> <span class="s">Allow</span>
      <span class="na">ObjectType</span><span class="pi">:</span> <span class="s">00000000-0000-0000-0000-000000000000</span>
      <span class="na">ActiveDirectorySecurityInheritance</span><span class="pi">:</span> <span class="s">Descendents</span>
      <span class="na">InheritedObjectType</span><span class="pi">:</span> <span class="s">User</span> <span class="c1"># User"</span>


  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory Organization-Accounts-Users OU Delegation Group Permissions Descendents</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">ActiveDirectoryDsc/ADObjectPermissionEntry</span>
    <span class="na">properties</span><span class="pi">:</span> 
      <span class="na">Ensure</span><span class="pi">:</span> <span class="s">Present</span>
      <span class="na">Path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[concat('OU=Users,OU=Accounts,OU=Organization,',</span><span class="nv"> </span><span class="s">parameters('DomainRootPath'))]"</span>
      <span class="na">IdentityReference</span><span class="pi">:</span> <span class="s">OU-Organization-Accounts-Users-RWD</span>
      <span class="na">ActiveDirectoryRights</span><span class="pi">:</span> 
        <span class="pi">-</span> <span class="s">GenericAll</span>
      <span class="na">AccessControlType</span><span class="pi">:</span> <span class="s">Allow</span>
      <span class="na">ObjectType</span><span class="pi">:</span> <span class="s">00000000-0000-0000-0000-000000000000</span>
      <span class="na">ActiveDirectorySecurityInheritance</span><span class="pi">:</span> <span class="s">Descendents</span>
      <span class="na">InheritedObjectType</span><span class="pi">:</span> <span class="s">User</span> <span class="c1"># User</span>

      <span class="c1">## User.NoSync</span>

  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory Organization-Accounts-Users.NoSync OU Delegation Group Permissions Descendents</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">ActiveDirectoryDsc/ADObjectPermissionEntry</span>
    <span class="na">properties</span><span class="pi">:</span> 
      <span class="na">Ensure</span><span class="pi">:</span> <span class="s">Present</span>
      <span class="na">Path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[concat('OU=Users.NoSync,OU=Accounts,OU=Organization,',</span><span class="nv"> </span><span class="s">parameters('DomainRootPath'))]"</span>
      <span class="na">IdentityReference</span><span class="pi">:</span> <span class="s">OU-Organization-Accounts-Users.NoSync-RWD</span>
      <span class="na">ActiveDirectoryRights</span><span class="pi">:</span> 
        <span class="pi">-</span> <span class="s">GenericAll</span>
      <span class="na">AccessControlType</span><span class="pi">:</span> <span class="s">Allow</span>
      <span class="na">ObjectType</span><span class="pi">:</span> <span class="s">00000000-0000-0000-0000-000000000000</span>
      <span class="na">ActiveDirectorySecurityInheritance</span><span class="pi">:</span> <span class="s">Descendents</span>
      <span class="na">InheritedObjectType</span><span class="pi">:</span> <span class="s">User</span> <span class="c1"># User</span>

      
<span class="c1"># Computer objects in the Organization-Devices OU are delegated to a security group with permissions to manage computer accounts and other objects within the OU effectively.</span>

  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory Organization-Devices OU Delegation Group Permissions</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">ActiveDirectoryDsc/ADObjectPermissionEntry</span>
    <span class="na">properties</span><span class="pi">:</span> 
      <span class="na">Ensure</span><span class="pi">:</span> <span class="s">Present</span>
      <span class="na">Path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[concat('OU=Devices,OU=Organization,',</span><span class="nv"> </span><span class="s">parameters('DomainRootPath'))]"</span>
      <span class="na">IdentityReference</span><span class="pi">:</span> <span class="s">OU-Organization-Devices-RWD</span>
      <span class="na">ActiveDirectoryRights</span><span class="pi">:</span> 
        <span class="pi">-</span> <span class="s">GenericAll</span>
      <span class="na">AccessControlType</span><span class="pi">:</span> <span class="s">Allow</span>
      <span class="na">ObjectType</span><span class="pi">:</span> <span class="s">00000000-0000-0000-0000-000000000000</span>
      <span class="na">ActiveDirectorySecurityInheritance</span><span class="pi">:</span> <span class="s">Descendents</span>
      <span class="na">InheritedObjectType</span><span class="pi">:</span> <span class="s">Computer</span> <span class="c1"># Computer objects</span>


   
</code></pre></div></div>

<p>For granular OUs (like <code class="language-plaintext highlighter-rouge">Users</code>, <code class="language-plaintext highlighter-rouge">Users.NoSync</code>), the <code class="language-plaintext highlighter-rouge">InheritedObjectType</code> is set to <code class="language-plaintext highlighter-rouge">User</code>, restricting the ACE to apply only to user object descendants:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">ActiveDirectorySecurityInheritance</span><span class="pi">:</span> <span class="s">Descendents</span>
<span class="na">InheritedObjectType</span><span class="pi">:</span> <span class="s">User</span>
</code></pre></div></div>

<p>This enforces the principle of least privilege — delegation group members can only manage user objects in their designated OU subtree.</p>

<hr />

<h3 id="configadsfinegrained-password-policydscyaml--fine-grained-password-policies"><code class="language-plaintext highlighter-rouge">config/ads.finegrained-password-policy.dsc.yaml</code> — Fine-Grained Password Policies</h3>

<p>Applies Password Settings Objects (PSOs) to privileged groups, enforcing stricter password rules than the default domain policy:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">$schema</span><span class="pi">:</span> <span class="s">https://aka.ms/dsc/schemas/v3/bundled/config/document.json</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">ads-finegrained-password-policy-configuration</span>
  <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
  <span class="na">description</span><span class="pi">:</span> <span class="s">Active Directory configuration for AD FineGrained Password Policy</span>
  <span class="na">Microsoft.DSC</span><span class="pi">:</span>
    <span class="na">requiredSecurityContext</span><span class="pi">:</span> <span class="s">current</span> <span class="c1"># this is the default and just used as an example indicating this config works for admins and non-admins</span>

<span class="na">parameters</span><span class="pi">:</span>
  <span class="na">DomainRootPath</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
    <span class="na">defaultValue</span><span class="pi">:</span> <span class="s2">"</span><span class="s">DC=contoso,DC=com"</span>
<span class="na">resources</span><span class="pi">:</span>

  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory ADFineGrainedPasswordPolicy - Domain Admins</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">ActiveDirectoryDsc/ADFineGrainedPasswordPolicy</span>
    <span class="na">properties</span><span class="pi">:</span> 
      <span class="na">Name</span><span class="pi">:</span> <span class="s1">'</span><span class="s">DomainAdmins'</span>
      <span class="na">DisplayName</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Domain</span><span class="nv"> </span><span class="s">Admins</span><span class="nv"> </span><span class="s">Password</span><span class="nv"> </span><span class="s">Policy'</span>
      <span class="na">Description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">This</span><span class="nv"> </span><span class="s">is</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">Fine</span><span class="nv"> </span><span class="s">Grained</span><span class="nv"> </span><span class="s">Password</span><span class="nv"> </span><span class="s">Policy</span><span class="nv"> </span><span class="s">for</span><span class="nv"> </span><span class="s">Domain</span><span class="nv"> </span><span class="s">Admins'</span>
      <span class="na">Subjects</span><span class="pi">:</span> 
        <span class="pi">-</span> <span class="s">Domain Admins</span>
      <span class="na">ComplexityEnabled</span><span class="pi">:</span> <span class="no">true</span>
      <span class="na">LockoutDuration</span><span class="pi">:</span> <span class="s1">'</span><span class="s">00:30:00'</span>
      <span class="na">LockoutObservationWindow</span><span class="pi">:</span> <span class="s1">'</span><span class="s">00:30:00'</span>
      <span class="na">LockoutThreshold</span><span class="pi">:</span> <span class="m">5</span>
      <span class="na">MaxPasswordAge</span><span class="pi">:</span> <span class="s1">'</span><span class="s">90.00:00:00'</span>
      <span class="na">MinPasswordAge</span><span class="pi">:</span> <span class="s1">'</span><span class="s">1.00:00:00'</span>
      <span class="na">MinPasswordLength</span><span class="pi">:</span> <span class="m">15</span>
      <span class="na">PasswordHistoryCount</span><span class="pi">:</span> <span class="m">6</span>
      <span class="na">ReversibleEncryptionEnabled</span><span class="pi">:</span> <span class="no">false</span>
      <span class="na">ProtectedFromAccidentalDeletion</span><span class="pi">:</span> <span class="no">true</span>
      <span class="na">Precedence</span><span class="pi">:</span> <span class="m">10</span>

  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Active Directory ADFineGrainedPasswordPolicy - Enterprise Admins</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">ActiveDirectoryDsc/ADFineGrainedPasswordPolicy</span>
    <span class="na">properties</span><span class="pi">:</span> 
      <span class="na">Name</span><span class="pi">:</span> <span class="s1">'</span><span class="s">EnterpriseAdmins'</span>
      <span class="na">DisplayName</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Enterprise</span><span class="nv"> </span><span class="s">Admins</span><span class="nv"> </span><span class="s">Password</span><span class="nv"> </span><span class="s">Policy'</span>
      <span class="na">Description</span><span class="pi">:</span> <span class="s1">'</span><span class="s">This</span><span class="nv"> </span><span class="s">is</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">Fine</span><span class="nv"> </span><span class="s">Grained</span><span class="nv"> </span><span class="s">Password</span><span class="nv"> </span><span class="s">Policy</span><span class="nv"> </span><span class="s">for</span><span class="nv"> </span><span class="s">Enterprise</span><span class="nv"> </span><span class="s">Admins'</span>
      <span class="na">Subjects</span><span class="pi">:</span> 
        <span class="pi">-</span> <span class="s">Enterprise Admins</span>
      <span class="na">ComplexityEnabled</span><span class="pi">:</span> <span class="no">true</span>
      <span class="na">LockoutDuration</span><span class="pi">:</span> <span class="s1">'</span><span class="s">00:30:00'</span>
      <span class="na">LockoutObservationWindow</span><span class="pi">:</span> <span class="s1">'</span><span class="s">00:30:00'</span>
      <span class="na">LockoutThreshold</span><span class="pi">:</span> <span class="m">5</span>
      <span class="na">MaxPasswordAge</span><span class="pi">:</span> <span class="s1">'</span><span class="s">90.00:00:00'</span>
      <span class="na">MinPasswordAge</span><span class="pi">:</span> <span class="s1">'</span><span class="s">1.00:00:00'</span>
      <span class="na">MinPasswordLength</span><span class="pi">:</span> <span class="m">15</span>
      <span class="na">PasswordHistoryCount</span><span class="pi">:</span> <span class="m">6</span>
      <span class="na">ReversibleEncryptionEnabled</span><span class="pi">:</span> <span class="no">false</span>
      <span class="na">ProtectedFromAccidentalDeletion</span><span class="pi">:</span> <span class="no">true</span>
      <span class="na">Precedence</span><span class="pi">:</span> <span class="m">11</span>
</code></pre></div></div>

<p>Two PSOs are defined:</p>

<table>
  <thead>
    <tr>
      <th>PSO name</th>
      <th>Applied to</th>
      <th>Min length</th>
      <th>Max age</th>
      <th>Precedence</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">DomainAdmins</code></td>
      <td>Domain Admins</td>
      <td>15</td>
      <td>90 days</td>
      <td>10</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">EnterpriseAdmins</code></td>
      <td>Enterprise Admins</td>
      <td>15</td>
      <td>90 days</td>
      <td>11</td>
    </tr>
  </tbody>
</table>

<p>Lower <code class="language-plaintext highlighter-rouge">Precedence</code> numbers take priority when multiple PSOs apply to the same user.</p>

<hr />

<h2 id="parameter-files">Parameter Files</h2>

<h3 id="parametersadsparametersyaml"><code class="language-plaintext highlighter-rouge">parameters/ads.parameters.yaml</code></h3>

<p>Shared by most sub-configurations to supply the domain LDAP root path:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">parameters</span><span class="pi">:</span>
  <span class="na">DomainRootPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">DC=contoso,DC=com"</span>
</code></pre></div></div>

<h3 id="parametersadsadmin-userparametersyaml"><code class="language-plaintext highlighter-rouge">parameters/ads.admin-user.parameters.yaml</code></h3>

<p>Provides all values required by the admin users configuration:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">parameters</span><span class="pi">:</span>
  <span class="na">Password</span><span class="pi">:</span>
    <span class="na">username</span><span class="pi">:</span> <span class="s">AdminTest</span>
    <span class="na">password</span><span class="pi">:</span> <span class="s">Password</span>
  <span class="na">DomainRootPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">DC=contoso,DC=com"</span>
  <span class="na">DomainName</span><span class="pi">:</span> <span class="s2">"</span><span class="s">contoso.com"</span>
</code></pre></div></div>

<hr />

<h2 id="deployment-workflow">Deployment Workflow</h2>

<h3 id="step-1--validate-configuration-dry-run">Step 1 — Validate configuration (dry-run)</h3>

<p>Before applying anything, use <code class="language-plaintext highlighter-rouge">test</code> mode to see what is out of compliance:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dsc</span><span class="w"> </span><span class="nx">config</span><span class="w"> </span><span class="nt">--parameters-file</span><span class="w"> </span><span class="o">.</span><span class="nx">\ads-root.dsc.parameters.yaml</span><span class="w"> </span><span class="nx">test</span><span class="w"> </span><span class="se">`
</span><span class="w">  </span><span class="nt">--file</span><span class="w"> </span><span class="o">.</span><span class="nx">\ads-root-forest.dsc.yaml</span><span class="w">
</span></code></pre></div></div>

<p>For the Stage 2 orchestrator:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dsc</span><span class="w"> </span><span class="nx">config</span><span class="w"> </span><span class="nt">--parameters-file</span><span class="w"> </span><span class="o">.</span><span class="nx">\ADS-Include\parameters\ads.parameters.yaml</span><span class="w"> </span><span class="nx">test</span><span class="w"> </span><span class="se">`
</span><span class="w">  </span><span class="nt">--file</span><span class="w"> </span><span class="o">.</span><span class="nx">\ADS-Include\ads.main.dsc.yaml</span><span class="w">
</span></code></pre></div></div>

<h3 id="step-2--apply-stage-1-create-the-forest">Step 2 — Apply Stage 1: Create the forest</h3>

<p>Run this on the first server that will become the Domain Controller. The server will be promoted and — if <code class="language-plaintext highlighter-rouge">SuppressReboot: false</code> — will reboot automatically.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dsc</span><span class="w"> </span><span class="nx">config</span><span class="w"> </span><span class="nt">--parameters-file</span><span class="w"> </span><span class="o">.</span><span class="nx">\ads-root.dsc.parameters.yaml</span><span class="w"> </span><span class="nx">set</span><span class="w"> </span><span class="se">`
</span><span class="w">  </span><span class="nt">--file</span><span class="w"> </span><span class="o">.</span><span class="nx">\ads-root-forest.dsc.yaml</span><span class="w">
</span></code></pre></div></div>

<h3 id="step-3--apply-stage-2-configure-ad-structure">Step 3 — Apply Stage 2: Configure AD structure</h3>

<p>After the DC is up and the domain is functional, run the main orchestrator from within the <code class="language-plaintext highlighter-rouge">ADS-Include</code> directory:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Set-Location</span><span class="w"> </span><span class="o">.</span><span class="nx">\ADS-Include</span><span class="w">

</span><span class="n">dsc</span><span class="w"> </span><span class="nx">config</span><span class="w"> </span><span class="nx">set</span><span class="w"> </span><span class="nt">--file</span><span class="w"> </span><span class="o">.</span><span class="nx">\ads.main.dsc.yaml</span><span class="w">
</span></code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">Include</code> resource resolves <code class="language-plaintext highlighter-rouge">configurationFile</code> paths relative to the location of <code class="language-plaintext highlighter-rouge">ads.main.dsc.yaml</code>, so the working directory must match.</p>

<h3 id="step-4--enable-trace-level-logging-troubleshooting">Step 4 — Enable trace-level logging (troubleshooting)</h3>

<p>If any resource fails, enable detailed output with <code class="language-plaintext highlighter-rouge">--trace-level</code>:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dsc</span><span class="w"> </span><span class="nt">--trace-level</span><span class="w"> </span><span class="nx">trace</span><span class="w"> </span><span class="nx">config</span><span class="w"> </span><span class="nt">--parameters-file</span><span class="w"> </span><span class="o">.</span><span class="nx">\parameters\ads.parameters.yaml</span><span class="w"> </span><span class="nx">set</span><span class="w"> </span><span class="se">`
</span><span class="w">  </span><span class="nt">--file</span><span class="w"> </span><span class="o">.</span><span class="nx">\ads.main.dsc.yaml</span><span class="w">
</span></code></pre></div></div>

<p>Available trace levels: <code class="language-plaintext highlighter-rouge">error</code>, <code class="language-plaintext highlighter-rouge">warn</code>, <code class="language-plaintext highlighter-rouge">info</code>, <code class="language-plaintext highlighter-rouge">debug</code>, <code class="language-plaintext highlighter-rouge">trace</code></p>

<h3 id="step-5--re-validate-after-apply">Step 5 — Re-validate after apply</h3>

<p>Confirm the configuration converged successfully:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dsc</span><span class="w"> </span><span class="nx">config</span><span class="w"> </span><span class="nx">test</span><span class="w"> </span><span class="nt">--file</span><span class="w"> </span><span class="o">.</span><span class="nx">\ads.main.dsc.yaml</span><span class="w">
</span></code></pre></div></div>

<p>A fully converged configuration returns <code class="language-plaintext highlighter-rouge">InDesiredState: true</code> for every resource.</p>

<hr />

<h2 id="dsc-v3-concepts-used-in-this-project">DSC v3 Concepts Used in This Project</h2>

<table>
  <thead>
    <tr>
      <th>Concept</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">$schema</code></td>
      <td>Declares the DSC v3 document schema — required in every config file</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Microsoft.DSC/Include</code></td>
      <td>Composes external YAML files into one logical configuration</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">parametersFile</code></td>
      <td>Merges a separate YAML parameter document into the included config</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">parameters()</code></td>
      <td>Function that references a declared parameter value</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">concat()</code></td>
      <td>Builds strings dynamically from literals and parameter values</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">resourceId()</code></td>
      <td>Returns a reference to another resource, used in <code class="language-plaintext highlighter-rouge">dependsOn</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">dependsOn</code></td>
      <td>Enforces ordering — a resource waits until its dependencies complete</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">secureObject</code></td>
      <td>Parameter type for credential objects — value is masked in logs</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">requiredSecurityContext: current</code></td>
      <td>Indicates the config can run under the current user context</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="recommended-execution-checklist">Recommended Execution Checklist</h2>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />DSC v3 (<code class="language-plaintext highlighter-rouge">dsc.exe</code>) installed and in <code class="language-plaintext highlighter-rouge">PATH</code></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">ActiveDirectoryDsc</code> module installed via PSGallery</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Running as a Domain Admin (or local Administrator for Stage 1)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Passwords in parameter files replaced with vault-sourced values before Organization use</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">dsc config test</code> passes cleanly before <code class="language-plaintext highlighter-rouge">dsc config set</code></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Stage 1 (forest creation) completes and DC reboots before running Stage 2</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Stage 2 executed from within the <code class="language-plaintext highlighter-rouge">ADS-Include</code> directory</li>
</ul>

<hr />

<h2 id="summary">Summary</h2>

<p>This project demonstrates how DSC v3 can manage the full lifecycle of an Active Directory environment — from forest creation to a Organization-ready OU structure, delegation model, and password policies — using nothing but declarative YAML files.</p>

<p>The <code class="language-plaintext highlighter-rouge">Microsoft.DSC/Include</code> pattern is the architectural cornerstone: it allows each concern (OUs, users, groups, delegation, password policy) to live in its own file while a single orchestrator file (<code class="language-plaintext highlighter-rouge">ads.main.dsc.yaml</code>) ties them together. Combined with parameter files, the same set of configurations can target any domain simply by swapping the <code class="language-plaintext highlighter-rouge">DomainRootPath</code> and <code class="language-plaintext highlighter-rouge">DomainName</code> values.</p>

<p>This approach brings Active Directory configuration into the modern infrastructure-as-code world — auditable, reproducible, and version-controlled.</p>

<h2 id="links-for-more-resources">Links for more resources</h2>

<ul>
  <li><a href="https://github.com/PowerShell/DSC">Microsoft DSCv3</a></li>
  <li><a href="https://github.com/dsccommunity/ActiveDirectoryDsc">DSC community</a></li>
</ul>]]></content><author><name>Michal Machniak</name><email>mmachniak@outlook.com</email></author><category term="Azure" /><category term="DSC" /><category term="IaaC" /><category term="Code" /><summary type="html"><![CDATA[This blog post explores how to use Microsoft Desired State Configuration v3 (DSC v3) to build, deploy, and manage infrastructure while reusing classic PowerShell DSC resources, focusing on the ActiveDirectoryDSC module. The article demonstrates how DSC v3 integrates with legacy DSC ecosystems using adapter resources, allowing organizations to continue using proven modules like **ActiveDirectoryDSC** without refactoring. This module provides automation capabilities for Active Directory, including domain deployment, domain controllers, trusts, users, groups, and organizational units.]]></summary></entry><entry><title type="html">How to Use AzureDevOpsDscv3 in Azure DevOps Pipelines</title><link href="https://mimachniak.github.io/sysopslife/2026/02/09/hot-to-use-dscv3-in-azure-devops/" rel="alternate" type="text/html" title="How to Use AzureDevOpsDscv3 in Azure DevOps Pipelines" /><published>2026-02-09T00:00:00+01:00</published><updated>2026-02-09T00:00:00+01:00</updated><id>https://mimachniak.github.io/sysopslife/2026/02/09/hot-to-use-dscv3-in-azure-devops</id><content type="html" xml:base="https://mimachniak.github.io/sysopslife/2026/02/09/hot-to-use-dscv3-in-azure-devops/"><![CDATA[<h1 id="how-to-use-azuredevopsdscv3-in-azure-devops-pipelines">How to Use AzureDevOpsDscv3 in Azure DevOps Pipelines</h1>

<p>This post shows a real-life example for using <a href="https://github.com/mimachniak/AzureDevOpsDscv3">AzureDevOpsDscv3</a> inside an Azure DevOps pipeline. The goal is to define Azure DevOps projects, users, and groups as code, then apply the configuration in a controlled pipeline.</p>

<h2 id="scenario">Scenario</h2>

<p>You are onboarding a new product team. Every environment must have:</p>

<ul>
  <li>A standard project created from a Git template</li>
  <li>A group created from Entra ID</li>
  <li>A baseline set of users</li>
</ul>

<p>Instead of clicking in the UI, you use DSC v3 to declare everything, and the pipeline applies it consistently across environments.</p>

<h2 id="prerequisites">Prerequisites</h2>

<ul>
  <li>Azure DevOps organization and a PAT with permissions to manage projects and users</li>
  <li>A self-hosted or Microsoft-hosted Windows agent</li>
  <li>PowerShell 7 and DSC v3 (<code class="language-plaintext highlighter-rouge">dsc</code> CLI)</li>
  <li>The AzureDevOpsDscv3 module from the PowerShell Gallery</li>
</ul>

<p>In the pipeline we will install the required modules, so the agent stays clean and the process is repeatable.</p>

<h2 id="repository-layout">Repository Layout</h2>

<p>Example structure you can place in your repo:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.
├─ dsc
│  └─ ado.dsc.yaml
├─ pipelines
│  └─ ado-dsc.yml
└─ README.md
</code></pre></div></div>

<h2 id="dsc-v3-configuration-file">DSC v3 Configuration File</h2>

<p>Below is a simplified config that creates a project, adds a user, and links an Entra ID group. Save it as <code class="language-plaintext highlighter-rouge">dsc/ado.dsc.yaml</code>.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">$schema</span><span class="pi">:</span> <span class="s">https://aka.ms/dsc/schemas/v3/bundled/config/document.json</span>
<span class="na">parameters</span><span class="pi">:</span>
<span class="err">	</span><span class="na">Token</span><span class="pi">:</span>
<span class="err">		</span><span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
<span class="na">		defaultValue</span><span class="pi">:</span> <span class="s">PAT-Token</span>
<span class="na">resources</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Configure Azure DevOps</span>
<span class="na">	type</span><span class="pi">:</span> <span class="s">Microsoft.Windows/WindowsPowerShell</span>
<span class="na">	properties</span><span class="pi">:</span>
<span class="err">		</span><span class="na">resources</span><span class="pi">:</span>
<span class="err">		</span><span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Create project</span>
<span class="na">			type</span><span class="pi">:</span> <span class="s">AzureDevOpsDscv3/ProjectResource</span>
<span class="na">			properties</span><span class="pi">:</span>
<span class="err">				</span><span class="na">Organization</span><span class="pi">:</span> <span class="s">ExampleOrganization</span>
<span class="na">				ProjectName</span><span class="pi">:</span> <span class="s">Contoso-Platform</span>
<span class="na">				Description</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Project</span><span class="nv"> </span><span class="s">created</span><span class="nv"> </span><span class="s">via</span><span class="nv"> </span><span class="s">DSC</span><span class="nv"> </span><span class="s">v3"</span>
<span class="err">				</span><span class="na">pat</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[parameters('Token')]"</span>
<span class="err">				</span><span class="na">SourceControlType</span><span class="pi">:</span> <span class="s">Git</span>
<span class="na">				Ensure</span><span class="pi">:</span> <span class="s">Present</span>
<span class="na">		- name</span><span class="pi">:</span> <span class="s">Add user</span>
<span class="na">			type</span><span class="pi">:</span> <span class="s">AzureDevOpsDscv3/OrganizationUserResource</span>
<span class="na">			properties</span><span class="pi">:</span>
<span class="err">				</span><span class="na">UserPrincipalName</span><span class="pi">:</span> <span class="s">dev1@contoso.com</span>
<span class="na">				Organization</span><span class="pi">:</span> <span class="s">ExampleOrganization</span>
<span class="na">				AccessLevel</span><span class="pi">:</span> <span class="s">Basic</span>
<span class="na">				Ensure</span><span class="pi">:</span> <span class="s">Present</span>
<span class="na">				pat</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[parameters('Token')]"</span>
<span class="err">		</span><span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Add group</span>
<span class="na">			type</span><span class="pi">:</span> <span class="s">AzureDevOpsDscv3/OrganizationGroupResource</span>
<span class="na">			properties</span><span class="pi">:</span>
<span class="err">				</span><span class="na">GroupOriginId</span><span class="pi">:</span> <span class="s">00000000-0000-0000-0000-000000000000</span>
<span class="na">				GroupDisplayName</span><span class="pi">:</span> <span class="s">Contoso-DevTeam</span>
<span class="na">				Organization</span><span class="pi">:</span> <span class="s">ExampleOrganization</span>
<span class="na">				AccessLevel</span><span class="pi">:</span> <span class="s">Basic</span>
<span class="na">				Ensure</span><span class="pi">:</span> <span class="s">Present</span>
<span class="na">				pat</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[parameters('Token')]"</span>
</code></pre></div></div>

<p>Notes:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">GroupOriginId</code> is the Entra ID group object ID.</li>
  <li><code class="language-plaintext highlighter-rouge">pat</code> is sourced from the <code class="language-plaintext highlighter-rouge">Token</code> parameter, which we inject in the pipeline.</li>
</ul>

<h2 id="azure-devops-pipeline">Azure DevOps Pipeline</h2>

<p>Create a pipeline YAML file, for example <code class="language-plaintext highlighter-rouge">pipelines/ado-dsc.yml</code>.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">trigger</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">main</span>

<span class="na">pool</span><span class="pi">:</span>
<span class="err">	</span><span class="na">vmImage</span><span class="pi">:</span> <span class="s">windows-latest</span>

<span class="na">variables</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">DscConfigPath</span>
<span class="na">	value</span><span class="pi">:</span> <span class="s">dsc/ado.dsc.yaml</span>

<span class="na">steps</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">checkout</span><span class="pi">:</span> <span class="s">self</span>

<span class="pi">-</span> <span class="na">task</span><span class="pi">:</span> <span class="s">PowerShell@2</span>
<span class="na">	displayName</span><span class="pi">:</span> <span class="s">Install DSC v3 and AzureDevOpsDscv3</span>
<span class="na">	inputs</span><span class="pi">:</span>
<span class="err">		</span><span class="na">pwsh</span><span class="pi">:</span> <span class="no">true</span>
<span class="na">		targetType</span><span class="pi">:</span> <span class="s">inline</span>
<span class="na">		script</span><span class="pi">:</span> <span class="pi">|</span>
<span class="err">	</span><span class="s">		Set-StrictMode -Version Latest</span>
<span class="err">	</span><span class="s">		$ErrorActionPreference = 'Stop'</span>

<span class="err">	</span><span class="s">		#(Note: If you are on a 32-bit system, use .x86 instead).</span>
            <span class="s">winget install Microsoft.VCRedist.2015+.x64 </span>

            <span class="s"># Install latest stable</span>
            <span class="s">winget install --id 9NVTPZWRC6KQ --source msstore</span>
<span class="err">	</span><span class="s">		Install-Module -Name AzureDevOpsDscv3 -Scope CurrentUser -Force</span>
<span class="err">	</span><span class="s">		dsc --version</span>

<span class="err">-</span><span class="s"> task: PowerShell@2</span>
<span class="err">	</span><span class="s">displayName: Inject PAT and apply configuration</span>
<span class="err">	</span><span class="s">inputs:</span>
<span class="err">	</span><span class="s">	pwsh: true</span>
<span class="err">	</span><span class="s">	targetType: inline</span>
<span class="err">	</span><span class="s">	script: |</span>
<span class="err">	</span><span class="s">		Set-StrictMode -Version Latest</span>
<span class="err">	</span><span class="s">		$ErrorActionPreference = 'Stop'</span>

<span class="err">	</span><span class="s">		$configPath = "$(DscConfigPath)"</span>
<span class="err">	</span><span class="s">		$tempPath = Join-Path $env:Agent_TempDirectory 'ado.dsc.yaml'</span>

<span class="err">	</span><span class="s">		(Get-Content $configPath -Raw) -replace 'PAT-Token', $env:ADO_PAT | Set-Content $tempPath</span>

<span class="err">	</span><span class="s">		dsc -l debug config set --file $tempPath</span>
<span class="err">	</span><span class="s">env:</span>
<span class="err">	</span><span class="s">	ADO_PAT: $(ADO_PAT)</span>
</code></pre></div></div>

<h3 id="secure-the-pat">Secure the PAT</h3>

<p>Create a variable in your Azure DevOps pipeline or a variable group:</p>

<ul>
  <li>Name: <code class="language-plaintext highlighter-rouge">ADO_PAT</code></li>
  <li>Type: secret</li>
</ul>

<p>Keep the PAT scoped to the minimum permissions required to manage projects, users, and groups in your organization.</p>

<h2 id="run-the-pipeline">Run the Pipeline</h2>

<ol>
  <li>Commit the DSC config and pipeline YAML.</li>
  <li>Create a new pipeline from <code class="language-plaintext highlighter-rouge">pipelines/ado-dsc.yml</code>.</li>
  <li>Run the pipeline.</li>
</ol>

<p>The pipeline installs the required modules, injects the PAT into the config, and executes <code class="language-plaintext highlighter-rouge">dsc config set</code> to apply the desired state.</p>

<h2 id="real-world-tips">Real-World Tips</h2>

<ul>
  <li>Store multiple configs per environment and pass the file path as a pipeline parameter.</li>
  <li>Use a dedicated service account for the PAT to keep audit trails clean.</li>
  <li>Add a validation step that runs <code class="language-plaintext highlighter-rouge">dsc config get</code> (or <code class="language-plaintext highlighter-rouge">dsc resource list</code>) to verify expected resources.</li>
  <li>For production, add a manual approval gate between validation and apply.</li>
</ul>

<h2 id="troubleshooting">Troubleshooting</h2>

<ul>
  <li>If <code class="language-plaintext highlighter-rouge">dsc</code> is not found, confirm the <code class="language-plaintext highlighter-rouge">Microsoft.PowerShell.DSC</code> module is installed and available in the <code class="language-plaintext highlighter-rouge">pwsh</code> session.</li>
  <li>If the pipeline fails to create a project, check the PAT scope and the Azure DevOps organization name.</li>
  <li>For group assignment, verify <code class="language-plaintext highlighter-rouge">GroupOriginId</code> is the Entra ID object ID, not the display name.</li>
</ul>

<h2 id="closing">Closing</h2>

<p>This pattern lets you manage Azure DevOps configuration as code with minimal drift. Start small with one project and a few users, then expand to include repos, pipelines, and permissions in a controlled, repeatable way.</p>]]></content><author><name>Michal Machniak</name><email>mmachniak@outlook.com</email></author><category term="Azure" /><category term="DevOps" /><category term="DSC" /><category term="AzureDevOps" /><category term="Pipeline" /><summary type="html"><![CDATA[A real world example that uses AzureDevOpsDscv3 to create a project, add a group, and assign users with a repeatable Azure DevOps pipeline.]]></summary></entry><entry><title type="html">Mastering Azure Networking: Best Practices for Building Scalable, Secure Cloud Infrastructure</title><link href="https://mimachniak.github.io/sysopslife/2025/10/20/Mastering-Azure-Networking-Best-Practices-for-Building-Scalable-Secure-Cloud-Infrastructure/" rel="alternate" type="text/html" title="Mastering Azure Networking: Best Practices for Building Scalable, Secure Cloud Infrastructure" /><published>2025-10-20T00:00:00+02:00</published><updated>2025-10-20T00:00:00+02:00</updated><id>https://mimachniak.github.io/sysopslife/2025/10/20/Mastering-Azure%20Networking-Best-Practices-for-Building-Scalable-Secure-Cloud-Infrastructure</id><content type="html" xml:base="https://mimachniak.github.io/sysopslife/2025/10/20/Mastering-Azure-Networking-Best-Practices-for-Building-Scalable-Secure-Cloud-Infrastructure/"><![CDATA[<h1 id="building-a-network-in-azure--from-zero-to-hero">Building a Network in Azure — From Zero to Hero</h1>

<p>Building a well-structured and secure network in Azure is one of the foundational steps toward a successful cloud deployment. Whether you’re migrating workloads, designing new environments, or improving existing infrastructure, Azure networking provides powerful tools to help you design for scalability, performance, and governance.</p>

<p>In this post, we’ll walk through the core principles and best practices for building a network in Azure — from initial planning to advanced connectivity options.</p>

<hr />

<h2 id="-start-with-the-azure-landing-zone-concept">🧭 Start with the Azure Landing Zone Concept</h2>

<p>Before diving into subnets and IP ranges, it’s crucial to understand the <strong>Azure Landing Zone</strong> model.</p>

<p>Microsoft defines an <strong>Azure Landing Zone</strong> as an environment that implements key design principles across <strong>eight design areas</strong> — governance, security, identity, networking, operations, management, and more. It ensures that your environment can scale and support multiple applications and workloads consistently.</p>

<h3 id="platform-vs-application-landing-zones">Platform vs Application Landing Zones</h3>

<p>Azure uses <strong>subscriptions</strong> to isolate and scale resources:</p>

<ul>
  <li><strong>Platform landing zones</strong> host shared services like networking, identity, and monitoring.</li>
  <li><strong>Application landing zones</strong> host workloads and apps.</li>
</ul>

<p>This separation allows for cleaner management boundaries, better security, and easier automation.</p>

<hr />

<h2 id="️-designing-your-network-topology">🕸️ Designing Your Network Topology</h2>

<p>One of the most common approaches for Azure networking is the <strong>hub-and-spoke topology</strong>.</p>

<h3 id="hub-and-spoke-model">Hub-and-Spoke Model</h3>

<p>In this model:</p>
<ul>
  <li>The <strong>hub</strong> is the central network that hosts shared components like firewalls, DNS, or VPN gateways.</li>
  <li>The <strong>spokes</strong> are individual VNets (Virtual Networks) that connect to the hub and host application workloads.</li>
</ul>

<p>This approach provides:</p>
<ul>
  <li>Centralized management of connectivity and security.</li>
  <li>Isolation between applications.</li>
  <li>Easier integration with on-premises networks.</li>
</ul>

<p>You can also use <strong>Azure Virtual Network Manager</strong> to create and manage:</p>
<ul>
  <li><strong>Hub-and-spoke topology</strong></li>
  <li><strong>Mesh topology</strong> (in preview)</li>
  <li><strong>Hybrid hub-spoke with direct spoke-to-spoke connectivity</strong></li>
</ul>

<p><img src="/assets/images/Azure/network/PL-Azure%20Networking-topology.png" alt="" /></p>

<hr />

<h2 id="-planning-your-network">🧱 Planning Your Network</h2>

<h3 id="mapping-on-premises-network-to-azure-network">Mapping on-premises network to Azure network</h3>

<p>Mapping VLANs to Azure networking depends on how and where you’re connecting Azure to your on-premises or extended network. 
Azure itself doesn’t use VLANs internally—it uses Virtual Networks (VNets) for segmentation—but VLANs still matter when you’re integrating Azure with your physical or hybrid infrastructure.</p>

<ul>
  <li>Use subnets within a VNet to logically segment traffic (similar to VLANs).</li>
  <li>Apply Network Security Groups (NSGs) or Azure Firewall rules for isolation.</li>
  <li>VLAN tagging or trunking is not available between Azure VMs or subnets.</li>
</ul>

<h3 id="naming-conventions">Naming Conventions</h3>

<p>A consistent naming convention is the foundation of an organized environment.<br />
Follow Microsoft’s <strong>Cloud Adoption Framework</strong> recommendations for naming standards:</p>
<blockquote>
  <table>
    <tbody>
      <tr>
        <td>[Cloud Adoption Framework</td>
        <td>Microsoft Learn](https://learn.microsoft.com/azure/cloud-adoption-framework/)</td>
      </tr>
    </tbody>
  </table>
</blockquote>

<p><img src="/assets/images/Azure/network/PL-Azure%20Networking-naming.drawio.png" alt="" /></p>

<h3 id="ip-addressing">IP Addressing</h3>

<p>When planning your VNets and subnets, avoid overlapping with your on-premises or partner networks.</p>

<h4 id="common-private-address-ranges">Common private address ranges:</h4>
<ul>
  <li><code class="language-plaintext highlighter-rouge">10.0.0.0/8</code></li>
  <li><code class="language-plaintext highlighter-rouge">172.16.0.0/12</code></li>
  <li><code class="language-plaintext highlighter-rouge">192.168.0.0/16</code></li>
  <li><code class="language-plaintext highlighter-rouge">100.64.0.0/10</code> (shared address space)</li>
</ul>

<p>Keep subnet masks small, such as <code class="language-plaintext highlighter-rouge">/28</code> or <code class="language-plaintext highlighter-rouge">/26</code>, to maintain flexibility and minimize wasted IPs.</p>

<h4 id="remember">Remember:</h4>
<p>Azure reserves certain IPs in every subnet:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">.0</code> — network address</li>
  <li><code class="language-plaintext highlighter-rouge">.1</code> — default gateway</li>
  <li><code class="language-plaintext highlighter-rouge">.2</code> and <code class="language-plaintext highlighter-rouge">.3</code> — Azure DNS mapping</li>
  <li><code class="language-plaintext highlighter-rouge">.255</code> — broadcast address</li>
</ul>

<h4 id="subnet-nameing-and-functionality-example">Subnet nameing and functionality example</h4>

<table>
  <thead>
    <tr>
      <th><strong>Subnet Name</strong></th>
      <th><strong>Description</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>AzureFirewallSubnet</td>
      <td>Reserved for Azure Firewall</td>
    </tr>
    <tr>
      <td>GatewaySubnet</td>
      <td>Reserved for Azure Virtual Gateway</td>
    </tr>
    <tr>
      <td>AzureBastionSubnet</td>
      <td>Reserved for Azure Bastion</td>
    </tr>
    <tr>
      <td>ApplicationGatewaySubnet</td>
      <td>Custom – required delegation for application gateway</td>
    </tr>
    <tr>
      <td>ManagementSubnet</td>
      <td>Custom – for any management services like Update Managers, Scanning tools</td>
    </tr>
    <tr>
      <td>IdentitySubnet</td>
      <td>Custom – for identity services like Active Directory, PIM, PAM</td>
    </tr>
    <tr>
      <td>DmzSubnet</td>
      <td>Custom – for services that will be published to the Internet</td>
    </tr>
    <tr>
      <td>ApplicationSubnet</td>
      <td>Custom – for applications like CRM front, Reporting Services</td>
    </tr>
    <tr>
      <td>BackendSubnet</td>
      <td>Custom – backend API / Integration bus</td>
    </tr>
    <tr>
      <td>DatabaseSubnet</td>
      <td>Custom – for database services</td>
    </tr>
  </tbody>
</table>

<h4 id="subnet-ranges">Subnet ranges</h4>

<table>
  <thead>
    <tr>
      <th><strong>Subnet Name</strong></th>
      <th><strong>Network Mask</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>AzureFirewallSubnet</td>
      <td>/26</td>
    </tr>
    <tr>
      <td>GatewaySubnet</td>
      <td>/26</td>
    </tr>
    <tr>
      <td>AzureBastionSubnet</td>
      <td>/26</td>
    </tr>
    <tr>
      <td>ApplicationGatewaySubnet</td>
      <td>/26</td>
    </tr>
    <tr>
      <td>ManagementSubnet</td>
      <td>/28</td>
    </tr>
    <tr>
      <td>IdentitySubnet</td>
      <td>/24</td>
    </tr>
    <tr>
      <td>DmzSubnet</td>
      <td>/24</td>
    </tr>
    <tr>
      <td>ApplicationSubnet</td>
      <td>/24</td>
    </tr>
    <tr>
      <td>BackendSubnet</td>
      <td>/24</td>
    </tr>
    <tr>
      <td>DatabaseSubnet</td>
      <td>/24</td>
    </tr>
  </tbody>
</table>

<h4 id="tool-for-to-help-design-your-subnets">Tool for to help design your subnets</h4>

<p>Visual Subnet Calculator</p>

<p>The Visual Subnet Calculator is a free, browser-based tool designed to assist network administrators and IT professionals in subnetting IPv4 networks.<br />
It offers a straightforward interface to quickly visualize and manage subnets without requiring advanced mathematical calculations.</p>

<blockquote>
  <p><a href="https://www.davidc.net/sites/default/subnets/subnets.html">Visual Subnet Calculator</a></p>
</blockquote>

<h2><img src="/assets/images/Azure/network/PL-Azure%20Networking-planner.png" alt="" /></h2>

<h2 id="-security-and-access">🔐 Security and Access</h2>

<p>Security starts with <strong>Network Security Groups (NSGs)</strong> is easier to unblock traffic, then setup block rules on production environment.<br />
It’s a best practice to:</p>
<ul>
  <li>Create <strong>custom rules</strong>.</li>
  <li>Block all inbound and outbound traffic by default.</li>
  <li>Explicitly allow only what’s required.</li>
</ul>

<p><img src="/assets/images/Azure/network/PL-Azure%20Networking-NSG.png" alt="" /></p>

<p><img src="/assets/images/Azure/network/PL-Azure%20Networking-Private-Subnet.png" alt="" /></p>

<hr />

<h2 id="-private-connectivity">🔗 Private Connectivity</h2>

<p>When connecting Azure services privately:</p>
<ul>
  <li>Use <strong>Private Link</strong> for secure, private access to Azure services like Storage, SQL, and Key Vault.</li>
  <li>Use <strong>Private DNS Zones</strong> to manage DNS resolution for private endpoints.
    <ul>
      <li>A single Private DNS Zone can be linked to multiple VNets.</li>
      <li>No need to deploy a separate DNS resolver for every network.</li>
    </ul>
  </li>
</ul>

<p><strong>Microsoft DNS</strong>: Azure IP address <strong>168.63.129.16</strong> is a virtual public IP address that facilitates communication channels to Azure platform resources. Customers can define any address space for their private virtual network in Azure. Therefore, the Azure platform resources must be presented as a unique public IP address</p>

<hr />

<h2 id="-monitoring-your-network">🔍 Monitoring Your Network</h2>

<p>Don’t forget about visibility and diagnostics.<br />
Use <strong>Network Watcher</strong> to:</p>
<ul>
  <li>Monitor traffic flows.</li>
  <li>Diagnose connectivity issues.</li>
  <li>Capture packets and analyze performance.</li>
</ul>

<hr />

<h2 id="-is-one-subscription-enough">🧰 Is One Subscription Enough?</h2>

<p>For small environments, a single Azure subscription can be sufficient.<br />
However, for larger organizations, <strong>multiple subscriptions</strong> provide better scalability, governance, and isolation between workloads or teams.</p>

<p>Example of building and envoling network in azure for organization</p>

<h3 id="example-for-organization-with-one-azure-subscription">Example for organization with one Azure subscription</h3>

<h4 id="example-1">Example 1</h4>
<ul>
  <li>One Azure subscription</li>
  <li>One Azure Virtual Network</li>
  <li>Azure Virtual Network with subntes (segmentation)</li>
  <li>Network Secuirty groups for all subnetes with DenyRules for Inbound / Outbound</li>
  <li>Azure Virtual Gateway for Site to Site VPN connection</li>
  <li>Azure Nat Gateway for NAT outbound traffic</li>
</ul>

<p><img src="/assets/images/Azure/network//PL-Azure%20Networking%20from%20Zero%20to%20Hero-V1.drawio.png" alt="" /></p>

<h4 id="example-2">Example 2</h4>
<ul>
  <li>One Azure subscription</li>
  <li>One Azure Virtual Network</li>
  <li>Azure Virtual Network with subntes (segmentation)</li>
  <li>Network Secuirty groups for all subnetes with DenyRules for Inbound / Outbound</li>
  <li>Azure Virtual Gateway for Site to Site VPN connection</li>
  <li>Azure Nat Gateway for NAT outbound traffic</li>
  <li>Azure Private DNS zones dedicated for Azure SQL services</li>
  <li>Azure privet link for Azure SQL server</li>
  <li>Azure SQL server without any Internet access</li>
</ul>

<p><img src="/assets/images/Azure/network//PL-Azure%20Networking%20from%20Zero%20to%20Hero-V2.drawio.png" alt="" /></p>

<h4 id="example-2-1">Example 2</h4>
<ul>
  <li>One Azure subscription</li>
  <li>One Azure Virtual Network</li>
  <li>Azure Virtual Network with subntes (segmentation)</li>
  <li>Network Secuirty groups for all subnetes with DenyRules for Inbound / Outbound</li>
  <li>Azure Virtual Gateway for Site to Site VPN connection</li>
  <li>Azure Nat Gateway for NAT outbound traffic</li>
  <li>Azure Private DNS zones dedicated for Azure SQL services</li>
  <li>Azure privet link for Azure SQL server</li>
  <li>Azure SQL server without any Internet access</li>
  <li>Azure Firewall that inspect all traffic between subnets</li>
  <li>Azure user definie route to pass traffic between subnets over Azure firewall</li>
</ul>

<p><img src="/assets/images/Azure/network//PL-Azure%20Networking%20from%20Zero%20to%20Hero-V3.drawio.png" alt="" /></p>

<h3 id="example-for-organization-with-multiple-azure-subscription-hub">Example for organization with multiple Azure subscription (Hub)</h3>

<h4 id="example-1-1">Example 1</h4>
<ul>
  <li>3 Azure subscription</li>
  <li>3 Azure Virtual Network</li>
  <li>Azure Virtual Network with subntes (segmentation)</li>
  <li>Network Secuirty groups for all subnetes with DenyRules for Inbound / Outbound</li>
  <li>Azure Virtual Gateway for Site to Site VPN connection</li>
  <li>Azure Nat Gateway for NAT outbound traffic</li>
  <li>Azure Private DNS zones dedicated for Azure SQL services</li>
  <li>Azure privet link for Azure SQL server</li>
</ul>

<p><img src="/assets/images/Azure/network//PL-Azure%20Networking%20from%20Zero%20to%20Hero-V4.drawio.png" alt="" /></p>

<ul>
  <li>3 Azure subscription</li>
  <li>3 Azure Virtual Network</li>
  <li>Azure Virtual Network with subntes (segmentation)</li>
  <li>Network Secuirty groups for all subnetes with DenyRules for Inbound / Outbound</li>
  <li>Azure Virtual Gateway for Site to Site VPN connection</li>
  <li>Azure Nat Gateway for NAT outbound traffic</li>
  <li>Azure Private DNS zones dedicated for Azure SQL services</li>
  <li>Azure privet link for Azure SQL server</li>
  <li>Azure Network Manager</li>
</ul>

<p><img src="/assets/images/Azure/network//PL-Azure%20Networking%20from%20Zero%20to%20Hero-V5.drawio.png" alt="" /></p>

<hr />

<h2 id="️-tips-and-best-practices">⚙️ Tips and Best Practices</h2>

<ul>
  <li>Create good naming convention</li>
  <li>Keep it simple</li>
  <li>Subnet with small mask like 28 / 26</li>
  <li>One Virtual Network per Azure Subscription per region</li>
  <li>Use NAT gateway</li>
  <li>Avoid overlap network spaces with you on-premises environment</li>
  <li>Use Hub spoken network if it’s needed</li>
  <li>Not all resources need to have privet access</li>
  <li>DNS resolving is important – all azure resources work over HTTPS</li>
  <li>Good description of routing tables</li>
  <li>Private DNS Zones can be linked to multiple Vnet don’t need to user DNS resolver</li>
  <li>NSG create custom rule block all Inboud / Outboud traffic</li>
  <li>For internet access, use <strong>NAT Gateways</strong> instead of assigning public IPs directly to VMs.</li>
</ul>

<hr />

<h2 id="-summary">🧩 Summary</h2>

<p>Building a robust Azure network requires thoughtful design and attention to detail.<br />
Start with a solid landing zone, plan your IPs and subnets carefully, use secure connectivity methods, and monitor everything.</p>

<p>When done right, your Azure network becomes the backbone for all your cloud workloads — secure, scalable, and future-proof.</p>

<hr />

<p><em>Author: Michał Machniak</em><br />
<em>System Administrator / Cloud Architect / DevOps</em><br />
<a href="https://mmachniak.net">mmachniak.net</a></p>

<blockquote>
  <p>“Automate everything you can — but plan your network manually first.”</p>
</blockquote>]]></content><author><name>Michal Machniak</name><email>mmachniak@outlook.com</email></author><category term="Azure" /><category term="Network" /><category term="Security" /><summary type="html"><![CDATA[Discover how to build a secure and scalable Azure network from the ground up. Learn how to start with a simple network for a small company and seamlessly extend it into an enterprise-grade architecture. Explore best practices for Azure Landing Zones, hub-and-spoke topology, private connectivity, DNS configuration, and network security to design flexible, future-ready cloud infrastructure in Microsoft Azure.]]></summary></entry><entry><title type="html">Delegate for Azure ARC least-privilege access for hybrid servers - Custom role</title><link href="https://mimachniak.github.io/sysopslife/2025/09/22/Azure-Arc-Custom-Role-Virtual-Machine-Operator/" rel="alternate" type="text/html" title="Delegate for Azure ARC least-privilege access for hybrid servers - Custom role" /><published>2025-09-22T00:00:00+02:00</published><updated>2025-09-22T00:00:00+02:00</updated><id>https://mimachniak.github.io/sysopslife/2025/09/22/Azure-Arc-Custom-Role-Virtual-Machine-Operator</id><content type="html" xml:base="https://mimachniak.github.io/sysopslife/2025/09/22/Azure-Arc-Custom-Role-Virtual-Machine-Operator/"><![CDATA[<h2 id="creating-a-custom-azure-role-for-azure-arc-hybrid-compute">Creating a Custom Azure Role for Azure Arc Hybrid Compute</h2>

<p>When working with Azure Arc–enabled servers, you often need to control access in a way that doesn’t fit the built-in roles provided by Azure. While roles like Reader, Contributor, or Azure Connected Machine Onboarding cover many scenarios, they might either grant too many permissions or not enough.</p>

<p>That’s where custom roles come in. By defining a custom role, you can tailor access so that users, groups, or service principals only get the permissions they actually need — nothing more, nothing less.</p>

<h2 id="why-create-a-custom-role-for-azure-arc">Why Create a Custom Role for Azure Arc?</h2>

<p>Some common scenarios include:</p>

<ul>
  <li>
    <p>Delegated administration: Allowing specific teams to manage extensions or policies on Arc-enabled machines, without giving them full subscription-wide rights.</p>
  </li>
  <li>
    <p>Security hardening: Enforcing least-privilege access so that hybrid server operators can perform their job without unnecessary permissions.</p>
  </li>
  <li>
    <p>Operational separation: Letting one team onboard servers, while another team manages monitoring or updates.</p>
  </li>
</ul>

<p>For example, you may want a role that only allows installation and management of VM extensions (like Azure Monitor Agent or Defender for Cloud extensions) on Arc servers — but nothing else.</p>

<h2 id="step-1-identify-the-required-permissions">Step 1: Identify the Required Permissions</h2>

<p>Azure roles are defined as sets of Actions and NotActions. For Azure Arc hybrid compute, common permissions include:</p>

<ul>
  <li>
    <p>Microsoft.HybridCompute/machines/read – View Arc machines</p>
  </li>
  <li>
    <p>Microsoft.HybridCompute/machines/extensions/* – Manage extensions</p>
  </li>
  <li>
    <p>Microsoft.HybridCompute/machines/write – Update properties (if needed)</p>
  </li>
</ul>

<p>You can explore available permissions with:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
az provider operation show <span class="nt">--namespace</span> Microsoft.HybridCompute

</code></pre></div></div>

<h2 id="step-2-define-the-custom-role-json">Step 2: Define the Custom Role JSON</h2>

<p>Save the following as ArcOperator.json:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"Name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"[Custom] Azure Arc Operator"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"IsCustom"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
  </span><span class="nl">"Description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"View, update patch managment for hybride VM."</span><span class="p">,</span><span class="w">
  </span><span class="nl">"Actions"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
                    </span><span class="s2">"*/read"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/operations/read"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/osType/agentVersions/read"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/osType/agentVersions/latest/read"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/read"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/installPatches/action"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/listAccessDetails/action"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/UpgradeExtensions/action"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/assessPatches/action"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/addExtensions/action"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/patchInstallationResults/read"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/patchInstallationResults/softwarePatches/read"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/extensions/read"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/extensions/write"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/extensions/delete"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/patchAssessmentResults/read"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/patchAssessmentResults/softwarePatches/read"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/runcommands/read"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/runcommands/write"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/runcommands/delete"</span><span class="p">,</span><span class="w">
                    </span><span class="s2">"Microsoft.HybridCompute/machines/write"</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"NotActions"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
  </span><span class="nl">"AssignableScopes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"/subscriptions/&lt;SUBSCRIPTION_ID&gt;"</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">Can</span><span class="w"> </span><span class="err">be</span><span class="w"> </span><span class="err">Managment</span><span class="w"> </span><span class="err">group</span><span class="w"> </span><span class="err">/</span><span class="w"> </span><span class="err">Resource</span><span class="w"> </span><span class="err">group</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h2 id="step-3-create-the-role-in-azure">Step 3: Create the Role in Azure</h2>

<p>Use the Azure CLI to create the role:</p>

<p>az role definition create –role-definition ./ArcOperator.json</p>

<p>To verify:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
az role definition list <span class="nt">--name</span> <span class="s2">"[Custom] Azure Arc Operator"</span>

</code></pre></div></div>

<h2 id="step-4-assign-the-role">Step 4: Assign the Role</h2>

<p>Assign the role to a user, group, or managed identity:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>az role assignment create <span class="se">\</span>
  <span class="nt">--assignee</span> &lt;USER_OR_SP_OBJECT_ID&gt; <span class="se">\</span>
  <span class="nt">--role</span> <span class="s2">"Arc Extension Operator"</span> <span class="se">\</span>
  <span class="nt">--scope</span> /subscriptions/&lt;SUBSCRIPTION_ID&gt;/resourceGroups/&lt;RESOURCE_GROUP&gt;
</code></pre></div></div>

<p>This ensures that the principal can only manage Arc extensions within the defined scope.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Azure Arc custom roles let you implement least-privilege access for hybrid servers. By carefully crafting role definitions, you can give teams exactly the permissions they need — no more, no less.</p>

<p>This approach improves security, simplifies operations, and ensures compliance with enterprise governance requirements.</p>]]></content><author><name>Michal Machniak</name><email>mmachniak@outlook.com</email></author><category term="Azure" /><category term="Shell" /><category term="VirtualMachine" /><summary type="html"><![CDATA[This blog post explains why organizations using Azure Arc–enabled servers should implement least-privilege access through custom roles. It walks through real-world scenarios where built-in Azure roles are either too broad or too restrictive and shows how to define a custom Azure role tailored for Arc hybrid compute,]]></summary></entry><entry><title type="html">Using the Microsoft Azure Bicep Graph Extension to Create Security Groups and Assign Roles in an Azure Landing Zone</title><link href="https://mimachniak.github.io/sysopslife/2025/08/11/Bicep-Graph-Extension-ALZ-RBAC/" rel="alternate" type="text/html" title="Using the Microsoft Azure Bicep Graph Extension to Create Security Groups and Assign Roles in an Azure Landing Zone" /><published>2025-08-11T00:00:00+02:00</published><updated>2025-08-11T00:00:00+02:00</updated><id>https://mimachniak.github.io/sysopslife/2025/08/11/Bicep-Graph-Extension-ALZ-RBAC</id><content type="html" xml:base="https://mimachniak.github.io/sysopslife/2025/08/11/Bicep-Graph-Extension-ALZ-RBAC/"><![CDATA[<h2 id="description">Description</h2>

<p>In large, multi-subscription environments, such as Azure Landing Zones, managing identity and access at scale is a constant challenge. Security groups are a key component of Azure RBAC (Role-Based Access Control), enabling centralized control of permissions for teams and workloads.
While you can configure them manually in the Azure Portal, Infrastructure as Code (IaC) ensures consistency, repeatability, and compliance.</p>

<p>With Bicep and the Microsoft Graph extension, we can define security groups and assign Azure roles programmatically—integrating identity management directly into our landing zone deployment workflows</p>

<h2 id="why-use-the-microsoft-graph-extension-in-bicep">Why Use the Microsoft Graph Extension in Bicep?</h2>
<p>By default, Bicep focuses on Azure Resource Manager (ARM) resources. However, security groups in Entra ID (formerly Azure AD) are not ARM resources—they are managed through Microsoft Graph. The Bicep Graph extension bridges this gap, enabling you to:</p>

<ul>
  <li>
    <p>Create Azure AD / Entra ID security groups.</p>
  </li>
  <li>
    <p>Assign users or service principals to those groups.</p>
  </li>
  <li>
    <p>Bind those groups to Azure roles within subscriptions or resource groups.</p>
  </li>
  <li>
    <p>This allows a single Bicep deployment to set up both your Azure resources and the associated identity access controls.</p>
  </li>
</ul>

<h2 id="example-of-code--creating-a-security-group-and-assigning-a-role">Example of code : Creating a Security Group and Assigning a Role</h2>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">
</span><span class="n">/</span><span class="o">*</span><span class="w">
</span><span class="n">SUMMARY:</span><span class="w"> </span><span class="nx">The</span><span class="w"> </span><span class="nx">Management</span><span class="w"> </span><span class="nx">Groups</span><span class="w"> </span><span class="nx">module</span><span class="w"> </span><span class="nx">deploys</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="nx">management</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">hierarchy</span><span class="w"> </span><span class="nx">in</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="nx">customer</span><span class="s1">'s tenant under the '</span><span class="nx">Tenant</span><span class="w"> </span><span class="nx">Root</span><span class="w"> </span><span class="nx">Group</span><span class="s1">'.
DESCRIPTION: Management Group hierarchy is created through a tenant-scoped Azure Resource Manager (ARM) deployment.  The hierarchy is:
  * Tenant Root Group
      * Organization
        ** Platform
        ** DEV
        ** STAGE
        ** PROD
      * Default - new onborded subscriptions
AUTHOR/S: Michał Machniak
VERSION: 1.0
Logs: Get-AzTenantDeployment -id /providers/Microsoft.Resources/deployments/azl | Get-AzTenantDeploymentOperation
Logs: Get-AzTenantDeployment -id /providers/Microsoft.Resources/deployments/azl-rbac | Get-AzTenantDeploymentOperation
*/

targetScope = '</span><span class="nx">tenant</span><span class="s1">'

extension '</span><span class="nx">br:mcr.microsoft.com/bicep/extensions/microsoftgraph/beta:1.0.0</span><span class="s1">' // Load beta extension
extension '</span><span class="nx">br:mcr.microsoft.com/bicep/extensions/microsoftgraph/v1.0:1.0.0</span><span class="s1">' // Load v1.0 extension

///////////////////////// PARAMETERS /////////////////////////

param par_list_secuirty_groups_name array = [
  {
    name: '</span><span class="nx">GAL-AAD-MG-Organization-Org-Reader</span><span class="s1">'
    description: '</span><span class="nx">Security</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="nx">permissions</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">read</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">management</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">hierarchy</span><span class="s1">'
    role: '</span><span class="nx">Reader</span><span class="s1">'
    securityEnabled: true
    mailEnabled: false // Need to be false is not supported by v1.0 and beta
    uniqueName: '</span><span class="nx">GALAADMGXYZOrgOrgReader</span><span class="s1">' // No whitspaces allowed
    MangmentGroupId: '</span><span class="nx">Organization</span><span class="s1">' // Management Group ID to assign the group to
  }
  {
    name: '</span><span class="nx">GAL-AAD-MG-Organization-Contributor</span><span class="s1">'
    description: '</span><span class="nx">Security</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="nx">permissions</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">contribute</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">management</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">hierarchy</span><span class="s1">'
    role: '</span><span class="nx">Contributor</span><span class="s1">'
    securityEnabled: true
    mailEnabled: false // Need to be false is not supported by v1.0 and beta
    uniqueName: '</span><span class="nx">GALAADMGXYZOrgOrgContributor</span><span class="s1">' // No whitspaces allowed
    MangmentGroupId: '</span><span class="nx">Organization</span><span class="s1">' // Management Group ID to assign the group to
  }
  {
    name:'</span><span class="nx">GAL-AAD-MG-Organization-Owner</span><span class="s1">'
    description: '</span><span class="nx">Security</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="nx">permissions</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">manage</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">management</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">hierarchy</span><span class="s1">'
    role: '</span><span class="nx">Owner</span><span class="s1">'
    securityEnabled: true
    mailEnabled: false // Need to be false is not supported by v1.0 and beta
    uniqueName: '</span><span class="nx">GALAADMGXYZOrgOrgOwner</span><span class="s1">' // No whitspaces allowed
    MangmentGroupId: '</span><span class="nx">Organization</span><span class="s1">' // Management Group ID to assign the group to
  }
  // Dev Groups
  {
    name: '</span><span class="nx">GAL-AAD-MG-DEV-Reader</span><span class="s1">'
    description: '</span><span class="nx">Security</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="nx">permissions</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">read</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">management</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">hierarchy</span><span class="s1">'
    role: '</span><span class="nx">Reader</span><span class="s1">'
    securityEnabled: true
    mailEnabled: false // Need to be false is not supported by v1.0 and beta
    uniqueName: '</span><span class="nx">GALAADMGDEVReader</span><span class="s1">' // No whitspaces allowed
    MangmentGroupId: '</span><span class="nx">DEV</span><span class="s1">' // Management Group ID to assign the group to
  }
  {
    name: '</span><span class="nx">GAL-AAD-MG-DEV-Contributor</span><span class="s1">'
    description: '</span><span class="nx">Security</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="nx">permissions</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">contribute</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">management</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">hierarchy</span><span class="s1">'
    role: '</span><span class="nx">Contributor</span><span class="s1">'
    securityEnabled: true
    mailEnabled: false // Need to be false is not supported by v1.0 and beta
    uniqueName: '</span><span class="nx">GALAADMGDEVContributor</span><span class="s1">' // No whitspaces allowed
    MangmentGroupId: '</span><span class="nx">DEV</span><span class="s1">' // Management Group ID to assign the group to

  }
  {
    name:'</span><span class="nx">GAL-AAD-MG-DEV-Owner</span><span class="s1">'
    description: '</span><span class="nx">Security</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="nx">permissions</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">manage</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">management</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">hierarchy</span><span class="s1">'
    role: '</span><span class="nx">Owner</span><span class="s1">'
    securityEnabled: true
    mailEnabled: false // Need to be false is not supported by v1.0 and beta
    uniqueName: '</span><span class="nx">GALAADMGDEVOwner</span><span class="s1">' // No whitspaces allowed
    MangmentGroupId: '</span><span class="nx">DEV</span><span class="s1">' // Management Group ID to assign the group to
  }
  // Prod groups
  {
    name: '</span><span class="nx">GAL-AAD-MG-PROD-Reader</span><span class="s1">'
    description: '</span><span class="nx">Security</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="nx">permissions</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">read</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">management</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">hierarchy</span><span class="s1">'
    role: '</span><span class="nx">Reader</span><span class="s1">'
    securityEnabled: true
    mailEnabled: false // Need to be false is not supported by v1.0 and beta
    uniqueName: '</span><span class="nx">GALAADMGPRODReader</span><span class="s1">' // No whitspaces allowed
    MangmentGroupId: '</span><span class="nx">PROD</span><span class="s1">' // Management Group ID to assign the group to
  }
  {
    name: '</span><span class="nx">GAL-AAD-MG-PROD-Contributor</span><span class="s1">'
    description: '</span><span class="nx">Security</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="nx">permissions</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">contribute</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">management</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">hierarchy</span><span class="s1">'
    role: '</span><span class="nx">Contributor</span><span class="s1">'
    securityEnabled: true
    mailEnabled: false // Need to be false is not supported by v1.0 and beta
    uniqueName: '</span><span class="nx">GALAADMGPRODContributor</span><span class="s1">' // No whitspaces allowed
    MangmentGroupId: '</span><span class="nx">PROD</span><span class="s1">' // Management Group ID to assign the group to

  }
  {
    name:'</span><span class="nx">GAL-AAD-MG-PROD-Owner</span><span class="s1">'
    description: '</span><span class="nx">Security</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="nx">permissions</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">manage</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">management</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">hierarchy</span><span class="s1">'
    role: '</span><span class="nx">Owner</span><span class="s1">'
    securityEnabled: true
    mailEnabled: false // Need to be false is not supported by v1.0 and beta
    uniqueName: '</span><span class="nx">GALAADMGPRODOwner</span><span class="s1">' // No whitspaces allowed
    MangmentGroupId: '</span><span class="nx">PROD</span><span class="s1">' // Management Group ID to assign the group to
  }
    // Prod Default
  {
    name: '</span><span class="nx">GAL-AAD-MG-Default-Reader</span><span class="s1">'
    description: '</span><span class="nx">Security</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="nx">permissions</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">read</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">management</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">hierarchy</span><span class="s1">'
    role: '</span><span class="nx">Reader</span><span class="s1">'
    securityEnabled: true
    mailEnabled: false // Need to be false is not supported by v1.0 and beta
    uniqueName: '</span><span class="nx">GALAADMGDefualtReader</span><span class="s1">' // No whitspaces allowed
    MangmentGroupId: '</span><span class="nx">Default</span><span class="s1">' // Management Group ID to assign the group to
  }
  {
    name: '</span><span class="nx">GAL-AAD-MG-Default-Contributor</span><span class="s1">'
    description: '</span><span class="nx">Security</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="nx">permissions</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">contribute</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">management</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">hierarchy</span><span class="s1">'
    role: '</span><span class="nx">Contributor</span><span class="s1">'
    securityEnabled: true
    mailEnabled: false // Need to be false is not supported by v1.0 and beta
    uniqueName: '</span><span class="nx">GALAADMDefualtContributor</span><span class="s1">' // No whitspaces allowed
    MangmentGroupId: '</span><span class="nx">Default</span><span class="s1">' // Management Group ID to assign the group to
  }
  {
    name:'</span><span class="nx">GAL-AAD-MG-Default-Owner</span><span class="s1">'
    description: '</span><span class="nx">Security</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="nx">permissions</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">manage</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">management</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">hierarchy</span><span class="s1">'
    role: '</span><span class="nx">Owner</span><span class="s1">'
    securityEnabled: true
    mailEnabled: false // Need to be false is not supported by v1.0 and beta
    uniqueName: '</span><span class="nx">GALAADMGDefualtOwner</span><span class="s1">' // No whitspaces allowed
    MangmentGroupId: '</span><span class="nx">Default</span><span class="s1">' // Management Group ID to assign the group to
  }

]


resource res_secuirty_group_alz '</span><span class="nx">Microsoft.Graph/groups</span><span class="err">@</span><span class="nx">beta</span><span class="s1">' = [for sec_group in par_list_secuirty_groups_name: {
  displayName: sec_group.name
  description: sec_group.description
  securityEnabled: true
  mailEnabled: false // Need to be false is not supported by v1.0 and beta
  mailNickname: toLower('</span><span class="nv">${sec_group.name}</span><span class="s1">')
  owners: {
    relationships: []
  }
  members: {
    relationships: []
  }
  uniqueName: sec_group.uniqueName  // No whitspaces allowed

}]



// Assign security groups to management groups - need delay against the creation of the security groups

module mod_mg_role_permissions '</span><span class="o">.</span><span class="nf">modules</span><span class="nx">/RoleAssigment/managmentGroups.bicep</span><span class="s1">' = [for i in range(0, length(par_list_secuirty_groups_name)) : {
  name: guid(par_list_secuirty_groups_name[i].name)
  scope: managementGroup('</span><span class="nv">${par_list_secuirty_groups_name[i].MangmentGroupId}</span><span class="s1">')
  params: {
    principalId: res_secuirty_group_alz[i].id
    roleDefinitionIdOrName: par_list_secuirty_groups_name[i].role
    managementGroupId: par_list_secuirty_groups_name[i].MangmentGroupId
  }
  dependsOn: [
    res_secuirty_group_alz
  ]
}]

output out_groups_id array = [for i in range(0, length(par_list_secuirty_groups_name)): res_secuirty_group_alz[i].id]

</span></code></pre></div></div>

<h2 id="example-deployment-command">Example Deployment Command:</h2>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">
</span><span class="n">New-AzTenantDeployment</span><span class="w"> </span><span class="nt">-Location</span><span class="w"> </span><span class="s2">"North Europe"</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="s2">"AAD-v2"</span><span class="w"> </span><span class="nt">-TemplateFile</span><span class="w"> </span><span class="o">.</span><span class="nx">\main-aad-loop.bicep</span><span class="w">

</span><span class="n">az</span><span class="w"> </span><span class="nx">deployment</span><span class="w"> </span><span class="nx">sub</span><span class="w"> </span><span class="nx">tenant</span><span class="w"> </span><span class="nt">--location</span><span class="w"> </span><span class="nx">northeurope</span><span class="w"> </span><span class="nt">--template-file</span><span class="w"> </span><span class="o">.</span><span class="nx">\main-aad-loop.bicep</span><span class="w">

</span></code></pre></div></div>

<h2 id="link-to-repo-with-code">Link to repo with code:</h2>

<p>You can find code example in my GitHub repository: <a href="https://github.com/mimachniak/sysopslife-scripts/tree/master/Azure/bicep/Microsoft-Graph-code">Github Bicep code example</a></p>

<h2 id="know-issue">Know issue</h2>

<p>When you create security groups in EntraID, it will take time for Graph API to procced chnages, so you can see that assigne permissions will faild because object wasn not found in Entra I, so this will required to rerun bicep as EntraID need some time.</p>

<h2 id="conclusion">Conclusion</h2>
<p>By using the Microsoft Azure Bicep Graph extension, you can extend your IaC workflows beyond Azure resources, directly into identity and access management. This approach helps ensure that your Azure Landing Zone deployments are secure, consistent, and repeatable—with all permissions configured as code.</p>]]></content><author><name>Michal Machniak</name><email>mmachniak@outlook.com</email></author><category term="Azure" /><category term="Bicep" /><category term="IaaC" /><category term="EntraID" /><summary type="html"><![CDATA[In large, multi-subscription environments, such as Azure Landing Zones, managing identity and access at scale is a constant challenge. Security groups are a key component of Azure RBAC (Role-Based Access Control), enabling centralized control of permissions for teams and workloads. While you can configure them manually in the Azure Portal, Infrastructure as Code (IaC) ensures consistency, repeatability, and compliance. With Bicep and the Microsoft Graph extension, we can define security groups and assign Azure roles programmatically—integrating identity management directly into our landing zone deployment workflows.]]></summary></entry><entry><title type="html">Bicep Microsoft Entra (Graph) Extension - GA</title><link href="https://mimachniak.github.io/sysopslife/2025/08/01/Bicep-Graph-Extension/" rel="alternate" type="text/html" title="Bicep Microsoft Entra (Graph) Extension - GA" /><published>2025-08-01T00:00:00+02:00</published><updated>2025-08-01T00:00:00+02:00</updated><id>https://mimachniak.github.io/sysopslife/2025/08/01/Bicep-Graph-Extension</id><content type="html" xml:base="https://mimachniak.github.io/sysopslife/2025/08/01/Bicep-Graph-Extension/"><![CDATA[<h2 id="description">Description</h2>

<p>The Microsoft Entra (Graph in v1 and beat) Bicep Extension allows you to provision and manage Microsoft Entra ID (formerly Azure AD) resources using Bicep, extending Infrastructure as Code beyond Azure resources into identity management.</p>

<p>Bicep works only with Azure Resource Manager (ARM) resources. With this extension, you can deploy and configure Entra ID:</p>

<ul>
  <li>Applications: <a href="https://learn.microsoft.com/en-us/graph/templates/bicep/reference/applications?view=graph-bicep-beta">Bicep Applications</a></li>
  <li>Service Principals: <a href="https://learn.microsoft.com/en-us/graph/templates/bicep/reference/serviceprincipals?view=graph-bicep-beta">Bicep Service Principals</a></li>
  <li>App Roles &amp; API Permissions: <a href="https://learn.microsoft.com/azure/azure-resource-manager/bicep/overvie">Bicep App Roles &amp; API Permissions</a></li>
  <li>Federated idenity cedentials: <a href="https://learn.microsoft.com/en-us/graph/templates/bicep/reference/federatedidentitycredentials?view=graph-bicep-beta">Bicep Federated idenity cedentials:</a></li>
  <li>Groups &amp; Group Membership <strong>(no mailenabled / distribution)</strong>: <a href="https://learn.microsoft.com/en-us/graph/templates/bicep/reference/groups?view=graph-bicep-beta">Bicep Groups &amp; Group Membership</a></li>
  <li>Users <strong>(ReadOnly mode)</strong>: <a href="https://learn.microsoft.com/en-us/graph/templates/bicep/reference/users?view=graph-bicep-beta">Bicep Users</a></li>
</ul>

<p>This is powered by Microsoft Graph API under the hood, so changes made through Bicep are reflected directly in Entra ID.</p>

<p>Key Benefits:</p>

<ul>
  <li>Unified IaC – manage Azure and Entra resources in a single Bicep deployment</li>
  <li>Automated Identity Management – create apps, assign roles, and manage groups as code</li>
  <li>CI/CD Ready – integrate identity provisioning into DevOps pipelines</li>
</ul>

<h2 id="microsoft-artifact-registry">Microsoft Artifact Registry</h2>

<p>Extension verions can be found in Microsoft Artifact Registry</p>

<p><strong>Microsoft Graph Bicep Extension (beta):</strong> https://mcr.microsoft.com/artifact/mar/bicep/extensions/microsoftgraph/beta/tags<br />
<strong>Microsoft Graph Bicep Extension:</strong>  https://mcr.microsoft.com/artifact/mar/bicep/extensions/microsoftgraph/v1.0/tags</p>

<h2 id="example-of-code">Example of code</h2>

<p>Example below deploy secuirty group in two verions of API:</p>

<pre><code class="language-bicep">
extension 'br:mcr.microsoft.com/bicep/extensions/microsoftgraph/beta:1.0.0' // Load beta extension
extension 'br:mcr.microsoft.com/bicep/extensions/microsoftgraph/v1.0:1.0.0' // Load v1.0 extension

resource res_account_v1 'Microsoft.Graph/users@v1.0' existing = {
  userPrincipalName: 'Tomasz.Oscypek@sys4ops.pl' // Existing user only supported in this edition
}

output accountId_v1 string = res_account_v1.id


resource res_account_beta 'Microsoft.Graph/users@beta' existing = {
  userPrincipalName: 'Tomasz.Oscypek@sys4ops.pl' // Existing user only supported in this edition
}

output accountId_beta string = res_account_beta.id


resource res_secuirty_group_beta 'Microsoft.Graph/groups@beta' = {
  displayName: 'Bicep Beta Security Group'
  description: 'Security group created by Bicep Beta extension'
  securityEnabled: true
  mailEnabled: false // Need to be false is not supported by v1.0 and beta
  mailNickname: 'bicep-beta-security-group'
  owners: {
    relationships: [res_account_beta.id]
  }
  uniqueName: 'BicepBetaSecurityGroup' // No whitspaces allowed

}

resource res_secuirty_group_v1 'Microsoft.Graph/groups@beta' = {
  displayName: 'Bicep Security Group'
  description: 'Security group created by Bicep extension'
  securityEnabled: true
  mailEnabled: false // Need to be false is not supported by v1.0 and beta
  mailNickname: 'bicep-security-group'
  uniqueName: 'BicepSecurityGroup' // No whitspaces allowed
  members: {
    relationships: [res_account_v1.id]
  }


}
</code></pre>

<h2 id="deploy-options">Deploy options</h2>

<p>We can deploy this code on any target scope as it support, and using <strong>Azure CLI</strong> or <strong>PowerShell</strong>. For troubleshooting is better to deploy on Resource Group or Subscription as it is easy to read deployment logs and check mistakes, example descibe below.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">
</span><span class="n">New-AzDeployment</span><span class="w"> </span><span class="nt">-Location</span><span class="w"> </span><span class="s2">"North Europe"</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="s2">"AAD"</span><span class="w"> </span><span class="nt">-TemplateFile</span><span class="w"> </span><span class="o">.</span><span class="nx">\main-aad.bicep</span><span class="w">

</span><span class="n">New-AzResourceGroupDeployment</span><span class="w"> </span><span class="nt">-ResourceGroupName</span><span class="w"> </span><span class="s2">"D-AUT-ITW"</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="s2">"AAD"</span><span class="w"> </span><span class="nt">-TemplateFile</span><span class="w"> </span><span class="o">.</span><span class="nx">\main-aad.bicep</span><span class="w">

</span></code></pre></div></div>

<h3 id="example-deploy-on-tenat-level-and-logs">Example deploy on tenat level and logs</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">
</span><span class="n">New-AzDeployment</span><span class="w"> </span><span class="nt">-Location</span><span class="w"> </span><span class="s2">"North Europe"</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="s2">"AAD"</span><span class="w"> </span><span class="nt">-TemplateFile</span><span class="w"> </span><span class="o">.</span><span class="nx">\main-aad.bicep</span><span class="w">  

</span></code></pre></div></div>

<p>Error message that was outputed on console:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">
</span><span class="n">New-AzDeployment:</span><span class="w"> </span><span class="nx">17:24:23</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nx">The</span><span class="w"> </span><span class="nx">deployment</span><span class="w"> </span><span class="s1">'AAD'</span><span class="w"> </span><span class="nx">failed</span><span class="w"> </span><span class="nx">with</span><span class="w"> </span><span class="nx">error</span><span class="p">(</span><span class="n">s</span><span class="p">)</span><span class="o">.</span><span class="w"> </span><span class="n">Showing</span><span class="w"> </span><span class="nx">2</span><span class="w"> </span><span class="nx">out</span><span class="w"> </span><span class="nx">of</span><span class="w"> </span><span class="nx">2</span><span class="w"> </span><span class="nx">error</span><span class="p">(</span><span class="n">s</span><span class="p">)</span><span class="o">.</span><span class="w">
</span><span class="n">Status</span><span class="w"> </span><span class="nx">Message:</span><span class="w"> </span><span class="p">{</span><span class="s2">"error"</span><span class="p">:{</span><span class="s2">"code"</span><span class="p">:</span><span class="s2">"Forbidden"</span><span class="p">,</span><span class="s2">"target"</span><span class="p">:</span><span class="s2">"/resources/res_secuirty_group_v1"</span><span class="p">,</span><span class="s2">"message"</span><span class="p">:</span><span class="s2">"Insufficient privileges to complete the operation. Graph client request id: cde03e9e-5a5c-48f1-83cb-3e1d385d3cb5. Graph request timestamp: 2025-07-31T15:24:20Z."</span><span class="p">}}</span><span class="w"> </span><span class="p">(</span><span class="n">Code:DeploymentOperationFailed</span><span class="p">)</span><span class="w">

</span><span class="n">Status</span><span class="w"> </span><span class="nx">Message:</span><span class="w"> </span><span class="p">{</span><span class="s2">"error"</span><span class="p">:{</span><span class="s2">"code"</span><span class="p">:</span><span class="s2">"Forbidden"</span><span class="p">,</span><span class="s2">"target"</span><span class="p">:</span><span class="s2">"/resources/res_secuirty_group_beta"</span><span class="p">,</span><span class="s2">"message"</span><span class="p">:</span><span class="s2">"Insufficient privileges to complete the operation. Graph client request id: 9b988e59-3ad3-4d27-b60c-4560513d2325. Graph request timestamp: 2025-07-31T15:24:20Z."</span><span class="p">}}</span><span class="w"> </span><span class="p">(</span><span class="n">Code:DeploymentOperationFailed</span><span class="p">)</span><span class="w">

</span><span class="n">CorrelationId:</span><span class="w"> </span><span class="nx">d95e67b1-ce6a-40d2-a9f4-31e16566d235</span><span class="w">

</span></code></pre></div></div>

<p>Issue in code was find when deployment was change to <strong>Resource Group</strong></p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">
</span><span class="err">&lt;</span><span class="n">code</span><span class="w"> </span><span class="nx">id</span><span class="o">=</span><span class="s1">''</span><span class="w"> </span><span class="n">style</span><span class="o">=</span><span class="s1">'white-space:pre-wrap'</span><span class="err">&gt;&lt;</span><span class="n">div</span><span class="err">&gt;</span><span class="p">{</span><span class="s2">"error"</span><span class="p">:{</span><span class="s2">"code"</span><span class="p">:</span><span class="s2">"BadRequest"</span><span class="p">,</span><span class="s2">"target"</span><span class="p">:</span><span class="s2">"/resources/res_secuirty_group_v1"</span><span class="p">,</span><span class="s2">"message"</span><span class="p">:</span><span class="s2">"Property 'UniqueName' cannot contain whitespace. Graph client request id: 9296c621-e36a-4c80-b704-a7e9baf37096. Graph request timestamp: 2025-07-31T15:29:23Z."</span><span class="p">}}</span><span class="err">&lt;</span><span class="n">/div</span><span class="err">&gt;&lt;</span><span class="nx">/code</span><span class="err">&gt;&lt;</span><span class="nx">/br</span><span class="err">&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Code:</span><span class="w"> </span><span class="nx">DeploymentOperationFailed</span><span class="p">)</span><span class="w">

</span></code></pre></div></div>
<h2 id="quick-templates-from-microsoft">Quick templates from Microsoft</h2>

<p>https://github.com/microsoftgraph/msgraph-bicep-types/tree/main/quickstart-templates</p>]]></content><author><name>Michal Machniak</name><email>mmachniak@outlook.com</email></author><category term="Azure" /><category term="EntraID" /><category term="Bicep" /><category term="IaaC" /><summary type="html"><![CDATA[The Microsoft Entra (Graph) Bicep Extension allows you to provision and manage Microsoft Entra ID (formerly Azure AD) resources using Bicep, extending Infrastructure as Code beyond Azure resources into identity management.]]></summary></entry><entry><title type="html">Loging into a Linux Server Connected to Azure Arc Using Entra ID from portal and OpenSSH client</title><link href="https://mimachniak.github.io/sysopslife/2025/07/30/Azure-Arc-Linux-Entra-logon/" rel="alternate" type="text/html" title="Loging into a Linux Server Connected to Azure Arc Using Entra ID from portal and OpenSSH client" /><published>2025-07-30T00:00:00+02:00</published><updated>2025-07-30T00:00:00+02:00</updated><id>https://mimachniak.github.io/sysopslife/2025/07/30/Azure-Arc-Linux-Entra-logon</id><content type="html" xml:base="https://mimachniak.github.io/sysopslife/2025/07/30/Azure-Arc-Linux-Entra-logon/"><![CDATA[<h2 id="description">Description</h2>

<p>Azure Arc extends the power of Azure to your on-premises and multi-cloud environments. One great feature it enables is logging into Linux servers using Entra ID (formerly Azure AD). This provides centralized identity management, RBAC, and conditional access for your Linux infrastructure. In this post, I’ll walk you through how to enable and use Entra ID to log in to a Linux server connected to Azure Arc.</p>

<table>
  <thead>
    <tr>
      <th>Distribution</th>
      <th>Version</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>AlmaLinux</td>
      <td>AlmaLinux 8, AlmaLinux 9</td>
    </tr>
    <tr>
      <td>Azure Linux (formerly known as Common Base Linux Mariner)</td>
      <td>CBL-Mariner 2.0, Azure Linux 3.0</td>
    </tr>
    <tr>
      <td>Debian</td>
      <td>Debian 9, Debian 10, Debian 11, Debian 12</td>
    </tr>
    <tr>
      <td>openSUSE</td>
      <td>openSUSE Leap 42.3, openSUSE Leap 15.1 to 15.5, openSUSE Leap 15.6+</td>
    </tr>
    <tr>
      <td>Oracle</td>
      <td>Oracle Linux 8, Oracle Linux 9</td>
    </tr>
    <tr>
      <td>RedHat Enterprise Linux (RHEL)</td>
      <td>RHEL 7.4 to RHEL 7.9, RHEL 8.3+, RHEL 9.0+</td>
    </tr>
    <tr>
      <td>Rocky</td>
      <td>Rocky 8, Rocky 9</td>
    </tr>
    <tr>
      <td>SUSE Linux Enterprise Server (SLES)</td>
      <td>SLES 12, SLES 15.1 to 15.5, SLES 15.6+</td>
    </tr>
    <tr>
      <td>Ubuntu</td>
      <td>Ubuntu 16.04 to Ubuntu 24.04</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/images/Azure/AADSSHLogin-0.png" alt="" /></p>

<h2 id="prerequisites">Prerequisites</h2>

<ul>
  <li>A Linux server connected to Azure Arc.</li>
  <li>Azure CLI installed.</li>
  <li>You are an Entra ID user with at least Virtual Machine User Login role.</li>
  <li>Azure Connected Machine agent is installed and running.</li>
</ul>

<h2 id="step-1-enable-entra-id-login-on-the-arc-enabled-server">Step 1: Enable Entra ID Login on the Arc-Enabled Server</h2>

<pre><code class="language-powwershell">
az connectedmachine extension create \
  --machine-name &lt;server-name&gt; \
  --resource-group &lt;resource-group&gt; \
  --location &lt;location&gt; \
  --name AADSSHLoginForLinux \
  --publisher Microsoft.Azure.ActiveDirectory \
  --type AADSSHLoginForLinux \
  --type-handler-version 1.0

</code></pre>

<blockquote>
  <p>Note: Replace <strong>server-name</strong>, <strong>resource-group</strong>, and <strong>location</strong> with appropriate values.</p>
</blockquote>

<p><img src="/assets/images/Azure/AADSSHLogin-1.png" alt="" /></p>

<h2 id="step-2-assign-entra-id-role">Step 2: Assign Entra ID Role</h2>

<p>Assign the Virtual Machine User Login role (or Virtual Machine Administrator Login if you need sudo access) to a user or group for the connected machine resource:</p>

<pre><code class="language-powwershell">
az role assignment create \
  --assignee &lt;user-object-id or user-UPN&gt; \
  --role "Virtual Machine User Login" \
  --scope "/subscriptions/&lt;sub-id&gt;/resourceGroups/&lt;resource-group&gt;/providers/Microsoft.HybridCompute/machines/&lt;server-name&gt;"

</code></pre>

<p><img src="/assets/images/Azure/AADSSHLogin-3.png" alt="" /></p>

<h2 id="step-3-install-aad-login-extension-if-not-already-installed">Step 3: Install AAD Login Extension (if not already installed)</h2>

<p>Check if the extension is installed:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nb">ls</span> /var/lib/waagent/custom-script/

</code></pre></div></div>

<p>You can also verify via the Azure portal under the “Extensions + applications” blade of the Arc server.</p>

<h2 id="step-4-login-to-the-server-using-az-ssh-or-ssh-with-aad">Step 4: Login to the Server Using az ssh or ssh with AAD</h2>

<p>If using the Azure CLI az ssh command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
az ssh arc <span class="nt">--subscription</span> &lt;subscription-id&gt; <span class="nt">--resource-group</span> &lt;resource-group&gt; <span class="nt">--name</span> &lt;server-name&gt;

</code></pre></div></div>

<p><img src="/assets/images/Azure/AADSSHLogin-4.png" alt="" /></p>

<p>Alternatively, sign in to Azure Linux VMs with Microsoft Entra ID supports exporting the OpenSSH certificate and configuration. That means you can use any SSH clients that support OpenSSH-based certificates to sign in through Microsoft Entra ID</p>

<p>Export SSH configuration for Virtual Machnine</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
az ssh config <span class="nt">--file</span> ~/.ssh/config <span class="nt">--ip</span> &lt;server-fqdn or IP&gt;

</code></pre></div></div>

<p>Import configuration on local SSh client (Windows example)</p>

<p><img src="/assets/images/Azure/AADSSHLogin-5.png" alt="" /></p>

<p>Create config files for host with <server-fqdn or="" IP=""> on local SSH client in .ssh folder</server-fqdn></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
Host 172.19.0.20
	User michal.machniak@xxxxx.pl
	CertificateFile <span class="s2">"C:/Users/Administrator.AD/.ssh/az_ssh_config/172.19.0.20/id_rsa.pub-aadcert.pub"</span>
	IdentityFile <span class="s2">"C:/Users/Administrator.AD/.ssh/az_ssh_config/172.19.0.20/id_rsa"</span>

</code></pre></div></div>

<p>Connect from local to server using Entra ID credentianls</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
ssh &lt;entra-username&gt;@&lt;server-fqdn or IP&gt;

</code></pre></div></div>

<p><img src="/assets/images/Azure/AADSSHLogin-6.png" alt="" /></p>

<p>Your SSH client must support OpenSSH and be configured to request tokens. If needed, install Azure CLI login extension:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
az extension add <span class="nt">--name</span> ssh

</code></pre></div></div>

<h2 id="step-5-troubleshooting-tips">Step 5: Troubleshooting Tips</h2>

<p>Ensure your user is assigned the proper Entra ID role.</p>

<p>Make sure the AADSSHLoginForLinux extension is in “Provisioning succeeded” state.</p>

<p>Check /var/log/auth.log on the server for login errors.</p>

<p>Use az ssh arc -d for debugging SSH connection.</p>

<h2 id="summary">Summary</h2>

<p>Logging in to Linux servers with Entra ID via Azure Arc improves your security posture and simplifies identity management. By following these steps, you can ensure secure, role-based access to your Arc-connected servers with the power of Entra ID.</p>]]></content><author><name>Michal Machniak</name><email>mmachniak@outlook.com</email></author><category term="Azure" /><category term="Shell" /><category term="VirtualMachine" /><summary type="html"><![CDATA[Azure Arc extends the power of Azure to your on-premises and multi-cloud environments. One great feature it enables is logging into Linux servers using Entra ID (formerly Azure AD). This provides centralized identity management, RBAC, and conditional access for your Linux infrastructure. In this post, I'll walk you through how to enable and use Entra ID to log in to a Linux server connected to Azure Arc.]]></summary></entry><entry><title type="html">A Guide to Revoking and Reconfiguring SSH Keys on Azure VMs</title><link href="https://mimachniak.github.io/sysopslife/2024/12/02/Azure-SSH-admin-access-revoke/" rel="alternate" type="text/html" title="A Guide to Revoking and Reconfiguring SSH Keys on Azure VMs" /><published>2024-12-02T00:00:00+01:00</published><updated>2024-12-02T00:00:00+01:00</updated><id>https://mimachniak.github.io/sysopslife/2024/12/02/Azure-SSH-admin-access-revoke</id><content type="html" xml:base="https://mimachniak.github.io/sysopslife/2024/12/02/Azure-SSH-admin-access-revoke/"><![CDATA[<h2 id="prerequisites">Prerequisites</h2>

<ul>
  <li>Azure CLI installed</li>
</ul>

<h2 id="generate-public-and-private-key-for-new-administrator">Generate public and private key for new administrator</h2>

<p><img src="/assets/images/Azure/SSH/1-SSH.png" alt="" /> 
  <img src="/assets/images/Azure/SSH/2-SSH.png" alt="" /></p>

<h2 id="create-an-administrativesudo-user-or-revoke-exsiting-one">Create an administrative/sudo user or revoke exsiting one</h2>

<p>In Azure CLI, login to your account then setup subscription that is hosting VM, and run Azure CLI command with those options:</p>

<ul>
  <li><strong>–resource-group</strong> - Name of resource group that is hosting Virtula Machine.</li>
  <li><strong>–name</strong> - name of Virtula Machnine</li>
  <li><strong>–username</strong> - new administrator account name or existing one.</li>
  <li><strong>–ssh-key-value</strong> - path to your saved public SSH key.
```
az login
az account set –subscription “xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”
az vm user update –resource-group rg-admin-001 –name vm-linuxadmin –username revokeadmin –ssh-key-value “C:\Temp\SSH\azure-admin-vm-public-key.pub”</li>
</ul>

<p>```</p>

<h2 id="connect-to-virtula-machine-using-new-account-and-ssh-key-putty-example">Connect to Virtula Machine using new account and SSH key (PuttY example)</h2>

<ol>
  <li>Start PuTTY</li>
  <li>Enter IP address of Virtual Machnine</li>
</ol>

<p><img src="/assets/images/Azure/SSH/3-SSH.png" alt="" /></p>

<ol>
  <li>
    <p>On menu naviagate to <strong>Connection</strong> –&gt; <strong>Data</strong> and enter admini login</p>

    <p><img src="/assets/images/Azure/SSH/5-SSH.png" alt="" /></p>
  </li>
  <li>
    <p>On menu naviagate to <strong>Connection</strong> –&gt; <strong>SSH</strong> –&gt; <strong>Auth</strong> –&gt; <strong>Credentials</strong> and load Private key located on the local storage.</p>

    <p><img src="/assets/images/Azure/SSH/4-SSH.png" alt="" /></p>
  </li>
  <li>
    <p>Connect to VM with new sudo administrator account.</p>
  </li>
</ol>]]></content><author><name>Michal Machniak</name><email>mmachniak@outlook.com</email></author><category term="Azure" /><category term="Shell" /><category term="VirtualMachine" /><summary type="html"><![CDATA[In this post, you’ll learn how to securely revoke access to your Azure Virtual Machines by replacing compromised or outdated SSH keys. Whether you’re responding to a potential security breach or performing routine key rotation, this step-by-step guide will walk you through identifying the affected keys, removing them from your VM, and safely adding new ones.]]></summary></entry><entry><title type="html">Azure Storage Account how to setup policy for object replication blobs between multi tenant for more then 10 blobs.</title><link href="https://mimachniak.github.io/sysopslife/2024/10/28/Azure-Storage-Account-object-replication-policy-multi-tenant/" rel="alternate" type="text/html" title="Azure Storage Account how to setup policy for object replication blobs between multi tenant for more then 10 blobs." /><published>2024-10-28T00:00:00+01:00</published><updated>2024-10-28T00:00:00+01:00</updated><id>https://mimachniak.github.io/sysopslife/2024/10/28/Azure-Storage-Account-object-replication-policy-multi-tenant</id><content type="html" xml:base="https://mimachniak.github.io/sysopslife/2024/10/28/Azure-Storage-Account-object-replication-policy-multi-tenant/"><![CDATA[<h2 id="prerequisites">Prerequisites</h2>

<ul>
  <li>Storage Account in different tenants.</li>
  <li>Az PowerShell module.</li>
</ul>

<h2 id="configuration">Configuration</h2>

<p>Configuration to setup in each storage account, we need to allow object replication between diffrent tenants.</p>

<ol>
  <li>Navigate to <strong>Storage Account</strong></li>
  <li>In navigation go to <strong>Object replication</strong></li>
  <li>In context menu go to <strong>Advanced Settings</strong></li>
  <li>
    <p>Allow cross tenant object access</p>

    <p><img src="/assets/images/Azure/ST-Sync-Multiple-Object/st-sync-00.png" alt="" /></p>

    <p><img src="/assets/images/Azure/ST-Sync-Multiple-Object/st-sync-0.png" alt="" /></p>
  </li>
</ol>

<h3 id="confiuration-destination-storage-account">Confiuration destination storage account</h3>

<ol>
  <li>Prepare policy file in destination storage account
    <ul>
      <li><strong>ruleId</strong> - for each blob need to unique guid</li>
      <li><strong>minCreationTime</strong> - For sync all content we need to setup date / time before day of creation</li>
      <li><strong>sourceAccount</strong> - object ID of source storage account</li>
      <li><strong>destinationAccount</strong>  - object ID of destinationAccount storage account</li>
    </ul>
  </li>
</ol>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"sourceAccount"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/subscriptions/xxxxxxxxxxxxxxxxxxxxxx/resourceGroups/RG_name_source/providers/Microsoft.Storage/storageAccounts/storageaccount_name_source"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"destinationAccount"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/subscriptions/xxxxxxxxxxxxxxxxxxxxxx/resourceGroups/RG_name_destination/providers/Microsoft.Storage/storageAccounts/storageaccount_name_destination"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"rules"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1f45eace-d5fb-4e8e-9e1c-cf8219615644"</span><span class="p">,</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">Rule</span><span class="w"> </span><span class="err">ID</span><span class="w"> </span><span class="err">need</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">be</span><span class="w"> </span><span class="err">guid</span><span class="w"> </span><span class="err">random</span><span class="w"> </span><span class="err">generated</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"dms5"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"dms5"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">For</span><span class="w"> </span><span class="err">sync</span><span class="w"> </span><span class="err">all</span><span class="w"> </span><span class="err">content</span><span class="w"> </span><span class="err">we</span><span class="w"> </span><span class="err">need</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">setup</span><span class="w"> </span><span class="err">date</span><span class="w"> </span><span class="err">/</span><span class="w"> </span><span class="err">time</span><span class="w"> </span><span class="err">before</span><span class="w"> </span><span class="err">day</span><span class="w"> </span><span class="err">of</span><span class="w"> </span><span class="err">creation</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0fa15940-e42e-4a25-959e-88f8132eaf9b"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h0472"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h0472"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"3bfa51f9-a4fb-4acf-a59a-8c5af8e4d61b"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h0473"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h0473"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"d5be2cea-7459-4dbb-865a-11c4f53ae3b1"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h0781"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h0781"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"5822c0c4-8b66-425f-8120-1f7d678737d7"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h0984"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h0984"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ec69dd0d-44d7-446e-b447-e5311e232241"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h1142"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h1142"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6f5fad1b-39ea-4d42-ad86-6c73719a4b66"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h1871"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h1871"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6f0a0272-24de-42cc-bccc-359edeafe71f"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h1988"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h1988"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"848fd02e-1ab9-4e82-8f19-569e05abdba5"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h2161"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h2161"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"db855105-a5d3-4202-9874-49386d94ba69"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h2907"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h2907"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6df9d571-93a5-4e6c-bb55-e9040b29e88c"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3054"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3054"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"85235bc7-8852-4631-b19b-e8b30119966e"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3118"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3118"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6bb2c27c-c649-4d60-b603-20280871ccca"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3254"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3254"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"615a2c0d-a803-491b-b1df-2d74be197ea0"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3263"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3263"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"dfd63c9d-60e2-4806-8b55-f01cc308677c"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3264"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3264"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"77701a25-2dca-4fd3-8b4f-590f12324471"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3265"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3265"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">

</span></code></pre></div></div>

<ol>
  <li>Logon to https://portal.azure.com</li>
  <li>Navigate to <strong>Storage Account</strong></li>
  <li>In navigation go to <strong>Object replication</strong> and <strong>Upload Rules</strong>
<img src="/assets/images/Azure/ST-Sync-Multiple-Object/st-sync-1.png" alt="" /></li>
  <li>Check that rules include all object and destination and source is correct.
 <img src="/assets/images/Azure/ST-Sync-Multiple-Object/st-sync-3.png" alt="" /></li>
</ol>

<h3 id="confiuration-source-storage-account-object-replication-policy">Confiuration source storage account object replication policy</h3>

<ol>
  <li>Get object replication rule ID on destination storage account.</li>
</ol>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">
</span><span class="n">Get-AzStorageObjectReplicationPolicy</span><span class="w"> </span><span class="nt">-ResourceGroupName</span><span class="w"> </span><span class="s2">"RG_name_destination"</span><span class="w"> </span><span class="nt">-StorageAccountName</span><span class="w"> </span><span class="s2">"storageaccount_name_destination""

</span></code></pre></div></div>
<p><img src="../assets/images/Azure/ST-Sync-Multiple-Object/st-sync-4.png" alt="" /></p>

<ol>
  <li>Prepare policy file in destination storage account
    <ul>
      <li><strong>ruleId</strong> - for each blob need to unique guid</li>
      <li><strong>minCreationTime</strong> - For sync all content we need to setup date / time before day of creation</li>
      <li><strong>sourceAccount</strong> - object ID of source storage account</li>
      <li><strong>destinationAccount</strong>  - object ID of destinationAccount storage account</li>
      <li><strong>policyId</strong> - policy ID of uploaded replication rules in destination storage account.</li>
    </ul>
  </li>
</ol>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"policyId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a50abfe6-68df-xxxx-xxxx-013b9f2a5050"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"sourceAccount"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/subscriptions/xxxxxxxxxxxxxxxxxxxxxx/resourceGroups/RG_name_source/providers/Microsoft.Storage/storageAccounts/storageaccount_name_source"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"destinationAccount"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/subscriptions/xxxxxxxxxxxxxxxxxxxxxx/resourceGroups/RG_name_destination/providers/Microsoft.Storage/storageAccounts/pstorageaccount_name_destination"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"rules"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1f45eace-d5fb-4e8e-9e1c-cf8219615644"</span><span class="p">,</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">Rule</span><span class="w"> </span><span class="err">ID</span><span class="w"> </span><span class="err">need</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">be</span><span class="w"> </span><span class="err">guid</span><span class="w"> </span><span class="err">random</span><span class="w"> </span><span class="err">generated</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"dms5"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"dms5"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">For</span><span class="w"> </span><span class="err">sync</span><span class="w"> </span><span class="err">all</span><span class="w"> </span><span class="err">content</span><span class="w"> </span><span class="err">we</span><span class="w"> </span><span class="err">need</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">setup</span><span class="w"> </span><span class="err">date</span><span class="w"> </span><span class="err">/</span><span class="w"> </span><span class="err">time</span><span class="w"> </span><span class="err">before</span><span class="w"> </span><span class="err">day</span><span class="w"> </span><span class="err">of</span><span class="w"> </span><span class="err">creation</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0fa15940-e42e-4a25-959e-88f8132eaf9b"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h0472"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h0472"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"3bfa51f9-a4fb-4acf-a59a-8c5af8e4d61b"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h0473"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h0473"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"d5be2cea-7459-4dbb-865a-11c4f53ae3b1"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h0781"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h0781"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"5822c0c4-8b66-425f-8120-1f7d678737d7"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h0984"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h0984"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ec69dd0d-44d7-446e-b447-e5311e232241"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h1142"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h1142"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6f5fad1b-39ea-4d42-ad86-6c73719a4b66"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h1871"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h1871"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6f0a0272-24de-42cc-bccc-359edeafe71f"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h1988"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h1988"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"848fd02e-1ab9-4e82-8f19-569e05abdba5"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h2161"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h2161"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"db855105-a5d3-4202-9874-49386d94ba69"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h2907"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h2907"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6df9d571-93a5-4e6c-bb55-e9040b29e88c"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3054"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3054"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"85235bc7-8852-4631-b19b-e8b30119966e"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3118"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3118"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6bb2c27c-c649-4d60-b603-20280871ccca"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3254"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3254"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"615a2c0d-a803-491b-b1df-2d74be197ea0"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3263"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3263"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"dfd63c9d-60e2-4806-8b55-f01cc308677c"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3264"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3264"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ruleId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"77701a25-2dca-4fd3-8b4f-590f12324471"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"sourceContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3265"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"destinationContainer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"h3265"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"minCreationTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1601-01-01T00:00:00Z"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<ol>
  <li>In navigation go to <strong>Object replication</strong> and <strong>Upload Rules</strong>
<img src="/assets/images/Azure/ST-Sync-Multiple-Object/st-sync-1.png" alt="" /></li>
  <li>Check that rules include all object and destination and source is correct.
 <img src="/assets/images/Azure/ST-Sync-Multiple-Object/st-sync-7.png" alt="" /></li>
</ol>]]></content><author><name>Michal Machniak</name><email>mmachniak@outlook.com</email></author><category term="Azure" /><category term="PowerShell" /><category term="Automation" /><category term="Azure" /><category term="CrossTenant" /><summary type="html"><![CDATA[Setup object replication policy for 10 to 1000 blobs between two storage account in different tenants.]]></summary></entry><entry><title type="html">Microsoft 365 Add Shared Contact to user personal mailbox for phone caller identification and more.</title><link href="https://mimachniak.github.io/sysopslife/2024/07/26/M365-MgGraph-Add-Multiple-contact/" rel="alternate" type="text/html" title="Microsoft 365 Add Shared Contact to user personal mailbox for phone caller identification and more." /><published>2024-07-26T00:00:00+02:00</published><updated>2024-07-26T00:00:00+02:00</updated><id>https://mimachniak.github.io/sysopslife/2024/07/26/M365-MgGraph-Add-Multiple-contact</id><content type="html" xml:base="https://mimachniak.github.io/sysopslife/2024/07/26/M365-MgGraph-Add-Multiple-contact/"><![CDATA[<h2 id="key-features">Key features</h2>

<ul>
  <li>Shared conntacts will be added to user personal mailbox.</li>
  <li>Multiple contacts can be added / updated.</li>
  <li>Caller ID will be displayed on user Phone.</li>
  <li>Contact will be displayed in user contact section on mailbox.</li>
  <li>Contact will be synchonized to user phone contact.</li>
</ul>

<h2 id="prerequisites">Prerequisites</h2>

<ul>
  <li>Azure Automation Account</li>
  <li>Service Principal for credentials</li>
</ul>

<h2 id="configuration">Configuration</h2>

<p>Here you can find how to configure your environment step by step. Script is running in version <strong>7</strong> as for big organization reqaired paraller objecte in the same time.</p>

<h3 id="configure-service-principal">Configure Service Principal</h3>

<ol>
  <li>Logon to https://entra.microsoft.com/</li>
  <li>
    <p>Create app registration</p>

    <p><img src="/assets/images/ShareContact/M365-EXO-PS-01.png" alt="" /></p>

    <p><img src="/assets/images/ShareContact/M365-EXO-PS-02.png" alt="" /></p>
  </li>
  <li>
    <p>Permission granted for application</p>

    <p><img src="/assets/images/ShareContact/M365-EXO-PS-03.png" alt="" /></p>
  </li>
</ol>

<h3 id="configure-azure-automation-account">Configure Azure Automation Account</h3>

<h3 id="import-microsoftgraph-modules">Import Microsoft.Graph modules</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">
</span><span class="n">Import-Module</span><span class="w"> </span><span class="nx">Microsoft.Graph.Authentication</span><span class="w">
</span><span class="n">Import-Module</span><span class="w"> </span><span class="nx">Microsoft.Graph.Beta.Users</span><span class="w">
</span><span class="n">Import-Module</span><span class="w"> </span><span class="nx">Microsoft.Graph.Users</span><span class="w">
</span><span class="n">Import-Module</span><span class="w"> </span><span class="nx">Microsoft.Graph.PersonalContacts</span><span class="w">
</span><span class="n">Import-Module</span><span class="w"> </span><span class="nx">Microsoft.Graph.Beta.PersonalContacts</span><span class="w">

</span></code></pre></div></div>

<ol>
  <li>Logon to https://portal.azure.com</li>
  <li>Navigate to automation account</li>
  <li>
    <p>On Menu select Modules -&gt; Add module
<img src="/assets/images/ShareContact/M365-EXO-PS-04.png" alt="" /></p>

    <p><img src="/assets/images/ShareContact/M365-EXO-PS-05.png" alt="" /></p>

    <p><img src="/assets/images/ShareContact/M365-EXO-PS-07.png" alt="" /></p>
  </li>
  <li>Create credentials for <strong>Runbook</strong> used in script.</li>
</ol>

<p><strong>User Name:</strong> Application ID<br />
<strong>Password:</strong> Application secret</p>

<p><img src="/assets/images/ShareContact/M365-EXO-PS-07.png" alt="" /></p>

<ol>
  <li>Create <strong>Runbook</strong> with PowerShell version <strong>7.X</strong></li>
</ol>

<p><img src="/assets/images/ShareContact/M365-EXO-PS-08.png" alt="" /></p>

<h3 id="powershell-script-on-github-run-by-automation-account">PowerShell script on GitHub run by Automation Account</h3>

<blockquote>
  <p>GitHub Link</p>
</blockquote>

<p>Link to script on <strong>GitHub</strong> <a href="https://github.com/mimachniak/sysopslife-scripts/blob/master/M365/M365-MgGraph-Add-Multiple-contact-Personal-Mailbox.ps1">M365-MgGraph-Add-Multiple-contact-Personal-Mailbox.ps1</a> can also be shown.</p>

<h2 id="example-of-contact-created-in-user-personal-mailbox">Example of contact created in user personal mailbox</h2>

<p>Example of output in Outlook</p>

<p><img src="/assets/images/ShareContact/M365-EXO-PS-09.png" alt="" /></p>]]></content><author><name>Michal Machniak</name><email>mmachniak@outlook.com</email></author><category term="Microsoft365" /><category term="PowerShell" /><category term="Azure" /><category term="PowerShell" /><category term="Automation" /><category term="M365" /><summary type="html"><![CDATA[Run by Azure Automation account PowerShell script that will add to user personal mailbox contact and update contact. You can distribution contacts to users based on organization requirements.]]></summary></entry></feed>