Skip to content

Latest commit

 

History

21 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

Get-AFDOriginCertChains

Get-AFDOriginCertChains.ps1 scans every Azure Front Door Standard/Premium and Classic profile the current identity can read across all enabled subscriptions, enumerates every origin, and classifies the TLS certificate chain each distinct HTTPS origin endpoint presents.

  • Authentication is Az PowerShell-only: use Az.Accounts and Connect-AzAccount. The script never calls Azure CLI.
  • Discovery uses Azure Resource Graph plus ARM REST.
  • Each distinct origin target is resolved to IP addresses once, and resolved public IPs are matched back to Azure public IP resources when possible.
  • Origins resolving to a discoverable Application Gateway public frontend are checked against their subnet NSG and effective WAF policy.
  • TLS probing deduplicates by HostName, HttpsPort, and OriginHostHeader.
  • CSV is always written. If ImportExcel is installed, a companion XLSX with the same base name is also written as a filterable table using the current workbook's Medium2 table style, with the top row frozen, row banding disabled, and host-related text columns preserved as text so literal IP addresses are not coerced into numbers.

Why Raw TLS Parsing

The script parses the TLS 1.2 Certificate message instead of relying on X509Chain alone.

  • It counts only the certificates the server actually sent.
  • It avoids false positives caused by locally cached intermediates.
  • It keeps chain completeness classification consistent across repeated runs.

Prerequisites

  • PowerShell 7 or later.
  • Az.Accounts PowerShell module.
  • Optional: ImportExcel if you want XLSX output.
  • An active Azure PowerShell login via Connect-AzAccount.
  • Permissions to list subscriptions, query Azure Resource Graph, and read Front Door, Application Gateway, virtual network, NSG, and WAF policy metadata.
  • Network access from the machine running the script to the origin HTTPS endpoints.

Install the required module if needed:

Install-Module Az.Accounts -Scope CurrentUser

Optional XLSX support:

Install-Module ImportExcel -Scope CurrentUser

Sign in before running the script:

Connect-AzAccount

Usage

./Get-AFDOriginCertChains.ps1
./Get-AFDOriginCertChains.ps1 -OutputCsvPath .\results.csv
./Get-AFDOriginCertChains.ps1 -ThrottleLimit 32 -TlsThrottleLimit 128
./Get-AFDOriginCertChains.ps1 -SkipTls
./Get-AFDOriginCertChains.ps1 -TlsTimeoutMs 10000

Parameters

Parameter Required Default Description
OutputCsvPath No afd-impacted-origins-<timestamp>.csv Output CSV path. If ImportExcel is installed, the script also writes an XLSX with the same base name.
ThrottleLimit No Dynamic Parallelism for ARM origin-group and origin enumeration.
TlsThrottleLimit No Dynamic Parallelism for TLS checks.
TlsTimeoutMs No 5000 Timeout in milliseconds for TCP and TLS operations.
SkipTls No Off Enumerate origins, resolve/classify IP addresses, and skip TLS checks.

The dynamic throttle defaults are derived from processor count and tuned separately for inventory and TLS probing.

Output

The CSV and optional XLSX contain one row per Front Door origin.

Key column groups:

  • Inventory: SubscriptionName, SubscriptionId, ResourceGroup, ProfileName, FrontDoorId, DeploymentModel, SkuName, OriginGroupName, OriginName
  • Origin settings: HostName, OriginHostHeader, HttpPort, HttpsPort, EnabledState, Priority, Weight, CertNameCheck
  • Resolved IPs: ResolvedAddresses, IpKind, AzureResourceId, ApplicationGatewayResourceId, AzurePrivateIpTag
  • Application Gateway security: ApplicationGatewayNsgResourceId, ApplicationGatewayWafPolicyId, AppGatewayFrontDoorSecurity, AppGatewayFrontDoorSecurityReason

AzureResourceId remains the generic resource associated with the public IP. When that resource is an Application Gateway, ApplicationGatewayResourceId repeats it in a dedicated filterable column.

AppGatewayFrontDoorSecurity is blank for non-Application-Gateway origins and otherwise contains:

Value Meaning
No The gateway subnet has no NSG, the NSG doesn't effectively allow AzureFrontDoor.Backend, or another public source is allowed on the origin HTTPS port.
Yes The NSG restricts public client traffic to AzureFrontDoor.Backend, but matching X-Azure-FDID enforcement wasn't found in an enabled Prevention-mode WAF policy.
Yes+WAF The NSG restriction is effective and every applicable WAF policy blocks requests whose X-Azure-FDID differs from this origin row's FrontDoorId.
Unknown Referenced Application Gateway security metadata couldn't be read or safely evaluated.

Application Gateway WAF policy precedence is evaluated from path rule to listener to gateway-wide policy. The check is conservative: broader public NSG allows, partial WAF coverage, Detection-mode policies, mismatched or multiple Front Door IDs, and earlier WAF allow rules don't qualify for Yes+WAF. Yes+WAF also requires an explicit OriginHostHeader; when it is blank, Front Door forwards the incoming hostname and the script can't determine a single effective Application Gateway listener from the origin record alone. Required platform/private rules such as GatewayManager, AzureLoadBalancer, and private-network sources don't invalidate the NSG result.

