DSCResources/MSFT_xComputer/MSFT_xComputer.psm1

[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidGlobalVars", "", Scope = "Function")]
param
(
)

function Get-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateLength(1, 15)]
        [ValidateScript( {$_ -inotmatch '[\/\\:*?"<>|]' })]
        [System.String]
        $Name,

        [Parameter()]
        [System.String]
        $DomainName,

        [Parameter()]
        [System.String]
        $JoinOU,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $Credential,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $UnjoinCredential,

        [Parameter()]
        [System.String]
        $WorkGroupName
    )

    Write-Verbose -Message "Getting computer state for '$($Name)'."

    $convertToCimCredential = New-CimInstance `
        -ClassName MSFT_Credential `
        -Property @{
            Username = [System.String] $Credential.UserName
            Password = [System.String] $null
        } `
        -Namespace root/microsoft/windows/desiredstateconfiguration `
        -ClientOnly

    $convertToCimUnjoinCredential = New-CimInstance `
        -ClassName MSFT_Credential `
        -Property @{
            Username = [System.String] $UnjoinCredential.UserName
            Password = [System.String]$null
        } `
        -Namespace root/microsoft/windows/desiredstateconfiguration `
        -ClientOnly

    $returnValue = @{
        Name             = $env:COMPUTERNAME
        DomainName       = Get-ComputerDomain
        JoinOU           = $JoinOU
        CurrentOU        = Get-ComputerOU
        Credential       = [ciminstance]$convertToCimCredential
        UnjoinCredential = [ciminstance]$convertToCimUnjoinCredential
        WorkGroupName    = (Get-CimInstance -Class 'Win32_ComputerSystem').Workgroup
    }

    $returnValue
}

function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateLength(1, 15)]
        [ValidateScript( {$_ -inotmatch '[\/\\:*?"<>|]' })]
        [System.String]
        $Name,

        [Parameter()]
        [System.String]
        $DomainName,

        [Parameter()]
        [System.String]
        $JoinOU,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $Credential,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $UnjoinCredential,

        [Parameter()]
        [System.String]
        $WorkGroupName
    )

    Assert-DomainOrWorkGroup -DomainName $DomainName -WorkGroupName $WorkGroupName

    if ($Name -eq 'localhost')
    {
        $Name = $env:COMPUTERNAME
    }

    if ($Credential)
    {
        if ($DomainName)
        {
            if ($DomainName -eq (Get-ComputerDomain))
            {
                # Rename the computer, but stay joined to the domain.
                Rename-Computer -NewName $Name -DomainCredential $Credential -Force
                Write-Verbose -Message "Renamed computer to '$($Name)'."
            }
            else
            {
                if ($Name -ne $env:COMPUTERNAME)
                {
                    # Rename the computer, and join it to the domain.
                    if ($UnjoinCredential)
                    {
                        Add-Computer -DomainName $DomainName -Credential $Credential -NewName $Name -UnjoinDomainCredential $UnjoinCredential -Force
                    }
                    else
                    {
                        if ($JoinOU)
                        {
                            Add-Computer -DomainName $DomainName -Credential $Credential -NewName $Name -OUPath $JoinOU -Force
                        }
                        else
                        {
                            Add-Computer -DomainName $DomainName -Credential $Credential -NewName $Name -Force
                        }
                    }
                    Write-Verbose -Message "Renamed computer to '$($Name)' and added to the domain '$($DomainName)."
                }
                else
                {
                    # Same computer name, and join it to the domain.
                    if ($UnjoinCredential)
                    {
                        Add-Computer -DomainName $DomainName -Credential $Credential -UnjoinDomainCredential $UnjoinCredential -Force
                    }
                    else
                    {
                        if ($JoinOU)
                        {
                            Add-Computer -DomainName $DomainName -Credential $Credential -OUPath $JoinOU -Force
                        }
                        else
                        {
                            Add-Computer -DomainName $DomainName -Credential $Credential -Force
                        }
                    }
                    Write-Verbose -Message "Added computer to domain '$($DomainName)."
                }
            }
        }
        elseif ($WorkGroupName)
        {
            if ($WorkGroupName -eq (Get-CimInstance -Class 'Win32_ComputerSystem').Workgroup)
            {
                # Rename the computer, but stay in the same workgroup.
                Rename-Computer -NewName $Name
                Write-Verbose -Message "Renamed computer to '$($Name)'."
            }
            else
            {
                if ($Name -ne $env:COMPUTERNAME)
                {
                    # Rename the computer, and join it to the workgroup.
                    Add-Computer -NewName $Name -Credential $Credential -WorkgroupName $WorkGroupName -Force
                    Write-Verbose -Message "Renamed computer to '$($Name)' and addded to workgroup '$($WorkGroupName)'."
                }
                else
                {
                    # Same computer name, and join it to the workgroup.
                    Add-Computer -WorkGroupName $WorkGroupName -Credential $Credential -Force
                    Write-Verbose -Message "Added computer to workgroup '$($WorkGroupName)'."
                }
            }
        }
        elseif ($Name -ne $env:COMPUTERNAME)
        {
            if (Get-ComputerDomain)
            {
                Rename-Computer -NewName $Name -DomainCredential $Credential -Force
                Write-Verbose -Message "Renamed computer to '$($Name)'."
            }
            else
            {
                Rename-Computer -NewName $Name -Force
                Write-Verbose -Message "Renamed computer to '$($Name)'."
            }
        }
    }
    else
    {
        if ($DomainName)
        {
            throw 'Missing domain join credentials.'
        }
        if ($WorkGroupName)
        {

            if ($WorkGroupName -eq (Get-CimInstance -Class 'Win32_ComputerSystem').Workgroup)
            {
                # Same workgroup, new computer name
                Rename-Computer -NewName $Name -force
                Write-Verbose -Message "Renamed computer to '$($Name)'."
            }
            else
            {
                if ($name -ne $env:COMPUTERNAME)
                {
                    # New workgroup, new computer name
                    Add-Computer -WorkgroupName $WorkGroupName -NewName $Name
                    Write-Verbose -Message "Renamed computer to '$($Name)' and added to workgroup '$($WorkGroupName)'."
                }
                else
                {
                    # New workgroup, same computer name
                    Add-Computer -WorkgroupName $WorkGroupName
                    Write-Verbose -Message "Added computer to workgroup '$($WorkGroupName)'."
                }
            }
        }
        else
        {
            if ($Name -ne $env:COMPUTERNAME)
            {
                Rename-Computer -NewName $Name
                Write-Verbose -Message "Renamed computer to '$($Name)'."
            }
        }
    }

    $global:DSCMachineStatus = 1
}

