Functions/Public/Invoke-SitecoreThumbprintValidation.ps1

function Invoke-SitecoreThumbprintValidation {
    <#
    .SYNOPSIS
        Verify Certificate Thumbprints across Sitecore Azure PaaS using Kudu
 
    .DESCRIPTION
        This function will download ConnectionStrings.config and AppSettings.config files from all App Services in a given Resource Group, then display any certificate thumbprints discrepencies.
 
    .EXAMPLE
        Invoke-SitecoreThumbprintValidation -ResourceSubscriptionId "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -ResourceGroupName "xx-xxxxxxxxxx-XP2-SMALL-PRD1"
 
   .EXAMPLE
        Invoke-SitecoreThumbprintValidation -ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -Group "xx-xxxxxxxxxx-XP2-SMALL-PRD1"
    #>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Alias("ID")]
        [string]$ResourceSubscriptionId,
        [Parameter(Mandatory = $true)]
        [Alias("Group")]
        [string]$ResourceGroupName,
        [Parameter(Mandatory = $false)]
        [Alias("Path")]
        [string]$DestinationPath

    )
    Write-Host "`n***********************************************`n Sitecore Azure Kudu Tools `n***********************************************" -ForegroundColor Black -BackgroundColor DarkCyan
    Write-Host "***********************************************`n Verify Sitecore Certificate Thumbprints `n***********************************************`n" -ForegroundColor Black -BackgroundColor Green
    
    Login-AzureRmAccount -SubscriptionName $ResourceSubscriptionId -ErrorAction Stop -Verbose > $null

    if ($null -ne $DestinationPath -and $DestinationPath -ne '') {
        if (Test-Path -Path $DestinationPath) {
            $BaseFolderPath = $DestinationPath
        }
        else {
            $BaseFolderPath = Get-BaseDownloadFolderPath
        }
    }
    else {
        $BaseFolderPath = Get-BaseDownloadFolderPath
    }

    Get-FilesFromAzure -ResourceSubscriptionId $ResourceSubscriptionId -ResourceGroupName $ResourceGroupName -OutputPath $BaseFolderPath
}

function Get-FilesFromAzure {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ResourceSubscriptionId,

        [Parameter(Mandatory = $true)]
        [string]$ResourceGroupName,

        [Parameter(Mandatory = $true)]
        [string]$OutputPath
    )

    $ResourcePrefixName = $($ResourceGroupName.ToLower())
    $currentDate = $((Get-Date).ToString('yyyyMMddhhmm'))
    $apps = "cd", "cm", "ma-ops", "ma-rep", "prc", "rep", "xc-collect", "xc-refdata", "xc-search"
    
    foreach ($app in $apps) {
        $base64AuthInfo = Get-AzureSubscriptionBase64Credentials -ResourceSubscriptionId $ResourceSubscriptionId -ResourceGroupName $ResourceGroupName -ResourceName "$($ResourcePrefixName)-$($app)"
        $apiBaseUrl = "https://$($ResourcePrefixName)-$app.scm.azurewebsites.net/api"
        $supportPackageName = "$($ResourcePrefixName)-$($app)_$((Get-Date).ToString('yyyyMMddhhmm'))" 
        $outfileRootPath = "$OutputPath\SAKT_ThumbprintVerification"
        If (!(Test-Path $outfileRootPath)) {
            New-Item -ItemType Directory -Force -Path $outfileRootPath | Out-Null
        }

        $downloadFolderName = "$($ResourceGroupName)_$currentDate"
        $outFileBasePath = "$outfileRootPath\$downloadFolderName" 
        If (!(Test-Path $outFileBasePath)) {
            New-Item -Path $outfileRootPath -Name $downloadFolderName -ItemType "directory" | Out-Null
        }

        $outTempPath = "$outfileRootPath\$downloadFolderName"
        New-Item -Path $outTempPath -Name $supportPackageName -ItemType "directory" -Force | Out-Null

        Write-Host "`n[" -NoNewline
        Write-Host "Certificate Thumbprint Verification" -ForegroundColor Black -BackgroundColor DarkCyan -NoNewline
        Write-Host "]: " -NoNewline


        Write-Host "Resource: " -NoNewline
        Write-Host "$($ResourcePrefixName)-$($app)" -ForegroundColor Green

        Receive-AppServiceContents -Base64Auth  $base64AuthInfo -BaseApiUrl $apiBaseUrl -BaseOutputPath "$outTempPath\$supportPackageName"
    }
    
    Confirm-MatchingThumbprintValues -FileDirectoryPath $outTempPath
}

function Receive-AppServiceContents {
    [CmdletBinding()]
    param (

        [Parameter(Mandatory = $true)]
        [string]$Base64Auth,
        
        [Parameter(Mandatory = $true)]
        [string]$BaseApiUrl,

        [Parameter(Mandatory = $true)]
        [string]$BaseOutputPath
        
    )
    Write-Host "[" -NoNewline
    Write-Host "Certificate Thumbprint Verification" -ForegroundColor Black -BackgroundColor DarkCyan -NoNewline
    Write-Host "]: " -NoNewline

    Write-Host "API URL: " -NoNewline
    Write-Host "$BaseApiUrl" -ForegroundColor Green 
    try {
        Get-NestedFolderFiles -Uri "$BaseApiUrl/vfs/site/wwwroot/App_Config/" -FolderDownloadPath $BaseOutputPath -Base64Auth $Base64Auth 
    }
    catch {
        Write-Host " X " -ForegroundColor Red
        Write-Host " > $($_.Exception.Message)`n" -ForegroundColor Red
    }
}

