Windows Server 2019 | Docs

Overview

Windows Server remains the industry standard domain controller for IdP (AD DS), IdM (AD UAC), X.509 (AD CS), DNS and DHCP. Linux (RHEL) hosts join into its domain via realm and SSSD.

However, engineers quickly learn that its prominence is due to clever vendor lock-in rather than any technological edge. In fact, WS2019 does not allow for any sort of automation, nonetheless DevOps/GitOps methods. Every domain becomes a handcrafted boutique; a once-per-universe configuration that will never, indeed can never, be repeated again. And the man hours required to craft one, even only to the point that it is tolerably sufficient, is gargantuan. As is its maintenance.

Vendor marketing:

… the operating system that bridges on-premises environments with Azure services enabling hybrid scenarios maximizing existing investments. Create cloud native and modernize traditional apps using containers and micro-services.

That last sentence is laughable. Enterprises are slowly escaping the Windows hellscape, and moving to a purely K8s-based infra. Though very slowly.

Installation options (2016/2019):

Three Versions

Requirements:

Windows Admin Center (WAC)

A locally-deployed, browser-based management tool set built to manage Windows Clients, Servers, and Clusters without needing to connect to the cloud. Windows Admin Center offers full control over all aspects of Windows-based server infrastructure and is particularly useful for managing on-prem servers.

WAC requires its own separate installation.WAC is not a built-in feature of WS2019.
While it is designed to manage Windows Server environments, WAC itself is a web-based management tool that must be downloaded and installed manually.

Once installed, you can use WAC to manage multiple Windows Servers, Hyper-V hosts, clusters, and even Windows 10 and 11 PCs from a single web-based interface. It simplifies server management tasks by centralizing the management tools and offering a modern, unified experience. You can install WAC on a Windows Server or Windows client and access it via a web browser. It can also be deployed in a high-availability setup in larger environments.

Realm v. Domain v. Forest v. Tree

πŸ”Ή Active Directory Terminology

Concept Description
Domain A logical grouping of objects (users, computers, etc.) under a single database and namespace (e.g., example.com).
Forest A collection of one or more domains that share a common schema and global catalog, with implicit trust.
Tree A collection of domains in a contiguous namespace within a forest.

πŸ”Ή Kerberos Terminology

Concept Description
Realm A Kerberos concept, representing an authentication boundaryβ€”usually maps 1:1 to an AD domain and written in ALL CAPS (e.g., EXAMPLE.COM).

🧭 Where They Intersect

Term Found In Example
Domain AD ad.lan
Forest AD ad.lan, corp.ad.lan β†’ part of the same forest
Realm Kerberos AD.LAN (used in krb5.conf, SSSD, etc.)

🧩 Summary

Term Role Example Scope
Domain Logical container example.com One namespace
Forest Trust boundary multiple domains Entire AD
Realm Auth boundary EXAMPLE.COM Often = Domain

Let me know if you want to map this to a Linux config or see how realm joins work.

Roles/Features

Selectable by checkbox at the AD Installation Wizard of the Server Manager GUI

Hyper-V Prep

Networking : Connectivity between WSL2 & Hyper-V VMs

The NAT subnet option : Overview of best options

@ network-nat.ps1

See params it configured by running network-get.ps1

  1. User above script or Hyper-V GUI to create the Internal Switch (InternalSwitchNAT1)
    • PowerShell script above creates the switch if not exist, yet using it to do so prior to finding the packet-forwarding solution mentioned below resulted in failure of connectivity tests.
    • Isolated, this is initally/automatically assigned an APIPA address
  2. Use PowerShell to create the NAT1 subnet (192.168.11.0/24) having route to WSL subnet (172.27.240.0/20) gateway.

    New-NetNat -Name "$NatName" -InternalIPInterfaceAddressPrefix "$NatCIDR"
    Set-NetIPAddress -InterfaceAlias "$WslAlias" -IPAddress "$WslGateway" -PrefixLength 20 
    
  3. Use ncpa.cpl (GUI) to manually set IP address within our declared NAT1 CIDR on $NatAlias interface (InternalSwitchNAT1).

  4. Enable IP packet forwarding, per adapter (switch), to provide connectivity across subnets.

    # Enable IP packet forwarding (across subnets) : Per adapter
    Set-NetIPInterface -InterfaceAlias "$ExtAlias" -Forwarding Enabled -Verbose
    Set-NetIPInterface -InterfaceAlias "$WslAlias" -Forwarding Enabled -Verbose
    Set-NetIPInterface -InterfaceAlias "$DefAlias" -Forwarding Enabled -Verbose
    Set-NetIPInterface -InterfaceAlias "$NatAlias" -Forwarding Enabled -Verbose
    # Else all at once
    Get-NetIPInterface | Where-Object {$_.InterfaceAlias -like 'vEthernet (*' } | Set-NetIPInterface -Forwarding Enabled
    
    