function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateLength(1, 15)]
        [ValidateScript( {$_ -inotmatch '[\/\\:*?"<>|]' })]
        [System.String]
        $Name,

        [Parameter()]
        [System.String]
        $JoinOU,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $Credential,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $UnjoinCredential,

        [Parameter()]
        [System.String]
        $DomainName,

        [Parameter()]
        [System.String]
        $WorkGroupName
    )

    Write-Verbose -Message 'Validate desired Name is a valid name'

    Write-Verbose -Message 'Checking if computer name is correct'
    if (($Name -ne 'localhost') -and ($Name -ne $env:COMPUTERNAME))
    {
        return $false
    }

    Assert-DomainOrWorkGroup -DomainName $DomainName -WorkGroupName $WorkGroupName

    if ($DomainName)
    {
        if (-not ($Credential))
        {
            throw 'Need to specify credentials with domain'
        }

        try
        {
            Write-Verbose "Checking if the machine is a member of $DomainName."

            if ($DomainName.Contains('.'))
            {
                $getComputerDomainParameters = @{
                    netbios = $false
                }
            }
            else
            {
                $getComputerDomainParameters = @{
                    netbios = $true
                }
            }

            return ($DomainName -eq (Get-ComputerDomain @getComputerDomainParameters))
        }
        catch
        {
            Write-Verbose 'The machine is not a domain member.'

            return $false
        }
    }
    elseif ($WorkGroupName)
    {
        Write-Verbose -Message "Checking if workgroup name is $WorkGroupName"

        return ($WorkGroupName -eq (Get-CimInstance -Class 'Win32_ComputerSystem').Workgroup)
    }
    else
    {
        # No Domain or Workgroup specified and computer name is correct
        return $true
    }
}

function Assert-DomainOrWorkGroup($DomainName, $WorkGroupName)
{
    if ($DomainName -and $WorkGroupName)
    {
        throw 'Only DomainName or WorkGroupName can be specified at once.'
    }
}

function Get-ComputerDomain
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [Switch]
        $NetBios
    )

    try
    {
        if ($NetBios)
        {
            $domainName = $ENV:USERDOMAIN
        }
        else
        {
            $domainName = ([System.DirectoryServices.ActiveDirectory.Domain]::GetComputerDomain()).Name
        }
        return $domainName
    }
    catch [System.Management.Automation.MethodInvocationException]
    {
        Write-Debug 'This machine is not a domain member.'
    }
}

function Get-ComputerOU
{
    $ou = $null

    if (Get-ComputerDomain)
    {
        $dn = $null
        $dn = ([adsisearcher]"(&(objectCategory=computer)(objectClass=computer)(cn=$env:COMPUTERNAME))").FindOne().Properties.distinguishedname
        $ou = $dn -replace '^(CN=.*?(?<=,))', ''
    }

    return $ou
}

Export-ModuleMember -Function *-TargetResource