function Get-NestedFolderFiles {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Uri,
      
        [Parameter(Mandatory = $true)]
        [string]$FolderDownloadPath,

        [Parameter(Mandatory = $true)]
        [string]$Base64Auth     
    )

    $FilesList = Invoke-RestMethod -Uri $Uri -Headers @{Authorization = ("{0}" -f $Base64Auth) }

    foreach ($file in $FilesList | Where-Object { ($_.name -eq "ConnectionStrings.config") -or ($_.name -eq "AppSettings.config") }) {

        if ($file.mime -eq "inode/directory") {
         
            New-Item -Path $FolderDownloadPath -Name $file.name -ItemType "directory" | Out-Null
            $folderPath = "$FolderDownloadPath\$($file.name)"

            $nextUri = "$uri$($file.name)/"

            # Call Get-FileDownload
            Get-NestedFolderFiles -Uri $nextUri -FolderDownloadPath $folderPath -Base64Auth $Base64Auth 
            
        }
        else {
            Write-Host "[" -NoNewline
            Write-Host "Certificate Thumbprint Verification" -ForegroundColor Black -BackgroundColor DarkCyan -NoNewline
            Write-Host "]: " -NoNewline

            Write-Host "Download: " -NoNewline
            Write-Host "$($file.name)..." -ForegroundColor Green -NoNewline
            Invoke-WebRequest  -Uri $file.href -Headers @{Authorization = ("{0}" -f $Base64Auth) } -OutFile "$FolderDownloadPath\$($file.name)"
            Write-Host " OK `n" -ForegroundColor Green -NoNewline
        }
    }
}

function Confirm-MatchingThumbprintValues {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$FileDirectoryPath
    )
    
    $count = 0
    $cNameStringsWithThumbprint = "xconnect.collection.certificate|xdb.marketingautomation.operations.client.certificate|xdb.marketingautomation.reporting.client.certificate|xdb.referencedata.client.certificate"
    $ThumbprintToMatch = ''
    $listOfAppSettings = Get-ChildItem -Path $FileDirectoryPath -Recurse | Where-Object { $_.Name -eq "AppSettings.config" } 
    
    $xcCollectAppSetting = $listOfAppSettings | Where-Object { $_.DirectoryName -match "xc-collect" } | Select-Object
    
 
    $xml = [xml](Get-Content $xcCollectAppSetting.FullName)
    $xml.SelectNodes("//appSettings/add") | ForEach-Object { 
        if ($_.Attributes[0].Value -eq "validateCertificateThumbprint") {
            $ThumbprintToMatch = $_.Attributes[1].Value
            Write-Host "Thumbprint set on xConnect Collect Role AppSettings.config: $ThumbprintToMatch `n" -ForegroundColor Green 
        }
    }

    if ($ThumbprintToMatch -eq '') {
        Write-Host "Could not find thumbprint value in xConnect Collect Role AppSettings.config file." -ForegroundColor Red
        Exit
    }

    Write-Host "Comparing thumbprint values in AppSettings.config files..." -ForegroundColor Yellow

    foreach ($appConfig in $listOfAppSettings | Where-Object { $_.DirectoryName -notmatch "xc-collect" }) {
        $xml = [xml](Get-Content $appConfig.FullName)
        $xml.SelectNodes("//appSettings/add") | ForEach-Object { 
            if ($_.Attributes[0].Value -eq "validateCertificateThumbprint") {
            
                Write-Host " > 'validateCertificateThumbprint' on $($appConfig.Directory.BaseName)..." -ForegroundColor DarkGray -NoNewline

                if ($_.Attributes[1].Value -eq $ThumbprintToMatch) {
                    Write-Host "matches`n" -ForegroundColor Green
                }
                else {
                    Write-Host "does not match" -ForegroundColor Red
                    Write-Host " "$appConfig.FullName "`n" -ForegroundColor Red
                    $count++
                }           
            }
        }
    }

    $listOfConnectionStrings = Get-ChildItem -Path $FileDirectoryPath -Recurse | Where-Object { $_.Name -eq "ConnectionStrings.config" } 

    Write-Host "Checking Thumbprint Values in ConnectionStrings.config files..." -ForegroundColor Yellow
    foreach ($item in $listOfConnectionStrings) {

        $xml = [xml](Get-Content $item.FullName)

        $xml.SelectNodes("//connectionStrings/add") | ForEach-Object { 

            if ($_.Name -match $cNameStringsWithThumbprint) {
                Write-Host " > $($_.Name) on $($item.Directory.BaseName)..." -ForegroundColor DarkGray -NoNewline
                if ($_.connectionString -match 'FindValue\=(.*)') {
                    $tpVal = $Matches[1]
                    if ($tpVal -match $ThumbprintToMatch) {
                        Write-Host "matches`n" -ForegroundColor Green
                    }
                    else {
                        Write-Host "does not match" -ForegroundColor Red
                        Write-Host " "$item.FullName "`n" -ForegroundColor Red
                        $count++
                    }
                }
                else {
                    Write-Host "unable to determine match." -ForegroundColor Yellow
                }
            }
        }
    }

    if ($count -eq 0) {
        Write-Host "No thumbprint discrepencies found.`n" -ForegroundColor Green
    }
    else {
        Write-Host "$count thumbprint discrepencies found!`n" -ForegroundColor Red
    }
 
}