Set-BuiltInWinSecureDNS.psm1

#Requires -RunAsAdministrator
Function Set-BuiltInWinSecureDNS {
    [Alias("Set-DOH")]
    [CmdletBinding(
        SupportsShouldProcess = $true
    )]
    param (
        [ValidateSet('Cloudflare', 'Google', 'Quad9', ErrorMessage = "The selected DNS over HTTPS provider is not supported by Windows. Please select a different provider or use the Set-CustomWinSecureDNS cmdlet.")]
        [Parameter(Mandatory)][string]$DoHProvider 
    )
    begin {

        Switch ($DoHProvider) {
            "cloudflare" {
                $DetectedDoHTemplate = "https://cloudflare-dns.com/dns-query"
            }
            "Google" {
                $DetectedDoHTemplate = "https://dns.google/dns-query"
            }
            "Quad9" {
                $DetectedDoHTemplate = "https://dns.quad9.net/dns-query"
            }
        }

        if (-NOT ((Get-DnsClientDohServerAddress | Where-Object { $_.DohTemplate -eq $DetectedDoHTemplate }).DohTemplate) ) {
            throw "The DNS over HTTPS provider $DoHProvider is not supported by Windows. Please select a different provider."
        }
    
        # Automatically detect the correct network adapter
        $ActiveNetworkInterface = Get-ActiveNetworkAdapterWinSecureDNS 
        $ActiveNetworkInterface

        # loop until the user confirms the detected adapter is the correct one, Selects the correct network adapter or Cancels
        switch (Select-Option -Options "Yes", "No - Select Manually", "Cancel" -Message "`nIs the detected network adapter correct ?") {
            "Yes" {
                $ActiveNetworkInterface = $ActiveNetworkInterface
            }
            "No - Select Manually" {
                $ActiveNetworkInterface = Get-ManualNetworkAdapterWinSecureDNS          
            } # properly exiting this advanced function is a bit tricky, so we use a variable to control the loop
            "Cancel" { $ShouldExit = $true; return } 
        }
        
        # if user chose to cancel the Get-ManualNetworkAdapterWinSecureDNS function, set the $shouldExit variable to $true and exit the function in the Process block
        if (!$ActiveNetworkInterface) { $ShouldExit = $true; return }
    }

    process {
        # if the user selected Cancel, do not proceed with the process block
        if ($ShouldExit) { break }

        # reset the network adapter's DNS servers back to default to take care of any IPv6 strays
        Set-DnsClientServerAddress -InterfaceIndex $ActiveNetworkInterface.ifIndex -ResetServerAddresses -ErrorAction Stop

        # delete all other previous DoH settings for ALL Interface - Windows behavior in settings when changing DoH settings is to delete all DoH settings for the interface we are modifying
        # but we need to delete all DoH settings for ALL interfaces in here because every time we virtualize a network adapter with external switch of Hyper-V,
        # Hyper-V assigns a new GUID to it, so it's better not to leave any leftover in the registry and clean up after ourselves
        Remove-item "HKLM:System\CurrentControlSet\Services\Dnscache\InterfaceSpecificParameters\*" -Recurse | Out-Null
  
        $DoHIPs = (Get-DnsClientDohServerAddress | Where-Object { $_.DohTemplate -eq $DetectedDoHTemplate }).ServerAddress

        $DoHIPs | ForEach-Object {

            # use the ipaddress type accelerator to check if the address is IPv4 or IPv6
            $IP = [ipaddress]$_
            if ($IP.AddressFamily -eq 'InterNetwork') {
                # defining registry path for DoH settings of the $ActiveNetworkInterface based on its GUID for IPv4
                $Path = "HKLM:System\CurrentControlSet\Services\Dnscache\InterfaceSpecificParameters\$($ActiveNetworkInterface.InterfaceGuid)\DohInterfaceSettings\Doh\$_"

                # add DoH settings for the specified Network adapter based on its GUID in registry
                # value 1 for DohFlags key means use automatic template for DoH, 2 means manual template, since we add our template to Windows, it's predefined so we use value 1
                New-Item -Path $Path -Force | Out-Null  
                New-ItemProperty -Path $Path -Name "DohFlags" -Value 1 -PropertyType Qword -Force

                Set-DnsClientDohServerAddress -ServerAddress $_ -DohTemplate $DetectedDoHTemplate -AllowFallbackToUdp $False -AutoUpgrade $True
            }
            elseif ($IP.AddressFamily -eq 'InterNetworkV6') {
                # defining registry path for DoH settings of the $ActiveNetworkInterface based on its GUID for IPv6
                $Path = "HKLM:System\CurrentControlSet\Services\Dnscache\InterfaceSpecificParameters\$($ActiveNetworkInterface.InterfaceGuid)\DohInterfaceSettings\Doh6\$_"

                # add DoH settings for the specified Network adapter based on its GUID in registry
                # value 1 for DohFlags key means use automatic template for DoH, 2 means manual template, since we already added our template to Windows, it's considered predefined, so we use value 1
                New-Item -Path $Path -Force | Out-Null  
                New-ItemProperty -Path $Path -Name "DohFlags" -Value 1 -PropertyType Qword -Force

                Set-DnsClientDohServerAddress -ServerAddress $_ -DohTemplate $DetectedDoHTemplate -AllowFallbackToUdp $False -AutoUpgrade $True
            }
        }

        # this is responsible for making the changes in Windows settings UI > Network and internet > $ActiveNetworkInterface.Name
        Set-DnsClientServerAddress -ServerAddresses $DoHIPs -InterfaceIndex $ActiveNetworkInterface.ifIndex -ErrorAction Stop
    }

    end {
        # clear DNS client Cache
        Clear-DnsClientCache  
    }

    <#
.SYNOPSIS
Easily and quickly configure DNS over HTTPS (DoH) in Windows
 
.LINK
https://github.com/HotCakeX/WinSecureDNSMgr
 
.DESCRIPTION
Easily and quickly configure DNS over HTTPS (DoH) in Windows
 
.FUNCTIONALITY
Using official Microsoft methods configures DNS over HTTPS in Windows
 
.PARAMETER DoHProvider
The name of the 3 built-in DNS over HTTPS providers: Cloudflare, Google and Quad9
 
#>

}