ADDSConfigurations.psm1
$ErrorActionPreference = "Stop" $PSDefaultParameterValues = @{ 'New-ADDSForest:DomainMode' = 'WinThreshold' 'New-ADDSForest:ForestMode' = 'WinThreshold' 'New-ADDSForest:DatabasePath' = "$env:SystemDrive\Windows\" 'New-ADDSForest:LogPath' = "$env:SystemDrive\Windows\NTDS\" 'New-ADDSForest:SysvolPath' = "$env:SystemDrive\Windows\" 'New-ADDSForest:Force' = $true } $RegisteredSecretVault = $null $AzureConnection = $null # Import the required modules function Install-RequiredModule { param( [string[]]$Name = @( 'Microsoft.PowerShell.SecretManagement', 'az.keyvault') ) $Name | ForEach-Object { if(-not (Get-Module -Name $_ -ListAvailable)){ try { Set-PSResourceRepository -Name PSGallery -Trusted Install-PSResource -Name $_ -Repository PSGallery -Scope AllUsers -Confirm:$false Write-Output "Module $_ installed successfully" } catch { Write-Error -Message "Failed to install module $_. Please see the error message below.:$_" } } else { Write-Output "Module $_ is already installed" } } } function Install-RequiredADModule { [string]$Name = 'AD-Domain-Services' if (-not (Get-WindowsFeature -Name $Name | Where-Object { $_.Installed -eq $true })) { try { install-WindowsFeature -Name $Name -IncludeManagementTools } catch { throw "Failed to install the required module $ModuleName. Please see the error message below.:$_" } } else { Write-Output "Module $ModuleName is already installed" } } # Add keys to a hashtable function Add-keys{ param($hash, $keys) $keys.GetEnumerator() | ForEach-Object { $hash.Add($_.Key, $_.Value) } } # Create a new environment path function New-EnvPath { param( [string]$Path, [string]$ChildPath ) return Join-Path @PSBoundParameters } function Test-Paths { param( [string[]]$Paths ) $paths | ForEach-Object { if (-not (Test-Path -Path $_)) { throw "Path $_ does not exist" } } } # function to connect to Azure function Connect-ToAzure { if($null -eq $AzureConnection){ try { # Check if there is an existing connection $existingConnection = Get-AzContext -ErrorAction SilentlyContinue if($existingConnection){ Write-Output "Already connected to Azure" return } # Connect to Azure if($null -eq $AzureConnection){ Connect-AzAccount -UseDeviceAuthentication $timeout = New-TimeSpan -Minutes 90 $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() while ($stopwatch.Elapsed -lt $timeout){ $Context = (Get-AzContext -ErrorAction SilentlyContinue).Account $AzureConnection = $Context if($AzureConnection){ write-output "Connected to Azure" return } } } } catch { Write-Error "Failed to connect to Azure. Please see the error message below.:$_" } } } # function to get the vault in Azure function Get-Vault { param( [string]$keyVaultName, [string]$ResourceGroupName ) Get-AzKeyVault @PSBoundParameters } # function to add the registered secret vault using SecretManagement module function Add-RegisteredSecretVault { param( [string]$Name = (Get-Vault).VaultName, [string]$ModuleName = "az.keyvault", [hashtable]$VaultParameters = @{ AZKVaultName = $Name SubscriptionId = (Get-AzContext).Subscription.Id } ) # Check if the vault is already registered $existingVault = Get-SecretVault -Name $Name -ErrorAction SilentlyContinue if($existingVault){ Write-Output "Secret vault $Name is already registered" return } if($null -eq $RegisteredSecretVault){ try { Register-SecretVault -Name $Name -ModuleName $ModuleName -VaultParameters $VaultParameters -Confirm:$false $Context = (Get-SecretVault -Name $Name).Name $RegisteredSecretVault = $Context if($RegisteredSecretVault){ write-output "Secret vault $Name registered successfully" return } } catch { Write-Error "Failed to register the secret vault. Please see the error message below.:$_" } } } # function to remove the registered secret vault function Remove-RegisteredSecretVault { param( [string]$Name = (Get-Vault).VaultName ) if(!$RegisteredSecretVault){ try { Unregister-SecretVault -Name $Name -Confirm:$false write-output "Secret vault $Name unregistered successfully" } catch { Write-Error "Failed to unregister the secret vault. Please see the error message below.:$_" } } } # function to disconnect from Azure function Disconnect-FromAzure { try { if(!$AzureConnection){ Disconnect-AzAccount -Confirm:$false $AzureConnection = $null write-output "Disconnected from Azure" } } catch { Write-Error "Failed to disconnect from Azure. Please see the error message below.:$_" } } # function to create a new AD Forest function New-ADDSForest { param( [string]$DomainName, [string]$DomainNetBiosName, [string]$DomainMode = 'WinThreshold', [string]$ForestMode = 'WinThreshold', [string]$DatabasePath = "$env:SystemDrive\Windows\", [string]$LogPath = "$env:SystemDrive\Windows\NTDS\", [string]$SysvolPath = "$env:SystemDrive\Windows\", [string]$KeyVaultName, [string]$ResourceGroupName, [string]$secretName ) # set the paths $LOG_PATH = New-EnvPath -Path $LogPath -ChildPath 'logs' $DATABASE_PATH = New-EnvPath -Path $DatabasePath -ChildPath 'ntds' $SYSVOL_PATH = New-EnvPath -Path $SysvolPath -ChildPath 'SYSVOL' # install required modules Install-RequiredModule Install-RequiredADModule # connect to Azure Connect-ToAzure # get the vault Get-Vault Add-RegisteredSecretVault # define common parameters $commonParams = @{ InstallDNS = $true DomainName = $DomainName DomainNetBiosName = $DomainNetBiosName DomainMode = $DomainMode ForestMode = $ForestMode DatabasePath = $DATABASE_PATH LogPath = $LOG_PATH SysvolPath = $SYSVOL_PATH Force = $true } # retrieve the safe mode administrator password $vaultName = (Get-Vault).VaultName [securestring]$safeModeAdministratorPassword = Get-Secret -Name $secretName -Vault $vaultName $param = $commonParams.Clone() $keys = @{ SafeModeAdministratorPassword = $safeModeAdministratorPassword } Add-keys -hash $param -keys $keys # create the new AD Forest Install-ADDSForest @param # remove the registered secret vault Remove-RegisteredSecretVault # disconnect from Azure Disconnect-FromAzure } # nested function to wrap the New-ADDSForest function function New-ADForest { <# .SYNOPSIS This function is used to create a new Active Directory Forest in on-premises or Azure. .DESCRIPTION This module is used to create a new Active Directory Forest in on-premises or Azure. It installs the required modules, connects to Azure, gets the vault, adds the registered secret vault, defines common parameters, retrieves the safe mode administrator password, creates the new AD Forest, removes the registered secret vault, and disconnects from Azure after the operation. .PARAMETER DomainName The fully qualified domain name of the new AD Forest. .PARAMETER DomainNetBiosName The NetBIOS name of the new AD Forest. .PARAMETER DomainMode The domain functional level of the new AD Forest. This is set to 'WinThreshold' by default. .PARAMETER ForestMode The forest functional level of the new AD Forest. This is set to 'WinThreshold' by default. .PARAMETER DatabasePath The path to the database folder of the new AD Forest. This is set to the default path of the system drive by default. But you can specify a different path (recommended). .PARAMETER LogPath The path to the log folder of the new AD Forest. This is set to the default path of the system drive by default. But you can specify a different path (recommended). .PARAMETER SysvolPath The path to the sysvol folder of the new AD Forest. This is set to the default path of the system drive by default. But you can specify a different path (recommended). .PARAMETER KeyVaultName The name of the key vault in Azure. .PARAMETER ResourceGroupName The name of the resource group in Azure where the key vault is located. .PARAMETER secretName The name of the secret in the key vault that contains the safe mode administrator password. .PARAMETER Force This is a switch parameter that forces the operation to continue without prompting for confirmation. .NOTES File Name : New-ADForest Author : Olamide Olaleye Prerequisite : PowerShell 7.2 and above. .LINK Specify a URI to a help page, this will show when Get-Help -Online is used. .EXAMPLE New-ADForest -DomainName "contoso.com" -DomainNetBiosName "CONTOSO" -KeyVaultName "mykeyvault" -ResourceGroupName "myresourcegroup" -secretName "safemodeadminpassword" This example creates a new Active Directory Forest with the specified parameters. The function assumes default values for the DomainMode, ForestMode, DatabasePath, LogPath, and SysvolPath parameters. .EXAMPLE New-ADForest -DomainName "contoso.com" -DomainNetBiosName "CONTOSO" -DatabasePath "D:\" -LogPath "e:\" -SysvolPath "F:\" -KeyVaultName "mykeyvault" -ResourceGroupName "myresourcegroup" -secretName "safemodeadminpassword" -Force This example creates a new Active Directory Forest with the specified parameters. The function specifies the DatabasePath, LogPath, and SysvolPath parameters. There is no requirement to specify the database, log and sysvol directory folders as the default values are used. .INPUTS System.String .OUTPUTS System.String #> [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Mandatory = $true)] [string]$DomainName, [Parameter(Mandatory = $true)] [string]$DomainNetBiosName, [Parameter(Mandatory = $false)] [string]$DomainMode = 'WinThreshold', [Parameter(Mandatory = $false)] [string]$ForestMode = 'WinThreshold', [Parameter(Mandatory = $false)] [string]$DatabasePath = "$env:SystemDrive\Windows\", [Parameter(Mandatory = $false)] [string]$LogPath = "$env:SystemDrive\Windows\NTDS\", [Parameter(Mandatory = $false)] [string]$SysvolPath = "$env:SystemDrive\Windows\", [Parameter(Mandatory = $true)] [string]$KeyVaultName, [Parameter(Mandatory = $true)] [string]$ResourceGroupName, [Parameter(Mandatory = $true)] [string]$secretName, [Parameter(Mandatory = $false)] [switch]$Force ) try { if($PSCmdlet.ShouldProcess($DomainName,"Create a new Active Directory Forest") -or $PSCmdlet.ShouldContinue("Do you want to continue?")) { New-ADDSForest @PSBoundParameters } else{ Write-Output "Operation cancelled" } } catch { throw "Failed to create the new AD Forest. Please see the error message below.:$_" } } # End of the functions to install and configure new AD Forest. # New funtions to Add additonal Domain Controller function Add-ADDomainController { param( [string]$DomainName, [string]$SiteName = 'Default-First-Site-Name', [string]$DatabasePath = "$env:SystemDrive\Windows\", [string]$LogPath = "$env:SystemDrive\Windows\NTDS\", [string]$SysvolPath = "$env:SystemDrive\Windows\", [string]$KeyVaultName, [string]$ResourceGroupName, [string]$SafeModeAdminSecretName, [string]$DomainAdminSecretName, [string]$DomainAdminUser ) # set paths $DatabasePath = New-EnvPath -Path $DatabasePath -ChildPath 'ntds' $LogPath = New-EnvPath -Path $LogPath -ChildPath 'ntds' $SysvolPath = New-EnvPath -Path $SysvolPath -ChildPath 'sysvol' # install required modules Install-RequiredModule Install-RequiredADModule # connect to azure Connect-ToAzure # get the vault Get-Vault Add-RegisteredSecretVault # define common parameters $commonParams = @{ DomainName = $DomainName SiteName = $SiteName DatabasePath = $DatabasePath LogPath = $LogPath SysvolPath = $SysvolPath Force = $true } # retrieve the safe mode admin password $vaultName = (Get-Vault).VaultName $credential = New-Object System.Management.Automation.PSCredential ($DomainAdminUser, (Get-Secret -Name $DomainAdminSecretName -Vault $vaultName)) [securestring]$safeModeAdministratorPassword = Get-Secret -Name $SafeModeAdminSecretName -Vault $vaultName $param = $commonParams.Clone() $keys = @{ SafeModeAdministratorPassword = $safeModeAdministratorPassword Credential = $credential } Add-keys -hash $param -keys $keys try { # add the domain controller6-** Install-ADDSDomainController @param } catch { Write-Error "Failed to add the domain controller. Please see the error message below.:$_" } finally { # remove the registered secret vault Remove-RegisteredSecretVault # disconnect from azure Disconnect-FromAzure } } function New-ADDSDomainController{ <# .SYNOPSIS This function is used to add an additional domain controller to an existing forest/domain. .DESCRIPTION This module is used to add an additional domain controller to an existing forest/domain. It installs the required modules, connects to Azure, gets the vault, adds the registered secret vault, defines common parameters, retrieves the safe mode administrator password, creates a secure credential object, retrieves the domain admin password, adds the domain controller, removes the registered secret vault, and disconnects from Azure after the operation. .PARAMETER DomainName The fully qualified domain name of the existing forest/domain. .PARAMETER SiteName The name of the site where the new domain controller will be located. This is set to 'Default-First-Site-Name' by default. .PARAMETER DatabasePath The path to the database folder of the new domain controller. This is set to the default path of the system drive by default. But you can specify a different path (recommended). .PARAMETER LogPath The path to the log folder of the new domain controller. This is set to the default path of the system drive by default. But you can specify a different path (recommended). .PARAMETER SysvolPath The path to the sysvol folder of the new domain controller. This is set to the default path of the system drive by default. But you can specify a different path (recommended). .PARAMETER KeyVaultName The name of the key vault in Azure. .PARAMETER ResourceGroupName The name of the resource group in Azure where the key vault is located. .PARAMETER SafeModeAdminSecretName The name of the secret in the key vault that contains the safe mode administrator password. .PARAMETER DomainAdminSecretName The name of the secret in the key vault that contains the domain administrator password. .PARAMETER DomainAdminUser The username of the domain administrator. .PARAMETER Force This is a switch parameter that forces the operation to continue without prompting for confirmation. .NOTES File Name : Add-ADDomainController Author : Olamide Olaleye Prerequisite : PowerShell 7.2 and above. .LINK Specify a URI to a help page, this will show when Get-Help -Online is used. .EXAMPLE New-ADDSDomainController -DomainName "contoso.com" -KeyVaultName "mykeyvault" -ResourceGroupName "myresourcegroup" -SafeModeAdminSecretName "safemodeadminpassword" -DomainAdminSecretName "domainadminpassword" -DomainAdminUser "domainadmin" This example adds an additional domain controller to an existing forest/domain with the specified parameters. The function assumes default values for the SiteName, DatabasePath, LogPath, and SysvolPath parameters. .EXAMPLE New-ADDSDomainController -DomainName "contoso.com" -SiteName "NewSite" -DatabasePath "D:\" -LogPath "e:\" -SysvolPath "F:\" -KeyVaultName "mykeyvault" -ResourceGroupName "myresourcegroup" -SafeModeAdminSecretName "safemodeadminpassword" -DomainAdminSecretName "domainadminpassword" -DomainAdminUser "domainadmin" -Force This example adds an additional domain controller to an existing forest/domain with the specified parameters. The function specifies the SiteName, DatabasePath, LogPath, and SysvolPath parameters. There is no requirement to specify the database, log and sysvol directory folders as the default values are used. .INPUTS System.String .OUTPUTS System.String #> [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter (Mandatory = $true)][string]$DomainName, [Parameter (Mandatory = $false)][string]$SiteName = 'Default-First-Site-Name', [Parameter (Mandatory = $false)][string]$DatabasePath = "$env:SystemDrive\Windows\", [Parameter (Mandatory = $false)][string]$LogPath = "$env:SystemDrive\Windows\NTDS\", [Parameter (Mandatory = $false)][string]$SysvolPath = "$env:SystemDrive\Windows\", [Parameter (Mandatory = $true)][string]$KeyVaultName, [Parameter (Mandatory = $true)][string]$ResourceGroupName, [Parameter (Mandatory = $true)][string]$SafeModeAdminSecretName, [Parameter (Mandatory = $true)][string]$DomainAdminSecretName, [Parameter (Mandatory = $true)][string]$DomainAdminUser, [Parameter (Mandatory = $false)][switch]$Force ) try { if($PSCmdlet.ShouldProcess($DomainName,"Add a new domain controller") -or $PSCmdlet.ShouldContinue("Do you want to continue?")){ Add-ADDomainController @PSBoundParameters } else{ Write-Output "Operation cancelled" } } catch { Write-Error "Failed to add the domain controller. Please see the error message below.:$_" } } # Export the functions in the module. Export-ModuleMember -Function New-ADForest -Cmdlet New-ADForest Export-ModuleMember -Function New-ADDSDomainController -Cmdlet Add-ADDomainController |