Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 234 additions & 0 deletions DSCResources/MSFT_xOfflineDomainJoin/MSFT_xOfflineDomainJoin.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
$moduleRoot = Split-Path `
-Path $MyInvocation.MyCommand.Path `
-Parent

#region LocalizedData
$Culture = 'en-us'
if (Test-Path -Path (Join-Path -Path $moduleRoot -ChildPath $PSUICulture))
{
$Culture = $PSUICulture
}
Import-LocalizedData `
-BindingVariable LocalizedData `
-Filename MSFT_xOfflineDomainJoin.psd1 `
-BaseDirectory $moduleRoot `
-UICulture $Culture
#endregion


function Get-TargetResource
{
[CmdletBinding()]
[OutputType([Hashtable])]
param
(
[parameter(Mandatory = $true)]
[ValidateSet('Yes')]
[System.String]
$IsSingleInstance,

[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$RequestFile
)

Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): "
$($LocalizedData.GettingOfflineDomainJoinMessage)
) -join '')

# It is not possible to read the ODJ file that was used to join a domain
# So it has to always be returned as blank.
$returnValue = @{
IsSingleInstance = 'Yes'
RequestFile = ''
}

#Output the target resource
$returnValue
} # Get-TargetResource


function Set-TargetResource
{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[ValidateSet('Yes')]
[System.String]
$IsSingleInstance,

[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$RequestFile
)

Write-Verbose -Message ( @( "$($MyInvocation.MyCommand): "
$($LocalizedData.ApplyingOfflineDomainJoinMessage)
) -join '')

# Check the ODJ Request file exists
if (-not (Test-Path -Path $RequestFile))
{
$errorId = 'RequestFileNotFoundError'
$errorCategory = [System.Management.Automation.ErrorCategory]::ObjectNotFound
$errorMessage = $($LocalizedData.RequestFileNotFoundError) `
-f $RequestFile
$exception = New-Object -TypeName System.ArgumentException `
-ArgumentList $errorMessage
$errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord `
-ArgumentList $exception, $errorId, $errorCategory, $null

$PSCmdlet.ThrowTerminatingError($errorRecord)
} # if

# Don't need to check if the domain is already joined because
# Set-TargetResource wouldn't fire unless it wasn't.
Join-Domain -RequestFile $RequestFile
} # Set-TargetResource


function Test-TargetResource
{
[CmdletBinding()]
[OutputType([Boolean])]
param
(
[parameter(Mandatory = $true)]
[ValidateSet('Yes')]
[System.String]
$IsSingleInstance,

[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$RequestFile
)

# Flag to signal whether settings are correct
[Boolean] $desiredConfigurationMatch = $true

Write-Verbose -Message ( @("$($MyInvocation.MyCommand): "
$($LocalizedData.CheckingOfflineDomainJoinMessage)
) -join '')

# Check the ODJ Request file exists
if (-not (Test-Path -Path $RequestFile))
{
$errorId = 'RequestFileNotFoundError'
$errorCategory = [System.Management.Automation.ErrorCategory]::ObjectNotFound
$errorMessage = $($LocalizedData.RequestFileNotFoundError) `
-f $RequestFile
$exception = New-Object -TypeName System.ArgumentException `
-ArgumentList $errorMessage
$errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord `
-ArgumentList $exception, $errorId, $errorCategory, $null

$PSCmdlet.ThrowTerminatingError($errorRecord)
} # if

$CurrentDomainName = Get-DomainName

if($CurrentDomainName)
{
# Domain is already joined.
Write-Verbose -Message ( @(
"$($MyInvocation.MyCommand): "
$($LocalizedData.DomainAlreadyJoinedhMessage) `
-f $CurrentDomainName `
) -join '' )
}
else
{
# Domain is not joined, so change is required.
Write-Verbose -Message ( @("$($MyInvocation.MyCommand): "
$($LocalizedData.DomainNotJoinedMessage)
) -join '')

$desiredConfigurationMatch = $false
} # if
return $desiredConfigurationMatch
} # Test-TargetResource


<#
.SYNOPSIS
Uses DJoin.exe to join a Domain using a ODJ Request File.
#>
function Join-Domain {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[System.String]
$RequestFile
)

Write-Verbose -Message ( @(
"$($MyInvocation.MyCommand): "
$($LocalizedData.AttemptingDomainJoinMessage) `
-f $RequestFile `
) -join '' )

