DSCResources/myAdHelper.psm1

#Tries to get-module, if impossible then add necessary windows feature, then import module
function LoadModuleFeature
{
    Param ($Module,
        $Feature)
    
    if ((Get-Module -Name $Module -ListAvailable) -eq $null)
    {
        Write-Host "Adding Feature $Feature"
        Add-WindowsFeature $Feature -ErrorAction SilentlyContinue
        
        Write-Host "Adding Module $Module"
        Import-Module $Module -ErrorAction SilentlyContinue
    }
}

#allows to use hardcoded tokens in DSC configs, being replaced by real value
function ReplacePartitionTokens
{
    param
    (
        [String]$Identity,
        [String]$Server,
        [System.Management.Automation.PSCredential]$Credential
    )
    $myParams = @{ } + $PSBoundParameters
    $myParams.Remove('Ensure') | out-null
    $myParams.Remove('Identity') | out-null
    
    LoadModuleFeature -Module ActiveDirectory -Feature RSAT-AD-PowerShell
    
    try
    {
        $ADRootDSE = Get-ADRootDSE @myParams
        if ($ADRootDSE)
        {
            $Conf = $ADRootDSE.configurationNamingContext
            $Domain = $ADRootDSE.defaultNamingContext
            $Schema = $ADRootDSE.schemaNamingContext
            $Result = $Identity -Replace ("(?s)%%configuration%%", $Conf) -Replace ("(?s)%%domain%%", $Domain) -Replace ("(?s)%%schema%%", $Schema)
        }
    }
    catch [Exception]
    {
        $Err = $_.Exception.GetType().FullName + ' - ' + $_.Exception.Message
        $Result = $null
    }
    if ($Err)
    {
        Write-Host $Err
    }
    $Result
}



function myGetAdObject
{
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [String]$Identity,
        [String]$Server,
        [String]$Filter,
        [String]$SearchBase,
        $Properties,
        [System.Management.Automation.PSCredential]$Credential
    )
    
    $myParams = @{ } + $PSBoundParameters
    $myParams.Remove('Ensure') | out-null
    $myParams.Remove('Debug') | out-null
    $myParams.Remove('ErrorAction') | out-null
    $myParams.Remove('Verbose') | out-null
    
    LoadModuleFeature -Module ActiveDirectory -Feature RSAT-AD-PowerShell
    
    try
    {
        $myAdObject = Get-ADObject @myParams
    }
    catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] { $Err = 'Object not found' }
    catch [Microsoft.ActiveDirectory.Management.ADServerDownException] { $Err = 'Server not ready' }
    catch [System.Security.Authentication.AuthenticationException] { $Err = 'Authentication rejected' }
    catch [Exception]
    {
        $Err = $_.Exception.GetType().FullName + ' - ' + $_.Exception.Message
        $myAdObject = $null
    }
    if ($Err)
    {
        Write-Host $Err
    }
    $myAdObject
}

function StartAndWaitWaitForProcessEnd
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]$Path,
        [String]$Arguments,
        [PSCredential]$Credential,
        [Int]$Delay = 7200,
        [String]$TaskName,
        [String]$ProcessToWaitFor
    )
    
    if ( (Test-Path $Path) -eq $false)
    {
        throw "Unable to find file: $Path"
        break
    }
    
    $Parent = [io.path]::GetDirectoryName($Path)
    $ProcessName = [io.path]::GetFileNameWithoutExtension($Path)
    

    $TaskAction = New-ScheduledTaskAction -Execute $Path -Argument $Arguments -WorkingDirectory $Parent
    $Task = Register-ScheduledTask -TaskName $TaskName -Action $TaskAction -User $Credential.UserName -Password $Credential.GetNetworkCredential().Password -RunLevel Highest -ErrorAction SilentlyContinue -Force
    
    if ($Task -ne $null -and $Task.State -eq "Ready")
    {
        Start-ScheduledTask -TaskName $TaskName
        
        $start = [DateTime]::Now; $ProcessId = 0;
        do
        {
            Write-Verbose "Waiting for process to start: $Path"
            Write-Host "Waiting for process to start: $Path"
            $IsExchSetupRunning = IsProcessRunning -ProcessName $ProcessToWaitFor
            Start-Sleep -Seconds 3
        }
        while ($IsExchSetupRunning -eq $false -and ([DateTime]::Now - $start).TotalSeconds -lt 60)
        
        if ($IsExchSetupRunning -eq $false)
        {
            throw "Unable to start process within 60 seconds: $Path"
        }
        else
        {
            Write-Verbose "Waiting for process to end: $Path"
            Write-Host "Waiting for process to end: $Path"
            $start = [DateTime]::Now;
            do
            {
                $IsExchSetupRunning = IsProcessRunning -ProcessName $ProcessToWaitFor
                Start-Sleep -Seconds 3
            }
            while ($IsExchSetupRunning -eq $true -and ([DateTime]::Now - $start).TotalSeconds -lt $Delay)
            
            if ($IsExchSetupRunning -eq $true)
            {
                Write-Error "Process not finished: $Path"
                Stop-ScheduledTask -TaskName $TaskName
            }
            Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue
        }
    }
    else
    {
        throw "Failed to start Scheduled Task $TaskName"
    }
}

#Checks whether setup is running by looking for if the ExSetup.exe process currently exists
function IsProcessRunning
{
    param ([string]$ProcessName)
    
    return ((Get-Process -Name $ProcessName -ErrorAction SilentlyContinue) -ne $null)
}