<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Deployment | DBA von Nebenan</title>
	<atom:link href="https://dbavonnebenan.de/tag/deployment/feed/" rel="self" type="application/rss+xml" />
	<link>https://dbavonnebenan.de</link>
	<description>Sql Server, Performance &#38; PowerShell Automation</description>
	<lastBuildDate>Fri, 06 Feb 2026 11:50:38 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://dbavonnebenan.de/wp-content/uploads/2025/04/cropped-www_logo-32x32.png</url>
	<title>Deployment | DBA von Nebenan</title>
	<link>https://dbavonnebenan.de</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Automate SQL Server HA: AD, SPN, and Cluster Setup</title>
		<link>https://dbavonnebenan.de/automate-sql-server-ha-ad-spn-and-cluster-setup_en/</link>
					<comments>https://dbavonnebenan.de/automate-sql-server-ha-ad-spn-and-cluster-setup_en/#respond</comments>
		
		<dc:creator><![CDATA[Gabriel]]></dc:creator>
		<pubDate>Fri, 06 Feb 2026 11:47:28 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Sql Server]]></category>
		<category><![CDATA[AD]]></category>
		<category><![CDATA[AlwaysOn]]></category>
		<category><![CDATA[Deployment]]></category>
		<category><![CDATA[eng]]></category>
		<category><![CDATA[GMSA]]></category>
		<category><![CDATA[SPN]]></category>
		<category><![CDATA[WSFC]]></category>
		<guid isPermaLink="false">https://dbavonnebenan.de/?p=669</guid>

					<description><![CDATA[<p>Production-Ready Windows Cluster Deployment: Automation of Failover Cluster, gMSA, Kerberos and local rights via PowerShell.</p>
<p>The post <a href="https://dbavonnebenan.de/automate-sql-server-ha-ad-spn-and-cluster-setup_en/">Automate SQL Server HA: AD, SPN, and Cluster Setup</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></description>
										<content:encoded><![CDATA[<div style="border: 1px dotted #eb2b14; border-radius: 6px; background-color: #eeeeee; padding-top: var(--wp--preset--spacing--30); padding-left: var(--wp--preset--spacing--30); padding-right: var(--wp--preset--spacing--30); padding-bottom: var(--wp--preset--spacing--30); " class="ub-styled-box ub-bordered-box wp-block-ub-styled-box" id="ub-styled-box-11670a24-effc-43d1-9b6f-f1421b3d4c12">
<p class="has-open-sauce-sans-font-family" style="margin-top:0;margin-right:0;margin-bottom:0;margin-left:0" id="ub-styled-box-bordered-content-">What awaits you here?</p>



<ul style="margin-top:0;margin-right:0;margin-bottom:0;margin-left:0" class="wp-block-list has-open-sauce-sans-font-family">
<li><a href="#WSFC" title="">Windows Server Failover Cluster Deployment with Extended AD and DNS Permissions</a></li>



<li><a href="#GMSA" title="">Identity Management: Group Managed Service Accounts (gMSA)</a></li>



<li><a href="#KERBEROS" title="">Kerberos and Service Principal Names (SPN)</a></li>



<li><a href="#LSP" title="">Local Security Policies</a></li>



<li>Background knowledge on why configurations are done the way they are</li>
</ul>


</div>


<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>


https://github.com/gabrielkoehl/DBAScriptBox


<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" id="0-intro" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Intro</h2>



<p>Who hasn&#8217;t been there? Before you can even start installing SQL Server, you&#8217;ve either repetitively clicked through pages of bad GUIs or, if available, juggled a loose collection of scripts to semi-automatically set up all dependencies and best practices.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:90%">
<p>Even worse is the case when, as a consultant, you don&#8217;t have Domain Admin access (which is how it should be) and you have to guide the customer — who may have never done this before — via Teams &amp; Co. If this isn&#8217;t already a planned installation with knowledge transfer, something like this creates pain. <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f92d.png" alt="🤭" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
</div>



<div class="wp-block-column is-vertically-aligned-top is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:10%">
<figure class="wp-block-image aligncenter size-full is-resized"><img fetchpriority="high" decoding="async" width="264" height="329" src="https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-4.png" alt="PAIN Emote" class="wp-image-516" style="aspect-ratio:0.8024665981500514;width:65px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-4.png 264w, https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-4-241x300.png 241w" sizes="(max-width: 264px) 100vw, 264px" /></figure>
</div>
</div>



<p>Accordingly, I&#8217;ve now gathered all my loose scripts and developed a function for each topic area that automates all these tasks and logs them for archival purposes. Thanks also to Claude for the support. Nobody reviews and documents code better than Claude. <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f60b.png" alt="😋" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>Now you might ask: Aren&#8217;t there dbatools and DSC? Sure, I like using those too. But transparent, traceable scripts without framework overhead convey knowledge better — especially when aspiring DBAs lack the big-picture perspective. Operating SQL Server starts at the bare metal, goes through storage, Windows Server, Active Directory, and ends at a Certificate Authority. And for critical infrastructure companies or small mid-sized businesses with 3 clusters, I don&#8217;t want to start a fundamental discussion about dbatools or DSC right from the beginning.</p>



<p>Let&#8217;s start with a production-ready Windows Server Failover Cluster — including CNO/VCO permissions, DNS registration, and quorum configuration. Everything repeatable, everything logged, everything traceable.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" id="1-das-lab-szenario" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">The Lab Scenario</h2>



<p style="margin-bottom:var(--wp--preset--spacing--40)">To make the steps tangible, the following environment and naming conventions are used</p>



<ul class="wp-block-list">
<li><strong>Nodes</strong>
<ul class="wp-block-list">
<li><code>LAB-NODE01</code> (192.168.100.12)</li>



<li><code>LAB-NODE02</code> (192.168.100.13)</li>
</ul>
</li>



<li><strong>Cluster Name</strong>
<ul class="wp-block-list">
<li><code>MSSQL-CL-01</code> (192.168.100.14)</li>
</ul>
</li>



<li><strong>SQL Instance</strong>
<ul class="wp-block-list">
<li><code>LAB22A</code></li>
</ul>
</li>



<li><strong>Domain</strong>
<ul class="wp-block-list">
<li><code>lab.local</code></li>
</ul>
</li>



<li><strong>Service Accounts</strong> Group Managed Service Accounts (gMSA)
<ul class="wp-block-list">
<li><code>g-LAB22A-oltp</code></li>



<li><code>g-LAB22A-agnt</code></li>
</ul>
</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--60)"/>



<h2 class="wp-block-heading" id="WSFC" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Windows Server Failover Cluster Deployment </h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-c901c877 wp-block-columns-is-layout-flex" style="border-width:1px;border-top-left-radius:2px;border-top-right-radius:2px;border-bottom-left-radius:2px;border-bottom-right-radius:2px;margin-top:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--20);padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--80);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--80)">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2692.png" alt="⚒" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/system/cluster_manageSQLCluster.ps1" target="_blank" rel="noopener" title="cluster_manageSQLCluster.ps1"><code>cluster_manageSQLCluster.ps1</code></a></p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d3.png" alt="📓" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><code><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/system/cluster_manageSQLCluster.md" target="_blank" rel="noopener" title="cluster_manageSQLCluster.md">cluster_manageSQLCluster.md</a></code></p>
</div>
</div>
</div>
</div>



