Skip to content

Instantly share code, notes, and snippets.

@fabricesemti80
Last active March 15, 2024 13:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fabricesemti80/a776b85767df3453f253a5a773437214 to your computer and use it in GitHub Desktop.
Save fabricesemti80/a776b85767df3453f253a5a773437214 to your computer and use it in GitHub Desktop.
AD DSC
<#
.SYNOPSIS
Script to setup Active Directory
.DESCRIPTION
This script is intended to set up a functioning AD forest with 1x root domain controller and one or more additional domain controllers
The script uses - for now - a self-signed cert to encryot the password, the thumbprint of this should be supplied on the machine.
.NOTES
(Use the `Create-SelfSignedCert` function in the module to generate a cert if this is needed)
.LINK
https://codeandkeep.com/Dsc-Install-AD-Forest/
https://codeandkeep.com/Dsc-Encrypting-Credentials/
https://github.com/dsccommunity/ActiveDirectoryDsc/wiki/ADDomain
https://github.com/dsccommunity/ActiveDirectoryDsc/wiki/WaitForADDomain
https://github.com/dsccommunity/ActiveDirectoryDsc/wiki/ADOrganizationalUnit
https://github.com/dsccommunity/NetworkingDsc
https://github.com/dsccommunity/NetworkingDsc/wiki
https://github.com/dsccommunity/NetworkingDsc/wiki/IPAddress
https://github.com/dsccommunity/NetworkingDsc/wiki/DnsServerAddress
.EXAMPLE
# if (-not $cred){
# $cred = (Get-Credential -Message "Enter new domain's credential")
# }
# # install domain - using existing cert
# & "\\tsclient\C\PROJECTS\dsc\SS.INFRA.DSC\NewAD\New-ADDSC.ps1"`
# -domainCred $cred `
# -certThumbprint "67A454BF08E151ACB012108D2EC4094258A4F494"
#>
[CmdletBinding()]
param (
# Store the current directory path
[Parameter(Mandatory = $false)] [string]
$CurrentPath = (Split-Path -Parent $PSCommandPath)
,
# Required modules
[Parameter(Mandatory = $false)]
$modules = @{
'ActiveDirectoryDsc' = '6.4.0'
'NetworkingDsc' = '9.0.0'
'ComputerManagementDsc' = '9.0.0'
}
,
# If module installation is also needed
[Parameter(Mandatory = $false)] [switch]
$preReq
,
# DSC encryption certificate
[Parameter(Mandatory )] [string]
$certThumbprint
,
# Domain admin credential (the local admin credential will be set to this one!)
#! user user@domain.com for the user
[Parameter(Mandatory = $false)] [pscredential]
$domainCred
,
# The certificate file generated by 'Create-SelfSignedCert'
[Parameter(Mandatory = $false)] [string]
$certFilePath = 'C:\dsc\cert\DscPubKey.cer'
)
begin
{
#^ load functions
Get-ChildItem $CurrentPath '*.psm1' | ForEach-Object { Import-Module $_.FullName -Force -WarningAction SilentlyContinue }
if ($preReq.IsPresent)
{
#^ Install modules
Install-Modules -modules $modules
}
$thisComputer = $env:COMPUTERNAME
}
process
{
# Self signed certificate in the local computer certificate store
$cert = Get-Item -Path "Cert:\LocalMachine\My\$certThumbprint"
#^ The certificate has been exported to this path
#^ Customize this with your details #TODO: move this to a data file
$config = @{
AllNodes = @(
@{
NodeName = '*'
DomainName = 'yourdomain.com' #FIXME: your domain FQDN
DomainNetbiosName = 'YOURDOMAIN' #FIXME: your domain NetBIOS
Thumbprint = $cert.Thumbprint
CertificateFile = $certFilePath
NTDSPath = 'C:\Windows\NTDS'
LogPath = 'C:\Windows\Logs'
SysvolPath = 'C:\Windows\SYSVOL'
TimeZone = 'GMT Standard Time'
Locale = "en-GB"
}
@{
NodeName = 'DC1CORPDC02P'
IPV4Address = '10.23.29.10/24'
Role = 'RootDomainController'
DNSServers = '127.0.0.1', '1.1.1.1' #NOTE: Cloudflare IP is optional
# # domain settings -->
ComplexityEnabled = $true
MinPasswordLength = 10
FirstSite = 'London-OY' #FIXME: your first site's name
AdditionalSites = @('London-SH', 'London-HQ', 'Wigan-BR') #FIXME: additional sites
SitelinkPrimaryMembers = @('London-OY', 'London-SH', 'London-HQ') #FIXME: optional
SitelinkSecondaryMembers = @('London-SH', 'London-OY', 'Wigan-BR') #FIXME: optional
NTPServer = '10.13.36.4' #FIXME: prefered NTP server
FailOverNTPServers = ("10.13.36.7",'time.windows.com') #FIXME: alternative NTP server
}
@{
NodeName = 'DC1CORPDC02P' #FIXME: additional DC
IPV4Address = '10.23.29.11/24' #FIXME: additional DC IP
Role = 'MemberDomainController'
DNSServers = '10.23.29.10', '1.1.1.1' #FIXME: first DC + any optional DNS servers
Site = 'London-OY' #FIXME: any valid site created on the first DC
IsGlobalCatalog = $true
NTPServer = '10.23.29.10' #FIXME: prefered NTP server
FailOverNTPServers = ('time.windows.com') #FIXME: alternative NTP server
}
)
}
if ($thisComputer -match 'DC1CORPDC01P')
{
#^ Generate configuration MOF files for the first DC
DomainFirstDC -ConfigurationData $config `
-OutputPath C:\dsc\AD `
-DomainCredential $domainCred `
-SafemodePassword $domainCred `
-NetAdapterName 'Ethernet0'
}
else
{
#^ Generate configuration MOF files for the additional DCs
DomainAdditionalDCs -ConfigurationData $config `
-OutputPath C:\dsc\AD `
-DomainCredential $domainCred `
-SafemodePassword $domainCred `
-NetAdapterName 'Ethernet0'
}
#^ Configure the LCM
Set-DscLocalConfigurationManager -Path C:\dsc\AD -Force -Verbose
#^ Apply the Dsc Configuration
Start-DscConfiguration -Path C:\dsc\AD -Force -Wait -Verbose
}
end
{
#^ Verify status of the configuration
$state = (Test-DscConfiguration -Path C:\dsc\AD)
Write-Host $($state.ResourcesNotInDesiredState | Format-Table -AutoSize -Wrap | Out-String) -ForegroundColor Red
Write-Host $($state.ResourcesInDesiredState | Format-Table -AutoSize -Wrap | Out-String) -ForegroundColor Green
Write-Warning 'Please review resources displayed in RED!'
}
function Install-Modules
{
<#
.PARAMETER modules
$modules = @{
'Module1' = '1.0'
'Module2' = '2.3'
'Module3' = '3.5'
}
#>
param (
[Parameter(Mandatory)]
$modules
)
if ( -not(Get-PSRepository -ErrorAction SilentlyContinue | Where-Object { $_.Name -like '*psgallery*' }) )
{
Write-Host -fore Magenta '>> Fixing PsGallery, please wait... <<'
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
Register-PSRepository -Default -Verbose
}
foreach ($moduleName in $modules.Keys)
{
$desiredVersion = $modules[$moduleName]
$installedModule = Get-Module -Name $moduleName -ListAvailable -ErrorAction SilentlyContinue | Where-Object { $_.Version -eq $desiredVersion }
if ($null -eq $installedModule)
{
Write-Host "$moduleName version $desiredVersion is not installed on $($Env:COMPUTERNAME). Installing..."
Install-Module -Name $moduleName -RequiredVersion $desiredVersion -Force -Confirm:$false
Write-Host "$moduleName version $desiredVersion has been installed on $($Env:COMPUTERNAME)."
}
else
{
Write-Host "$moduleName version $desiredVersion is already installed on $($Env:COMPUTERNAME)." -ForegroundColor Green
}
}
}
function Create-SelfSignedCert
{
param (
$certFolder = 'C:\dsc\cert'
,
$certStore = 'Cert:\LocalMachine\My'
,
$validYears = 2
)
$pubCertPath = Join-Path -Path $certFolder -ChildPath DscPubKey.cer
$expiryDate = (Get-Date).AddYears($validYears)
# You may want to delete this file after completing
$privateKeyPath = Join-Path -Path $ENV:TEMP -ChildPath DscPrivKey.pfx
$privateKeyPass = Read-Host -AsSecureString -Prompt 'Private Key Password'
if (!(Test-Path -Path $certFolder))
{
New-Item -Path $certFolder -Type Directory | Out-Null
}
$cert = New-SelfSignedCertificate -Type DocumentEncryptionCertLegacyCsp `
-DnsName 'DscEncryption' `
-HashAlgorithm SHA512 `
-NotAfter $expiryDate `
-KeyLength 4096 `
-CertStoreLocation $certStore
$cert | Export-PfxCertificate -FilePath $privateKeyPath `
-Password $privateKeyPass `
-Force
$cert | Export-Certificate -FilePath $pubCertPath
Import-Certificate -FilePath $pubCertPath `
-CertStoreLocation $certStore
Import-PfxCertificate -FilePath $privateKeyPath `
-CertStoreLocation $certStore `
-Password $privateKeyPass | Out-Null
}
configuration DomainFirstDC {
Param(
[Parameter(Position = 0)]
[String]$DomainMode = 'WinThreshold',
[Parameter(Position = 1)]
[String]$ForestMode = 'WinThreshold',
[Parameter(Position = 2, Mandatory = $true)]
[PSCredential]$DomainCredential,
[Parameter(Position = 3, Mandatory = $true)]
[PSCredential]$SafemodePassword,
[Parameter(Position = 4)]
[String]$NetAdapterName = 'Ethernet0',
[array]$featureNames = @(
'RSAT-AD-PowerShell',
'RSAT-ADDS',
'RSAT-AD-AdminCenter',
'RSAT-ADDS-Tools',
'RSAT-ADLDS'
)
)
Import-DscResource -ModuleName PSDesiredStateConfiguration
Import-DscResource -ModuleName ActiveDirectoryDsc
Import-DscResource -ModuleName NetworkingDsc
Import-DscResource -ModuleName ComputerManagementDsc
#* -------------------------------------------------------------------------------- FIRST DC ONLY
Node $AllNodes.Where{ $_.Role -eq 'RootDomainController' }.NodeName {
# * LOCALE and TIME
SystemLocale SystemLocaleExample
{
IsSingleInstance = 'Yes'
SystemLocale = $Node.Locale
}
Timezone TimeZoneConfiguration {
TimeZone = $NODE.TimeZone
IsSingleInstance = 'Yes'
}
$ntpServerValue1 = [string]($NODE.NTPServer | ForEach-Object { $_, (',0x{0:x}' -f '8') }) -replace ' ,',","
$ntpServerValue2 = [string]($NODE.FailOverNTPServers | %{$_, (",0x{0:x}" -f "a")}) -replace " ,",","
$ntpServerValue = $ntpServerValue1, $ntpServerValue2 -join " "
$ntpservers = @()
$ntpservers += $NODE.NTPServer
$ntpservers += $NODE.FailOverNTPServers | %{$_}
$timeRootPath = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\W32Time"
$NTPListPath = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\DateTime\Servers"
Service w32time
{
Name = "W32Time"
State = "Running"
StartupType = "Automatic"
}
Registry NTPType
{
Key = "$timeRootPath\Parameters"
ValueName = "Type"
ValueType = "String"
ValueData = "NTP"
Ensure = "Present"
}
Registry NTPServer
{
Key = "$timeRootPath\Parameters"
ValueName = "NTPServer"
ValueType = "String"
ValueData = $ntpServerValue
Ensure = "Present"
}
# * RESET LOCAL ADMIN PASSWORD
# this will be used as the default domain admin's password
User DomainAdmin
{
Ensure = 'Present'
UserName = 'Administrator'
Password = $DomainCredential
}
# * NETWORKING
NetAdapterName InterfaceRename
{
NewName = $NetAdapterName
}
IPAddress StaticIP
{
InterfaceAlias = $NetAdapterName
AddressFamily = 'IPv4'
IPAddress = $NODE.IPv4Address
DependsOn = '[NetAdapterName]InterfaceRename'
}
DnsServerAddress SetDnsServer
{
InterfaceAlias = $NetAdapterName
AddressFamily = 'IPv4'
Address = $NODE.DNSServers
DependsOn = '[NetAdapterName]InterfaceRename'
}
FirewallProfile DomainFirewallOff
{
Name = 'Domain'
Enabled = 'False'
}
FirewallProfile PublicFirewallOff
{
Name = 'Public'
Enabled = 'False'
}
FirewallProfile PrivateFirewallOff
{
Name = 'Private'
Enabled = 'False'
}
# * INSTALL FEATURES
WindowsFeature ADDSFeatureInstall
{
Ensure = 'Present'
Name = 'AD-Domain-Services'
DependsOn = '[NetAdapterName]InterfaceRename', '[User]DomainAdmin'
}
## additional Windows Features [non-essential but good to have!]
foreach ($featureName in $featureNames)
{
WindowsFeature $featureName
{
Ensure = 'Present'
Name = $featureName
DependsOn = '[WindowsFeature]ADDSFeatureInstall'
}
}
$DomainContainer = "DC=$($NODE.DomainName.Split('.') -join ',DC=')"
ADDomain 'ThisDomain'
{
DomainName = $NODE.DomainName
DomainNetbiosName = $NODE.DomainNetbiosName
ForestMode = $ForestMode
DomainMode = $DomainMode
Credential = $DomainCredential
SafemodeAdministratorPassword = $SafemodePassword
DatabasePath = $NODE.NTDSPath
LogPath = $NODE.LogPath
SysvolPath = $NODE.SysvolPath
DependsOn = '[WindowsFeature]ADDSFeatureInstall'
}
WaitForADDomain 'WaitForDomainInstall'
{
DomainName = $NODE.DomainName
Credential = $DomainCredential
RestartCount = 2
# RebootRetryCount = 2
# RetryCount = 10
# RetryIntervalSec = 60
DependsOn = '[ADDomain]ThisDomain'
}
# * STARTER ORGANIZATIONAL UNITS [OPTIONAL]
ADOrganizationalUnit 'CreateAccountsOU'
{
Name = 'Accounts'
Path = $DomainContainer
Ensure = 'Present'
Credential = $DomainCredential
DependsOn = '[WaitForADDomain]WaitForDomainInstall'
}
ADOrganizationalUnit 'AdminOU'
{
Name = 'Admin'
Path = "OU=Accounts,$DomainContainer"
Ensure = 'Present'
Credential = $DomainCredential
DependsOn = '[ADOrganizationalUnit]CreateAccountsOU'
}
ADOrganizationalUnit 'BusinessOU'
{
Name = 'Business'
Path = "OU=Accounts,$DomainContainer"
Ensure = 'Present'
Credential = $DomainCredential
DependsOn = '[ADOrganizationalUnit]CreateAccountsOU'
}
ADOrganizationalUnit 'ServiceOU'
{
Name = 'Service'
Path = "OU=Accounts,$DomainContainer"
Ensure = 'Present'
Credential = $DomainCredential
DependsOn = '[ADOrganizationalUnit]CreateAccountsOU'
}
# * PASSWORD POLICY
ADDomainDefaultPasswordPolicy 'DefaultPasswordPolicy'
{
DomainName = $NODE.DomainName
ComplexityEnabled = $NODE.ComplexityEnabled
MinPasswordLength = $NODE.MinPasswordLength
DependsOn = '[ADDomain]ThisDomain', '[WindowsFeature]ADDSFeatureInstall'
}
# * ENABLE RECYCLEBIN
ADOptionalFeature RecycleBin
{
FeatureName = 'Recycle Bin Feature'
EnterpriseAdministratorCredential = $DomainCredential
ForestFQDN = $NODE.DomainName
DependsOn = '[ADDomain]ThisDomain', '[WindowsFeature]ADDSFeatureInstall'
}
# * SITES AND SUBNETS
# # sites
ADReplicationSite 'FirstSite'
{
Ensure = 'Present'
Name = $NODE.FirstSite
RenameDefaultFirstSiteName = $true
DependsOn = '[ADDomain]ThisDomain', '[WindowsFeature]ADDSFeatureInstall'
}
foreach ($site in $NODE.AdditionalSites)
{
ADReplicationSite $site
{
Ensure = 'Present'
Name = $site
DependsOn = '[ADDomain]ThisDomain', '[WindowsFeature]ADDSFeatureInstall'
}
}
# # site replications
ADReplicationSiteLink 'PrimarySitelink'
{
Name = 'PrimarySitelink'
SitesIncluded = $NODE.SitelinkPrimaryMembers
Cost = 100
ReplicationFrequencyInMinutes = 15
Ensure = 'Present'
}
ADReplicationSiteLink 'SecondarySitelink'
{
Name = 'SecondarySitelink'
SitesIncluded = $NODE.SitelinkSecondaryMembers
Cost = 159
ReplicationFrequencyInMinutes = 20
Ensure = 'Present'
}
# # site subnets
ADReplicationSubnet 'London-OY_10.23.29.0/24'
{
Name = '10.23.29.0/24'
Site = 'London-OY'
Location = 'Olivers Yard DC'
Description = 'Infrastructure Management Subnet'
}
ADReplicationSubnet 'London-SH_10.24.29.0/24'
{
Name = '10.24.29.0/24'
Site = 'London-SH'
Location = 'Shoreditch DC'
Description = 'Infrastructure Management Subnet'
}
# * LCM CONFIGURATION
LocalConfigurationManager
{
CertificateId = $NODE.Thumbprint
RebootNodeIfNeeded = $true
}
}
}
configuration DomainAdditionalDCs {
Param(
[Parameter(Position = 0)]
[String]$DomainMode = 'WinThreshold',
[Parameter(Position = 1)]
[String]$ForestMode = 'WinThreshold',
[Parameter(Position = 2, Mandatory = $true)]
[PSCredential]$DomainCredential,
[Parameter(Position = 3, Mandatory = $true)]
[PSCredential]$SafemodePassword,
[Parameter(Position = 4)]
[String]$NetAdapterName = 'Ethernet0',
[array]$featureNames = @(
'RSAT-AD-PowerShell',
'RSAT-ADDS',
'RSAT-AD-AdminCenter',
'RSAT-ADDS-Tools',
'RSAT-ADLDS'
)
)
Import-DscResource -ModuleName PSDesiredStateConfiguration
Import-DscResource -ModuleName ActiveDirectoryDsc
Import-DscResource -ModuleName NetworkingDsc
Import-DscResource -ModuleName ComputerManagementDsc
#* -------------------------------------------------------------------------------- ADDITIONAL DCS ONLY
Node $AllNodes.Where{ $_.Role -eq 'MemberDomainController' }.NodeName {
# * LOCALE and TIME
SystemLocale SystemLocaleExample
{
IsSingleInstance = 'Yes'
SystemLocale = $Node.Locale
}
Timezone TimeZoneConfiguration {
TimeZone = $NODE.TimeZone
IsSingleInstance = 'Yes'
}
$ntpServerValue1 = [string]($NODE.NTPServer | ForEach-Object { $_, (',0x{0:x}' -f '8') }) -replace ' ,',","
$ntpServerValue2 = [string]($NODE.FailOverNTPServers | %{$_, (",0x{0:x}" -f "a")}) -replace " ,",","
$ntpServerValue = $ntpServerValue1, $ntpServerValue2 -join " "
$ntpservers = @()
$ntpservers += $NODE.NTPServer
$ntpservers += $NODE.FailOverNTPServers | %{$_}
$timeRootPath = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\W32Time"
$NTPListPath = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\DateTime\Servers"
Service w32time
{
Name = "W32Time"
State = "Running"
StartupType = "Automatic"
}
Registry NTPType
{
Key = "$timeRootPath\Parameters"
ValueName = "Type"
ValueType = "String"
ValueData = "NTP"
Ensure = "Present"
}
Registry NTPServer
{
Key = "$timeRootPath\Parameters"
ValueName = "NTPServer"
ValueType = "String"
ValueData = $ntpServerValue
Ensure = "Present"
}
# * NETWORKING
NetAdapterName InterfaceRename
{
NewName = $NetAdapterName
}
IPAddress StaticIP
{
InterfaceAlias = $NetAdapterName
AddressFamily = 'IPv4'
IPAddress = $NODE.IPv4Address
DependsOn = '[NetAdapterName]InterfaceRename'
}
DnsServerAddress SetDnsServer
{
InterfaceAlias = $NetAdapterName
AddressFamily = 'IPv4'
Address = $NODE.DNSServers
DependsOn = '[NetAdapterName]InterfaceRename'
}
FirewallProfile DomainFirewallOff
{
Name = 'Domain'
Enabled = 'False'
}
FirewallProfile PublicFirewallOff
{
Name = 'Public'
Enabled = 'False'
}
FirewallProfile PrivateFirewallOff
{
Name = 'Private'
Enabled = 'False'
}
# * INSTALL FEATURES
WindowsFeature ADDSFeatureInstall
{
Ensure = 'Present'
Name = 'AD-Domain-Services'
DependsOn = '[NetAdapterName]InterfaceRename'
}
## additional Windows Features [non-essential but good to have!]
foreach ($featureName in $featureNames)
{
WindowsFeature $featureName
{
Ensure = 'Present'
Name = $featureName
DependsOn = '[WindowsFeature]ADDSFeatureInstall'
}
}
# * JOIN DOMAIN AND PROMOTE TO DC
WaitForADDomain 'WaitForDomainInstall'
{
DomainName = $NODE.DomainName
Credential = $DomainCredential
RestartCount = 2
DependsOn = '[DnsServerAddress]SetDnsServer', '[WindowsFeature]ADDSFeatureInstall'
}
ADDomainController 'DomainControllerMinimal'
{
DomainName = $NODE.DomainName
Credential = $DomainCredential
SafeModeAdministratorPassword = $SafeModePassword
DatabasePath = $NODE.NTDSPath
LogPath = $NODE.LogPath
SysvolPath = $NODE.SysvolPath
IsGlobalCatalog = $NODE.IsGlobalCatalog
DependsOn = '[WaitForADDomain]WaitForDomainInstall'
}
# *LCM CONFIGURATION
LocalConfigurationManager
{
CertificateId = $NODE.Thumbprint
RebootNodeIfNeeded = $true
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment