<# .SYNOPSIS Flushes the Windows DNS client cache on this computer or a list of remote computers. .DESCRIPTION Runs Clear-DnsClientCache (falling back to ipconfig /flushdns where the DnsClient module isn't available) and reports how many entries were cached before and after. Remote computers are reached with Invoke-Command over PowerShell remoting, in parallel. One unreachable machine doesn't stop the rest. Supports -WhatIf. .PARAMETER ComputerName One or more computers to flush. Defaults to this one. Accepts pipeline input, so Get-Content .\pcs.txt | .\Clear-DnsCache.ps1 works. .PARAMETER Credential Credentials for the remote computers. Not needed if your current account is an admin there. .PARAMETER ThrottleLimit How many remote computers to work on at once. Default: 32. .EXAMPLE .\Clear-DnsCache.ps1 .EXAMPLE Get-Content .\lab-pcs.txt | .\Clear-DnsCache.ps1 -Credential (Get-Credential) #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('CN', 'Name')] [ValidateNotNullOrEmpty()] [string[]]$ComputerName = $env:COMPUTERNAME, [pscredential]$Credential, [ValidateRange(1, 256)] [int]$ThrottleLimit = 32 ) begin { # This block runs on the target machine, local or remote. $flush = { $haveModule = [bool](Get-Command Clear-DnsClientCache -ErrorAction SilentlyContinue) $before = if ($haveModule) { @(Get-DnsClientCache -ErrorAction SilentlyContinue).Count } else { $null } if ($haveModule) { Clear-DnsClientCache $method = 'Clear-DnsClientCache' } else { $null = ipconfig.exe /flushdns if ($LASTEXITCODE -ne 0) { throw "ipconfig /flushdns exited with code $LASTEXITCODE" } $method = 'ipconfig /flushdns' } # Whatever is left is usually the hosts file, which gets reloaded straight back in. $after = if ($haveModule) { @(Get-DnsClientCache -ErrorAction SilentlyContinue).Count } else { $null } [pscustomobject]@{ EntriesBefore = $before EntriesAfter = $after Method = $method } } $localNames = @('.', 'localhost', $env:COMPUTERNAME) $remote = [System.Collections.Generic.List[string]]::new() } process { foreach ($computer in $ComputerName) { if (-not $PSCmdlet.ShouldProcess($computer, 'Flush DNS client cache')) { continue } if ($localNames -contains $computer) { try { $r = & $flush [pscustomobject]@{ ComputerName = $env:COMPUTERNAME Status = 'Flushed' EntriesBefore = $r.EntriesBefore EntriesAfter = $r.EntriesAfter Method = $r.Method Error = $null } } catch { [pscustomobject]@{ ComputerName = $env:COMPUTERNAME; Status = 'Failed'; EntriesBefore = $null; EntriesAfter = $null; Method = $null; Error = $_.Exception.Message } } } else { $remote.Add($computer) } } } end { if ($remote.Count -eq 0) { return } Write-Verbose "Flushing $($remote.Count) remote computer(s), $ThrottleLimit at a time." $icm = @{ ComputerName = $remote ScriptBlock = $flush ThrottleLimit = $ThrottleLimit ErrorAction = 'SilentlyContinue' ErrorVariable = 'remoteErrors' } if ($Credential) { $icm.Credential = $Credential } $results = @(Invoke-Command @icm) foreach ($r in $results) { [pscustomobject]@{ ComputerName = $r.PSComputerName Status = 'Flushed' EntriesBefore = $r.EntriesBefore EntriesAfter = $r.EntriesAfter Method = $r.Method Error = $null } } # Anything that didn't answer gets its own row, with the error if we can match it up. $answered = @($results.PSComputerName) foreach ($computer in $remote | Where-Object { $answered -notcontains $_ }) { $err = $remoteErrors | Where-Object { "$($_.TargetObject)" -eq $computer -or "$($_.OriginInfo.PSComputerName)" -eq $computer } | Select-Object -First 1 [pscustomobject]@{ ComputerName = $computer Status = 'Failed' EntriesBefore = $null EntriesAfter = $null Method = $null Error = if ($err) { $err.Exception.Message } else { 'No response (unreachable, or remoting not enabled)' } } } }