<div style="height:14px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="has-text-align-left">The WSFC forms the foundation for AlwaysOn Availability Groups. The script manages the entire lifecycle from feature installation to witness configuration.</p>



<h3 class="wp-block-heading has-text-align-left" id="3-feature-installation-amp-cluster-erstellung" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Feature Installation &amp; Cluster Creation</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">First, the <code>Failover-Clustering</code> feature is installed on the individual nodes and the cluster is created. An important aspect of automation is the Cluster Name Object (CNO): It needs write permissions in DNS and Active Directory to be able to automatically create listener records later. </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; auto-links: false; title: ; notranslate">
.\cluster_manageSQLCluster.ps1 `
    -Operation InstallFeature `
    -Nodes &quot;LAB-NODE01&quot;,&quot;LAB-NODE02&quot; `
    -LogPath &quot;C:\Temp\Cluster_Feature_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<p class="has-text-align-left" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Afterwards, the cluster is created. The script additionally sets the corresponding permissions in AD and DNS.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
.\cluster_manageSQLCluster.ps1 `
    -Operation CreateCluster `
    -ClusterName &quot;MSSQL-CL-01&quot; `
    -Nodes &quot;LAB-NODE01&quot;,&quot;LAB-NODE02&quot; `
    -StaticIP &quot;192.168.100.14&quot; `
    -DnsDomain &quot;lab.local&quot; `
    -TargetOU &quot;OU=lab_computer,DC=lab,DC=local&quot; `
    -LogPath &quot;C:\Temp\Cluster_Create_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<h3 class="wp-block-heading" id="4-file-share-witness-quorum" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">File Share Witness (Quorum)</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">For a 2-node cluster, a witness is mandatory to avoid split-brain scenarios. Here, a file share on the domain controller (<code>\\LAB-DC\quorum$</code>) is used. </p>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)"><strong>Manual Intermediate Step</strong><br>The Cluster Computer Object (<code>MSSQL-CL-01$</code>) requires <strong>Full Control</strong> at both the share and NTFS permission level of the witness path (<code>\\LAB-DC\quorum$</code>). This must be ensured before running the following command.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
.\cluster_manageSQLCluster.ps1 `
    -Operation SetWitness `
    -ClusterName &quot;MSSQL-CL-01&quot; `
    -WitnessShare &quot;\\LAB-DC\quorum$&quot; `
    -LogPath &quot;C:\Temp\Cluster_Witness_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<h3 class="wp-block-heading" id="5-verwendete-microsoft-cmdlets" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Microsoft Cmdlets Used</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">The script primarily uses the <a href="https://learn.microsoft.com/en-us/powershell/module/failoverclusters/" target="_blank" rel="noopener" title="FailoverClusters"><code>FailoverClusters</code></a> module</p>



<ul style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)" class="wp-block-list">
<li><code>New-Cluster</code>
<ul class="wp-block-list">
<li>Creates the cluster, adds the nodes, and sets the management IP</li>
</ul>
</li>



<li><code>Set-ClusterQuorum</code>
<ul class="wp-block-list">
<li>Configures the witness mode (here: File Share Witness)</li>
</ul>
</li>



<li><code>Install-WindowsFeature</code>
<ul class="wp-block-list">
<li>Installs the RSAT tools and the cluster service</li>
</ul>
</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70)"/>