The Application Gateway analysis applies only when every resolved public address can be correlated to an accessible Application Gateway public IP resource. Mixed or uncorrelated public DNS answers are reported as Unknown. Internal-only and Azure Front Door Private Link Application Gateways aren't discoverable through this public-IP association and leave the Application Gateway security columns blank.

AzurePrivateIpTag is populated from the Private_IP tag on the matched Azure public IP resource. When the public-IP TLS probe fails to retrieve certificates and a private IP tag is present, the script falls back to probing the private IP directly (Phase 7b). If the private-IP probe succeeds, the TLS and certificate columns are replaced with the private-IP results. TcpAttemptedAddresses shows only the public IP when it succeeded, or both the public and private IPs when both were tested.

  • TLS results: TlsPort, TlsStatus, TcpAttemptedAddresses, TcpConnectedAddress, ServerCertificateCount, DigiCertIssued, LeafSubject, LeafIssuer, LeafNotAfterUtc, IntermediateSubject, IntermediateIssuer, IntermediateNotAfterUtc, RootSubject, RootIssuer, RootNotAfterUtc

TlsStatus is self-describing: on a successful handshake it holds the chain classification (FullChain, PartialChain, NoChain, Expired*, NoCert); on a TCP failure it holds the raw socket error in the form <code> (<name>), for example 10060 (TimedOut); DNS failures are reported as DnsFailure: <message>, and TLS handshake errors as TlsError: <message>.

The certificate columns reflect the server-sent chain positions: Leaf* is certificate #1 (the site cert), Intermediate* is certificate #2 (the CA that signed the leaf), and Root* is the last certificate in the chain (typically the self-signed root).

Console output includes:

  • phase-based progress updates for discovery, inventory, Application Gateway security analysis, and TLS probing
  • a TLS status breakdown by origin records, which matches the CSV/XLSX row counts
  • a TLS status breakdown by distinct TLS targets, which matches the deduplicated probe count

TLS Status Values

TlsStatus Meaning
FullChain The server sent 3 or more certificates.
ExpiredFullChain Full chain was sent, but the leaf certificate is expired.
PartialChain The server sent 2 certificates.
ExpiredPartialChain Partial chain was sent, but the leaf certificate is expired.
NoChain The server sent only 1 certificate.
ExpiredNoChain Only 1 certificate was sent and the leaf certificate is expired.
NoCert The TLS Certificate message contained no certificates.
Skipped TLS probing was skipped with -SkipTls.
MSFT Origin host name belongs to a Microsoft-owned Azure PaaS public DNS suffix (see Azure Private Endpoint DNS, "Public DNS zone forwarders" column). Microsoft manages the TLS chain for these endpoints, so DNS resolution, TCP and TLS probing are skipped.
DnsFailure[: <message>] DNS resolution failed for the origin hostname.
<code> (<name>) TCP connect failed. Example: 10060 (TimedOut), 10061 (ConnectionRefused), 10054 (ConnectionReset), 10065 (HostUnreachable).
TlsError: <message> TCP connected, but the TLS handshake failed (including TLS timeout).

Resilience

ARM REST calls (Azure Resource Graph paging, Standard/Premium origin-group and origin enumeration, and Classic Front Door GETs) are routed through an internal Invoke-ArmRequestWithRetry wrapper. It retries on:

  • HTTP 408, 429, 500, 502, 503, 504
  • HTML outage interstitials (the AzureResourceManager "Our services aren't available right now" page with Ref A/B/C tokens) that some ARM edges return during regional incidents or throttling
  • transient DNS, connection reset/closure, and timeout failures while reaching Azure Resource Manager

The wrapper honors the Retry-After header (delta-seconds or HTTP-date). Otherwise it uses exponential backoff with ±20% jitter, capped at 30 seconds per wait. Default MaxAttempts is 6. Transient failures are logged to the console as ARM transient failure (status=...) on attempt N/Max; retrying in <ms> ms... so retry activity is visible during a scan.

Notes

  • There is no input inventory file. The script discovers accessible subscriptions and profiles directly.
  • Classic backend pools are normalized into the same row shape as Standard/Premium origin groups.
  • Resolved-IP and Application Gateway security columns are populated even when -SkipTls is used.
  • Because the CSV and XLSX contain one row per origin, per-status row counts in those files can be higher than the distinct TLS target counts shown in the console summary.
  • TLS 1.2 is forced because the TLS 1.3 certificate message is encrypted and cannot be parsed reliably without key material.
  • Connection diagnostics include TlsStatus (which carries the raw TCP/TLS error detail on failure), TcpAttemptedAddresses, and TcpConnectedAddress so TCP and TLS failures can be triaged directly from the export.

Troubleshooting

If the script fails early:

  • verify Az.Accounts is installed
  • verify you have signed in with Connect-AzAccount
  • verify your identity can query Azure Resource Graph and read Front Door profiles in the target subscriptions

If many origins return a TCP error code (for example 10060 (TimedOut), 10061 (ConnectionRefused)) or TlsError: <message>:

  • confirm the machine running the script can reach the origin network
  • verify firewall, NSG, proxy, routing, and DNS behavior
  • verify the origin listens on the configured HTTPS port
  • verify the SNI hostname expected by the origin matches OriginHostHeader

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages