Skip to content

Commit

Permalink
DomainMode and ForestMode implemented (#199)
Browse files Browse the repository at this point in the history
- xADDomain is now capable of setting the forest and domain functional level (issue #187).
  • Loading branch information
nyanhp authored and johlju committed Jun 18, 2018
1 parent fcd53fd commit fb9e3cc
Show file tree
Hide file tree
Showing 19 changed files with 3,838 additions and 3,320 deletions.
89 changes: 89 additions & 0 deletions DSCResources/MSFT_xADCommon/MSFT_xADCommon.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ data localizedString
MembersIsEmptyError = The Members parameter is empty. At least one group member must be provided.
IncludeAndExcludeConflictError = The member '{0}' is included in both '{1}' and '{2}' parameter values. The same member must not be included in both '{1}' and '{2}' parameter values.
IncludeAndExcludeAreEmptyError = The '{0}' and '{1}' parameters are either both null or empty. At least one member must be specified in one of these parameters.
ModeConversionError = Converted mode {0} is not a {1}.
CheckingMembers = Checking for '{0}' members.
MembershipCountMismatch = Membership count is not correct. Expected '{0}' members, actual '{1}' members.
Expand Down Expand Up @@ -599,3 +600,91 @@ function Test-ADReplicationSite

return ($null -ne $site);
}

function ConvertTo-DeploymentForestMode
{
[CmdletBinding()]
[OutputType([Microsoft.DirectoryServices.Deployment.Types.ForestMode])]
param
(
[Parameter(
Mandatory = $true,
ParameterSetName = 'ById')]
[UInt16]
$ModeId,

[Parameter(
Mandatory = $true,
ParameterSetName = 'ByName')]
[AllowNull()]
[System.Nullable``1[Microsoft.ActiveDirectory.Management.ADForestMode]]
$Mode,

[ValidateNotNullOrEmpty()]
[System.String]
$ModuleName = 'xActiveDirectory'
)

$convertedMode = $null

if ($PSCmdlet.ParameterSetName -eq 'ByName' -and $Mode)
{
$convertedMode = $Mode -as [Microsoft.DirectoryServices.Deployment.Types.ForestMode]
}

if ($PSCmdlet.ParameterSetName -eq 'ById')
{
$convertedMode = $ModeId -as [Microsoft.DirectoryServices.Deployment.Types.ForestMode]
}

if ([enum]::GetValues([Microsoft.DirectoryServices.Deployment.Types.ForestMode]) -notcontains $convertedMode)
{
return $null
}

return $convertedMode
}

function ConvertTo-DeploymentDomainMode
{
[CmdletBinding()]
[OutputType([Microsoft.DirectoryServices.Deployment.Types.DomainMode])]
param
(
[Parameter(
Mandatory = $true,
ParameterSetName = 'ById')]
[UInt16]
$ModeId,

[Parameter(
Mandatory = $true,
ParameterSetName = 'ByName')]
[AllowNull()]
[System.Nullable``1[Microsoft.ActiveDirectory.Management.ADDomainMode]]
$Mode,

[ValidateNotNullOrEmpty()]
[System.String]
$ModuleName = 'xActiveDirectory'
)

$convertedMode = $null

if ($PSCmdlet.ParameterSetName -eq 'ByName' -and $Mode)
{
$convertedMode = $Mode -as [Microsoft.DirectoryServices.Deployment.Types.DomainMode]
}

if ($PSCmdlet.ParameterSetName -eq 'ById')
{
$convertedMode = $ModeId -as [Microsoft.DirectoryServices.Deployment.Types.DomainMode]
}

if ([enum]::GetValues([Microsoft.DirectoryServices.Deployment.Types.DomainMode]) -notcontains $convertedMode)
{
return $null
}

return $convertedMode
}
46 changes: 38 additions & 8 deletions DSCResources/MSFT_xADDomain/MSFT_xADDomain.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ data localizedData
ResourceNotInDesiredState = Resource '{0}' is NOT in the desired state.
RetryingGetADDomain = Attempt {0} of {1} to call Get-ADDomain failed, retrying in {2} seconds.
UnhandledError = Unhandled error occured, detail here: {0}
FaultExceptionAndDomainShouldExist = ServiceModel FaultException detected and domain should exist, performing retry...
FaultExceptionAndDomainShouldExist = ServiceModel FaultException detected and domain should exist, performing retry...
'@
}

Expand Down Expand Up @@ -99,7 +99,13 @@ function Get-TargetResource
[String] $LogPath,