<h2 class="wp-block-heading" id="GMSA" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Identity Management: gMSA</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-c901c877 wp-block-columns-is-layout-flex" style="border-width:1px;border-top-left-radius:2px;border-top-right-radius:2px;border-bottom-left-radius:2px;border-bottom-right-radius:2px;margin-top:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--20);padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--80);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--80)">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2692.png" alt="⚒" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_createGMSA.ps1" target="_blank" rel="noopener" title="security_createGMSA.ps1"><code>security_createGMSA.ps1</code></a></p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d3.png" alt="📓" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_createGMSA.md" target="_blank" rel="noopener" title="security_createGMSA.md"><code>security_createGMSA.md</code></a></p>
</div>
</div>
</div>
</div>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Using standard users as service accounts can lead to expired passwords and downtime. The recommended method for SQL Server is <a href="https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/group-managed-service-accounts/group-managed-service-accounts/group-managed-service-accounts-overview" target="_blank" rel="noopener" title="gMSA"><code>Group Managed Service Accounts (gMSA)</code></a>. Here, the Key Distribution Service (KDS) of Active Directory handles password rotation; nobody needs to know the password.</p>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-right:0;margin-bottom:var(--wp--preset--spacing--50);margin-left:0">KDS Root Key</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Before the first gMSA can be created, a KDS Root Key must exist once per forest. If a key already exists, the creation will be skipped.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
.\security_createGMSA.ps1 `
    -InitializeKDS `
    -LogPath &quot;C:\Temp\KDS_Init.log&quot;
</pre></div>


<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-right:0;margin-bottom:var(--wp--preset--spacing--50);margin-left:0">gMSA Creation</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Instead of assigning servers directly to a gMSA, it is best practice to create a security group (<code>SQL_LAB_CL01</code>) that contains the computer accounts of the cluster nodes. In addition to setting up the actual gMSA, this script can also create the security groups and handle the assignments. And always keep the descriptions of AD objects well-maintained. </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
.\security_createGMSA.ps1 `
    -AccountName @( `
        @(&quot;g-LAB22A-oltp&quot;, &quot;SQL Server Engine Account for LAB22A&quot;), `
        @(&quot;g-LAB22A-agnt&quot;, &quot;SQL Server Agent Account for LAB22A&quot;) ) `
    -SecurityGroupName &quot;SQL_LAB_CL01&quot; `
    -SecurityGroupDescription &quot;SQL Server Cluster CL01 gMSA Group&quot; `
    -ServerNames &quot;LAB-NODE01&quot;,&quot;LAB-NODE02&quot; `
    -CreateSecurityGroup `
    -SecurityGroupOU &quot;OU=lab_secgrp,DC=lab,DC=local&quot; `
    -PasswordIntervalDays 90 `
    -LogPath &quot;C:\Temp\gMSA_Creation_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:0">After this step, a restart of the nodes is required for the new group membership to take effect.</p>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-right:0;margin-bottom:var(--wp--preset--spacing--50);margin-left:0">Local Registration</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">After creation, the gMSAs must be registered locally on the nodes. The following script can be used for this purpose.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# Required for local setup (Local Admin)
Add-WindowsFeature -Name RSAT-AD-PowerShell

$gMSAs = @(
    &#039;g-LAB22A-oltp&#039;,
    &#039;g-LAB22A-agnt&#039;
)

foreach ($gMSA in $gMSAs) {

    if (Test-ADServiceAccount -Identity $gMSA -ErrorAction SilentlyContinue) {
        try {
            Install-ADServiceAccount -Identity $gMSA -ErrorAction Stop
            Write-Host &quot;$gMSA installed&quot; -ForegroundColor Green
        } catch {
            Write-Host &quot;$gMSA - Installation error: $($_.Exception.Message)&quot; -ForegroundColor Red
        }
    } else {
        Write-Host &quot;$gMSA does not exist&quot; -ForegroundColor Yellow
    }
}
</pre></div>


<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:0">After this step, a restart of the nodes is required for the new group membership to take effect.</p>



<h3 class="wp-block-heading" id="5-verwendete-microsoft-cmdlets" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Microsoft Cmdlets Used</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">The script primarily uses the <a href="https://learn.microsoft.com/en-us/powershell/module/activedirectory/?view=windowsserver2025-ps" target="_blank" rel="noopener" title="ActiveDirectory"><code>ActiveDirectory</code></a> module</p>



<ul style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)" class="wp-block-list">
<li><code>Add-KdsRootKey</code>
<ul class="wp-block-list">
<li>Creates the root key for the Group Key Distribution Service</li>
</ul>
</li>



<li><code>New-ADServiceAccount</code>
<ul class="wp-block-list">
<li>Creates the gMSA object in AD</li>
</ul>
</li>



<li><code>Set-ADServiceAccount</code>
<ul class="wp-block-list">
<li>Configures which computers (PrincipalsAllowedToRetrieveManagedPassword) are allowed to read the password</li>
</ul>
</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70)"/>



<h2 class="wp-block-heading" id="KERBEROS" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Kerberos and Service Principal Names (SPN)</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-c901c877 wp-block-columns-is-layout-flex" style="border-width:1px;border-top-left-radius:2px;border-top-right-radius:2px;border-bottom-left-radius:2px;border-bottom-right-radius:2px;margin-top:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--20);padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--80);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--80)">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2692.png" alt="⚒" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><code><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_setSqlSpn.ps1" target="_blank" rel="noopener" title="cluster_manageSQLCluster.ps1">security_setSqlSpn.ps1</a></code></p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d3.png" alt="📓" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><code><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_setSqlSpn.md" target="_blank" rel="noopener" title="">security_setSqlSpn</a></code><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_setSqlSpn.md" title=""><code>.</code>md</a></p>
</div>
</div>
</div>
</div>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">A gMSA does not have the right to write its own SPNs in AD by default. When the SQL service starts, the registration often fails, leading to NTLM fallbacks. Even though I prefer to set SPNs in a controlled and manual manner, the automatic registration must be technically possible to avoid fallbacks.</p>



