DSCResources/MSFT_xSQLServerFailoverClusterSetup/MSFT_xSQLServerFailoverClusterSetup.psm1

# NOTE: This resource requires WMF5 and PsDscRunAsCredential

$currentPath = Split-Path -Parent $MyInvocation.MyCommand.Path
Write-Debug -Message "CurrentPath: $currentPath"

# Load Common Code
Import-Module $currentPath\..\..\xSQLServerHelper.psm1 -Verbose:$false -ErrorAction Stop

function Get-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [parameter(Mandatory = $true)]
        [ValidateSet("Prepare","Complete")]
        [System.String]
        $Action,

        [System.String]
        $SourcePath = "$PSScriptRoot\..\..\",

        [System.String]
        $SourceFolder = "Source",

        [parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        $SetupCredential,

        [System.Management.Automation.PSCredential]
        $SourceCredential,

        [System.Boolean]
        $SuppressReboot,

        [System.Boolean]
        $ForceReboot,

        [parameter(Mandatory = $true)]
        [System.String]
        $Features,

        [parameter(Mandatory = $true)]
        [System.String]
        $InstanceName,

        [System.String]
        $InstanceID = $InstanceName,

        [System.String]
        $PID,

        [System.String]
        $UpdateEnabled = $True,

        [System.String]
        $UpdateSource = ".\Updates",

        [System.String]
        $SQMReporting,

        [System.String]
        $ErrorReporting,

        [System.String]
        $FailoverClusterGroup = "SQL Server ($InstanceName)",

        [parameter(Mandatory = $true)]
        [System.String]
        $FailoverClusterNetworkName,

        [System.String]
        $FailoverClusterIPAddress,

        [System.String]
        $InstallSharedDir,

        [System.String]
        $InstallSharedWOWDir,

        [System.String]
        $InstanceDir,

        [parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        $SQLSvcAccount,

        [System.Management.Automation.PSCredential]
        $AgtSvcAccount = $SQLSvcAccount,

        [System.String]
        $SQLCollation,

        [System.String[]]
        $SQLSysAdminAccounts,

        [System.String]
        $SecurityMode,

        [System.Management.Automation.PSCredential]
        $SAPwd = $SetupCredential,

        [System.String]
        $InstallSQLDataDir,

        [System.String]
        $SQLUserDBDir,

        [System.String]
        $SQLUserDBLogDir,

        [System.String]
        $SQLTempDBDir,

        [System.String]
        $SQLTempDBLogDir,

        [System.String]
        $SQLBackupDir,

        [System.Management.Automation.PSCredential]
        $ASSvcAccount = $SQLSvcAccount,

        [System.String]
        $ASCollation,

        [System.String[]]
        $ASSysAdminAccounts,

        [System.String]
        $ASDataDir,

        [System.String]
        $ASLogDir,

        [System.String]
        $ASBackupDir,

        [System.String]
        $ASTempDir,

        [System.String]
        $ASConfigDir,

        [System.Management.Automation.PSCredential]
        $ISSvcAccount = $SQLSvcAccount,

        [System.String]
        $ISFileSystemFolder
    )

    $InstanceName = $InstanceName.ToUpper()

    Import-Module $PSScriptRoot\..\..\xPDT.psm1

    if($SourceCredential)
    {
        NetUse -SourcePath $SourcePath -Credential $SourceCredential -Ensure "Present"
    }
    $Path = Join-Path -Path (Join-Path -Path $SourcePath -ChildPath $SourceFolder) -ChildPath "setup.exe"
    $Path = ResolvePath $Path
    Write-Verbose "Path: $Path"
    $SQLVersion = GetSQLVersion -Path $Path
    if($SourceCredential)
    {
        NetUse -SourcePath $SourcePath -Credential $SourceCredential -Ensure "Absent"
    }
    
    if($InstanceName -eq "MSSQLSERVER")
    {
        $DBServiceName = "MSSQLSERVER"
        $AgtServiceName = "SQLSERVERAGENT"
        $ASServiceName = "MSSQLServerOLAPService"
    }
    else
    {
        $DBServiceName = "MSSQL`$$InstanceName"
        $AgtServiceName = "SQLAgent`$$InstanceName"
        $ASServiceName = "MSOLAP`$$InstanceName"
    }
    $ISServiceName = "MsDtsServer" + $SQLVersion + "0"

    if(Get-WmiObject -Namespace root/mscluster -Class MSCluster_ResourceGroup  -ErrorAction SilentlyContinue | Where-Object {$_.Name -eq $FailoverClusterGroup})
    {
        $Complete = $true
        $FailoverClusterNetworkName = (Get-ClusterGroup -Name $FailoverClusterGroup | Get-ClusterResource | Where-Object {$_.ResourceType -eq "Network Name"} | Get-ClusterParameter -Name "Name").Value
        $FailoverClusterIPAddress = (Get-ClusterGroup -Name $FailoverClusterGroup | Get-ClusterResource | Where-Object {$_.ResourceType -eq "IP Address"} | Get-ClusterParameter -Name "Address").Value
    }
    else
    {
        $FailoverClusterGroup = $null
        $FailoverClusterNetworkName = $null
        $FailoverClusterIPAddress = $null
        $Complete = $false
    }
    
    $Services = Get-Service
    $Features = ""
    if($Services | Where-Object {$_.Name -eq $DBServiceName})
    {
        $Features += "SQLENGINE,"
        $SQLSvcAccountUsername = (Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq $DBServiceName}).StartName
        $AgtSvcAccountUsername = (Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq $AgtServiceName}).StartName
        $InstanceID = ((Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL' -Name $InstanceName).$InstanceName).Split(".")[1]
        $FullInstanceID = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL' -Name $InstanceName).$InstanceName
        $InstanceID = $FullInstanceID.Split(".")[1]
        $InstanceDir = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$FullInstanceID\Setup" -Name 'SqlProgramDir').SqlProgramDir.Trim("\")
    }
    if($Services | Where-Object {$_.Name -eq $ASServiceName})
    {
        $Features += "AS,"
        $ASSvcAccountUsername = (Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq $ASServiceName}).StartName
    }
    if($Services | Where-Object {$_.Name -eq $ISServiceName})
    {
        $Features += "IS,"
        $ISSvcAccountUsername = (Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq $ISServiceName}).StartName
    }
    $Products = Get-WmiObject -Class Win32_Product
    switch($SQLVersion)
    {
        "11"
        {
            $IdentifyingNumber = "{A7037EB2-F953-4B12-B843-195F4D988DA1}"
        }
        "12"
        {
            $IdentifyingNumber = "{75A54138-3B98-4705-92E4-F619825B121F}"
        }
    }
    if($Products | Where-Object {$_.IdentifyingNumber -eq $IdentifyingNumber})
    {
        $Features += "SSMS,"
    }
    switch($SQLVersion)
    {
        "11"
        {
            $IdentifyingNumber = "{7842C220-6E9A-4D5A-AE70-0E138271F883}"
        }
        "12"
        {
            $IdentifyingNumber = "{B5ECFA5C-AC4F-45A4-A12E-A76ABDD9CCBA}"
        }
    }
    if($Products | Where-Object {$_.IdentifyingNumber -eq $IdentifyingNumber})
    {
        $Features += "ADV_SSMS,"
    }
    $Features = $Features.Trim(",")
    if($Features -ne "")
    {
        switch($SQLVersion)
        {
            "11"
            {
                $InstallSharedDir = (GetFirstItemPropertyValue -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components" -Name "FEE2E540D20152D4597229B6CFBC0A69")
                $InstallSharedWOWDir = (GetFirstItemPropertyValue -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components" -Name "A79497A344129F64CA7D69C56F5DD8B4")
            }
            "12"
            {
                $InstallSharedDir = (GetFirstItemPropertyValue -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components" -Name "FEE2E540D20152D4597229B6CFBC0A69")
                $InstallSharedWOWDir = (GetFirstItemPropertyValue -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components" -Name "C90BFAC020D87EA46811C836AD3C507F")
            }
            "13"
            {
                $InstallSharedDir = (GetFirstItemPropertyValue -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components" -Name "FEE2E540D20152D4597229B6CFBC0A69")
                $InstallSharedWOWDir = (GetFirstItemPropertyValue -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components" -Name "A79497A344129F64CA7D69C56F5DD8B4")
            }
        }
    }

    $returnValue = @{
        Action = $Action
        SourcePath = $SourcePath
        SourceFolder = $SourceFolder
        Features = $Features
        InstanceName = $InstanceName
        InstanceID = $InstanceID
        FailoverClusterGroup = $FailoverClusterGroup
        FailoverClusterNetworkName = $FailoverClusterNetworkName
        FailoverClusterIPAddress = $FailoverClusterIPAddress
        InstallSharedDir = $InstallSharedDir
        InstallSharedWOWDir = $InstallSharedWOWDir
        InstanceDir = $InstanceDir
        SQLSvcAccountUsername = $SQLSvcAccountUsername
        AgtSvcAccountUsername = $AgtSvcAccountUsername
        ASSvcAccountUsername = $ASSvcAccountUsername
        ISSvcAccountUsername = $ISSvcAccountUsername
    }

    $returnValue
}


function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory = $true)]
        [ValidateSet("Prepare","Complete")]
        [System.String]
        $Action,

        [System.String]
        $SourcePath = "$PSScriptRoot\..\..\",

        [System.String]
        $SourceFolder = "Source",

        [parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        $SetupCredential,

        [System.Management.Automation.PSCredential]
        $SourceCredential,

        [System.Boolean]
        $SuppressReboot,

        [System.Boolean]
        $ForceReboot,

        [parameter(Mandatory = $true)]
        [System.String]
        $Features,

        [parameter(Mandatory = $true)]
        [System.String]
        $InstanceName,

        [System.String]
        $InstanceID = $InstanceName,

        [System.String]
        $PID,

        [System.String]
        $UpdateEnabled = $True,

        [System.String]
        $UpdateSource = ".\Updates",

        [System.String]
        $SQMReporting,

        [System.String]
        $ErrorReporting,

        [System.String]
        $FailoverClusterGroup = "SQL Server ($InstanceName)",

        [parameter(Mandatory = $true)]
        [System.String]
        $FailoverClusterNetworkName,

        [System.String]
        $FailoverClusterIPAddress,

        [System.String]
        $InstallSharedDir,

        [System.String]
        $InstallSharedWOWDir,

        [System.String]
        $InstanceDir,

        [parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        $SQLSvcAccount,

        [System.Management.Automation.PSCredential]
        $AgtSvcAccount = $SQLSvcAccount,

        [System.String]
        $SQLCollation,

        [System.String[]]
        $SQLSysAdminAccounts,

        [System.String]
        $SecurityMode,

        [System.Management.Automation.PSCredential]
        $SAPwd = $SetupCredential,

        [System.String]
        $InstallSQLDataDir,

        [System.String]
        $SQLUserDBDir,

        [System.String]
        $SQLUserDBLogDir,

        [System.String]
        $SQLTempDBDir,

        [System.String]
        $SQLTempDBLogDir,

        [System.String]
        $SQLBackupDir,

        [System.Management.Automation.PSCredential]
        $ASSvcAccount = $SQLSvcAccount,

        [System.String]
        $ASCollation,

        [System.String[]]
        $ASSysAdminAccounts,

        [System.String]
        $ASDataDir,

        [System.String]
        $ASLogDir,

        [System.String]
        $ASBackupDir,

        [System.String]
        $ASTempDir,

        [System.String]
        $ASConfigDir,

        [System.Management.Automation.PSCredential]
        $ISSvcAccount = $SQLSvcAccount,

        [System.String]
        $ISFileSystemFolder
    )

    $InstanceName = $InstanceName.ToUpper()

    Import-Module $PSScriptRoot\..\..\xPDT.psm1
        
    if($SourceCredential)
    {
        NetUse -SourcePath $SourcePath -Credential $SourceCredential -Ensure "Present"
        $TempFolder = [IO.Path]::GetTempPath()
        & robocopy.exe (Join-Path -Path $SourcePath -ChildPath $SourceFolder) (Join-Path -Path $TempFolder -ChildPath $SourceFolder) /e
        $SourcePath = $TempFolder
        NetUse -SourcePath $SourcePath -Credential $SourceCredential -Ensure "Absent"
    }
    $Path = Join-Path -Path (Join-Path -Path $SourcePath -ChildPath $SourceFolder) -ChildPath "setup.exe"
    $Path = ResolvePath $Path
    $SQLVersion = GetSQLVersion -Path $Path
    
    foreach($feature in $Features.Split(","))
    { 
        if(($SQLVersion -eq "13") -and (($feature -eq "SSMS") -or ($feature -eq "ADV_SSMS")))
        {
            Throw New-TerminatingError -ErrorType FeatureNotSupported -FormatArgs @($feature) -ErrorCategory InvalidData
        }
    }

    switch($Action)
    {
        "Prepare"
        {
            # If SQL shared components already installed, clear InstallShared*Dir variables
            switch($SQLVersion)
            {
                "11"
                {
                    if((Get-Variable -Name "InstallSharedDir" -ErrorAction SilentlyContinue) -and (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\30AE1F084B1CF8B4797ECB3CCAA3B3B6" -ErrorAction SilentlyContinue))
                    {
                        Set-Variable -Name "InstallSharedDir" -Value ""
                    }
                    if((Get-Variable -Name "InstallSharedWOWDir" -ErrorAction SilentlyContinue) -and (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\A79497A344129F64CA7D69C56F5DD8B4" -ErrorAction SilentlyContinue))
                    {
                        Set-Variable -Name "InstallSharedWOWDir" -Value ""
                    }
                }
                "12"
                {
                    if((Get-Variable -Name "InstallSharedDir" -ErrorAction SilentlyContinue) -and (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\FEE2E540D20152D4597229B6CFBC0A69" -ErrorAction SilentlyContinue))
                    {
                        Set-Variable -Name "InstallSharedDir" -Value ""
                    }
                    if((Get-Variable -Name "InstallSharedWOWDir" -ErrorAction SilentlyContinue) -and (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\C90BFAC020D87EA46811C836AD3C507F" -ErrorAction SilentlyContinue))
                    {
                        Set-Variable -Name "InstallSharedWOWDir" -Value ""
                    }
                }
                "13"
                {
                    if((Get-Variable -Name "InstallSharedDir" -ErrorAction SilentlyContinue) -and (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\FEE2E540D20152D4597229B6CFBC0A69" -ErrorAction SilentlyContinue))
                    {
                        Set-Variable -Name "InstallSharedDir" -Value ""
                    }
                    if((Get-Variable -Name "InstallSharedWOWDir" -ErrorAction SilentlyContinue) -and (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\A79497A344129F64CA7D69C56F5DD8B4" -ErrorAction SilentlyContinue))
                    {
                        Set-Variable -Name "InstallSharedWOWDir" -Value ""
                    }
                }
            }

            # Create install arguments
            $Arguments = "/SkipRules=`"Cluster_VerifyForErrors`" /Quiet=`"True`" /IAcceptSQLServerLicenseTerms=`"True`" /Action=`"PrepareFailoverCluster`""
            $ArgumentVars = @(
                "InstanceName",
                "InstanceID",
                "UpdateEnabled",
                "UpdateSource",
                "Features",
                "PID",
                "SQMReporting",
                "ErrorReporting",
                "InstallSharedDir",
                "InstallSharedWOWDir",
                "InstanceDir"
            )
            foreach($ArgumentVar in $ArgumentVars)
            {
                if((Get-Variable -Name $ArgumentVar).Value -ne "")
                {
                    $Arguments += " /$ArgumentVar=`"" + (Get-Variable -Name $ArgumentVar).Value + "`""
                }
            }
            if($Features.Contains("SQLENGINE"))
            {
                $Arguments += " /AgtSvcAccount=`"" + $AgtSvcAccount.UserName + "`""
                $Arguments += " /AgtSvcPassword=`"" + $AgtSvcAccount.GetNetworkCredential().Password + "`""
                $Arguments += " /SQLSvcAccount=`"" + $SQLSvcAccount.UserName + "`""
                $Arguments += " /SQLSvcPassword=`"" + $SQLSvcAccount.GetNetworkCredential().Password + "`""
            }
            if($Features.Contains("AS"))
            {
                $Arguments += " /ASSvcAccount=`"" + $ASSvcAccount.UserName + "`""
                $Arguments += " /ASSvcPassword=`"" + $ASSvcAccount.GetNetworkCredential().Password + "`""
            }
            if($Features.Contains("IS"))
            {
                $Arguments += " /ISSvcAccount=`"" + $ISSvcAccount.UserName + "`""
                $Arguments += " /ISSvcPassword=`"" + $ISSvcAccount.GetNetworkCredential().Password + "`""
            }
        }
        "Complete"
        {
            # Remove trailing "\" from paths
            foreach($Var in @("InstallSQLDataDir","SQLUserDBDir","SQLUserDBLogDir","SQLTempDBDir","SQLTempDBLogDir","SQLBackupDir","ASDataDir","ASLogDir","ASBackupDir","ASTempDir","ASConfigDir","ISFileSystemFolder"))
            {
                if(Get-Variable -Name $Var -ErrorAction SilentlyContinue)
                {
                    Set-Variable -Name $Var -Value (Get-Variable -Name $Var).Value.TrimEnd("\")
                }
            }

            # Discover which cluster disks need to be added to this cluster group
            $Drives = @()
            foreach($Var in @("InstallSQLDataDir","SQLUserDBDir","SQLUserDBLogDir","SQLTempDBDir","SQLTempDBLogDir","SQLBackupDir","ASDataDir","ASLogDir","ASBackupDir","ASTempDir","ASConfigDir","ISFileSystemFolder"))
            {
                if(
                    (Get-Variable -Name $Var -ErrorAction SilentlyContinue) -and `
                    ((Get-Variable -Name $Var).Value.Length -ge 2) -and `
                    ((Get-Variable -Name $Var).Value.Substring(1,1) -eq ":")
                )
                {
                    $Drives += (Get-Variable -Name $Var).Value.Substring(0,2)
                }
            }
            $Drives = $Drives | Sort-Object -Unique
            $FailoverClusterDisks = @()
            $DiskResources = Get-WmiObject -Class MSCluster_Resource -Namespace root/mscluster | Where-Object {$_.Type -eq "Physical Disk"}
            foreach($DiskResource in $DiskResources)
            {
                $Disks = Get-WmiObject -Namespace root/mscluster -Query "Associators of {$DiskResource} Where ResultClass=MSCluster_Disk"
                foreach($Disk in $Disks)
                {
                    $Partitions = Get-WmiObject -Namespace root/mscluster -Query "Associators of {$Disk} Where ResultClass=MSCluster_DiskPartition"
                    foreach($Partition in $Partitions)
                    {
                        foreach($Drive in $Drives)
                        {
                            if($Partition.Path -eq $Drive)
                            {
                                $FailoverClusterDisks += $DiskResource.Name
                            }
                        }
                    }
                }
            }

            # Discover which cluster network to use for this cluster group
            $ClusterNetworks = @(Get-WmiObject -Namespace root/mscluster -Class MSCluster_Network)
            if([String]::IsNullOrEmpty($FailoverClusterIPAddress))
            {
                $FailoverClusterIPAddresses = "IPv4;DHCP;" + $ClusterNetwork[0].Name
            }
            else
            {
                $FailoverClusterIPAddressDecimal = ConvertDecimalIP -IPAddress $FailoverClusterIPAddress
                foreach($ClusterNetwork in $ClusterNetworks)
                {
                    $ClusterNetworkAddressDecimal = ConvertDecimalIP -IPAddress $ClusterNetwork.Address
                    $ClusterNetworkAddressMaskDecimal = ConvertDecimalIP -IPAddress $ClusterNetwork.AddressMask
                    if(($FailoverClusterIPAddressDecimal -band $ClusterNetworkAddressMaskDecimal) -eq ($ClusterNetworkAddressDecimal -band $ClusterNetworkAddressMaskDecimal))
                    {
                        $FailoverClusterIPAddresses = "IPv4;$FailoverClusterIPAddress;" + $ClusterNetwork.Name + ";" + $ClusterNetwork.AddressMask
                    }
                }
            }

            # Create install arguments
            $Arguments = "/SkipRules=`"Cluster_VerifyForErrors`" /Quiet=`"True`" /IAcceptSQLServerLicenseTerms=`"True`" /Action=`"CompleteFailoverCluster`""
            $ArgumentVars = @(
                "InstanceName",
                "FailoverClusterGroup",
                "FailoverClusterNetworkName",
                "FailoverClusterIPAddresses"
            )
            if($Features.Contains("SQLENGINE"))
            {
                $ArgumentVars += @(
                    "SecurityMode",
                    "SQLCollation",
                    "InstallSQLDataDir",
                    "SQLUserDBDir",
                    "SQLUserDBLogDir",
                    "SQLTempDBDir",
                    "SQLTempDBLogDir",
                    "SQLBackupDir"
                )
            }
            if($Features.Contains("AS"))
            {
                $ArgumentVars += @(
                    "ASCollation",
                    "ASDataDir",
                    "ASLogDir",
                    "ASBackupDir",
                    "ASTempDir",
                    "ASConfigDir"
                )
            }
            foreach($ArgumentVar in $ArgumentVars)
            {
                if((Get-Variable -Name $ArgumentVar).Value -ne "")
                {
                    $Arguments += " /$ArgumentVar=`"" + (Get-Variable -Name $ArgumentVar).Value + "`""
                }
            }
            if($FailoverClusterDisks.Count -ne 0)
            {
                $Arguments += " /FailoverClusterDisks="
                foreach($FailoverClusterDisk in $FailoverClusterDisks)
                {
                    $Arguments +="`"$FailoverClusterDisk`" "
                }
                $Arguments = $Arguments.Trim()
            }
            if($Features.Contains("SQLENGINE"))
            {
                $Arguments += " /SQLSysAdminAccounts=`"" + $SetupCredential.UserName + "`""
                if($PSBoundParameters.ContainsKey("SQLSysAdminAccounts"))
                {
                    foreach($AdminAccount in $SQLSysAdminAccounts)
                    {
                        $Arguments += " `"$AdminAccount`""
                    }
                }
                if($SecurityMode -eq "SQL")
                {
                    $Arguments += " /SAPwd=`"" + $SAPwd.GetNetworkCredential().Password + "`""
                }
            }
            if($Features.Contains("AS"))
            {
                $Arguments += " /ASSysAdminAccounts=`"" + $SetupCredential.UserName + "`""
                if($PSBoundParameters.ContainsKey("ASSysAdminAccounts"))
                {
                    foreach($AdminAccount in $ASSysAdminAccounts)
                    {
                        $Arguments += " `"$AdminAccount`""
                    }
                }
            }
        }
    }
    
    # Replace sensitive values for verbose output
    $Log = $Arguments
    if($PID -ne "")
    {
        $Log = $Log.Replace($PID,"*****-*****-*****-*****-*****")
    }
    if($SecurityMode -eq "SQL")
    {
        $Log = $Log.Replace($SAPwd.GetNetworkCredential().Password,"********")
    }
    $LogVars = @("AgtSvcAccount","SQLSvcAccount","ASSvcAccount","ISSvcAccount")
    foreach($LogVar in $LogVars)
    {
        if((Get-Variable -Name $LogVar).Value -ne "")
        {
            $Log = $Log.Replace((Get-Variable -Name $LogVar).Value.GetNetworkCredential().Password,"********")
        }
    }

    Write-Verbose "Path: $Path"
    Write-Verbose "Arguments: $Log"

    switch($Action)
    {
        'Prepare'
        {
            $Process = StartWin32Process -Path $Path -Arguments $Arguments -Credential $SetupCredential -AsTask
        }
        'Complete'
        {
            $Process = StartWin32Process -Path $Path -Arguments $Arguments
        }
    }
    Write-Verbose $Process
    WaitForWin32ProcessEnd -Path $Path -Arguments $Arguments -Credential $SetupCredential

    # Additional "Prepare" actions
    if($Action -eq "Prepare")
    {
        # Configure integration services
        if($Features.Contains("IS"))
        {
            $MsDtsSrvrPath = (Get-ItemProperty -Path ("HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\" + $SQLVersion + "0\SSIS\ServiceConfigFile") -Name '(default)').'(default)'
            if(Test-Path $MsDtsSrvrPath)
            {
                $MsDtsSrvr = [XML](Get-Content $MsDtsSrvrPath)
                if($FailoverClusterNetworkName -eq "")
                {
                    $FailoverClusterNetworkName = "."
                }
                if($InstanceName -eq "MSSQLSERVER")
                {
                    $MsDtsSrvr.DtsServiceConfiguration.TopLevelFolders.Folder | Where-Object {$_.type -eq "SqlServerFolder"} | ForEach-Object {$_.ServerName = "$FailoverClusterNetworkName"}
                }
                else
                {
                    $MsDtsSrvr.DtsServiceConfiguration.TopLevelFolders.Folder | Where-Object {$_.type -eq "SqlServerFolder"} | ForEach-Object {$_.ServerName = "$FailoverClusterNetworkName\$InstanceName"}
                }
                $MsDtsSrvr.Save($MsDtsSrvrPath)
                Restart-Service -Name ("MsDtsServer" + $SQLVersion + "0")
            }
        }
    }

    # Additional "Complete" actions
    if($Action -eq "Complete")
    {
        # Workaround for Analysis Services IPv6 issue, see KB2658571
        if($Features.Contains("AS"))
        {
            $msmredirpath = [Environment]::ExpandEnvironmentVariables("%ProgramFiles(x86)%\Microsoft SQL Server\90\Shared\ASConfig\msmdredir.ini")
            if(Test-Path ($msmredirpath))
            {
                $msmdredir = [XML](Get-Content $msmredirpath)
                if($msmdredir.ConfigurationSettings.Instances.Instance | Where-Object {$_.Name -eq $InstanceName} | ForEach-Object {$_.PortIPv6})
                {
                    $Entry = $msmdredir.ConfigurationSettings.Instances.Instance | Where-Object {$_.Name -eq $InstanceName} | ForEach-Object {$_.SelectSingleNode('PortIPv6')}
                    $msmdredir.ConfigurationSettings.Instances.Instance | Where-Object {$_.Name -eq $InstanceName} | ForEach-Object {$_.RemoveChild($Entry)}
                    $msmdredir.Save($msmredirpath)
                    Stop-ClusterGroup -Name $FailoverClusterGroup
                    Start-ClusterGroup -Name $FailoverClusterGroup
                }
            }
        }

        # Create path for Integration Services
        if($Features.Contains("IS") -and ($ISFileSystemFolder -ne ""))
        {
            if(($ISFileSystemFolder.Length -ge 2) -and ($ISFileSystemFolder.Substring(1,1) -eq ":"))
            {
                Invoke-Command -ScriptBlock {
                    $ISFileSystemFolder = $args[0]
                    if((Test-Path -Path $ISFileSystemFolder.Substring(0,2)) -and !(Test-Path -Path $ISFileSystemFolder))
                    {
                        New-Item -Path $ISFileSystemFolder -ItemType Directory
                    }
                } -ComputerName . -ArgumentList @($ISFileSystemFolder)
            }
        }
    }
    
    if($ForceReboot -or ((Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' -Name 'PendingFileRenameOperations' -ErrorAction SilentlyContinue) -ne $null))
    {
        if(!($SuppressReboot))
        {
            $global:DSCMachineStatus = 1
        }
        else
        {
            Write-Verbose "Suppressing reboot"
        }
    }

    if(!(Test-TargetResource @PSBoundParameters))
    {
        throw New-TerminatingError -ErrorType TestFailedAfterSet -ErrorCategory InvalidResult
    }
}


function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [parameter(Mandatory = $true)]
        [ValidateSet("Prepare","Complete")]
        [System.String]
        $Action,

        [System.String]
        $SourcePath = "$PSScriptRoot\..\..\",

        [System.String]
        $SourceFolder = "Source",

        [parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        $SetupCredential,

        [System.Management.Automation.PSCredential]
        $SourceCredential,

        [System.Boolean]
        $SuppressReboot,

        [System.Boolean]
        $ForceReboot,

        [parameter(Mandatory = $true)]
        [System.String]
        $Features,

        [parameter(Mandatory = $true)]
        [System.String]
        $InstanceName,

        [System.String]
        $InstanceID = $InstanceName,

        [System.String]
        $PID,

        [System.String]
        $UpdateEnabled = $True,

        [System.String]
        $UpdateSource = ".\Updates",

        [System.String]
        $SQMReporting,

        [System.String]
        $ErrorReporting,

        [System.String]
        $FailoverClusterGroup = "SQL Server ($InstanceName)",

        [parameter(Mandatory = $true)]
        [System.String]
        $FailoverClusterNetworkName,

        [System.String]
        $FailoverClusterIPAddress,

        [System.String]
        $InstallSharedDir,

        [System.String]
        $InstallSharedWOWDir,

        [System.String]
        $InstanceDir,

        [parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        $SQLSvcAccount,

        [System.Management.Automation.PSCredential]
        $AgtSvcAccount = $SQLSvcAccount,

        [System.String]
        $SQLCollation,

        [System.String[]]
        $SQLSysAdminAccounts,

        [System.String]
        $SecurityMode,

        [System.Management.Automation.PSCredential]
        $SAPwd = $SetupCredential,

        [System.String]
        $InstallSQLDataDir,

        [System.String]
        $SQLUserDBDir,

        [System.String]
        $SQLUserDBLogDir,

        [System.String]
        $SQLTempDBDir,

        [System.String]
        $SQLTempDBLogDir,

        [System.String]
        $SQLBackupDir,

        [System.Management.Automation.PSCredential]
        $ASSvcAccount = $SQLSvcAccount,

        [System.String]
        $ASCollation,

        [System.String[]]
        $ASSysAdminAccounts,

        [System.String]
        $ASDataDir,

        [System.String]
        $ASLogDir,

        [System.String]
        $ASBackupDir,

        [System.String]
        $ASTempDir,

        [System.String]
        $ASConfigDir,

        [System.Management.Automation.PSCredential]
        $ISSvcAccount = $SQLSvcAccount,

        [System.String]
        $ISFileSystemFolder
    )

    switch($Action)
    {
        "Prepare"
        {
            $SQLData = Get-TargetResource @PSBoundParameters

            $result = $true
            foreach($Feature in $Features.Split(","))
            {
                if(!($SQLData.Features.Contains($Feature)))
                {
                    $result = $false
                }
            }
        }
        "Complete"
        {
            if(Get-WmiObject -Namespace root/mscluster -Class MSCluster_ResourceGroup  -ErrorAction SilentlyContinue | Where-Object {$_.Name -eq $FailoverClusterGroup})
            {
                $result = $true
            }
            else
            {
                $result = $false
            }
        }
    }
    
    $result
}


function GetSQLVersion
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [String]
        $Path
    )

    (Get-Item -Path $Path).VersionInfo.ProductVersion.Split(".")[0]
}


function GetFirstItemPropertyValue
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [String]
        $Path,

        [Parameter(Mandatory=$true)]
        [String]
        $Name
    )

    if(Get-ItemProperty -Path "$Path\$Name" -ErrorAction SilentlyContinue)
    {
        $FirstName = ((Get-ItemProperty -Path "$Path\$Name") | Get-Member -MemberType NoteProperty | Where-Object {$_.Name.Substring(0,2) -ne "PS"}).Name[0]
        (Get-ItemProperty -Path "$Path\$Name" -Name $FirstName).$FirstName.TrimEnd("\")
    }
}


function ConvertDecimalIP
{
    [CmdLetBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [Net.IPAddress]
        $IPAddress
    )
 
    $i = 3
    $DecimalIP = 0
    $IPAddress.GetAddressBytes() | ForEach-Object {
        $DecimalIP += $_ * [Math]::Pow(256,$i)
        $i--
    }
 
    return [UInt32]$DecimalIP
}


Export-ModuleMember -Function *-TargetResource