[Parameter()] [ValidateNotNullOrEmpty()]
[String] $SysvolPath
[String] $SysvolPath,

[Parameter()] [ValidateSet('Win2008', 'Win2008R2', 'Win2012', 'Win2012R2', 'WinThreshold')]
[String] $ForestMode,

[Parameter()] [ValidateSet('Win2008', 'Win2008R2', 'Win2012', 'Win2012R2', 'WinThreshold')]
[String] $DomainMode
)

Assert-Module -ModuleName 'ADDSDeployment';
Expand All @@ -117,10 +123,12 @@ function Get-TargetResource
## We're already a domain member, so take the credentials out of the equation
Write-Verbose ($localizedData.QueryDomainADWithLocalCredentials -f $domainFQDN);
$domain = Get-ADDomain -Identity $domainFQDN -ErrorAction Stop;
$forest = Get-ADForest -Identity $domain.Forest -ErrorAction Stop
}
else {
Write-Verbose ($localizedData.QueryDomainWithCredential -f $domainFQDN);
$domain = Get-ADDomain -Identity $domainFQDN -Credential $DomainAdministratorCredential -ErrorAction Stop;
$domain = Get-ADDomain -Identity $domainFQDN -Credential $DomainAdministratorCredential -ErrorAction Stop
$forest = Get-ADForest -Identity $domain.Forest -Credential $DomainAdministratorCredential -ErrorAction Stop
}

## No need to check whether the node is actually a domain controller. If we don't throw an exception,
Expand All @@ -132,6 +140,8 @@ function Get-TargetResource
DomainName = $domain.DnsRoot;
ParentDomainName = $domain.ParentDomain;
DomainNetBIOSName = $domain.NetBIOSName;
ForestMode = (ConvertTo-DeploymentForestMode -Mode $forest.ForestMode) -as [String]
DomainMode = (ConvertTo-DeploymentDomainMode -Mode $domain.DomainMode) -as [String]
}

return $targetResource;
Expand Down Expand Up @@ -207,7 +217,13 @@ function Test-TargetResource
[String] $LogPath,

[Parameter()] [ValidateNotNullOrEmpty()]
[String] $SysvolPath
[String] $SysvolPath,

[Parameter()] [ValidateSet('Win2008', 'Win2008R2', 'Win2012', 'Win2012R2', 'WinThreshold')]
[String] $ForestMode,

[Parameter()] [ValidateSet('Win2008', 'Win2008R2', 'Win2012', 'Win2012R2', 'WinThreshold')]
[String] $DomainMode
)

$targetResource = Get-TargetResource @PSBoundParameters
Expand Down Expand Up @@ -280,11 +296,17 @@ function Set-TargetResource
[String] $LogPath,

[Parameter()] [ValidateNotNullOrEmpty()]
[String] $SysvolPath
[String] $SysvolPath,

[Parameter()] [ValidateSet('Win2008', 'Win2008R2', 'Win2012', 'Win2012R2', 'WinThreshold')]
[String] $ForestMode,

[Parameter()] [ValidateSet('Win2008', 'Win2008R2', 'Win2012', 'Win2012R2', 'WinThreshold')]
[String] $DomainMode
)

# Debug can pause Install-ADDSForest/Install-ADDSDomain, so we remove it.
[ref] $null = $PSBoundParameters.Remove("Debug");
[ref] $null = $PSBoundParameters.Remove('Debug');
## Not entirely necessary, but run Get-TargetResouece to ensure we raise any pre-flight errors.
$targetResource = Get-TargetResource @PSBoundParameters;

Expand All @@ -311,7 +333,11 @@ function Set-TargetResource
{
$installADDSParams['SysvolPath'] = $SysvolPath;
}

if ($PSBoundParameters.ContainsKey('DomainMode'))
{
$installADDSParams['DomainMode'] = $DomainMode;
}

if ($PSBoundParameters.ContainsKey('ParentDomainName'))
{
Write-Verbose -Message ($localizedData.CreatingChildDomain -f $DomainName, $ParentDomainName);
Expand All @@ -334,11 +360,15 @@ function Set-TargetResource
{
$installADDSParams['DomainNetbiosName'] = $DomainNetBIOSName;
}
if ($PSBoundParameters.ContainsKey('ForestMode'))
{
$installADDSParams['ForestMode'] = $ForestMode
}
Install-ADDSForest @installADDSParams;
Write-Verbose -Message ($localizedData.CreatedForest -f $DomainName);
}