<p style="margin-bottom:var(--wp--preset--spacing--50)">The script solves this problem by granting the gMSA the rights in AD to manage its own SPNs, in addition to creating the SPNs. It sets ACLs on the nodes for <code>Read</code>, <code>Write</code>, and <code>Validated write</code> on the <code>servicePrincipalName</code> attribute.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
.\security_setSqlSpn.ps1 `
    -ServiceAccount &quot;LAB\g-LAB22A-oltp&quot; `
    -InstanceName &quot;LAB22A&quot; `
    -Port 51101 `
    -Hostnames &quot;LAB-NODE01&quot;,&quot;LAB-NODE02&quot; `
    -Domain &quot;lab.local&quot; `
    -IsGMSA `
    -SetADPermissions `
    -LogPath &quot;C:\Temp\SPN_AlwaysOn_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<h3 class="wp-block-heading" id="5-verwendete-microsoft-cmdlets" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Microsoft Cmdlets Used</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">On one hand, <a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/setspn" target="_blank" rel="noopener" title="setspn.exe"><code>setspn.exe</code></a> is used for SPN management, and on the other hand, the <a href="https://learn.microsoft.com/en-us/powershell/module/activedirectory/?view=windowsserver2025-ps" target="_blank" rel="noopener" title=""><code>ActiveDirectory</code></a> module is used again to verify accounts and extract SIDs.</p>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Since none of the modules can manipulate Active Directory permissions, the .NET Framework <a href="https://learn.microsoft.com/en-us/dotnet/api/system.directoryservices?view=windowsdesktop-10.0" target="_blank" rel="noopener" title="System.DirectoryServices"><code>System.DirectoryServices</code></a> is used directly for this purpose, which provides various classes to completely turn AD inside out. </p>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70)"/>



<h2 class="wp-block-heading" id="LSP" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Local Security Policies</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-c901c877 wp-block-columns-is-layout-flex" style="border-width:1px;border-top-left-radius:2px;border-top-right-radius:2px;border-bottom-left-radius:2px;border-bottom-right-radius:2px;margin-top:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--20);padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--80);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--80)">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2692.png" alt="⚒" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><code><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_setSqlLocalSec.ps1" target="_blank" rel="noopener" title="security_setSqlLocalSec.ps1">security_setSqlLocalSec.ps1</a></code></p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d3.png" alt="📓" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_setSqlLocalSec.md" target="_blank" rel="noopener" title=""><code>security_setSqlLocalSec.md</code></a></p>
</div>
</div>
</div>
</div>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">As everywhere in IT, SQL Server should also follow the <a href="https://en.wikipedia.org/wiki/Principle_of_least_privilege" target="_blank" rel="noopener" title="PoLP"><code>PoLP</code></a>, which is why no service account gets local administrator privileges. For SQL Server to still run flawlessly and performantly, the service account needs specific rights in the operating system (&#8222;User Rights Assignment&#8220;). Anyone who has done this manually a few times will be very happy about this solution. This must be executed locally on the respective node.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# ===== Set Engine Permissions for gMSA =====

    .\security_setSqlLocalSec.ps1 `
        -ServiceAccount &quot;g-LAB22A-oltp$&quot; `
        -ServiceType &quot;engine&quot; `
        -IsGMSA `
        -LogPath &quot;C:\Temp\LAB22A_Engine_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;

# ===== Set Agent Permissions for gMSA =====

    .\security_setSqlLocalSec.ps1 `
        -ServiceAccount &quot;g-LAB22A-agnt$&quot; `
        -ServiceType &quot;agent&quot; `
        -IsGMSA `
        -LogPath &quot;C:\Temp\LAB22A_Agent_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Microsoft Cmdlets Used</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Since PowerShell has no native cmdlets for editing local security policies, the Windows tool <a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/secedit" target="_blank" rel="noopener" title="secedit.exe"><code>secedit.exe</code></a> is used and its export is manipulated.</p>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70)"/>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Summary</h2>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">With these four building blocks, the foundation for a SQL Server installation has been laid. It wasn&#8217;t just an installation that was performed, but a valid, documented configuration that considers identity (gMSA), network (DNS/SPN), and OS performance. In the next step, the actual SQL Server instance can be installed.</p>



<p>Also check out the linked MD files — they contain background information on best practices and further articles from Microsoft</p>



