Skip to content

Instantly share code, notes, and snippets.

@alexbujduveanu
Created March 26, 2025 20:33
Show Gist options
  • Save alexbujduveanu/a6c4c9759740c27a79b17e9bf48a0ea1 to your computer and use it in GitHub Desktop.
Save alexbujduveanu/a6c4c9759740c27a79b17e9bf48a0ea1 to your computer and use it in GitHub Desktop.
Windows Hardening Script
#Requires -RunAsAdministrator
<#
.SYNOPSIS
Windows Hardening Script for Cloud Environments (AWS, GCP, Azure)
.DESCRIPTION
This script implements security hardening measures for Windows instances
running in public cloud environments while maintaining usability.
.NOTES
Run with administrator privileges
#>
# Script Configuration
$LogFile = "C:\Logs\Windows-Cloud-Hardening.log"
$BackupFolder = "C:\Backup\Registry-$(Get-Date -Format 'yyyyMMdd')"
# Create Log and Backup directories
New-Item -ItemType Directory -Path (Split-Path $LogFile -Parent) -Force | Out-Null
New-Item -ItemType Directory -Path $BackupFolder -Force | Out-Null
# Initialize Log
function Write-Log {
param([string]$Message)
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
"$Timestamp - $Message" | Out-File -FilePath $LogFile -Append
Write-Host "$Timestamp - $Message"
}
Write-Log "Starting Windows hardening script for cloud environments"
# Backup Registry Keys before modifications
Write-Log "Creating registry backups"
try {
reg export "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies" "$BackupFolder\Policies.reg" /y
reg export "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" "$BackupFolder\Lsa.reg" /y
reg export "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" "$BackupFolder\WindowsUpdate.reg" /y
reg export "HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell" "$BackupFolder\PowerShell.reg" /y
reg export "HKLM\SOFTWARE\Microsoft\.NETFramework" "$BackupFolder\NETFramework.reg" /y
Write-Log "Registry backups completed successfully"
}
catch {
Write-Log "Error creating registry backups: $_"
}
#################################################
# 1. Account Policies and Authentication Hardening
#################################################
Write-Log "Configuring account policies and authentication"
# Enhance password policies
try {
# Set minimum password length to 12 characters
net accounts /minpwlen:12
# Set maximum password age to 60 days
net accounts /maxpwage:60
# Set password history to remember 24 passwords
net accounts /uniquepw:24
# Set account lockout threshold to 5 bad attempts
net accounts /lockoutthreshold:5
# Set account lockout duration to 30 minutes
net accounts /lockoutduration:30
# Set account lockout observation window to 30 minutes
net accounts /lockoutwindow:30
Write-Log "Password policies configured successfully"
}
catch {
Write-Log "Error setting password policies: $_"
}
# LSA Protection
try {
# Enable LSA protection - Prevents reading memory from LSA process
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" -Name "RunAsPPL" -Value 1 -PropertyType DWORD -Force
Write-Log "LSA protection enabled"
}
catch {
Write-Log "Error enabling LSA protection: $_"
}
# Credential Guard (if supported by hardware)
try {
$DevGuardStatus = Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard
if ($DevGuardStatus.SecurityServicesRunning -contains 1) {
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" -Name "LsaCfgFlags" -Value 1 -PropertyType DWORD -Force
Write-Log "Credential Guard enabled"
}
else {
Write-Log "Credential Guard not supported on this hardware - skipping"
}
}
catch {
Write-Log "Error configuring Credential Guard: $_"
}
# Disable NTLM (only if using Kerberos exclusively)
# Commented out by default to avoid potential access issues
<#
try {
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" -Name "LmCompatibilityLevel" -Value 5 -PropertyType DWORD -Force
Write-Log "NTLM disabled, using Kerberos only"
}
catch {
Write-Log "Error disabling NTLM: $_"
}
#>
# Enable Windows Defender Credential Guard
try {
# Check if virtualization-based security is available
$SecureBootState = Confirm-SecureBootUEFI -ErrorAction SilentlyContinue
if ($SecureBootState) {
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard" -Name "EnableVirtualizationBasedSecurity" -Value 1 -PropertyType DWORD -Force
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard" -Name "RequirePlatformSecurityFeatures" -Value 3 -PropertyType DWORD -Force
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\LSA" -Name "LsaCfgFlags" -Value 1 -PropertyType DWORD -Force
Write-Log "Windows Defender Credential Guard enabled"
}
else {
Write-Log "Secure boot not enabled, skipping Credential Guard configuration"
}
}
catch {
Write-Log "Error configuring Windows Defender Credential Guard: $_"
}
#################################################
# 2. PowerShell and .NET Hardening
#################################################
Write-Log "Configuring PowerShell hardening measures"
# Enable PowerShell logging and auditing
try {
# Create PowerShell profile path if it doesn't exist
$PowerShellPolicyPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell"
if (-not (Test-Path $PowerShellPolicyPath)) {
New-Item -Path $PowerShellPolicyPath -Force | Out-Null
}
# Enable script block logging
$ScriptBlockPath = "$PowerShellPolicyPath\ScriptBlockLogging"
if (-not (Test-Path $ScriptBlockPath)) {
New-Item -Path $ScriptBlockPath -Force | Out-Null
}
New-ItemProperty -Path $ScriptBlockPath -Name "EnableScriptBlockLogging" -Value 1 -PropertyType DWORD -Force
# Enable PowerShell Module Logging
$ModuleLoggingPath = "$PowerShellPolicyPath\ModuleLogging"
if (-not (Test-Path $ModuleLoggingPath)) {
New-Item -Path $ModuleLoggingPath -Force | Out-Null
}
New-ItemProperty -Path $ModuleLoggingPath -Name "EnableModuleLogging" -Value 1 -PropertyType DWORD -Force
# Enable PowerShell Transcription
$TranscriptionPath = "$PowerShellPolicyPath\Transcription"
if (-not (Test-Path $TranscriptionPath)) {
New-Item -Path $TranscriptionPath -Force | Out-Null
}
New-ItemProperty -Path $TranscriptionPath -Name "EnableTranscripting" -Value 1 -PropertyType DWORD -Force
New-ItemProperty -Path $TranscriptionPath -Name "EnableInvocationHeader" -Value 1 -PropertyType DWORD -Force
New-ItemProperty -Path $TranscriptionPath -Name "OutputDirectory" -Value "C:\PowerShellLogs" -PropertyType STRING -Force
New-Item -Path "C:\PowerShellLogs" -ItemType Directory -Force | Out-Null
Write-Log "PowerShell logging enabled"
}
catch {
Write-Log "Error setting PowerShell logging: $_"
}
# Configure Constrained Language Mode
# This is a powerful security control but can impact functionality
try {
# Create Environment variable for system-wide Constrained Language Mode
[Environment]::SetEnvironmentVariable("__PSLockdownPolicy", "4", "Machine")
# Create a startup script to enforce constrained language mode at system startup
$StartupScriptPath = "C:\Windows\ConstrainedLanguage.ps1"
@"
`$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
"@ | Out-File -FilePath $StartupScriptPath -Force
# Create a registry entry to run this script when PowerShell starts
$PSStartupRegPath = "HKLM:\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell"
Set-ItemProperty -Path $PSStartupRegPath -Name "ExecutionPolicy" -Value "AllSigned" -Force
Write-Log "PowerShell Constrained Language Mode configured"
}
catch {
Write-Log "Error setting PowerShell Constrained Language Mode: $_"
}
# Disable PowerShell v2 to prevent downgrade attacks
try {
Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2 -NoRestart
Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root -NoRestart
Write-Log "PowerShell v2 disabled"
}
catch {
Write-Log "Error disabling PowerShell v2: $_"
}
# Secure .NET Framework settings
try {
# Set .NET Framework to use strong cryptography for all versions
$NetFrameworkVersions = @("v2.0.50727", "v4.0.30319")
foreach($version in $NetFrameworkVersions) {
# For 32-bit .NET
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\.NETFramework\$version" -Name "SchUseStrongCrypto" -Value 1 -PropertyType DWORD -Force
# For 64-bit .NET
New-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\$version" -Name "SchUseStrongCrypto" -Value 1 -PropertyType DWORD -Force
}
Write-Log ".NET Framework strong cryptography enabled"
}
catch {
Write-Log "Error configuring .NET Framework: $_"
}
#################################################
# 3. System Hardening
#################################################
Write-Log "Configuring system hardening measures"
# Enable Windows Defender security features
try {
# Enable real-time monitoring
Set-MpPreference -DisableRealtimeMonitoring $false
# Enable cloud-based protection
Set-MpPreference -MAPSReporting Advanced
Set-MpPreference -SubmitSamplesConsent SendAllSamples
# Enable behavior monitoring
Set-MpPreference -DisableBehaviorMonitoring $false
# Enable exploit protection
Set-MpPreference -EnableControlledFolderAccess Enabled
# Enable network protection
Set-MpPreference -EnableNetworkProtection Enabled
# Attack Surface Reduction rules
# Block credential stealing from the Windows local security authority subsystem (lsass.exe)
# This prevents attackers from using tools like Mimikatz to extract passwords and hashes from memory
Set-MpPreference -AttackSurfaceReductionRules_Ids D4F940AB-401B-4EFC-AADC-AD5F3C50688A -AttackSurfaceReductionRules_Actions Enabled
# Block process creations originating from PSExec and WMI commands
# This blocks common lateral movement techniques used by attackers after they've gained initial access
Set-MpPreference -AttackSurfaceReductionRules_Ids 5BEB7EFE-FD9A-4556-801D-275E5FFC04CC -AttackSurfaceReductionRules_Actions Enabled
# Block JavaScript or VBScript from launching downloaded executable content
# This prevents scripts from downloading and executing malicious payloads
Set-MpPreference -AttackSurfaceReductionRules_Ids BE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550 -AttackSurfaceReductionRules_Actions Enabled
Write-Log "Windows Defender security features enabled"
}
catch {
Write-Log "Error configuring Windows Defender: $_"
}
# Registry hardening settings
try {
# Disable LLMNR (Link-Local Multicast Name Resolution)
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient" -Force | Out-Null
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient" -Name "EnableMulticast" -Value 0 -PropertyType DWORD -Force
# Disable NBT-NS (NetBIOS Name Service)
$NICs = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE
foreach($NIC in $NICs) {
$NIC.SetTcpipNetbios(2) | Out-Null
}
# Disable SMBv1
Set-SmbServerConfiguration -EnableSMB1Protocol $false -Force
# Enable SMB Encryption
Set-SmbServerConfiguration -EncryptData $true -Force
# Disable anonymous access to named pipes and shares
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanManServer\Parameters" -Name "RestrictNullSessAccess" -Value 1 -PropertyType DWORD -Force
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" -Name "RestrictAnonymous" -Value 2 -PropertyType DWORD -Force
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" -Name "EveryoneIncludesAnonymous" -Value 0 -PropertyType DWORD -Force
# Enable Data Execution Prevention (DEP)
bcdedit.exe /set nx AlwaysOn
# Disable AutoRun
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer" -Name "NoDriveTypeAutoRun" -Value 255 -PropertyType DWORD -Force
# Disable Remote Assistance
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Remote Assistance" -Name "fAllowToGetHelp" -Value 0 -PropertyType DWORD -Force
# Disable Admin Shares to reduce attack surface
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" -Name "AutoShareWks" -Value 0 -PropertyType DWORD -Force
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" -Name "AutoShareServer" -Value 0 -PropertyType DWORD -Force
Write-Log "Admin shares disabled"
Write-Log "Registry hardening completed"
}
catch {
Write-Log "Error setting registry hardening: $_"
}
# Network hardening
try {
# Enable Windows Firewall for all profiles
Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True
# Block inbound connections by default
Set-NetFirewallProfile -Profile Domain,Public,Private -DefaultInboundAction Block
# Allow outbound connections by default
Set-NetFirewallProfile -Profile Domain,Public,Private -DefaultOutboundAction Allow
# Enable Windows Firewall logging
Set-NetFirewallProfile -Profile Domain,Public,Private -LogBlocked True -LogMaxSize 4096
# Only allow secure RDP (NLA) connections
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "SecurityLayer" -Value 2 -PropertyType DWORD -Force
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "UserAuthentication" -Value 1 -PropertyType DWORD -Force
Write-Log "Network hardening completed"
}
catch {
Write-Log "Error configuring network hardening: $_"
}
#################################################
# 4. Additional Cloud-Specific Hardening
#################################################
Write-Log "Configuring cloud-specific hardening measures"
# Disable weak TLS/SSL protocols
try {
# Disable SSL 2.0
New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server" -Force | Out-Null
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server" -Name "Enabled" -Value 0 -PropertyType DWORD -Force
# Disable SSL 3.0
New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server" -Force | Out-Null
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server" -Name "Enabled" -Value 0 -PropertyType DWORD -Force
# Disable TLS 1.0
New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server" -Force | Out-Null
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server" -Name "Enabled" -Value 0 -PropertyType DWORD -Force
# Disable TLS 1.1
New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server" -Force | Out-Null
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server" -Name "Enabled" -Value 0 -PropertyType DWORD -Force
# Enable TLS 1.2
New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server" -Force | Out-Null
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server" -Name "Enabled" -Value 1 -PropertyType DWORD -Force
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server" -Name "DisabledByDefault" -Value 0 -PropertyType DWORD -Force
# Enable TLS 1.3 (if supported)
New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Server" -Force | Out-Null
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Server" -Name "Enabled" -Value 1 -PropertyType DWORD -Force
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Server" -Name "DisabledByDefault" -Value 0 -PropertyType DWORD -Force
Write-Log "Weak TLS/SSL protocols disabled"
}
catch {
Write-Log "Error configuring TLS/SSL protocols: $_"
}
# Disable unnecessary services based on cloud environment
try {
$ServicesToDisable = @(
"Browser", # Computer Browser service
"SharedAccess", # Internet Connection Sharing
"lltdsvc", # Link-Layer Topology Discovery Mapper
"SSDPSRV", # SSDP Discovery
"upnphost", # UPnP Device Host
"WMPNetworkSvc", # Windows Media Player Network Sharing
"WSearch" # Windows Search (can consume resources in server environments)
)
foreach ($Service in $ServicesToDisable) {
Stop-Service -Name $Service -Force -ErrorAction SilentlyContinue
Set-Service -Name $Service -StartupType Disabled -ErrorAction SilentlyContinue
}
Write-Log "Unnecessary services disabled"
}
catch {
Write-Log "Error disabling unnecessary services: $_"
}
# Configure Windows Updates for high-availability production database servers
try {
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -Force | Out-Null
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Force | Out-Null
# Option 3 = Notify before download and installation (no automatic reboots)
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "NoAutoUpdate" -Value 0 -PropertyType DWORD -Force
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "AUOptions" -Value 3 -PropertyType DWORD -Force
# Prevent automatic restarts
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -Name "NoAutoRebootWithLoggedOnUsers" -Value 1 -PropertyType DWORD -Force
# Set maintenance window to a low-impact time (e.g., Sunday at 2 AM)
# Use 0 for Sunday, 1-6 for Monday-Saturday
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "ScheduledInstallDay" -Value 0 -PropertyType DWORD -Force
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "ScheduledInstallTime" -Value 2 -PropertyType DWORD -Force
# Specify a deadline for required updates (5 days) for critical security updates
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -Name "RebootRelaunchTimeout" -Value 1440 -PropertyType DWORD -Force
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -Name "RebootRelaunchTimeoutEnabled" -Value 1 -PropertyType DWORD -Force
Write-Log "Windows Updates configured for high-availability environment"
}
catch {
Write-Log "Error setting automatic updates: $_"
}
#################################################
# 5. File System Hardening
#################################################
Write-Log "Configuring file system hardening"
# Enable File System Auditing
try {
auditpol /set /category:"Object Access" /success:enable /failure:enable
Write-Log "File system auditing enabled"
}
catch {
Write-Log "Error enabling file system auditing: $_"
}
# Set restricted permissions on sensitive folders
try {
$SensitiveFolders = @(
"C:\Windows\System32\config",
"C:\Windows\System32\drivers\etc",
"C:\Program Files",
"C:\Program Files (x86)",
"C:\Windows\System32"
)
foreach ($Folder in $SensitiveFolders) {
if (Test-Path $Folder) {
# Using icacls to set restrictive permissions
icacls $Folder /inheritance:r
icacls $Folder /grant:r "BUILTIN\Administrators:(OI)(CI)F"
icacls $Folder /grant:r "NT AUTHORITY\SYSTEM:(OI)(CI)F"
}
}
Write-Log "Restrictive permissions set on sensitive folders"
}
catch {
Write-Log "Error setting folder permissions: $_"
}
#################################################
# Cleanup and Reporting
#################################################
Write-Log "Hardening completed - generating report"
# Generate system info report
try {
$ReportFile = "C:\Logs\HardeningReport_$(Get-Date -Format 'yyyyMMdd').txt"
"Windows Hardening Status Report - $(Get-Date)" | Out-File -FilePath $ReportFile
"" | Out-File -FilePath $ReportFile -Append
# Windows Version
"OS Information:" | Out-File -FilePath $ReportFile -Append
Get-CimInstance -ClassName Win32_OperatingSystem | Format-List Caption, Version, BuildNumber, ServicePackMajorVersion | Out-File -FilePath $ReportFile -Append
# Windows Update Status
"Windows Update Status:" | Out-File -FilePath $ReportFile -Append
Get-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" | Format-List | Out-File -FilePath $ReportFile -Append
# Firewall Status
"Firewall Status:" | Out-File -FilePath $ReportFile -Append
Get-NetFirewallProfile | Format-Table Name, Enabled, DefaultInboundAction, DefaultOutboundAction | Out-File -FilePath $ReportFile -Append
# PowerShell Security Settings
"PowerShell Security Status:" | Out-File -FilePath $ReportFile -Append
"Constrained Language Mode: $($ExecutionContext.SessionState.LanguageMode)" | Out-File -FilePath $ReportFile -Append
# Windows Defender Status
"Windows Defender Status:" | Out-File -FilePath $ReportFile -Append
Get-MpComputerStatus | Format-List AntivirusEnabled, AntispywareEnabled, RealTimeProtectionEnabled, IoavProtectionEnabled, BehaviorMonitorEnabled | Out-File -FilePath $ReportFile -Append
Write-Log "System report generated at $ReportFile"
}
catch {
Write-Log "Error generating system report: $_"
}
Write-Log "Hardening script execution completed. Please review the log file at $LogFile and report at $ReportFile"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment