DSCResources/HWG_cDFSNameSpace/HWG_cDFSNameSpace.psm1

#######################################################################################
# cDFSNameSpace :This resource is used to create, edit or remove DFS namespaces
# for both domain and local DFS.
# When the server is the last server in the namespace, the namespace itself will be removed.
#######################################################################################

data LocalizedData
{
# culture="en-US"
ConvertFrom-StringData -StringData @'
GettingNamespaceMessage=Getting [{0}] DFS namespace [{1} {2}].
DFSAvailabilityMessage=DFS {0} is {2}available [{1}].
NamespaceExistsMessage=DFS namespace [{0} {1}] exists.
TestErrorMessage=[Error] {0} [{1}] and [{2}] do not match.
NamespaceUpdatedMessage=Setting DFS namespace {0} to value "{1}".
NamespaceRequirementError=[Error] DFS Requirement {0} "{1}" was not available.
NamespaceRemoveMessage=Removing {0} from DFS namespace [{1}].
'@

}

######################################################################################
# The Get-TargetResource cmdlet.
######################################################################################
function Get-TargetResource 
{
    [OutputType([Hashtable])]
    param
    (
        [parameter(Mandatory = $true)]
        [String]
        $NameSpace,

        [parameter(Mandatory = $true)]
        [ValidateSet('Present','Absent')]
        [String]
        $Ensure,
        
        [String]
        $ComputerName = ($ENV:COMPUTERNAME),

        [String]
        $DomainName
    )       

    Write-Verbose -Message ( @(
            "$($MyInvocation.MyCommand): "
            $($LocalizedData.GettingNamespaceMessage) -f $ComputerName, $DomainName, $NameSpace
    ) -join '' )

    # Create pathname
    if ($DomainName) 
    {
        $DFSRootName = $DomainName
    }
    else 
    {
        $DFSRootName = $ComputerName.ToUpper()
    }

    $Path = "\\$DFSRootName\$NameSpace"

    # get DFS root settings
    $error.Clear()
    $DFSnRoot = Get-DfsnRoot -ComputerName $ComputerName 
    
    # $DFSnRoot is Nok, DFS is not available
    if ($error){
        $ReturnValue = @{
            Error = 'DFS root not available'
        }
        $Availability = 'not '        
    }    

    Write-Verbose -Message ( @(
            "$($MyInvocation.MyCommand): "
            $($LocalizedData.DFSAvailabilityMessage) -f 'root', $DFSRootName, $Availability
    ) -join '' )

    # $DFSnRoot is ok, DFS is available, check if path is allready configured
    $Result = Get-DfsnRootTarget -Path $Path -ErrorAction SilentlyContinue 

    # No DFS available
    if ($ReturnValue)
    {
        $ReturnValue += @{
            Present = 'Absent'
        }
    }

    else
    {
        $DFSnRoot = Get-DfsnRoot -Path $Path -ErrorAction SilentlyContinue    
                
        $ReturnValue = @{
            Path          = $DFSnRoot.Path
            Type          = $DFSnRoot.Type
            Properties    = $DFSnRoot.Properties
            TimeToLiveSec = $DFSnRoot.TimeToLiveSec
            State         = $DFSnRoot.State
            Description   = $DFSnRoot.Description                        
        }

        $MemberValue = 'Absent'

        # path is allready configured
        if ($Result)
        {
            # Get path members
            $Members += $Result | ForEach-Object -Process { $_.TargetPath.split('\')[2]}        
            
            # Computername allready member of dfs
            if ($Members -contains $Computername)
            {
                $MemberValue = 'Present'
            }
        }
        else
        {
            $Availability = 'not '
        }

        $ReturnValue += @{
            Members = $Members
            Present = $MemberValue
        }
        
        Write-Verbose -Message ( @(
            "$($MyInvocation.MyCommand): "
            $($LocalizedData.DFSAvailabilityMessage) -f 'NameSpace', $Path, $Availability
        ) -join '' )

        if ($MemberValue -eq 'Absent')
        {
            $Availability = 'not '
        }

        Write-Verbose -Message ( @(
                "$($MyInvocation.MyCommand): "
                $($LocalizedData.DFSAvailabilityMessage) -f 'ComputerName', $Path, $Availability
        ) -join '' )   
    }

    $ReturnValue

} # Get-TargetResource

######################################################################################
# The Set-TargetResource cmdlet.
######################################################################################
function Set-TargetResource
{
    param
    (
        [parameter(Mandatory = $true)]
        [String]
        $NameSpace,

        [parameter(Mandatory = $true)]
        [ValidateSet('Present','Absent')]
        [String]
        $Ensure,

        [String]
        $ComputerName = ($ENV:COMPUTERNAME),

        [String]
        $DomainName,
        
        [String]
        $Description
    )

    Write-Verbose -Message ( @(
            "$($MyInvocation.MyCommand): "
            $($LocalizedData.GettingNamespaceMessage) -f $ComputerName, $DomainName, $NameSpace
    ) -join '' )

    # Create pathnames
    if ($DomainName) 
    {
        $DFSRootName = $DomainName
    }
    else 
    {
        $DFSRootName = $ComputerName.ToUpper()
    }

    $Path = "\\$DFSRootName\$NameSpace"
    $TargetPath = "\\$($ComputerName.ToUpper())\$NameSpace"

    # Lookup the existing namespaces
    $DFSnRoot = Get-DfsnRoot -ComputerName $ComputerName -ErrorAction Stop       

    Write-Verbose -Message ( @(
            "$($MyInvocation.MyCommand): "
            $($LocalizedData.DFSAvailabilityMessage) -f 'root', $DFSRootName, $Availability
    ) -join '' )

    if ($Ensure -eq 'Present')
    {
        # Set desired Configuration
        $DesiredConfiguration = @{
                Path = $Path
                State = 'online'                                 
        }

        # check if we want to use New-DfsnRoot or Set-DfsnRoot; Get DFS root Settings specified path
        $DFSnRoot = Get-DfsnRoot -Path $Path -ErrorAction SilentlyContinue

        if (!$DFSnRoot)
        {
            # New-DfsnRoot: Check share availability
            if (!$(Get-SmbShare $NameSpace -ErrorAction SilentlyContinue))
            {
                Write-Verbose -Message ( @(
                    "$($MyInvocation.MyCommand): "
                    $($LocalizedData.NamespaceRequirementError) -f 'share', $NameSpace
                ) -join '' )
            }                

            else
            {
                # Add additional information
                if ($DomainName)
                {
                    $DesiredConfiguration += @{
                        Type = 'DomainV2'
                    }                    
                }
                else
                {
                    $DesiredConfiguration += @{
                        Type = 'Standalone'
                    }
                }

                if (!$Description)
                {
                    $Description = "DFS of namespace $NameSpace"
                }                    

                $DesiredConfiguration += @{
                    TargetPath  = $TargetPath
                    Description = $Description
                }

                # create New-DfsnRoot
                New-DfsnRoot @DesiredConfiguration | Out-Null                   
                $DesiredConfiguration.GetEnumerator() | ForEach-Object -Process {                
                    Write-Verbose -Message ( @(
                        "$($MyInvocation.MyCommand): "
                        $($LocalizedData.NamespaceUpdatedMessage) -f $_.name, $_.value
                    ) -join '' )  
                }
            }
        }
        else
        {
            if (!$($DFSnRoot.Description))
            {
                $DesiredConfiguration += @{                    
                    Description = "DFS of namespace $NameSpace"
                }
            }

            # reset settings
            Set-DfsnRoot @DesiredConfiguration | Out-Null
            $DesiredConfiguration.GetEnumerator() | ForEach-Object -Process {                
                Write-Verbose -Message ( @(
                    "$($MyInvocation.MyCommand): "
                    $($LocalizedData.NamespaceUpdatedMessage) -f $_.name, $_.value
                ) -join '' )            
            }
                    
            # Get settings root target
            $DFSnRootTarget = Get-DfsnRootTarget -Path $Path -ErrorAction SilentlyContinue 

            # Get path members
            $Members += $DFSnRootTarget | ForEach-Object -Process { $_.TargetPath.split('\')[2]}        
            
            # Computername is no member of dfs namespace
            if ($Members -notcontains $Computername)                
            {
                # add server to namespace
                new-DfsnRootTarget -Path $path -TargetPath $TargetPath | Out-Null
            }
        }
    }
        
    # Remove DFS namespace if absent is set
    else
    {
        # Get settings root target
        $DFSnRootTarget = Get-DfsnRootTarget -Path $Path -ErrorAction SilentlyContinue 

        # Get path members
        $Members += $DFSnRootTarget | ForEach-Object -Process { $_.TargetPath.split('\')[2]}        
            
        # Computername is last member of dfs namespace
        if ($Members.count -eq 1)                
        {
            # remove namespace
            Remove-DfsnRoot -Path $Path -Confirm:$false -Force | out-null
            Write-Verbose -Message ( @(
                "$($MyInvocation.MyCommand): "
                $($LocalizedData.NamespaceRemoveMessage) -f 'last server', $Path
            ) -join '' )  
        }            
        else
        {                
            # remove computername from namespace
            remove-DfsnRootTarget -Path $Path -TargetPath $TargetPath -Confirm:$false | out-null

            Write-Verbose -Message ( @(
                "$($MyInvocation.MyCommand): "
                $($LocalizedData.NamespaceRemoveMessage) -f $ComputerName, $Path
            ) -join '' )   
        }
            
        Remove-SmbShare  -Name $NameSpace -Confirm:$false | out-null
        Write-Verbose -Message ( @(
            "$($MyInvocation.MyCommand): "
            $($LocalizedData.NamespaceRemoveMessage) -f 'share', $ComputerName
        ) -join '' )             
    }
} # Set-TargetResource

######################################################################################
# The Test-TargetResource cmdlet.
######################################################################################
function Test-TargetResource
{
    [OutputType([System.Boolean])]
    param
    (
        [parameter(Mandatory = $true)]
        [String]
        $NameSpace,

        [parameter(Mandatory = $true)]
        [ValidateSet('Present','Absent')]
        [String]
        $Ensure,

        [String]
        $ComputerName = ($ENV:COMPUTERNAME),

        [String]
        $DomainName,
        
        [String]
        $Description
    )

    # Flag to signal whether settings are correct
    [Boolean] $DesiredConfigurationMatch = $true    
    
    # Gather result from the test
    $ResultfromGet = Get-TargetResource -Namespace $NameSpace `
                                        -ComputerName $ComputerName `
                                        -DomainName $DomainName `
                                        -Ensure $Ensure
            
    # The test returned an error, display and stop
    if ($ResultfromGet.Error)
    {
        Write-Verbose -Message ("$($MyInvocation.MyCommand): [Error] $($ResultfromGet.Error)") 
        $DesiredConfigurationMatch = $false
    }
    
    else 
    {
        # Test ensure: Present / Absent
        if ($ResultfromGet.Present -ne $Ensure)
        {
            Write-Verbose -Message ( @(
                    "$($MyInvocation.MyCommand): "
                    $($LocalizedData.TestErrorMessage) -f 'ensuration', $Ensure, $ResultfromGet.Present
            ) -join '' )          
            $DesiredConfigurationMatch = $false
        }
        
        if ($Ensure -like 'Present')
        {
            # Test Description
            if (($Description) -and ($ResultfromGet.Description -ne $Description)) 
            {
                Write-Verbose -Message ( @(
                        "$($MyInvocation.MyCommand): "
                        $($LocalizedData.TestErrorMessage) -f 'Descriptions', $Description, $ResultfromGet.Description
                ) -join '' )                    
                $DesiredConfigurationMatch = $false
            }
                       
            # Test Type Domain/ Standalone
            if ( (($DomainName) -and ($ResultfromGet.Type -notmatch 'Domain')) -or
                ((!$DomainName) -and ($ResultfromGet.Type -notmatch 'Standalone'))
            )
            {                    
                Write-Verbose -Message ( @(
                        "$($MyInvocation.MyCommand): "
                        $($LocalizedData.TestErrorMessage) -f 'Domains', $DomainName, $($ResultfromGet.Type)
                ) -join '' ) 
                $DesiredConfigurationMatch = $false
            }        
        }
    }
               
    return $DesiredConfigurationMatch 

} # Test-TargetResource
######################################################################################

Export-ModuleMember -Function *-TargetResource