$Result = & djoin.exe @(
'/REQUESTODJ'
'/LOADFILE'
$RequestFile
'/WINDOWSPATH'
$ENV:SystemRoot
'/LOCALOS')
if ($LASTEXITCODE -eq 0)
{
# Notify DSC that a reboot is required.
$global:DSCMachineStatus = 1
}
else
{
Write-Verbose -Message $Result

$errorId = 'DjoinError'
$errorCategory = [System.Management.Automation.ErrorCategory]::ObjectNotFound
$errorMessage = $($LocalizedData.DjoinError) `
-f $LASTEXITCODE
$exception = New-Object -TypeName System.ArgumentException `
-ArgumentList $errorMessage
$errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord `
-ArgumentList $exception, $errorId, $errorCategory, $null

$PSCmdlet.ThrowTerminatingError($errorRecord)
} # if

Write-Verbose -Message ( @(
"$($MyInvocation.MyCommand): "
$($LocalizedData.DomainJoinedMessage) `
-f $RequestFile `
) -join '' )
} # function Join-Domain


<#
.SYNOPSIS
Returns the name of the Domain the computer is joined to or
$null if not domain joined.
#>
function Get-DomainName
{
[CmdletBinding()]
[OutputType([System.String])]
param()

# Use CIM to detect the domain name so that this will work on Nano Server.
$ComputerSystem = Get-CimInstance -ClassName win32_computersystem -Namespace root\cimv2
if ($ComputerSystem.Workgroup)
{
return $null
}
else
{
$ComputerSystem.Domain
}
} # function Get-DomainName


Export-ModuleMember -Function *-TargetResource
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[ClassVersion("1.0.0.0"), FriendlyName("xOfflineDomainJoin")]
class MSFT_xOfflineDomainJoin : OMI_BaseResource
{
[Key, Description("Specifies the resource is a single instance, the value must be 'Yes'"), ValueMap{"Yes"}, Values{"Yes"}] String IsSingleInstance;
[Required, Description("The full path to the Offline Domain Join Request file to use.")] String RequestFile;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
ConvertFrom-StringData @'
GettingOfflineDomainJoinMessage=Getting the Offline Domain Join State.
ApplyingOfflineDomainJoinMessage=Applying the Offline Domain Join State.
AttemptingDomainJoinMessage=Attempting domain join using ODJ Request file '{0}'.
DomainJoinedMessage=Domain joined using ODJ Request file '{0}'. Reboot will be required.
CheckingOfflineDomainJoinMessage=Checking the Offline Domain Join State.
DomainAlreadyJoinedhMessage=The computer is already joined to a domain '{0}'. Change not required.
DomainNotJoinedMessage=The computer is not joined to a domain. Change required.
RequestFileNotFoundError=The ODJ Request file '{0}' does not exist.
DjoinError=Error {0} occured requesting the Offline Domain Join.
'@
21 changes: 21 additions & 0 deletions Examples/Sample_xOfflineDomainJoin.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
configuration Sample_xOfflineDomainJoin
{
param
(
[string[]]$NodeName = 'localhost'
)

Import-DSCResource -ModuleName xComputerManagement

Node $NodeName
{
xOfflineDomainJoin ODJ
{
RequestFile = 'C:\ODJ\ODJBlob.txt'
IsSingleInstance = 'Yes'
}
}
}

Sample_xOfflineDomainJoin
Start-DscConfiguration -Path Sample_xOfflineDomainJoin -Wait -Verbose -Force
44 changes: 41 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ To easily use PowerShell 4.0 on older operating systems, install WMF 4.0.
Please read the installation instructions that are present on both the download page and the release notes for WMF 4.0

## Description
The xComputerManagement module contains the xComputer DSC Resource.
This DSC Resource allows you to configure a computer by changing its name and modifying its domain or workgroup.
The xComputerManagement module contains the following resources:
* xComputer - allows you to configure a computer by changing its name and modifying its domain or workgroup.
* xOfflineDomainJoin - allows you to join computers to an AD Domain using an [Offline Domain Join](https://technet.microsoft.com/en-us/library/offline-domain-join-djoin-step-by-step(v=ws.10).aspx) request file.

## Details
## xComputer
xComputer resource has following properties:

* Name: The desired computer name
Expand All @@ -45,9 +46,19 @@ xComputer resource has following properties:
* Credential: Credential to be used to join or leave domain
* CurrentOU: A read-only property that specifies the organizational unit that the computer account is currently in

## xOfflineDomainJoin
xOfflineDomainJoin resource is a [Single Instance](https://msdn.microsoft.com/en-us/powershell/dsc/singleinstance) resource that can only be used once in a configuration and has following properties:

* IsSingleInstance: Must be set to 'Yes'. Required.
* RequestFile: The full path to the Offline Domain Join request file. Required.

## Versions

### Unreleased
* Added the following resources:
* MSFT_xOfflineDomainJoin resource to join computers to an AD Domain using an Offline Domain Join request file.
* xComputer: Changed credential generation code in tests to avoid triggering PSSA rule PSAvoidUsingConvertToSecureStringWithPlainText.
Renamed unit test file to match the name of Resource file.

### 1.5.0.0
* Update Unit tests to use the standard folder structure and test templates.
Expand Down Expand Up @@ -297,5 +308,32 @@ Sample_xComputer_DomainToWorkgroup -ConfigurationData $ConfigData -MachineName <
****************************#>
```

### Join a Domain using an ODJ Request File
This example will join the computer to a domain using the ODJ request file C:\ODJ\ODJRequest.txt.

```powershell
configuration Sample_xOfflineDomainJoin
{
param
(
[string[]]$NodeName = 'localhost'
)

Import-DSCResource -ModuleName xComputerManagement

Node $NodeName
{
xOfflineDomainJoin ODJ
{
RequestFile = 'C:\ODJ\ODJRequest.txt'
IsSingleInstance = 'Yes'
}
}
}

Sample_xOfflineDomainJoin
Start-DscConfiguration -Path Sample_xOfflineDomainJoin -Wait -Verbose -Force
```

## Contributing
Please check out common DSC Resources [contributing guidelines](https://github.com/PowerShell/DscResource.Kit/blob/master/CONTRIBUTING.md).
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ try
InModuleScope $Global:DSCResourceName {

Describe $Global:DSCResourceName {

$SecPassword = ConvertTo-SecureString -String 'password' -AsPlainText -Force
# A real password isn't needed here - use this next line to avoid triggering PSSA rule
$SecPassword = New-Object -Type SecureString
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList 'USER',$SecPassword
$NotComputerName = if($env:COMPUTERNAME -ne 'othername'){'othername'}else{'name'}

Expand Down
Loading