<figure class="wp-block-image aligncenter size-full" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--60)"><img decoding="async" width="307" height="328" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_2.png" alt="good to know emote" class="wp-image-345" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_2.png 307w, https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_2-281x300.png 281w" sizes="(max-width: 307px) 100vw, 307px" /></figure><p>The post <a href="https://dbavonnebenan.de/automate-sql-server-ha-ad-spn-and-cluster-setup_en/">Automate SQL Server HA: AD, SPN, and Cluster Setup</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://dbavonnebenan.de/automate-sql-server-ha-ad-spn-and-cluster-setup_en/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SQL Server HA: AD, SPN und Cluster Setup automatisieren</title>
		<link>https://dbavonnebenan.de/automate-sql-server-ha-ad-spn-and-cluster-setup_de/</link>
					<comments>https://dbavonnebenan.de/automate-sql-server-ha-ad-spn-and-cluster-setup_de/#respond</comments>
		
		<dc:creator><![CDATA[Gabriel]]></dc:creator>
		<pubDate>Fri, 06 Feb 2026 11:20:00 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Sql Server]]></category>
		<category><![CDATA[AD]]></category>
		<category><![CDATA[AlwaysOn]]></category>
		<category><![CDATA[DE]]></category>
		<category><![CDATA[Deployment]]></category>
		<category><![CDATA[GMSA]]></category>
		<category><![CDATA[SPN]]></category>
		<category><![CDATA[WSFC]]></category>
		<guid isPermaLink="false">https://dbavonnebenan.de/?p=641</guid>

					<description><![CDATA[<p>Production-Ready Windows Cluster Deployment: Automatisierung von Failover Cluster, gMSA, Kerberos und lokalen Rechten via PowerShell.</p>
<p>The post <a href="https://dbavonnebenan.de/automate-sql-server-ha-ad-spn-and-cluster-setup_de/">SQL Server HA: AD, SPN und Cluster Setup automatisieren</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></description>
										<content:encoded><![CDATA[<div style="border: 1px dotted #eb2b14; border-radius: 6px; background-color: #eeeeee; padding-top: var(--wp--preset--spacing--30); padding-left: var(--wp--preset--spacing--30); padding-right: var(--wp--preset--spacing--30); padding-bottom: var(--wp--preset--spacing--30); " class="ub-styled-box ub-bordered-box wp-block-ub-styled-box" id="ub-styled-box-8bf7c8f5-ed36-44d1-82b0-6849bd1507a5">
<p class="has-open-sauce-sans-font-family" style="margin-top:0;margin-right:0;margin-bottom:0;margin-left:0" id="ub-styled-box-bordered-content-">Was erwartet dich hier?</p>



<ul style="margin-top:0;margin-right:0;margin-bottom:0;margin-left:0" class="wp-block-list has-open-sauce-sans-font-family">
<li><a href="#WSFC" title="">Windows Server Failover Cluster Deployment mit erweiterten AD- und DNS-Berechtigungen</a></li>



<li><a href="#GMSA" title="">Identity Management: Group Managed Service Accounts (gMSA)</a></li>



<li><a href="#KERBEROS" title="">Kerberos und Service Principal Names (SPN)</a></li>



<li><a href="#LSP" title="">Lokale Sicherheitsrichtlinien (Local Security Policies)</a></li>



<li>Hintergrundwissen warum Konfigurationen gemacht werden wie sie gemacht werden</li>
</ul>


</div>


<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>


https://github.com/gabrielkoehl/DBAScriptBox


<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" id="0-intro" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Intro</h2>



<p>Wer kennt es nicht? Bevor man überhaupt mit der Installation von SQL Server loslegen kann, hat man sich entweder repetitiv durch seitenweise schlechte GUIs geklickt oder wenn verfügbar, mit einer losen Sammlung an Scripten jongliert und semiautomatisch alle Abhängigkeiten und Best Practices eingerichtet.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:80%">
<p>Noch schlimmer ist der Fall, wenn man als Berater gar keinen Domain Admin hat (was auch so sein sollte) und man den Kunden, der dies vielleicht noch nie gemacht hat, per Teams &amp; Co anleiten muss. Wenn dies nicht eh eine geplante Installation mit Know-how-Transfer ist, erzeugt so etwas Schmerz. <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f92d.png" alt="🤭" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:20%">
<figure class="wp-block-image aligncenter size-full is-resized"><img decoding="async" width="264" height="329" src="https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-4.png" alt="PAIN Emote" class="wp-image-516" style="aspect-ratio:0.8024665981500514;width:75px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-4.png 264w, https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-4-241x300.png 241w" sizes="(max-width: 264px) 100vw, 264px" /></figure>
</div>
</div>



<p>Dementsprechend habe ich nun mal all meine losen Scripte zusammengewürfelt und für jeden Themenbereich eine recht umfangreiche Funktion entwickelt, welche all diese Tasks automatisiert und für Archivzwecke loggt. Danke auch an Claude für die Unterstützung. Keiner kontrolliert und dokumentiert Code besser als Claude. <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f60b.png" alt="😋" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>Nun könnte man sich fragen: Es gibt doch dbatools und DSC? Klar, nutze ich auch gerne. Aber transparente, nachvollziehbare Scripts ohne Framework-Overhead vermitteln Wissen besser &#8211; gerade wenn angehenden DBAs der Blick fürs Ganze fehlt. Betrieb von SQL Server fängt auf dem Blech an, geht über Storage, Windows Server, Active Directory und hört bei einer Certificate Authority auf. Und bei KRITIS-Unternehmen oder kleinen Mittelständlern mit 3 Clustern will ich zu Beginn keine Grundsatzdiskussion über dbatools oder DSC führen.</p>



<p>Los geht&#8217;s mit einem Production-Ready Windows Server Failover Cluster &#8211; inklusive CNO/VCO-Berechtigungen, DNS-Registrierung und Quorum-Konfiguration. Alles wiederholbar, alles geloggt, alles nachvollziehbar.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" id="1-das-lab-szenario" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Das Lab Szenario</h2>



<p style="margin-bottom:var(--wp--preset--spacing--40)">Um die Schritte greifbar zu machen, wird folgende Umgebung und Naming-Conventions genutzt</p>



<ul class="wp-block-list">
<li><strong>Nodes</strong>
<ul class="wp-block-list">
<li><code>LAB-NODE01</code> (192.168.100.12)</li>



<li><code>LAB-NODE02</code> (192.168.100.13)</li>
</ul>
</li>



<li><strong>Cluster Name</strong>
<ul class="wp-block-list">
<li><code>MSSQL-CL-01</code> (192.168.100.14)</li>
</ul>
</li>



<li><strong>SQL Instanz</strong>
<ul class="wp-block-list">
<li><code>LAB22A</code></li>
</ul>
</li>



<li><strong>Domain</strong>
<ul class="wp-block-list">
<li><code>lab.local</code></li>
</ul>
</li>



<li><strong>Service Accounts</strong> Group Managed Service Accounts (gMSA)
<ul class="wp-block-list">
<li><code>g-LAB22A-oltp</code></li>



<li><code>g-LAB22A-agnt</code></li>
</ul>
</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--60)"/>



<h2 class="wp-block-heading" id="WSFC" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Windows Server Failover Cluster Deployment </h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-c901c877 wp-block-columns-is-layout-flex" style="border-width:1px;border-top-left-radius:2px;border-top-right-radius:2px;border-bottom-left-radius:2px;border-bottom-right-radius:2px;margin-top:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--20);padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--80);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--80)">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2692.png" alt="⚒" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/system/cluster_manageSQLCluster.ps1" target="_blank" rel="noopener" title="cluster_manageSQLCluster.ps1"><code>cluster_manageSQLCluster.ps1</code></a></p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d3.png" alt="📓" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><code><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/system/cluster_manageSQLCluster.md" target="_blank" rel="noopener" title="cluster_manageSQLCluster.md">cluster_manageSQLCluster.md</a></code></p>
</div>
</div>
</div>
</div>



