DSCResources/MSFT_xADDomainTrust/MSFT_xADDomainTrust.psm1
# Localized messages data LocalizedData { # culture="en-US" ConvertFrom-StringData @' MissingRoleMessage = Please ensure that the {0} role is installed CheckingTrustMessage = Checking if Trust between {0} and {1} exists ... TestTrustMessage = Trust is {0} between source and target domains and it should be {1} RemovingTrustMessage = Removing trust between {0} and {1} domains ... DeleteTrustMessage = Trust between specified domains is now absent AddingTrustMessage = Adding domain trust between {0} and {1} ... SetTrustMessage = Trust between specified domains is now present CheckPropertyMessage = Checking for {0} between domains ... DesiredPropertyMessage = {0} between domains is set correctly NotDesiredPropertyMessage = {0} between domains is not correct. Expected {1}, actual {2} SetPropertyMessage = {0} between domains is set '@ } function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [parameter(Mandatory)] [String]$SourceDomainName, [parameter(Mandatory)] [String]$TargetDomainName, [parameter(Mandatory)] [PSCredential]$TargetDomainAdministratorCredential, [parameter(Mandatory)] [ValidateSet("External")] [String]$TrustType, [parameter(Mandatory)] [ValidateSet("Bidirectional","Inbound","Outbound")] [String]$TrustDirection, [ValidateSet("Present","Absent")] [String]$Ensure = 'Present' ) #region Input Validation # Load the .NET assembly try { Add-type -AssemblyName System.DirectoryServices } # If not found, means ADDS role is not installed catch { $missingRoleMessage = $($LocalizedData.MissingRoleMessage) -f 'AD-Domain-Services' New-TerminatingError -errorId ActiveDirectoryRoleMissing -errorMessage $missingRoleMessage -errorCategory NotInstalled } #endregion try { # Create the target domain object $trgDirectoryContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('domain',$TargetDomainName, $TargetDomainAdministratorCredential.UserName, $TargetDomainAdministratorCredential.GetNetworkCredential().Password) $trgDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($trgDirectoryContext) # Create the source domain object $srcDirectoryContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('domain',$SourceDomainName) $srcDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($srcDirectoryContext) # Find trust betwen source & destination. $trust = $srcDomain.GetTrustRelationship($trgDomain) $Ensure = 'Present' } catch { $Ensure = 'Absent' } # return a credential object without password $CIMCredential = New-CimInstance -ClassName MSFT_Credential -ClientOnly ` -Namespace root/microsoft/windows/desiredstateconfiguration ` -Property @{ UserName = [string]$TargetDomainAdministratorCredential.UserName Password = [string]$null } @{ SourceDomainName = $SourceDomainName TargetDomainName = $TargetDomainName Ensure = $Ensure TrustType = $trust.TrustType TrustDirection = $trust.TrustDirection TargetDomainAdministratorCredential = $CIMCredential } } function Set-TargetResource { [CmdletBinding()] param ( [parameter(Mandatory)] [String]$SourceDomainName, [parameter(Mandatory)] [String]$TargetDomainName, [parameter(Mandatory)] [PSCredential]$TargetDomainAdministratorCredential, [parameter(Mandatory)] [ValidateSet("External")] [String]$TrustType, [parameter(Mandatory)] [ValidateSet("Bidirectional","Inbound","Outbound")] [String]$TrustDirection, [ValidateSet("Present","Absent")] [String]$Ensure = 'Present' ) if($PSBoundParameters.ContainsKey('Debug')){$null = $PSBoundParameters.Remove('Debug')} Validate-ResourceProperties @PSBoundParameters -Apply } function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [parameter(Mandatory)] [String]$SourceDomainName, [parameter(Mandatory)] [String]$TargetDomainName, [parameter(Mandatory)] [PSCredential]$TargetDomainAdministratorCredential, [parameter(Mandatory)] [ValidateSet("External")] [String]$TrustType, [parameter(Mandatory)] [ValidateSet("Bidirectional","Inbound","Outbound")] [String]$TrustDirection, [ValidateSet("Present","Absent")] [String]$Ensure = 'Present' ) #region Input Validation # Load the .NET assembly try { Add-type -AssemblyName System.DirectoryServices } # If not found, means ADDS role is not installed catch { $missingRoleMessage = $($LocalizedData.MissingRoleMessage) -f 'AD-Domain-Services' New-TerminatingError -errorId ActiveDirectoryRoleMissing -errorMessage $missingRoleMessage -errorCategory NotInstalled } #endregion if($PSBoundParameters.ContainsKey('Debug')){$null = $PSBoundParameters.Remove('Debug')} Validate-ResourceProperties @PSBoundParameters } #region Helper Functions function Validate-ResourceProperties { [Cmdletbinding()] param ( [parameter(Mandatory)] [String]$SourceDomainName, [parameter(Mandatory)] [String]$TargetDomainName, [parameter(Mandatory)] [PSCredential]$TargetDomainAdministratorCredential, [parameter(Mandatory)] [ValidateSet("External")] [String]$TrustType, [parameter(Mandatory)] [ValidateSet("Bidirectional","Inbound","Outbound")] [String]$TrustDirection, [ValidateSet("Present","Absent")] [String]$Ensure = 'Present', [Switch]$Apply ) try { $checkingTrustMessage = $($LocalizedData.CheckingTrustMessage) -f $SourceDomainName,$TargetDomainName Write-Verbose -Message $checkingTrustMessage #Create the target domain object $trgDirectoryContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('domain',$TargetDomainName, $TargetDomainAdministratorCredential.UserName, $($TargetDomainAdministratorCredential.GetNetworkCredential()).Password) $trgDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($trgDirectoryContext) # Create the source domain object $srcDirectoryContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('domain',$SourceDomainName) $srcDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($srcDirectoryContext) # Find trust try { # Find trust betwen source & destination. $trust = $srcDomain.GetTrustRelationship($TargetDomainName) $TestTrustMessage = $($LocalizedData.TestTrustMessage) -f 'present',$Ensure Write-Verbose -Message $TestTrustMessage if($Ensure -eq 'Present') { #region Test for trust direction $CheckPropertyMessage = $($LocalizedData.CheckPropertyMessage) -f 'trust direction' Write-Verbose -Message $CheckPropertyMessage # Set the trust direction if not correct if($trust.TrustDirection -ne $TrustDirection) { $notDesiredPropertyMessage = $($LocalizedData.NotDesiredPropertyMessage) -f 'Trust direction',$TrustDirection,$trust.TrustDirection Write-Verbose -Message $notDesiredPropertyMessage if($Apply) { $srcDomain.UpdateTrustRelationship($trgDomain,$TrustDirection) $setPropertyMessage = $($LocalizedData.SetPropertyMessage) -f 'Trust direction' Write-Verbose -Message $setPropertyMessage } else { return $false } } # end trust direction is not correct # Trust direction is correct else { $desiredPropertyMessage = $($LocalizedData.DesiredPropertyMessage) -f 'Trust direction' Write-Verbose -Message $desiredPropertyMessage } #endregion trust direction #region Test for trust type $CheckPropertyMessage = $($LocalizedData.CheckPropertyMessage) -f 'trust type' Write-Verbose -Message $CheckPropertyMessage # Set the trust type if not correct if($trust.TrustType-ne $TrustType) { $notDesiredPropertyMessage = $($LocalizedData.NotDesiredPropertyMessage) -f 'Trust type',$TrustType,$trust.TrustType Write-Verbose -Message $notDesiredPropertyMessage if($Apply) { # Only way to fix the trust direction is to delete it and create again # TODO: Add a property to ask user permission to delete an existing trust $srcDomain.DeleteTrustRelationship($trgDomain) $srcDomain.CreateTrustRelationship($trgDomain,$TrustDirection) $setPropertyMessage = $($LocalizedData.SetPropertyMessage) -f 'Trust type' Write-Verbose -Message $setPropertyMessage } else { return $false } } # end trust type is not correct # Trust type is correct else { $desiredPropertyMessage = $($LocalizedData.DesiredPropertyMessage) -f 'Trust direction' Write-Verbose -Message $desiredPropertyMessage } #endregion Test for trust type # If both trust type and trust direction are correct, return true if(-not $Apply) { return $true } } # end Ensure -eq present # If the trust should be absent, remove the trust else { if($Apply) { $removingTrustMessage = $($LocalizedData.RemovingTrustMessage) -f $SourceDomainName,$TargetDomainName Write-Verbose -Message $removingTrustMessage $srcDomain.DeleteTrustRelationship($trgDomain) $deleteTrustMessage = $LocalizedData.DeleteTrustMessage Write-Verbose -Message $deleteTrustMessage } else { return $false } } # end Ensure -eq absent } # end find trust # Trust does not exist between source and destination catch [System.DirectoryServices.ActiveDirectory.ActiveDirectoryObjectNotFoundException] { $TestTrustMessage = $($LocalizedData.TestTrustMessage) -f 'absent',$Ensure Write-Verbose -Message $TestTrustMessage if($Ensure -eq 'Present') { if($Apply) { $addingTrustMessage = $($LocalizedData.AddingTrustMessage) -f $SourceDomainName,$TargetDomainName Write-Verbose -Message $addingTrustMessage $srcDomain.CreateTrustRelationship($trgDomain,$TrustDirection) $setTrustMessage = $LocalizedData.SetTrustMessage Write-Verbose -Message $setTrustMessage } else { return $false } } # end Ensure -eq Present else { if(-not $Apply) { return $true } } } # end no trust }# end getting directory object catch [System.DirectoryServices.ActiveDirectory.ActiveDirectoryObjectNotFoundException] { throw } } # Internal function to throw terminating error with specified errroCategory, errorId and errorMessage function New-TerminatingError { [CmdletBinding()] param ( [Parameter(Mandatory)] [String]$errorId, [Parameter(Mandatory)] [String]$errorMessage, [Parameter(Mandatory)] [System.Management.Automation.ErrorCategory]$errorCategory ) $exception = New-Object System.InvalidOperationException $errorMessage $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $null throw $errorRecord } #endregion Export-ModuleMember -Function *-TargetResource |