DSCResources/ArcGIS_DataStore_TLS/ArcGIS_DataStore_TLS.psm1

$modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules'

# Import the ArcGIS Common Modules
Import-Module -Name (Join-Path -Path $modulePath `
        -ChildPath (Join-Path -Path 'ArcGIS.Common' `
            -ChildPath 'ArcGIS.Common.psm1'))

function Get-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [parameter(Mandatory = $true)]    
        [System.String]
        $DatastoreMachineHostName,

        [parameter(Mandatory = $true)]
        [System.String]
        $CName,
        
        [parameter(Mandatory = $true)]
        [System.String]
        $CertificateFileLocation,
        
        [parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        $CertificatePassword
    )


    $null 
}

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

        [parameter(Mandatory = $true)]
        [System.String]
        $CName,
        
        [parameter(Mandatory = $true)]
        [System.String]
        $CertificateFileLocation,
        
        [parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        $CertificatePassword
    )

    [System.Reflection.Assembly]::LoadWithPartialName("System.Web") | Out-Null
    
    $MachineFQDN = if($DatastoreMachineHostName){ Get-FQDN $DatastoreMachineHostName }else{ Get-FQDN $env:COMPUTERNAME }

    $ServiceName = 'ArcGIS Data Store'
    $RegKey = Get-EsriRegistryKeyForService -ServiceName $ServiceName
    $DataStoreInstallDirectory = (Get-ItemProperty -Path $RegKey -ErrorAction Ignore).InstallDir.TrimEnd('\')  

    Wait-ForUrl -Url "https://$($MachineFQDN):2443/arcgis/datastoreadmin/configure?f=json" -MaxWaitTimeInSeconds 180 -SleepTimeInSeconds 5 -HttpMethod 'GET' -Verbose

    $CertificateTest = Test-DataStoreCertificate -CertificateFileLocation $CertificateFileLocation `
                                            -CertificatePassword $CertificatePassword `
                                            -MachineFQDN $MachineFQDN -Verbose
    if(-not($CertificateTest)){
        Write-Verbose "Thumbprint of certificate configured with datastore doesn't matches the certificate provided. Updating it."
        Invoke-DataStoreUpdateSSLCertificateTool -DataStoreInstallDirectory $DataStoreInstallDirectory `
                                                -CertificateFileLocation $CertificateFileLocation `
                                                -CertificatePassword $CertificatePassword -CName $CName -Verbose
        Write-Verbose "Certificate update successful."
    }else{
        Write-Verbose "Thumbprint of certificate configured with datastore matches the certificate provided"
    }
}

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

        [parameter(Mandatory = $true)]
        [System.String]
        $CName,
        
        [parameter(Mandatory = $true)]
        [System.String]
        $CertificateFileLocation,
        
        [parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        $CertificatePassword
    )

    [System.Reflection.Assembly]::LoadWithPartialName("System.Web") | Out-Null

    $MachineFQDN = if($DatastoreMachineHostName){ Get-FQDN $DatastoreMachineHostName }else{ Get-FQDN $env:COMPUTERNAME }
    Wait-ForUrl -Url "https://$($MachineFQDN):2443/arcgis/datastoreadmin/configure?f=json" -MaxWaitTimeInSeconds 180 -SleepTimeInSeconds 5 -HttpMethod 'GET' -Verbose

    $result = $true
    $CertificateTest = Test-DataStoreCertificate -CertificateFileLocation $CertificateFileLocation `
                                            -CertificatePassword $CertificatePassword -Verbose `
                                            -MachineFQDN $MachineFQDN
    if(-not($CertificateTest)){
        Write-Verbose "Thumbprint of certificate configured with datastore doesn't matches the certificate provided."
        $result = $False
    }else{
        Write-Verbose "Thumbprint of certificate configured with datastore matches the certificate provided"
    }

    $result
}

function Test-DataStoreCertificate
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [System.String]
        $CertificateFileLocation,

        [System.String]
        $MachineFQDN,

        [System.Management.Automation.PSCredential]
        $CertificatePassword
    )

    $Cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
    $Cert.Import($CertificateFileLocation, $CertificatePassword.GetNetworkCredential().Password, 32)

    $webRequest = [Net.WebRequest]::Create("https://$($MachineFQDN):2443/arcgis/datastoreadmin/configure?f=json")
    try { $_d = $webRequest.GetResponse() } catch {}
    
    return $webRequest.ServicePoint.Certificate.GetCertHashString() -ieq $Cert.Thumbprint
}

function Invoke-DataStoreUpdateSSLCertificateTool
{
    [CmdletBinding()]
    param(
        [System.String]
        $DataStoreInstallDirectory,

        [System.String]
        $CertificateFileLocation,
        
        [System.Management.Automation.PSCredential]
        $CertificatePassword,

        [System.String]
        $CName
    )

    $UpdateDataStoreSSLCertificateToolPath = Join-Path $DataStoreInstallDirectory 'tools\updatesslcertificate.bat'
    
    if(-not(Test-Path $UpdateDataStoreSSLCertificateToolPath -PathType Leaf)){
        throw "$UpdateDataStoreSSLCertificateToolPath not found"
    }
    $RandomString = -join((65..90) + (97..122) | Get-Random -Count 5 | % {[char]$_})
    $CertAlias = "$($RandomString)$($CName)"

    $psi = New-Object System.Diagnostics.ProcessStartInfo
    $psi.FileName = $UpdateDataStoreSSLCertificateToolPath
    $psi.Arguments = "$CertificateFileLocation $($CertificatePassword.GetNetworkCredential().Password) $CertAlias --prompt no"
    $psi.UseShellExecute = $false #start the process from it's own executable file
    $psi.RedirectStandardOutput = $true #enable the process to read from standard output
    $psi.RedirectStandardError = $true #enable the process to read from standard error
    $psi.EnvironmentVariables["AGSDATASTORE"] = [environment]::GetEnvironmentVariable("AGSDATASTORE","Machine")

    $p = [System.Diagnostics.Process]::Start($psi)
    $p.WaitForExit()
    $result = $null
    $op = $p.StandardOutput.ReadToEnd()
    if($p.ExitCode -eq 0) {
        Write-Verbose "Update successful - $op"
        $result = $op
    }else{
        $err = $p.StandardError.ReadToEnd()
        Write-Verbose $err
        if($err -and $err.Length -gt 0) {
            throw "ArcGIS Data Store 'updatesslcertificate.bat' tool failed. Output - $op. Error - $err"
        }
    }
    $result
}

Export-ModuleMember -Function *-TargetResource