<div style="height:14px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="has-text-align-left">Der WSFC bildet das Fundament für AlwaysOn Availability Groups. Das Script steuert den gesamten Lifecycle von der Feature-Installation bis zur Witness-Konfiguration.</p>



<h3 class="wp-block-heading has-text-align-left" id="3-feature-installation-amp-cluster-erstellung" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Feature Installation &amp; Cluster Erstellung</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Zunächst wird das Feature <code>Failover-Clustering</code> auf den einzelnen Nodes installiert und der Cluster erstellt. Ein wichtiger Aspekt bei der Automatisierung ist das Cluster-Objekt (CNO): Es benötigt Schreibrechte im DNS und Active Directory um später automatisiert Listener-Records anlegen zu können. </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; auto-links: false; title: ; notranslate">
.\cluster_manageSQLCluster.ps1 `
    -Operation InstallFeature `
    -Nodes &quot;LAB-NODE01&quot;,&quot;LAB-NODE02&quot; `
    -LogPath &quot;C:\Temp\Cluster_Feature_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<p class="has-text-align-left" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Anschließend wird der Cluster erstellt. Das Script setzt zusätzlich die entsprechenden Berechtigungen in AD und DNS.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
.\cluster_manageSQLCluster.ps1 `
    -Operation CreateCluster `
    -ClusterName &quot;MSSQL-CL-01&quot; `
    -Nodes &quot;LAB-NODE01&quot;,&quot;LAB-NODE02&quot; `
    -StaticIP &quot;192.168.100.14&quot; `
    -DnsDomain &quot;lab.local&quot; `
    -TargetOU &quot;OU=lab_computer,DC=lab,DC=local&quot; `
    -LogPath &quot;C:\Temp\Cluster_Create_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<h3 class="wp-block-heading" id="4-file-share-witness-quorum" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">File Share Witness (Quorum)</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Für einen 2-Node Cluster ist ein Witness zwingend erforderlich, um Split-Brain-Szenarien zu vermeiden. Hier wird ein File Share auf dem Domain Controller (<code>\\LAB-DC\quorum$</code>) genutzt. </p>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)"><strong>Manueller Zwischenschritt</strong><br>Das Cluster-Computer-Objekt (<code>MSSQL-CL-01$</code>) benötigt <strong>Full Control</strong> auf Ebene der Share- und NTFS-Berechtigungen des Witness-Pfads (<code>\\LAB-DC\quorum$</code>). Dies muss vor dem folgenden Befehl sichergestellt sein.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
.\cluster_manageSQLCluster.ps1 `
    -Operation SetWitness `
    -ClusterName &quot;MSSQL-CL-01&quot; `
    -WitnessShare &quot;\\LAB-DC\quorum$&quot; `
    -LogPath &quot;C:\Temp\Cluster_Witness_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<h3 class="wp-block-heading" id="5-verwendete-microsoft-cmdlets" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Verwendete Microsoft Cmdlets</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Das Script nutzt primär das <a href="https://learn.microsoft.com/en-us/powershell/module/failoverclusters/" target="_blank" rel="noopener" title="FailoverClusters"><code>FailoverClusters</code></a> Modul</p>



<ul style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)" class="wp-block-list">
<li><code>New-Cluster</code>
<ul class="wp-block-list">
<li>Erstellt den Cluster, bindet die Nodes ein und setzt die Management-IP</li>
</ul>
</li>



<li><code>Set-ClusterQuorum</code>
<ul class="wp-block-list">
<li>Konfiguriert den Witness-Modus (hier: File Share Witness)</li>
</ul>
</li>



<li><code>Install-WindowsFeature</code>
<ul class="wp-block-list">
<li>Installiert die RSAT-Tools und den Cluster-Dienst</li>
</ul>
</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70)"/>



<h2 class="wp-block-heading" id="GMSA" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Identity Management: gMSA</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-c901c877 wp-block-columns-is-layout-flex" style="border-width:1px;border-top-left-radius:2px;border-top-right-radius:2px;border-bottom-left-radius:2px;border-bottom-right-radius:2px;margin-top:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--20);padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--80);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--80)">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2692.png" alt="⚒" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_createGMSA.ps1" target="_blank" rel="noopener" title="security_createGMSA.ps1"><code>security_createGMSA.ps1</code></a></p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d3.png" alt="📓" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_createGMSA.md" target="_blank" rel="noopener" title="security_createGMSA.md"><code>security_createGMSA.md</code></a></p>
</div>
</div>
</div>
</div>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Die Nutzung von Standard-Usern als Service Accounts kann zu abgelaufenen Passwörtern und Downtime führen. Die empfohlene Methode für SQL Server sind <a href="https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/group-managed-service-accounts/group-managed-service-accounts/group-managed-service-accounts-overview" target="_blank" rel="noopener" title="gMSA"><code>Group Managed Service Accounts (gMSA)</code></a>. Hierbei übernimmt der Key Distribution Service (KDS) des Active Directory die Passwortrotation; Niemand muss das Passwort kennen.</p>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-right:0;margin-bottom:var(--wp--preset--spacing--50);margin-left:0">KDS Root Key</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Bevor der erste gMSA erstellt werden kann, muss einmalig pro Forest ein KDS Root Key existieren. Sollte bereits ein Key existieren, wird die Erstellung übersprungen.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
.\security_createGMSA.ps1 `
    -InitializeKDS `
    -LogPath &quot;C:\Temp\KDS_Init.log&quot;