"Finished" | Out-File -FilePath (Get-TrackingFilename -DomainName $DomainName) -Force
'Finished' | Out-File -FilePath (Get-TrackingFilename -DomainName $DomainName) -Force

# Signal to the LCM to reboot the node to compensate for the one we
# suppressed from Install-ADDSForest/Install-ADDSDomain
Expand Down
2 changes: 2 additions & 0 deletions DSCResources/MSFT_xADDomain/MSFT_xADDomain.schema.mof
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ class MSFT_xADDomain : OMI_BaseResource
[Write, Description("Path to a directory that contains the domain database")] String DatabasePath;
[Write, Description("Path to a directory for the log file that will be written")] String LogPath;
[Write, Description("Path to a directory where the Sysvol file will be written")] String SysvolPath;
[Write, Description("The Forest Functional Level for the entire forest"), ValueMap{"Win2008", "Win2008R2", "Win2012", "Win2012R2", "WinThreshold"}, Values{"Win2008", "Win2008R2", "Win2012", "Win2012R2", "WinThreshold"}] String ForestMode;
[Write, Description("The Domain Functional Level for the entire domain"), ValueMap{"Win2008", "Win2008R2", "Win2012", "Win2012R2", "WinThreshold"}, Values{"Win2008", "Win2008R2", "Win2012", "Win2012R2", "WinThreshold"}] String DomainMode;
};
61 changes: 61 additions & 0 deletions Examples/Resources/xADDomain/1-NewForest.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<#
.EXAMPLE
This example will create a new domain with a new forest and a forest functional level of Server 2016
#>
$ConfigurationData = @{
AllNodes = @(
@{
NodeName = 'localhost'
FFL = 'WinThreshold'
DomainName = 'contoso.com'

<#
NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION.
This is added so that AppVeyor automatic tests can pass, otherwise
the tests will fail on passwords being in plain text and not being
encrypted. Because it is not possible to have a certificate in
AppVeyor to encrypt the passwords we need to add the parameter
'PSDscAllowPlainTextPassword'.
NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION.
#>
PSDscAllowPlainTextPassword = $true
}
)
}

configuration Example
{
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]
$DomainAdministratorCredential
)

Import-DscResource -ModuleName PSDscResources
Import-DscResource -ModuleName xActiveDirectory
node $AllNodes.NodeName
{
WindowsFeature ADDS
{
Name = 'AD-Domain-Services'
Ensure = 'Present'
}

WindowsFeature RSAT
{
Name = 'RSAT-AD-PowerShell'
Ensure = 'Present'
}

xADDomain $Node.DomainName
{
DomainName = $Node.DomainName
DomainAdministratorCredential = $DomainAdministratorCredential
SafemodeAdministratorPassword = $DomainAdministratorCredential
ForestMode = $Node.FFL
}
}

}
63 changes: 63 additions & 0 deletions Examples/Resources/xADDomain/2-NewChildDomain.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<#
.EXAMPLE
This example will create a new child domain in an existing forest with a Domain Functional Level of Windows Server 2012R2
#>
$ConfigurationData = @{
AllNodes = @(
@{
NodeName = 'localhost'
DFL = 'Win2012R2'
DomainName = 'child'
ParentDomain = 'contoso.com'

<#
NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION.
This is added so that AppVeyor automatic tests can pass, otherwise
the tests will fail on passwords being in plain text and not being
encrypted. Because it is not possible to have a certificate in
AppVeyor to encrypt the passwords we need to add the parameter
'PSDscAllowPlainTextPassword'.
NOTE! THIS IS NOT RECOMMENDED IN PRODUCTION.
#>
PSDscAllowPlainTextPassword = $true
}
)
}

configuration Example
{
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]
$DomainAdministratorCredential
)

Import-DscResource -ModuleName PSDscResources
Import-DscResource -ModuleName xActiveDirectory
node $AllNodes.NodeName
{
WindowsFeature ADDS
{
Name = 'AD-Domain-Services'
Ensure = 'Present'
}

WindowsFeature RSAT
{
Name = 'RSAT-AD-PowerShell'
Ensure = 'Present'
}

xADDomain $Node.DomainName
{
DomainName = $Node.DomainName
DomainAdministratorCredential = $DomainAdministratorCredential
SafemodeAdministratorPassword = $DomainAdministratorCredential
DomainMode = $Node.DFL
ParentDomainName = $node.ParentDomain
}
}

}
Loading

0 comments on commit fb9e3cc

Please sign in to comment.