mqsft.dnsexternal.psm1
|
# ============================================================================== # mqsft.dnsexternal.psm1 - AUTO-GENERATED by Create_Module_0_0_7.ps1 v0.0.7 # ------------------------------------------------------------------------------ # Module Version : 1.0.4 # Script Version : 0.0.7 # Built : 2026-02-24 17:31:11 # Source : C:\Modules\functions\mqsft.dnsexternal # # DO NOT EDIT THIS FILE DIRECTLY. # Edit source files in C:\Modules\functions\mqsft.dnsexternal and re-run Create_Module_0_0_7.ps1. # ============================================================================== Set-StrictMode -Version Latest # --- Source: Get-MailDomainInfrastructure.ps1 ---------------------------- function Get-MailDomainInfrastructure { <# .SYNOPSIS Analyzes complete mail infrastructure for domains - both inbound (MX) and outbound (SPF/DKIM/DMARC). .DESCRIPTION By default, performs comprehensive analysis of both inbound and outbound mail configuration: - INBOUND: MX records, mail servers, provider identification - OUTBOUND: SPF, DKIM, DMARC records and policies Use -InboundOnly or -OutboundOnly to focus on specific areas. .PARAMETER Domains Array of domain names to analyze. Supports subdomains. .PARAMETER InboundOnly Only check inbound mail configuration (MX records) .PARAMETER OutboundOnly Only check outbound mail configuration (SPF, DKIM, DMARC) .PARAMETER ExportPath Optional: Directory path to export reports (auto-generates filenames with timestamp) .PARAMETER IncludeIPInfo Resolve IP addresses for mail servers (slower but more detailed) .EXAMPLE Get-MailDomainInfrastructure -Domains "example.com","subdomain.example.com" Checks both inbound and outbound configuration .EXAMPLE Get-MailDomainInfrastructure -Domains "example.com" -InboundOnly Only checks MX records .EXAMPLE Get-MailDomainInfrastructure -Domains $domains -ExportPath "C:\Reports" Full analysis with exported reports .EXAMPLE Get-Content domains.txt | Get-MailDomainInfrastructure -OutboundOnly Only checks SPF, DKIM, and DMARC from file #> [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string[]]$Domains, [switch]$InboundOnly, [switch]$OutboundOnly, [string]$ExportPath, [switch]$IncludeIPInfo ) begin { # Determine what to check $CheckInbound = -not $OutboundOnly $CheckOutbound = -not $InboundOnly # Mail provider patterns $ProviderPatterns = @{ 'Microsoft 365' = @("*.mail.protection.outlook.com", "*.outlook.com", "*.office365.com") 'Google Workspace' = @("*.google.com", "*.googlemail.com", "aspmx.l.google.com") 'Proofpoint' = @("*.pphosted.com", "*.proofpoint.com") 'Mimecast' = @("*.mimecast.com", "*.mimecast-offshore.com") 'Barracuda' = @("*.barracudanetworks.com", "*.cuda-inc.com") 'Cloudflare' = @("*.cloudflare.net") 'Zoho Mail' = @("*.zoho.com", "*.zohomail.com") 'Amazon SES' = @("*.amazonaws.com", "inbound-smtp.*.amazonaws.com") 'GoDaddy' = @("*.secureserver.net", "*.godaddy.com") 'Mailgun' = @("*.mailgun.org") 'SendGrid' = @("*.sendgrid.net") } $AllResults = @() $Timestamp = Get-Date -Format "HH:mm:dd:MM:yyyy" $FileTimestamp = Get-Date -Format "yyyyMMdd_HHmmss" } process { foreach ($Domain in $Domains) { Write-Host "`n[$Timestamp] Analyzing: $Domain" -ForegroundColor Cyan Write-Host ("=" * 80) -ForegroundColor DarkGray $DomainResult = [PSCustomObject]@{ Timestamp = $Timestamp Domain = $Domain # Inbound MXRecords = @() MXCount = 0 PrimaryProvider = "Unknown" AllProviders = @() # Outbound SPF = @{ Configured = $false Record = "" Analysis = "" } DKIM = @{ Configured = $false Selectors = @() } DMARC = @{ Configured = $false Policy = "" Record = "" } # Status InboundStatus = "Not Checked" OutboundStatus = "Not Checked" Errors = @() } # ============================================ # INBOUND MAIL CONFIGURATION (MX Records) # ============================================ if ($CheckInbound) { Write-Host "`n INBOUND MAIL CONFIGURATION" -ForegroundColor Green Write-Host " " + ("-" * 50) -ForegroundColor DarkGray try { $MXRecords = Resolve-DnsName -Name $Domain -Type MX -ErrorAction Stop | Where-Object { $_.Type -eq 'MX' } if ($MXRecords) { $DomainResult.InboundStatus = "Configured" $DomainResult.MXCount = $MXRecords.Count $ProvidersFound = @() foreach ($MX in $MXRecords | Sort-Object Preference) { $Provider = "Unknown/Custom" # Identify provider foreach ($ProviderName in $ProviderPatterns.Keys) { foreach ($Pattern in $ProviderPatterns[$ProviderName]) { if ($MX.NameExchange -like $Pattern) { $Provider = $ProviderName break } } if ($Provider -ne "Unknown/Custom") { break } } if ($Provider -ne "Unknown/Custom" -and $Provider -notin $ProvidersFound) { $ProvidersFound += $Provider } # Get IP if requested $IPAddress = $null if ($IncludeIPInfo) { try { $IPLookup = Resolve-DnsName -Name $MX.NameExchange -Type A -ErrorAction SilentlyContinue $IPAddress = ($IPLookup | Where-Object { $_.Type -eq 'A' } | Select-Object -First 1).IPAddress } catch { $IPAddress = "N/A" } } $MXInfo = [PSCustomObject]@{ Priority = $MX.Preference MailServer = $MX.NameExchange Provider = $Provider IPAddress = $IPAddress } $DomainResult.MXRecords += $MXInfo $ProviderDisplay = if ($Provider -ne "Unknown/Custom") { "[$Provider]" } else { "" } Write-Host " Priority $($MX.Preference): $($MX.NameExchange) $ProviderDisplay" -ForegroundColor White } $DomainResult.PrimaryProvider = if ($ProvidersFound.Count -gt 0) { $ProvidersFound[0] } else { "Unknown/Custom" } $DomainResult.AllProviders = $ProvidersFound Write-Host "`n ā MX Records: $($MXRecords.Count) found" -ForegroundColor Green Write-Host " ā Primary Provider: $($DomainResult.PrimaryProvider)" -ForegroundColor Green } else { $DomainResult.InboundStatus = "No MX Records" Write-Host " ā No MX records found" -ForegroundColor Yellow } } catch { $DomainResult.InboundStatus = "Error" $DomainResult.Errors += "MX: $($_.Exception.Message)" Write-Host " ā Failed to resolve MX records: $($_.Exception.Message)" -ForegroundColor Red } } # ============================================ # OUTBOUND MAIL CONFIGURATION # ============================================ if ($CheckOutbound) { Write-Host "`n OUTBOUND MAIL CONFIGURATION" -ForegroundColor Magenta Write-Host " " + ("-" * 50) -ForegroundColor DarkGray # SPF Check Write-Host "`n SPF (Sender Policy Framework):" -ForegroundColor Yellow try { $SPFRecord = Resolve-DnsName -Name $Domain -Type TXT -ErrorAction Stop | Where-Object { $_.Strings -match '^v=spf1' } if ($SPFRecord) { $SPFString = $SPFRecord.Strings -join "" $DomainResult.SPF.Configured = $true $DomainResult.SPF.Record = $SPFString # Analyze SPF $Analysis = @() if ($SPFString -match '\+all') { $Analysis += "ā WARNING: Allows all senders (+all)" } elseif ($SPFString -match '~all') { $Analysis += "ā Soft fail configured (~all)" } elseif ($SPFString -match '-all') { $Analysis += "ā Hard fail configured (-all)" } else { $Analysis += "ā No 'all' mechanism found" } $DomainResult.SPF.Analysis = $Analysis -join "; " Write-Host " ā SPF Record Found" -ForegroundColor Green Write-Host " Record: $SPFString" -ForegroundColor Gray foreach ($note in $Analysis) { Write-Host " $note" -ForegroundColor Gray } } else { Write-Host " ā No SPF record configured" -ForegroundColor Red Write-Host " ā Recommendation: Configure SPF to prevent email spoofing" -ForegroundColor Yellow } } catch { $DomainResult.Errors += "SPF: $($_.Exception.Message)" Write-Host " ā Error checking SPF: $($_.Exception.Message)" -ForegroundColor Red } # DKIM Check (common selectors) Write-Host "`n DKIM (DomainKeys Identified Mail):" -ForegroundColor Yellow $CommonSelectors = @('default', 'google', 'k1', 'selector1', 'selector2', 'dkim', 'mail', 's1', 's2') $DKIMFound = @() foreach ($Selector in $CommonSelectors) { try { $DKIMDomain = "$Selector._domainkey.$Domain" $DKIMRecord = Resolve-DnsName -Name $DKIMDomain -Type TXT -ErrorAction SilentlyContinue | Where-Object { $_.Strings -match 'v=DKIM1' } if ($DKIMRecord) { $DKIMFound += $Selector } } catch { } } if ($DKIMFound.Count -gt 0) { $DomainResult.DKIM.Configured = $true $DomainResult.DKIM.Selectors = $DKIMFound Write-Host " ā DKIM Records Found" -ForegroundColor Green Write-Host " Selectors: $($DKIMFound -join ', ')" -ForegroundColor Gray } else { Write-Host " ā No DKIM records found (checked common selectors)" -ForegroundColor Red Write-Host " ā¹ Note: DKIM may use custom selectors not checked" -ForegroundColor Gray } # DMARC Check Write-Host "`n DMARC (Domain-based Message Authentication):" -ForegroundColor Yellow try { $DMARCDomain = "_dmarc.$Domain" $DMARCRecord = Resolve-DnsName -Name $DMARCDomain -Type TXT -ErrorAction Stop | Where-Object { $_.Strings -match '^v=DMARC1' } if ($DMARCRecord) { $DMARCString = $DMARCRecord.Strings -join "" $DomainResult.DMARC.Configured = $true $DomainResult.DMARC.Record = $DMARCString # Extract policy if ($DMARCString -match 'p=(\w+)') { $Policy = $matches[1] $DomainResult.DMARC.Policy = $Policy $PolicyColor = switch ($Policy) { 'none' { 'Yellow' } 'quarantine' { 'Cyan' } 'reject' { 'Green' } default { 'White' } } Write-Host " ā DMARC Policy Found" -ForegroundColor Green Write-Host " Policy: $Policy" -ForegroundColor $PolicyColor Write-Host " Record: $DMARCString" -ForegroundColor Gray } } else { Write-Host " ā No DMARC policy configured" -ForegroundColor Red Write-Host " ā Recommendation: Configure DMARC to protect against email spoofing" -ForegroundColor Yellow } } catch { $DomainResult.Errors += "DMARC: $($_.Exception.Message)" Write-Host " ā Error checking DMARC: $($_.Exception.Message)" -ForegroundColor Red } # Overall outbound status $OutboundConfigured = $DomainResult.SPF.Configured -or $DomainResult.DKIM.Configured -or $DomainResult.DMARC.Configured $DomainResult.OutboundStatus = if ($OutboundConfigured) { "Configured" } else { "Not Configured" } } $AllResults += $DomainResult } } end { # ============================================ # SUMMARY REPORT # ============================================ Write-Host "`n`n" + ("=" * 80) -ForegroundColor Cyan Write-Host "MAIL DOMAIN INFRASTRUCTURE SUMMARY - $Timestamp" -ForegroundColor Cyan Write-Host ("=" * 80) -ForegroundColor Cyan $TotalDomains = $AllResults.Count if ($CheckInbound) { Write-Host "`nINBOUND CONFIGURATION:" -ForegroundColor Green $InboundConfigured = @($AllResults | Where-Object { $_.InboundStatus -eq "Configured" }).Count Write-Host " Total Domains: $TotalDomains" Write-Host " With MX Records: $InboundConfigured" Write-Host " Missing MX Records: $($TotalDomains - $InboundConfigured)" # Provider summary $Providers = $AllResults | Where-Object { $_.PrimaryProvider -ne "Unknown" -and $_.PrimaryProvider -ne "Unknown/Custom" } | Group-Object PrimaryProvider | Sort-Object Count -Descending if ($Providers) { Write-Host "`n Provider Distribution:" foreach ($Provider in $Providers) { Write-Host " $($Provider.Name): $($Provider.Count) domain(s)" -ForegroundColor White } } } if ($CheckOutbound) { Write-Host "`nOUTBOUND CONFIGURATION:" -ForegroundColor Magenta $SPFConfigured = @($AllResults | Where-Object { $_.SPF.Configured }).Count $DKIMConfigured = @($AllResults | Where-Object { $_.DKIM.Configured }).Count $DMARCConfigured = @($AllResults | Where-Object { $_.DMARC.Configured }).Count Write-Host " Total Domains: $TotalDomains" Write-Host " With SPF: $SPFConfigured ($([math]::Round(($SPFConfigured/$TotalDomains)*100,1))%)" Write-Host " With DKIM: $DKIMConfigured ($([math]::Round(($DKIMConfigured/$TotalDomains)*100,1))%)" Write-Host " With DMARC: $DMARCConfigured ($([math]::Round(($DMARCConfigured/$TotalDomains)*100,1))%)" $FullyConfigured = @($AllResults | Where-Object { $_.SPF.Configured -and $_.DKIM.Configured -and $_.DMARC.Configured }).Count Write-Host "`n Fully Protected (SPF+DKIM+DMARC): $FullyConfigured ($([math]::Round(($FullyConfigured/$TotalDomains)*100,1))%)" } # Export if requested if ($ExportPath) { if (-not (Test-Path $ExportPath)) { New-Item -ItemType Directory -Path $ExportPath -Force | Out-Null } $DomainList = ($AllResults.Domain -join "_").Substring(0, [Math]::Min(50, ($AllResults.Domain -join "_").Length)) $BaseFileName = "${DomainList}_${FileTimestamp}" # Export detailed results to CSV $CSVPath = Join-Path $ExportPath "${BaseFileName}_detailed.csv" $AllResults | Select-Object Timestamp, Domain, MXCount, PrimaryProvider, @{N='SPF_Configured';E={$_.SPF.Configured}}, @{N='SPF_Record';E={$_.SPF.Record}}, @{N='DKIM_Configured';E={$_.DKIM.Configured}}, @{N='DKIM_Selectors';E={$_.DKIM.Selectors -join ','}}, @{N='DMARC_Configured';E={$_.DMARC.Configured}}, @{N='DMARC_Policy';E={$_.DMARC.Policy}}, InboundStatus, OutboundStatus | Export-Csv -Path $CSVPath -NoTypeInformation Write-Host "`nā Detailed report exported: $CSVPath" -ForegroundColor Green # Export MX records to separate CSV if ($CheckInbound) { $MXPath = Join-Path $ExportPath "${BaseFileName}_mx_records.csv" $AllResults | ForEach-Object { $Domain = $_.Domain $_.MXRecords | ForEach-Object { [PSCustomObject]@{ Domain = $Domain Priority = $_.Priority MailServer = $_.MailServer Provider = $_.Provider IPAddress = $_.IPAddress } } } | Export-Csv -Path $MXPath -NoTypeInformation Write-Host "ā MX records exported: $MXPath" -ForegroundColor Green } } Write-Host "`n" + ("=" * 80) -ForegroundColor Cyan # Return structured results return [PSCustomObject]@{ Timestamp = $Timestamp Domains = $AllResults Summary = @{ TotalDomains = $TotalDomains InboundConfigured = @($AllResults | Where-Object { $_.InboundStatus -eq "Configured" }).Count SPFConfigured = @($AllResults | Where-Object { $_.SPF.Configured }).Count DKIMConfigured = @($AllResults | Where-Object { $_.DKIM.Configured }).Count DMARCConfigured = @($AllResults | Where-Object { $_.DMARC.Configured }).Count } } } } # Quick usage examples: <# # Full analysis (default - both inbound and outbound) Get-MailDomainInfrastructure -Domains "example.com","subdomain.example.com" # Inbound only (MX records) Get-MailDomainInfrastructure -Domains "example.com" -InboundOnly # Outbound only (SPF/DKIM/DMARC) Get-MailDomainInfrastructure -Domains "example.com" -OutboundOnly # With exports Get-MailDomainInfrastructure -Domains $domains -ExportPath "C:\MailReports" # From file Get-Content domains.txt | Get-MailDomainInfrastructure -ExportPath ".\Reports" # Store results for further analysis $Results = Get-MailDomainInfrastructure -Domains "example.com","test.com" $Results.Summary $Results.Domains | Where-Object { $_.PrimaryProvider -eq "Microsoft 365" } #> # --- Source: Monitor-NameServerChange.ps1 -------------------------------- function Monitor-NameServerChange { <# .SYNOPSIS Monitors a domain for DNS nameserver (NS) record changes. .DESCRIPTION Continuously queries the specified domain for NS records and compares them against the initial baseline set retrieved at startup. The function will loop at a defined interval until: - A change in nameservers is detected (returns $true), or - The optional maximum number of checks is reached (returns $false). Useful for: - Tracking DNS propagation - Monitoring domain migrations - Validating registrar or hosting changes - Change verification during cutovers .PARAMETER Domain The domain name to monitor (e.g. example.com). This parameter is mandatory. .PARAMETER CheckInterval Number of seconds between DNS checks. Default value is 10 seconds. .PARAMETER MaxChecks Maximum number of checks before stopping. Default is 0, which means run indefinitely until a change is detected. .PARAMETER ShowProgress If specified, displays the current nameserver values during each check. .EXAMPLE Monitor-NameServerChange -Domain example.com Monitors example.com every 10 seconds until a nameserver change is detected. .EXAMPLE Monitor-NameServerChange -Domain example.com -CheckInterval 30 -ShowProgress Checks every 30 seconds and displays current NS records each cycle. .EXAMPLE Monitor-NameServerChange -Domain example.com -MaxChecks 20 Checks up to 20 times, then exits if no change is detected. .OUTPUTS System.Boolean Returns: - $true if a nameserver change is detected - $false if maximum checks are reached or initial lookup fails .NOTES Author: Mike Quick Requires: Resolve-DnsName (PowerShell 4.0+) Intended for DNS monitoring and change validation scenarios. #> [CmdletBinding()] param( [Parameter(Mandatory=$true)] [string]$Domain, [Parameter(Mandatory=$false)] [int]$CheckInterval = 10, [Parameter(Mandatory=$false)] [int]$MaxChecks = 0, [Parameter(Mandatory=$false)] [switch]$ShowProgress ) Write-Host "Starting nameserver monitoring for: $Domain" -ForegroundColor Cyan Write-Host "Check interval: $CheckInterval seconds" -ForegroundColor Cyan Write-Host "" # Get initial nameservers Write-Host "Getting initial nameservers..." -ForegroundColor Yellow try { $nsRecords = Resolve-DnsName -Name $Domain -Type NS -ErrorAction Stop $baselineNS = $nsRecords | Where-Object { $_.Type -eq 'NS' } | Select-Object -ExpandProperty NameHost | ForEach-Object { $_.TrimEnd('.').ToLower() } | Sort-Object -Unique } catch { Write-Host "ERROR: Could not retrieve initial nameservers for $Domain" -ForegroundColor Red Write-Host "Error: $_" -ForegroundColor Red return $false } if (-not $baselineNS) { Write-Host "ERROR: No nameservers found for $Domain" -ForegroundColor Red return $false } Write-Host "Baseline nameservers (watching for changes):" -ForegroundColor Yellow $baselineNS | ForEach-Object { Write-Host " - $_" -ForegroundColor White } Write-Host "" Write-Host "Monitoring for changes..." -ForegroundColor Cyan Write-Host "" $checkCount = 0 $startTime = Get-Date # Monitoring loop while ($true) { $checkCount++ $currentTime = Get-Date $elapsed = $currentTime - $startTime # Query current nameservers try { $nsRecords = Resolve-DnsName -Name $Domain -Type NS -ErrorAction Stop $currentNS = $nsRecords | Where-Object { $_.Type -eq 'NS' } | Select-Object -ExpandProperty NameHost | ForEach-Object { $_.TrimEnd('.').ToLower() } | Sort-Object -Unique } catch { Write-Host "[$checkCount] $(Get-Date -Format 'HH:mm:ss') - Failed to query nameservers" -ForegroundColor Red $currentNS = $null } if ($null -eq $currentNS) { # Continue on error } else { # Compare nameservers $match = ($currentNS.Count -eq $baselineNS.Count) -and ($baselineNS | ForEach-Object { $_ -in $currentNS } | Where-Object { -not $_ }).Count -eq 0 if (-not $match) { # Change detected! Write-Host "" Write-Host "SUCCESS! Nameserver change detected!" -ForegroundColor Green Write-Host "Domain: $Domain" -ForegroundColor Green Write-Host "" Write-Host "Original nameservers:" -ForegroundColor Yellow $baselineNS | ForEach-Object { Write-Host " - $_" -ForegroundColor White } Write-Host "" Write-Host "New nameservers:" -ForegroundColor Green $currentNS | ForEach-Object { Write-Host " - $_" -ForegroundColor White } Write-Host "" Write-Host "Total checks: $checkCount" -ForegroundColor Cyan Write-Host "Total time elapsed: $($elapsed.ToString('hh\:mm\:ss'))" -ForegroundColor Cyan Write-Host "Change detected at: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Cyan return $true } else { $status = "[$checkCount] $(Get-Date -Format 'HH:mm:ss') - No change detected" if ($ShowProgress) { Write-Host $status -ForegroundColor Gray Write-Host " Current: $($currentNS -join ', ')" -ForegroundColor DarkGray } else { Write-Host $status -ForegroundColor Gray } } } # Check if max checks reached if ($MaxChecks -gt 0 -and $checkCount -ge $MaxChecks) { Write-Host "" Write-Host "Maximum checks ($MaxChecks) reached without detecting change" -ForegroundColor Red Write-Host "Total time elapsed: $($elapsed.ToString('hh\:mm\:ss'))" -ForegroundColor Cyan return $false } Start-Sleep -Seconds $CheckInterval } } |