</pre></div>


<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-right:0;margin-bottom:var(--wp--preset--spacing--50);margin-left:0">gMSA Erstellung</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Anstatt Server direkt einem gMSA zuzuordnen, ist es Best Practice, eine Security Group zu erstellen (<code>SQL_LAB_CL01</code>), welche die Computerkonten der Cluster-Nodes enthält. Neben der Einrichtung der eigentlichen gMSA kann dieses Script auch die Security Gruppen erstellen und die Zuordnungen vornehmen. Und immer schön die Beschreibungen bei AD-Objekten pflegen. </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
.\security_createGMSA.ps1 `
    -AccountName @( `
        @(&quot;g-LAB22A-oltp&quot;, &quot;SQL Server Engine Account for LAB22A&quot;), `
        @(&quot;g-LAB22A-agnt&quot;, &quot;SQL Server Agent Account for LAB22A&quot;) ) `
    -SecurityGroupName &quot;SQL_LAB_CL01&quot; `
    -SecurityGroupDescription &quot;SQL Server Cluster CL01 gMSA Group&quot; `
    -ServerNames &quot;LAB-NODE01&quot;,&quot;LAB-NODE02&quot; `
    -CreateSecurityGroup `
    -SecurityGroupOU &quot;OU=lab_secgrp,DC=lab,DC=local&quot; `
    -PasswordIntervalDays 90 `
    -LogPath &quot;C:\Temp\gMSA_Creation_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:0">Nach diesem Schritt ist ein Neustart der Nodes erforderlich, damit die neue Gruppenmitgliedschaft wirksam wird.</p>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-right:0;margin-bottom:var(--wp--preset--spacing--50);margin-left:0">Lokale Registrierung</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Nach der Erstellung müssen die gMSA lokal auf den Nodes registriert werden. Hierzu kann folgendes  Script genutzt werden.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# Wichtig für lokale Einrichtung ( Local Admin )
Add-WindowsFeature -Name RSAT-AD-PowerShell

$gMSAs = @(
    &#039;g-LAB22A-oltp&#039;,
    &#039;g-LAB22A-agnt&#039;
)

foreach ($gMSA in $gMSAs) {

    if (Test-ADServiceAccount -Identity $gMSA -ErrorAction SilentlyContinue) {
        try {
            Install-ADServiceAccount -Identity $gMSA -ErrorAction Stop
            Write-Host &quot;$gMSA installiert&quot; -ForegroundColor Green
        } catch {
            Write-Host &quot;$gMSA - Fehler bei Installation: $($_.Exception.Message)&quot; -ForegroundColor Red
        }
    } else {
        Write-Host &quot;$gMSA existiert nicht&quot; -ForegroundColor Yellow
    }
}
</pre></div>


<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:0">Nach diesem Schritt ist ein Neustart der Nodes erforderlich, damit die neue Gruppenmitgliedschaft (TGT) wirksam wird.</p>



<h3 class="wp-block-heading" id="5-verwendete-microsoft-cmdlets" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Verwendete Microsoft Cmdlets</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Das Script nutzt primär das <a href="https://learn.microsoft.com/en-us/powershell/module/activedirectory/?view=windowsserver2025-ps" target="_blank" rel="noopener" title="ActiveDirectory"><code>ActiveDirectory</code></a> Modul</p>



<ul style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)" class="wp-block-list">
<li><code>Add-KdsRootKey</code>
<ul class="wp-block-list">
<li>Erzeugt den Root Key für den Group Key Distribution Service</li>
</ul>
</li>



<li><code>New-ADServiceAccount</code>
<ul class="wp-block-list">
<li>Erstellt das gMSA Objekt im AD</li>
</ul>
</li>



<li><code>Set-ADServiceAccount</code>
<ul class="wp-block-list">
<li>Konfiguriert, welche Computer (PrincipalsAllowedToRetrieveManagedPassword) das Passwort lesen dürfen</li>
</ul>
</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70)"/>



<h2 class="wp-block-heading" id="KERBEROS" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Kerberos und Service Principal Names (SPN)</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-c901c877 wp-block-columns-is-layout-flex" style="border-width:1px;border-top-left-radius:2px;border-top-right-radius:2px;border-bottom-left-radius:2px;border-bottom-right-radius:2px;margin-top:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--20);padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--80);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--80)">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2692.png" alt="⚒" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><code><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_setSqlSpn.ps1" target="_blank" rel="noopener" title="cluster_manageSQLCluster.ps1">security_setSqlSpn.ps1</a></code></p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d3.png" alt="📓" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><code><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_setSqlSpn.md" target="_blank" rel="noopener" title="">security_setSqlSpn</a></code><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_setSqlSpn.md" title=""><code>.</code>md</a></p>
</div>
</div>
</div>
</div>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Ein gMSA besitzt standardmäßig nicht das Recht, seine eigenen SPNs im AD zu schreiben. Startet der SQL Dienst, schlägt die Registrierung oft fehl, was zu NTLM-Fallbacks führt. Auch wenn ich SPNs bevorzugt kontrolliert und manuell setze, muss die automatische Registrierung technisch möglich sein, um Fallbacks zu vermeiden.</p>