WSL2 has connectivity to Windows 11 host (External) network regardless; however, NAT subnet does not until packet forwarding is enabled.

Verify connectivity from WSL2 to NAT1 subnet; route between Ubuntu (WSL2) host and a0.lime.lan (NAT1) host:

Ubuntu [13:23:45] [1] [#0] /c/TEMP
☩ traceroute a0.lime.lan
traceroute to a0.lime.lan (192.168.11.104), 64 hops max
1   172.24.208.1  0.432ms  0.227ms  0.139ms    # WSL Gateway
2   192.168.11.104  0.448ms  0.230ms  0.287ms  # Target Hyper-V VM host

To test NAT1 prior to provisioning DHCP/DNS server on that subnet, manually add the IP and route on that CIDR to the host's network device. This task is performed from a Hyper-V Connect session:

# Manually assign IP within NAT1 CIDR
sudo ip addr add 192.168.11.111/24 dev eth0

# Add default gatewayy (The IPv4 declared at NAT1 adapter) 
sudo ip route add default via 192.168.11.1

Now test @ WSL:

☩ traceroute 192.168.11.111
traceroute to 192.168.11.111 (192.168.11.111), 64 hops max
  1   172.27.240.1  0.361ms  0.175ms  0.160ms
  2   192.168.11.111  0.326ms  0.231ms  0.164ms

Success!

Add local DNS for best performance:

@ network-dns.ps1

Keep the default DNS configuration of WSL2 host:

...
# DNS @ WSL2 network is best handled automatically, 
# which auto-generates /etc/resolv.conf
# The nameserver is set to WSL's virtual DNS server (10.255.255.254)
# And it sets the default search domain for unqualified dowmain names:
# ☩ cat /etc/resolv.conf
# ...
# nameserver 10.255.255.254
# search SEMPERLAN hsd1.md.comcast.net

Create VM of Hyper-V

Install Windows Server 2019

WARNING:

Boot from ISO fails when installing Windows Server 2019 host on Hyper-V VM is using the Start button from Hyper-V menu. Rather, select Connect option, and press Start button only from inside that window. This method captures the keyboard, allowing OS install on "Press any key …".

Any other method bricks the VM.

Windows Server Install

  1. Connect
    • Options : Local Resources : More : Drives : Scratch (S:) or whatever to share drive(s) between host and VM.
    • Save conguration (checkbox)
  2. Start (button in the Connect window)
    • "GENERATION2" screen displays
    • Windows Setup window (eventually) appears
      • Install option: "Custom ..." is the only viable/allowable.
      • Without Guest services of Settings menu, disk size is hardcoded to 127 GB. No options. ISO has no drivers.
      • "Customize settings"
        • "User name: Administrator" (hardcoded)
        • Password: Admin!123
    • Shutdown
      • Merge at Hyper-V takes many minutes.
  3. Remove DVD at Hyper-V Settings.
  4. Connect/Start
    • Boots into Server Manager
      • Enter password
      • Local Server

Activate @ "Not Activated"

How to Activate via KMS using Microsoft Activation Scripts (MAS) method at a PowerShell terminal:

Install-WindowsFeature -Name VolumeActivation -IncludeManagementTools

# Run this statement and select the "KMS" option at its menu
irm https://get.activated.win | iex

Disable MS Online Account Sign-in Requirement

The default OS install has a "Welcome" login sequence requiring a Microsoft account. It blocks user from login until user signs up for MS account, enters creds of existing account, or selects a button that is effectively a "Repeatedly block and bother me again later".

This statement cures that:

Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "NoConnectedUser" -Value 3 -Type DWord

Configure Windows Server

  1. Ethernet adapter (ncpa.cpl)
    • Reference the network (InternalSwitchNAT1) installed earlier
      in a PowerShell session at the host (Windows 11) :
    • Ethernet adapter configuration:
      • Properties:
        • IPv6 (Uncheck)
        • IPv4 (check) : Properties:
          • "Use the following IP address" (check)
            • IP address: 192.168.11.2 (Self)
            • Subnet mask: 255.255.255.0 (24 bit)
            • Default gateway: 192.168.11.1 (NAT1 IPv4)
  2. Local Server
    • Computer name: dc1 (Domain Controller 1)
    • Ethernet: 192.168.11.2
      • Set static IP to that declared at subnet (InternalSwitchNAT1) configured earlier.
    • Time zone: Eastern
    • IE Enhanced Security Configuration: Off (All)
      • IE Enhanced Security Configuration
        • Off @ Admin
        • Off @ User
      • Time zone : Change : EST

Server Manager is the GUI and main portal of Windows Server 2019 Desktop. Windows Admin Center (WAC)" is a web UI providing remote access to the server. It is installed separately, either on the AD server or any remote host

Share Files between Host and VM

How to add network share(s):

Add Roles

  1. Open Server Manager.
  2. Click Manage and select Add Roles and Features.
  3. In the Add Roles and Features Wizard:
    • Click Next through the default screens :
      "Role-based or feature-based installation".
    • Select sever ...
    • At Server Roles screen, select whatever role(s)
    • Click Next and proceed with the installation.
  4. After installation, you'll be prompted to Complete $hellip; Configuration. Click on the notification to complete.

ADDS and DNS

Adding the Active Directory Domain Services (ADDS) role by Wizard adds the required DNS role, so need select only the ADDS role.

Reference video: "Installing Active Directory Domain Services in Windows Server 2022, along with DNS and DHCP"

Add ADDS Role (and DNS role too) :

Use the Wizard

Configure DNS

ADDS Wizard configures Forward Lookup Zone, but not Revers.

  1. Open DNS Manager (dnsmgmt.msc).
  2. Right-click the server and select Configure a DNS Server.
    • Select Create a Forward Lookup Zone.
    • Name the zone something relevant to your internal network (e.g., lime.lan).
    • Set the DNS server to use forwarders for external lookups (e.g., point it to 8.8.8.8 for Google DNS or your preferred DNS servers).

Create a Reverse Lookup Zone. The primary reverse lookup zone needs a pointer (PTR) record. The ADDS Wizard does not do this for us.

DNS @ ADDS : "_msdcs.<domain>"

The "_msdcs.<domain>" zone created during the Active Directory Domain Services (AD DS) integration with the DNS role (via Wizard) is a critical component of AD DS's dynamic DNS infrastructure. It enables seamless communication and operation of the AD environment by providing a dynamically updated repository of service location information.

ADD DHCP Role

Use the Wizard

Configure DHCP

@ PowerShell

XML scripts exported from Wizards allow for scripted configuration, either again here, or on other hosts:


$dns    = "DNS.DeploymentConfigTemplate.xml"
Windows-Feature -ConfigurationFilePath $dns

$dhcp   = "DHCP.DeploymentConfigTemplate.xml"
Windows-Feature -ConfigurationFilePath $dhcp

Separately, this is is a PowerShell method of scripting the DHCP server. It is cobbled together from references:

$NatNtwk    = "192.168.11"
$HostName   = "dc1.lime.lan"
$NatDNS     = "$NatNtwk.2"

# These first statements are performed by the Add-Role Wizard
Install-WindowsFeature DHCP -IncludeManagementTools
Add-DhcpServerSecurityGroup -ComputerName "$HostName" 
netsh dhcp add securitygroups
# If in ADDS
Add-DhcpServerInDC -DnsName "$hostName" -IPAddress "$NatDNS" -PassThru

# The following statements may be run *after* the above objects exist.

# Create/Define Scope
Add-DhcpServerv4Scope -Name "lime.lan" `
    -StartRange "$NatNtwk\.100" `
    -EndRange "$NatNtwk\.200" `
    -SubnetMask 255.255.255.0 `
    -LeaseDuration 8.00:00:00 `
    -State Active `
    -PassThru 

# Verify ADDS Authorized
Get-DhcpServerInDC
# IPAddress            DnsName
# ---------            -------
# 192.168.11.2         dc1.lime.lan

Restart-Service DHCPServer 

# Query available parameters
Get-DhcpServerv4<TAB>

@ GUI

  1. Open DHCP Manager (dhcpmgmt.msc).
  2. Right-click on IPv4 and select New Scope.
    • Scope Name: lime.lan.
    • IP Range: Set the IP range for the internal network. Align these with InternalSwitchNAT1 subnet (network-nat.ps1) parameters. See by running network-get.ps1
      • Start IP: 192.168.11.100
      • End IP: 192.168.11.200
      • Subnet Mask: 255.255.255.0
    • Default Gateway: Set this to the vEthernet interface on the host (192.168.11.1).
    • DNS: Set to this Windows Server IP, since it's the DNS server for our internal network. We set this to 192.168.11.2; static IP at Windows Server's Local Server menu, aligned with delcared subnet params. See PowerShell scripts.
      • Manually set it.
  3. Activate the Scope.
    • Right-click the new scope and click Activate.

To get a new IP address from DHCP:

Other tasks:

Active Directory Users and Computers (ADUC)

In AD, there's a built-in container named CN=Users, which is not technically an OU. Rather it's just a default container for user accounts and groups created during domain setup.

Built-in Containers

lime.lan
└── Users (CN=Users, DC=lime, DC=lan)
    β”œβ”€β”€ Administrator
    β”œβ”€β”€ Domain Admins
    β”œβ”€β”€ Domain Users
    β”œβ”€β”€ Guest
    └── Other default groups/accounts

OUs (Organizational Units) are those we create:

lime.lan
└── OU1 (OU=OU1,DC=lime,DC=lan)
    β”œβ”€β”€ IT
    β”œβ”€β”€ DevOps
    └── OU1-01

Naming conventions for AD Groups/Users of both Windows and Linux hosts:

Create admin User

@ Server Manager : Tools : Active Directory Users and Computers (ADUC)

1. Create Organizational Units (OU)

Create new OU, OU1, and two OUs (DevOps, IT) nested under OU1.

Resulting Structure:

2. Create User

An AD user has creds for authn/authz against ADDS of Windows Server 2019 (DC1) at any host joined into that AD domain AKA realm.

Resulting Creds:

3. Add User admin to Group Domain Admins

Click on user admin, right-click Properties > Member of, and Add… > Select Groups > Check Names > type "domain" and select "Domain Admins".

Delegation : "Delegate Control"

Delegation in AD DS refers to granting specific administrative permissions to a user or group for managing objects within an Organizational Unit (OU), without giving them full domain administrator privileges.

For example, if OU1 contains IT and DevOps sub-OUs, and you want IT admins to manage only the IT users and DevOps admins to manage only DevOps users, you can delegate specific administrative tasks to them without granting Domain Admin access.

List all

Get-ACL "AD:OU=OU1,DC=lime,DC=lan" | Format-List

Join Windows host (Win11) into ADDS Domain

@ Win11

Join RHEL host into ADDS Domain

Integrating RHEL systems directly with ADDS

You can join Red Hat Enterprise Linux (RHEL) hosts to an Active Directory (AD) domain by using the System Security Services Daemon (SSSD) or the Samba Winbind service to access AD resources.

Test on RHEL8 host a0.lime.lan with AD user admin member of AD group Domain Admins.

Prep

# Install pkgs
all='
    realmd 
    sssd 
    sssd-tools 
    samba-common 
    samba-common-tools 
    krb5-workstation 
    oddjob 
    oddjob-mkhomedir
'
sudo dnf install -y $all

# Add services / Open ports 
systemctl is-active firewalld &&
cat <<EOH |xargs -I{} sudo firewall-cmd --permanent --add-service={}
kerberos
dns
ldap
samba
EOH

systemctl is-active firewalld &&
    sudo firewall-cmd --reload

Also required for NFS to configure Kerberos security via AD:

dnf install -y krb5-workstation nfs-utils rpcbind

@ realm prior to join:

domain=lime.lan
#sudo hostnamectl set-hostname $(hostname).$domain

dc=dc1.$domain
realm discover $dc
lime.lan
  type: kerberos
  realm-name: LIME.LAN
  domain-name: lime.lan
  configured: no
  server-software: active-directory
  client-software: sssd
  required-package: oddjob
  required-package: oddjob-mkhomedir
  required-package: sssd
  required-package: adcli
  required-package: samba-common-tools

Join

Requires an AD DS Administrator credentials

u1@a0 [15:39:28] [1] [#0] ~
☩ sudo realm join --user=Administrator $dc
Password for Administrator@LIME.LAN:

u1@a0 [15:39:51] [1] [#0] ~
☩

Leave/Join (AD) Domain

Changes at AD to Service Principal Names (SPNs), which may be required, e.g., adding an NFS server that uses Kerberos, are best handled by leaving and then rejoining the domain. Else the new SPNs are not recognized at RHEL hosts, as revealed at "klist -k /etc/krb.keytab".

There are other methods of adding the new entries to *.keytab, using PowerShell (ktpass) at AD server (below), but they are tedious, so use only if necessary.

@ u1@a0

domain=lime.lan
dc=dc1.$domain
realm discover $dc
realm list
sudo realm leave $domain
sudo realm join --user=Administrator $dc
#> Password for u1:
#> Password for Administrator:
realm discover $dc
☩ realm list
lime.lan
  type: kerberos
  realm-name: LIME.LAN
  domain-name: lime.lan
  configured: kerberos-member
  server-software: active-directory
  client-software: sssd
  required-package: oddjob
  required-package: oddjob-mkhomedir
  required-package: sssd
  required-package: adcli
  required-package: samba-common-tools
  login-formats: %U@lime.lan
  login-policy: allow-realm-logins

Yet doing so resets all downstream configurations, e.g., /etc/sssd/sssd.conf

Recreate Kerberos *.keytab having all SPNs

After adding an NFS server at host a0, Kerberos (klist) did not recognize the NFS server, so reconfigured RHEL/AD :

  1. realm leave at all RHEL hosts that are NFS clients
  2. Run this PowerShell script at domain controller (Windows Server)
  3. realm join at all RHEL hosts that are NFS clients

@ AD server

$Realm = "LIME.LAN"
$Domain = "LIME"   # NetBIOS domain name
$Machine = "A0"
$KeytabPath = "C:\$Machine.keytab"

# Get all SPNs assigned to the machine
$SPNs = setspn -L "$Machine$" | Select-String -Pattern "^\s+\S+" | ForEach-Object { $_.ToString().Trim() }

# Check if SPNs were found
if (-not $SPNs) {
    Write-Host "No SPNs found for $Machine$ in domain $Realm"
    exit 1
}

# Generate keytab for the first SPN (without -append)
$FirstSPN = $SPNs[0]
$FirstPrincipal = "$FirstSPN@$Realm"
Write-Host "Adding $FirstPrincipal to keytab..."
ktpass -out "$KeytabPath" `
    -princ $FirstPrincipal `
    -mapuser "$Domain\$Machine$" `
    -crypto AES256-SHA1 `
    -ptype KRB5_NT_SRV_HST `
    -pass +rndpass

# Add remaining SPNs with -append
$SPNs | Select-Object -Skip 1 | ForEach-Object {
    $SPN = $_
    $Principal = "$SPN@$Realm"
    Write-Host "Appending $Principal to keytab..."
    
    ktpass -out "$KeytabPath" `
        -princ $Principal `
        -mapuser "$Domain\$Machine$" `
        -crypto AES256-SHA1 `
        -ptype KRB5_NT_SRV_HST `
        -pass +rndpass `
        -append
}

Write-Host "Keytab generated at $KeytabPath"

Verify Join

Enable Authn via SSSD

Enable and Start SSSD:
Ensure SSSD is running to handle authentication:

sudo systemctl enable --now sssd

By default, only admins can log in.

Allow all AD users and groups of domain:

sudo realm permit --all

Optionally, configure Home Directory Creation:
If you want AD users to get home directories automatically on login:

sudo systemctl enable --now oddjobd

oddjob

Allows for other-than-default configuration AD users:

Configure

@ /etc/sssd/sssd.conf

...
[domain/lime.lan]
...
fallback_homedir = /home/%u
use_fully_qualified_names = False
...

Verify

☩ ansibash sudo cat /etc/sssd/sssd.conf 2>/dev/null \
    |grep -e == -e use_fully_qualified_names -e fallback_
=== u2@a0
fallback_homedir = /home/%u
use_fully_qualified_names = False
=== u2@a1
fallback_homedir = /home/%u
use_fully_qualified_names = False
=== u2@a2
fallback_homedir = /home/%u
use_fully_qualified_names = False
=== u2@a3
fallback_homedir = /home/%u
use_fully_qualified_names = False
☩ ssh u2@a1
Last login: Fri Mar 14 08:05:44 2025 from 172.24.217.171
@ /home/u2/.bash_functions
@ /home/u2/.bashrc_git
@ /home/u2/.bashrc_k8s
@ /home/u2/.bashrc_vim
@ /home/u2/.bashrc
@ /home/u2/.bash_profile
u2@a1 [08:30:15] [1] [#0] ~

Create AD User

Or add the existing RHEL user to AD (not advised). Either way, use Windows Server tool AD UAC (AD Users and Computers).

AD UAC

Then …

Allow SSH by "ssh $user@$host" instead of requiring "ssh $user@$domain@$host" for AD DC users:

@ /etc/sssd/sssd.conf

...
[domain/lime.lan]
...
fallback_homedir = /home/%u
use_fully_qualified_names = False
...

Verify LDAP synchs Users/Groups with AD

☩ ssh a0
...
u1@a0 [10:33:32] [1] [#0] ~
☩ groups
u1 wheel domain users linux-users linux-sudoers

If auth fails, check service logs

sudo journalctl -u sssd -f
sudo tail -f /var/log/secure

Verify Kerberos

@ u1@a0

u1@a0 [08:05:27] [1] [#0] ~
☩ kinit admin@LIME.LAN
Password for admin@LIME.LAN:

u1@a0 [08:06:40] [1] [#0] ~
☩ klist
Ticket cache: KCM:1000
Default principal: admin@LIME.LAN

Valid starting     Expires            Service principal
03/02/25 08:06:36  03/02/25 18:06:36  krbtgt/LIME.LAN@LIME.LAN
        renew until 03/09/25 09:06:14
☩ kinit u1@LIME.LAN
Password for u1@LIME.LAN:

☩ klist
Ticket cache: KCM:1000:28381
Default principal: u1@LIME.LAN

Valid starting     Expires            Service principal
03/02/25 08:11:22  03/02/25 18:11:22  krbtgt/LIME.LAN@LIME.LAN
        renew until 03/09/25 09:11:17

☩ klist -l
Principal name                 Cache name
--------------                 ----------
u1@LIME.LAN                    KCM:1000:28381
admin@LIME.LAN                 KCM:1000
☩ utc;utco;utcz;gmt
2025-03-02T08:17:57 [EST]
2025-03-02T08:17:57-0500
2025-03-02T13:17:57Z
2025-03-02T13:17:57Z

SSH Auth using PKI

Now setup PKI for key-based authentication

# Generate key pair
ssh-keygen -t ecdsa -C admin@lime.lan -f ~/.ssh/dc1_admin
# Push key to target host
ssh-copy-id -i ~/.ssh/dc1_admin admin@a0.lime.lan
# Configure
vi ~/.ssh/config
Host admin
    Hostname 192.168.11.103
    User admin
    IdentityFile ~/.ssh/dc1_admin

Test/Verify

Ubuntu [18:34:30] [1] [#0] ~
☩ ssh admin
...
[admin@a0 ~]$ 

Fix SELinux objects

# View current
☩ id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

# Fix
☩ sudo semanage login -a -s sysadm_u 'admin@lime.lan'

Verify after logout/in

# View current (fixed)
☩ id -Z
staff_u:staff_r:staff_t:s0-s0:c0.c1023

Login Lockout v. Sudo (Non-)Lockout

@ /etc/pam.d/sudo

#%PAM-1.0
auth       include      system-auth
account    include      system-auth
password   include      system-auth
session    include      system-auth

@ /etc/pam.d/system-auth

auth     required  pam_faillock.so preauth silent audit deny=3 unlock_time=600
auth     required  pam_faillock.so authfail audit deny=3 unlock_time=600

AD Certificate Services (AD CS)

See snapshots of the GUI

  1. Open Server Manager.
  2. Click Manage and select Add Roles and Features.
  3. In the Add Roles and Features Wizard:
    • Active Directory Certificate Services : Roles (checkboxes):
      • Certificate Authority
      • Certificate Authority Web Enrollment
        • CA name (checkbox)
        • Target CA: dc1.lime.lan\lime-DC1-CA
      • Certificate Enrollment Web Service (CES)
        • Type: Client certificate authentication
      • Server Certificate
        • Specify a Server Authentication Certificate
          • Choose an existing certificate for SSL encryption
            • dc1.lime.lan (One year expiry)
      • Add the designated ADCS (web service) User (Administrator) to Group IIS_IUSRS
  4. Save the Root CA certificate
    • Open Certification Authority GUI (crtsrv)
      • Right-click on the certificate (dc1.lime.lan)
      • Open
        • Details : "Copy to File" (button) : Next :
          • "Base-64 encoded X.509 (.CER)" (checkbox) : Next ... Save file.
            • E.g., ca-root-dc1.lime.lan.cer
  5. Import the Root CA certficate into host(s) and/or client(s) trust store, so that the IIS-served TLS certificate of Certificate Services web page will validate.
    • @ Windows 11, open certlm.msc and import to "Trusted Root Certification Authorities" folder.

Root CA certificate

Save the root CA certificate using PowerShell

$caName     = "lime-DC1-CA"
$filePath   = "ca-root-dc1.lime.lan.cer"
$cert       = Get-ADObject -LDAPFilter "(cn=$caName)" -SearchBase "CN=Certification Authorities,CN=Public Key Services,CN=Services,$((Get-ADRootDSE).configurationNamingContext)" -Properties "cACertificate"
[System.IO.File]::WriteAllBytes($filePath, $cert."cACertificate"[0])

Obtain a TLS certificate via CSR

@ https://dc1.lime.lan/certsrv/

  1. Authenticate (against ADDS) by Username/Password
    • Administrator
      • User must be member of Group IIS_IUSRS
  2. Request a certificate
  3. Submit a Certificate Request or Renewal Request
    • Saved Request (HTML FORM : text input box):
      • Paste the CSR of PKCS#10 (New) / PKCS#7 (Renewal) format
    • Certificate Template:
      • Web Server (dropdown)
    • Additional Attributes (text input box):
      • Leave blank
  4. Submit (button)

CSR

AD CS requires CSR in PKCS#10/#7 (New/Renew) format. OpenSSL generates the request (*.csr) in that format by default.

Regarding Windows Server 2019 and prior, AD CS offers only RSA-based certificates unless that role is configured otherwise, which is a non-trivial task that nearly no organization performs.

root=lime.lan
cn=kube.$root
TLS_ST=MD
TLS_L=AAC
TLS_O=DisselTree
TLS_OU=ops
## Create the configuration file (CNF) : See man config
## See: man openssl-req : CONFIGURATION FILE FORMAT section
## https://www.openssl.org/docs/man1.0.2/man1/openssl-req.html
cat <<-EOH |tee $cn.cnf
[ req ]
prompt              = no        # Disable interactive prompts.
default_bits        = 2048      # Key size for RSA keys. Ignored for Ed25519.
default_md          = sha256    # Hashing algorithm.
distinguished_name  = req_distinguished_name 
req_extensions      = v3_req    # Extensions to include in the request.
[ req_distinguished_name ] 
CN              = $cn                   # Common Name
C               = ${TLS_C:-US}          # Country
ST              = ${TLS_ST:-NY}         # State or Province
L               = ${TLS_L:-Gotham}      # Locality name
O               = ${TLS_O:-Foobar Inc}  # Organization name
OU              = ${TLS_OU:-GitOps}     # Organizational Unit name
emailAddress    = admin@$root 
[ v3_req ]
subjectAltName      = @alt_names
keyUsage            = digitalSignature
extendedKeyUsage    = serverAuth
[ alt_names ]
DNS.1 = $cn
DNS.2 = *.$cn   # Wildcard. CA must allow, else declare each subdomain.
EOH

# RSA
openssl req -new -noenc -config $cn.cnf -extensions v3_req -newkey rsa:2048 -keyout $cn.key -out $cn.csr 
# ED25519
openssl req -new -noenc -config $cn.cnf -extensions v3_req -newkey ed25519 -keyout $cn.key -out $cn.csr
# ECDSA (NIST P-256 curve)
openssl req -new -noenc -config $cn.cnf -extensions v3_req -newkey ec:<(openssl ecparam -name prime256v1 -genkey) -keyout $cn.key -out $cn.csr

Request the TLS certificate using the HTML FORM of Windows AD CS web server at https://dc1.lime.lan/certsrv/ . The response has both full-chain and end-entity certificates encoded in PKCS#7 and having DER (binary) format.