54 lines
4.4 KiB
PowerShell
54 lines
4.4 KiB
PowerShell
#Requires -Version 5.1
|
|
[CmdletBinding(SupportsShouldProcess)]
|
|
param(
|
|
[ValidateSet('Plan','Apply','Restore','Confirm','Validate','CaptureStart','CaptureStop')][string]$Mode='Plan',
|
|
[ValidateSet('Observe','BlockOptionalTelemetry','Isolate')][string]$Profile='Observe',
|
|
[int]$RollbackMinutes=10,[string]$Journal,[string]$CaptureDirectory,[switch]$FromRecoveryTask,[switch]$Json
|
|
)
|
|
Set-StrictMode -Version Latest;$ErrorActionPreference='Stop'
|
|
Import-Module (Join-Path $PSScriptRoot 'src/Wind.Core.psm1')
|
|
Import-Module (Join-Path $PSScriptRoot 'src/Wind.Network.psm1')
|
|
$stateRoot=Join-Path ([Environment]::GetFolderPath('LocalApplicationData')) 'MsPrivate\network'
|
|
if($Mode -eq 'Plan'){$x=Get-WindNetworkPlan -Profile $Profile -RollbackMinutes $RollbackMinutes;if($Json){$x|ConvertTo-Json -Depth 8}else{$x.Items|Format-Table -Wrap};return}
|
|
if($Mode -eq 'Validate'){$x=Get-WindNetworkValidation;if($Json){$x|ConvertTo-Json -Depth 8}else{$x|Format-List};return}
|
|
if($Mode -eq 'CaptureStart'){
|
|
if(-not $CaptureDirectory){$CaptureDirectory=Join-Path $stateRoot ('capture-'+[guid]::NewGuid().ToString('N'))}
|
|
$CaptureDirectory=Resolve-WindCaptureDirectory -Path $CaptureDirectory -StateRoot $stateRoot
|
|
$etl=Join-Path $CaptureDirectory 'capture.etl';if(Test-Path -LiteralPath $etl){throw 'Capture output already exists. Choose a new capture directory.'}
|
|
if(-not $PSCmdlet.ShouldProcess($CaptureDirectory,'Start packet capture')){return}
|
|
Assert-WindMutationAllowed
|
|
[IO.Directory]::CreateDirectory($CaptureDirectory)|Out-Null
|
|
Invoke-WindPacketMonitor -ArgumentList @('start','--capture','--comp','nics','--pkt-size','0','--file-name',$etl)
|
|
[pscustomobject]@{Status='Running';Directory=$CaptureDirectory;Warning='Capture can contain private addresses and DNS names. Stop and delete or export it explicitly.'}|ConvertTo-Json
|
|
return
|
|
}
|
|
if($Mode -eq 'CaptureStop'){
|
|
if(-not $CaptureDirectory){throw 'CaptureStop requires -CaptureDirectory.'};$full=Resolve-WindCaptureDirectory -Path $CaptureDirectory -StateRoot $stateRoot
|
|
if(-not $PSCmdlet.ShouldProcess($full,'Stop packet capture and convert to PCAPNG')){return}
|
|
Assert-WindMutationAllowed
|
|
$etl=Join-Path $full 'capture.etl';$pcap=Join-Path $full 'capture.pcapng'
|
|
if(-not(Test-Path -LiteralPath $etl -PathType Leaf)){throw 'Capture ETL file is missing; no capture was stopped.'}
|
|
if(Test-Path -LiteralPath $pcap){throw 'Converted capture already exists; preserve or move it before retrying conversion.'}
|
|
Invoke-WindPacketMonitor -ArgumentList @('stop')
|
|
Invoke-WindPacketMonitor -ArgumentList @('etl2pcap',$etl,'--out',$pcap)
|
|
if(-not(Test-Path -LiteralPath $pcap -PathType Leaf)){throw 'Packet capture conversion returned success without creating the PCAPNG file.'}
|
|
Save-WindJournal -Journal (Get-WindNetworkValidation) -Path (Join-Path $full 'connections.json')
|
|
Write-Output "Capture stopped: $full";return
|
|
}
|
|
Assert-WindMutationAllowed
|
|
if($Mode -eq 'Apply'){
|
|
if($Profile -eq 'Observe'){throw 'Observe has no firewall changes to apply.'};if(-not $PSCmdlet.ShouldProcess('Windows Firewall',$Profile)){return}
|
|
[IO.Directory]::CreateDirectory($stateRoot)|Out-Null
|
|
if(-not $Journal){$Journal=Join-Path $stateRoot ((Get-Date -Format 'yyyyMMdd-HHmmss')+'-'+[guid]::NewGuid().ToString('N')+'.json')}
|
|
$runtime=Join-Path $stateRoot 'recovery-runtime';[IO.Directory]::CreateDirectory((Join-Path $runtime 'src'))|Out-Null;[IO.Directory]::CreateDirectory((Join-Path $runtime 'config'))|Out-Null
|
|
Copy-Item -LiteralPath $PSCommandPath -Destination (Join-Path $runtime 'Network.ps1') -Force
|
|
foreach($name in @('Wind.Core.psm1','Wind.Network.psm1','MsPrivate.IO.psm1')){Copy-Item -LiteralPath (Join-Path $PSScriptRoot ('src\'+$name)) -Destination (Join-Path $runtime ('src\'+$name)) -Force}
|
|
Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'config\network.json') -Destination (Join-Path $runtime 'config\network.json') -Force
|
|
$result=Invoke-WindNetworkApply -Profile $Profile -RollbackMinutes $RollbackMinutes -JournalPath $Journal -RecoveryScript (Join-Path $runtime 'Network.ps1') -Confirm:$false
|
|
Write-Output "Network journal: $Journal";$result|Format-List;return
|
|
}
|
|
if(-not $Journal){throw "$Mode requires -Journal."}
|
|
$saved=Get-Content -LiteralPath $Journal -Raw|ConvertFrom-Json
|
|
$journalWriter=Get-Command Save-WindJournal -CommandType Function;$save={param($x)& $journalWriter -Journal $x -Path $Journal}.GetNewClosure()
|
|
if($Mode-eq'Confirm'){Confirm-WindNetwork -Journal $saved -Save $save|Format-List}else{Restore-WindNetwork -Journal $saved -Save $save|Format-List}
|