<p style="margin-bottom:var(--wp--preset--spacing--50)">Das Script löst dieses Problem, indem es zusätzlich zur SPN Erstellung dem gMSA die Rechte im AD gewährt, seine eigenen SPNs zu verwalten. Es setzt ACLs der Nodes für <code>Read</code>, <code>Write</code> und <code>Validated write</code> auf das Attribut <code>servicePrincipalName</code>.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
.\security_setSqlSpn.ps1 `
    -ServiceAccount &quot;LAB\g-LAB22A-oltp&quot; `
    -InstanceName &quot;LAB22A&quot; `
    -Port 51101 `
    -Hostnames &quot;LAB-NODE01&quot;,&quot;LAB-NODE02&quot; `
    -Domain &quot;lab.local&quot; `
    -IsGMSA `
    -SetADPermissions `
    -LogPath &quot;C:\Temp\SPN_AlwaysOn_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<h3 class="wp-block-heading" id="5-verwendete-microsoft-cmdlets" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Verwendete Microsoft Cmdlets</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Zum einen wird <a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/setspn" target="_blank" rel="noopener" title="setspn.exe"><code>setspn.exe</code></a> für Verwaltung der SPN genutzt, zum anderen wieder das <a href="https://learn.microsoft.com/en-us/powershell/module/activedirectory/?view=windowsserver2025-ps" target="_blank" rel="noopener" title=""><code>ActiveDirectory</code></a> Modul, um Accounts zu verifizieren und die SIDs zu extrahieren.</p>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Da keines der Module Active Directory Permsission manipulieren kann, wird hierfür direkt das .NET Framework <a href="https://learn.microsoft.com/en-us/dotnet/api/system.directoryservices?view=windowsdesktop-10.0" target="_blank" rel="noopener" title="System.DirectoryServices"><code>System.DirectoryServices</code></a> genutzt, welches diverse Klassen bereitstellt, um das AD komplett auf Links zu drehen. </p>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70)"/>



<h2 class="wp-block-heading" id="LSP" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Lokale Sicherheitsrichtlinien (Local Security Policies)</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-c901c877 wp-block-columns-is-layout-flex" style="border-width:1px;border-top-left-radius:2px;border-top-right-radius:2px;border-bottom-left-radius:2px;border-bottom-right-radius:2px;margin-top:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--20);padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--80);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--80)">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2692.png" alt="⚒" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><code><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_setSqlLocalSec.ps1" target="_blank" rel="noopener" title="security_setSqlLocalSec.ps1">security_setSqlLocalSec.ps1</a></code></p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d3.png" alt="📓" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_setSqlLocalSec.md" target="_blank" rel="noopener" title=""><code>security_setSqlLocalSec.md</code></a></p>
</div>
</div>
</div>
</div>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Wie überall in der IT sollte auch beim Sql Server nach dem <a href="https://en.wikipedia.org/wiki/Principle_of_least_privilege" target="_blank" rel="noopener" title="PoLP"><code>PoLP</code></a> gearbeitet werden, weshalb kein Service Account den lokalen Administrator bekommt.Damit SQL Server trotzdem fehlerfrei und performant läuft, benötigt der Service Account spezifische Rechte im Betriebssystem (&#8222;User Rights Assignment&#8220;). Wer dies ein paar mal manuell gemacht hat, wird sich sehr über diese Lösung freuen. Diese muss lokal auf dem jeweiligen Node ausgeführt werden.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# ===== Set Engine Permissions for gMSA =====

    .\security_setSqlLocalSec.ps1 `
        -ServiceAccount &quot;g-LAB22A-oltp$&quot; `
        -ServiceType &quot;engine&quot; `
        -IsGMSA `
        -LogPath &quot;C:\Temp\LAB22A_Engine_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;

# ===== Set Agent Permissions for gMSA =====

    .\security_setSqlLocalSec.ps1 `
        -ServiceAccount &quot;g-LAB22A-agnt$&quot; `
        -ServiceType &quot;agent&quot; `
        -IsGMSA `
        -LogPath &quot;C:\Temp\LAB22A_Agent_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Verwendete Microsoft Cmdlets</h3>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Da PowerShell keine nativen Cmdlets besitzt, um lokale Sicherheitsrichtlinien (LSA) zu bearbeiten, wird das Windows-Tool <a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/secedit" target="_blank" rel="noopener" title="secedit.exe"><code>secedit.exe</code></a> genutzt und der Export manipuliert.</p>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70)"/>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Summary</h2>



<p style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Mit diesen vier Bausteinen wurde das Fundament für eine Sql Server Installation gelegt. Es wurde nicht nur eine Installation durchgeführt, sondern eine valide, dokumentierte Konfiguration geschaffen, die Identität (gMSA), Netzwerk (DNS/SPN) und OS-Performance (LSA) berücksichtigt. Im nächsten Schritt kann die eigentliche SQL Server Instanz installiert werden.</p>



<p>Schaut euch auch die verlinkten MD-Files an, dort stehen Hintergrundinfos zu den Best Practices und weiterführende Artikel von Microsoft</p>



<figure class="wp-block-image aligncenter size-full" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--60)"><img loading="lazy" decoding="async" width="307" height="328" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_2.png" alt="good to know emote" class="wp-image-345" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_2.png 307w, https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_2-281x300.png 281w" sizes="auto, (max-width: 307px) 100vw, 307px" /></figure><p>The post <a href="https://dbavonnebenan.de/automate-sql-server-ha-ad-spn-and-cluster-setup_de/">SQL Server HA: AD, SPN und Cluster Setup automatisieren</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://dbavonnebenan.de/automate-sql-server-ha-ad-spn-and-cluster-setup_de/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
