FileVault Key Rotation đ
FileVault Key Rotation đ
Section titled âFileVault Key Rotation đâOverview
Section titled âOverviewâThis PowerShell script provides automated FileVault recovery key rotation for macOS devices managed through Microsoft Intune. The script enhances security by regularly rotating FileVault recovery keys while maintaining proper audit trails and backup procedures.
Prerequisites
Section titled âPrerequisitesâSystem Requirements
Section titled âSystem Requirementsâ- macOS: 10.15 (Catalina) or later
- Microsoft Intune: Device management and script deployment
- PowerShell: 7.0 or later for cross-platform compatibility
- Administrator Privileges: Required for FileVault operations
Dependencies
Section titled âDependenciesâ- FileVault: Must be enabled on target devices
- Intune Management: Device enrolled in Intune
- Secure Storage: Encrypted location for key backup
- Logging Infrastructure: Centralized logging system
Script Configuration
Section titled âScript ConfigurationâParameters
Section titled âParametersâparam( [Parameter(Mandatory=$false)] [string]$KeyBackupLocation = "/var/log/filevault-keys",
[Parameter(Mandatory=$false)] [string]$LogPath = "/var/log/filevault-rotation.log",
[Parameter(Mandatory=$false)] [int]$KeyRetentionDays = 90,
[Parameter(Mandatory=$false)] [switch]$ForceRotation = $false,
[Parameter(Mandatory=$false)] [switch]$TestMode = $false)Global Variables
Section titled âGlobal Variablesâ# Configuration settings$ScriptVersion = "2.1.0"$CompanyName = "YourCompany"$SupportEmail = "support@yourcompany.com"$MaxRotationAttempts = 3$RotationIntervalDays = 30Script Implementation
Section titled âScript ImplementationâMain Function
Section titled âMain Functionâfunction Start-FileVaultKeyRotation { param( [string]$BackupPath, [string]$LogPath, [int]$RetentionDays, [bool]$Force, [bool]$Test )
try { # Initialize logging Initialize-Logging -LogPath $LogPath
Write-Log "Starting FileVault key rotation - Version $ScriptVersion" Write-Log "Parameters: BackupPath=$BackupPath, RetentionDays=$RetentionDays, Force=$Force, Test=$Test"
# Check prerequisites if (-not (Test-Prerequisites)) { Write-Log "Prerequisites check failed" -Level "ERROR" return $false }
# Get current FileVault status $fVaultStatus = Get-FileVaultStatus if (-not $fVaultStatus.Enabled) { Write-Log "FileVault is not enabled on this device" -Level "WARNING" return $false }
# Check if rotation is needed if (-not $Force -and -not (Test-RotationNeeded -LastRotation $fVaultStatus.LastRotation)) { Write-Log "Key rotation not needed at this time" return $true }
# Perform key rotation $rotationResult = Invoke-KeyRotation -TestMode $Test if ($rotationResult.Success) { Write-Log "Key rotation completed successfully"
# Backup new key $backupResult = Backup-RecoveryKey -Key $rotationResult.NewKey -BackupPath $BackupPath if ($backupResult.Success) { Write-Log "Recovery key backed up successfully" } else { Write-Log "Failed to backup recovery key" -Level "ERROR" }
# Clean up old keys Remove-OldKeys -RetentionDays $RetentionDays -BackupPath $BackupPath
return $true } else { Write-Log "Key rotation failed: $($rotationResult.ErrorMessage)" -Level "ERROR" return $false }
} catch { Write-Log "Unexpected error in Start-FileVaultKeyRotation: $($_.Exception.Message)" -Level "ERROR" return $false }}Helper Functions
Section titled âHelper FunctionsâPrerequisites Check
Section titled âPrerequisites Checkâfunction Test-Prerequisites { try { # Check if running on macOS if ($PSVersionTable.Platform -ne "Unix" -or $env:OS -ne "Darwin") { Write-Log "This script must be run on macOS" -Level "ERROR" return $false }
# Check FileVault availability $fVaultCheck = & fdesetup status 2>&1 if ($LASTEXITCODE -ne 0) { Write-Log "FileVault command not available" -Level "ERROR" return $false }
# Check admin privileges if (-not (Test-AdminPrivileges)) { Write-Log "Administrator privileges required" -Level "ERROR" return $false }
# Create backup directory if needed if (-not (Test-Path $KeyBackupLocation)) { New-Item -Path $KeyBackupLocation -ItemType Directory -Force | Out-Null Write-Log "Created backup directory: $KeyBackupLocation" }
return $true } catch { Write-Log "Prerequisites check failed: $($_.Exception.Message)" -Level "ERROR" return $false }}FileVault Status Check
Section titled âFileVault Status Checkâfunction Get-FileVaultStatus { try { $statusOutput = & fdesetup status $enabled = $statusOutput -match "FileVault is (On|Off)" $lastRotation = Get-LastRotationDate
return [PSCustomObject]@{ Enabled = ($statusOutput -match "FileVault is On") Status = $statusOutput LastRotation = $lastRotation RecoveryKeyPresent = Test-RecoveryKeyPresent } } catch { Write-Log "Failed to get FileVault status: $($_.Exception.Message)" -Level "ERROR" return $null }}Key Rotation Implementation
Section titled âKey Rotation Implementationâfunction Invoke-KeyRotation { param([bool]$TestMode)
try { Write-Log "Starting key rotation process"
if ($TestMode) { Write-Log "Running in test mode - no actual rotation will occur" return [PSCustomObject]@{ Success = $true NewKey = "TEST-KEY-$(Get-Random -Maximum 9999)" RotationDate = Get-Date TestMode = $true } }
# Generate new recovery key $newKey = & fdesetup changerecoverykey -personal 2>&1 if ($LASTEXITCODE -ne 0) { throw "Failed to generate new recovery key: $newKey" }
# Extract key from output $keyPattern = 'Recovery key: ([A-Z0-9\-]+)' if ($newKey -match $keyPattern) { $extractedKey = $matches[1] } else { throw "Could not extract recovery key from output" }
# Update last rotation date Set-LastRotationDate -Date (Get-Date)
Write-Log "Successfully generated new recovery key"
return [PSCustomObject]@{ Success = $true NewKey = $extractedKey RotationDate = Get-Date TestMode = $false }
} catch { Write-Log "Key rotation failed: $($_.Exception.Message)" -Level "ERROR" return [PSCustomObject]@{ Success = $false ErrorMessage = $_.Exception.Message RotationDate = $null } }}Key Backup Function
Section titled âKey Backup Functionâfunction Backup-RecoveryKey { param( [string]$Key, [string]$BackupPath )
try { $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" $hostname = & hostname $filename = "filevault-key-$hostname-$timestamp.txt" $fullPath = Join-Path $BackupPath $filename
# Create backup file with metadata $backupContent = @"FileVault Recovery Key Backup==============================Generated: $(Get-Date)Hostname: $hostnameKey: $KeyScript Version: $ScriptVersionCompany: $CompanyName
IMPORTANT: Store this key in a secure location.This key can unlock the encrypted disk if the user forgets their password."@
# Write backup file Set-Content -Path $fullPath -Value $backupContent -Encoding UTF8
# Set secure permissions & chmod 600 $fullPath
# Encrypt backup file if possible if (Test-CommandAvailable "gpg") { try { & gpg --symmetric --cipher-algo AES256 --compress-algo 1 --s2k-mode 3 --s2k-digest-algo SHA512 --s2k-count 65536 --passphrase-file "/etc/filevault-passphrase" --output "$fullPath.gpg" $fullPath if ($LASTEXITCODE -eq 0) { Remove-Item $fullPath -Force $fullPath = "$fullPath.gpg" Write-Log "Recovery key backup encrypted with GPG" } } catch { Write-Log "GPG encryption failed, keeping plaintext backup" -Level "WARNING" } }
Write-Log "Recovery key backed up to: $fullPath"
return [PSCustomObject]@{ Success = $true BackupPath = $fullPath Timestamp = $timestamp }
} catch { Write-Log "Failed to backup recovery key: $($_.Exception.Message)" -Level "ERROR" return [PSCustomObject]@{ Success = $false ErrorMessage = $_.Exception.Message } }}Cleanup Old Keys
Section titled âCleanup Old Keysâfunction Remove-OldKeys { param( [int]$RetentionDays, [string]$BackupPath )
try { $cutoffDate = (Get-Date).AddDays(-$RetentionDays) $keyFiles = Get-ChildItem -Path $BackupPath -Filter "filevault-key-*.txt*" -File
$removedCount = 0 foreach ($file in $keyFiles) { if ($file.CreationTime -lt $cutoffDate) { Write-Log "Removing old key file: $($file.Name)" Remove-Item $file.FullName -Force $removedCount++ } }
if ($removedCount -gt 0) { Write-Log "Removed $removedCount old key files older than $RetentionDays days" } else { Write-Log "No old key files to remove" }
} catch { Write-Log "Failed to remove old keys: $($_.Exception.Message)" -Level "ERROR" }}Utility Functions
Section titled âUtility FunctionsâLogging Function
Section titled âLogging Functionâfunction Write-Log { param( [string]$Message, [ValidateSet("INFO", "WARNING", "ERROR", "DEBUG")] [string]$Level = "INFO" )
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $hostname = & hostname $logEntry = "[$timestamp] [$hostname] [$Level] $Message"
# Write to log file Add-Content -Path $LogPath -Value $logEntry -Encoding UTF8
# Write to console if running interactively if (-not $env:USERPROFILE) { switch ($Level) { "ERROR" { Write-Host $logEntry -ForegroundColor Red } "WARNING" { Write-Host $logEntry -ForegroundColor Yellow } "DEBUG" { Write-Host $logEntry -ForegroundColor Gray } default { Write-Host $logEntry -ForegroundColor Green } } }}Admin Privileges Check
Section titled âAdmin Privileges Checkâfunction Test-AdminPrivileges { $currentUser = & id -u return $currentUser -eq 0}Command Availability Check
Section titled âCommand Availability Checkâfunction Test-CommandAvailable { param([string]$Command)
try { $null = & which $Command 2>&1 return $LASTEXITCODE -eq 0 } catch { return $false }}Date Management Functions
Section titled âDate Management Functionsâfunction Get-LastRotationDate { $stateFile = "/var/filevault-rotation-state.json"
if (Test-Path $stateFile) { try { $state = Get-Content $stateFile | ConvertFrom-Json return [DateTime]$state.LastRotation } catch { Write-Log "Failed to read rotation state file" -Level "WARNING" return (Get-Date).AddDays(-$RotationIntervalDays - 1) } } else { return (Get-Date).AddDays(-$RotationIntervalDays - 1) }}
function Set-LastRotationDate { param([DateTime]$Date)
$stateFile = "/var/filevault-rotation-state.json" $state = @{ LastRotation = $Date.ToString("yyyy-MM-ddTHH:mm:ssZ") ScriptVersion = $ScriptVersion Hostname = & hostname }
try { Set-Content -Path $stateFile -Value ($state | ConvertTo-Json -Depth 3) -Encoding UTF8 } catch { Write-Log "Failed to write rotation state file: $($_.Exception.Message)" -Level "WARNING" }}Deployment Instructions
Section titled âDeployment InstructionsâIntune Script Deployment
Section titled âIntune Script Deploymentâ-
Create Script Package:
- Save script as
FileVaultKeyRotation.ps1 - Create deployment package with configuration files
- Test script in development environment
- Save script as
-
Configure Intune Settings:
- Navigate to Microsoft Endpoint Manager
- Create new PowerShell script deployment
- Set script parameters for your environment
- Assign to appropriate device groups
-
Deployment Configuration:
{ "scriptSettings": { "fileName": "FileVaultKeyRotation.ps1", "runAsAccount": "system", "enforceSignatureCheck": false, "runAs32Bit": false }, "scheduleSettings": { "runOnce": false, "recurrence": "weekly", "startDateTime": "2024-01-01T02:00:00Z" }}Monitoring and Alerting
Section titled âMonitoring and Alertingâ# Create monitoring script for Intunefunction Monitor-FileVaultRotation { $logPath = "/var/log/filevault-rotation.log" $errorPattern = "ERROR"
$recentErrors = Select-String -Path $logPath -Pattern $errorPattern -Context 2 | Where-Object { $_.LineNumber -gt (Get-Content $logPath).Count - 100 }
if ($recentErrors.Count -gt 0) { # Send alert to IT team Send-Alert -Message "FileVault rotation errors detected" -Details $recentErrors }}Security Considerations
Section titled âSecurity ConsiderationsâKey Protection
Section titled âKey Protectionâ- Secure Storage: Store backup keys in encrypted location
- Access Control: Limit access to backup files
- Audit Trail: Log all key operations
- Retention Policy: Define key retention periods
Compliance Requirements
Section titled âCompliance Requirementsâ- Data Protection: Ensure compliance with data protection regulations
- Audit Logs: Maintain comprehensive audit trail
- Key Escrow: Implement secure key escrow process
- Documentation: Maintain security procedures documentation
Troubleshooting
Section titled âTroubleshootingâCommon Issues
Section titled âCommon Issuesâ- Permission Errors: Ensure script runs with appropriate privileges
- FileVault Status: Verify FileVault is properly enabled
- Backup Failures: Check storage space and permissions
- Network Issues: Ensure connectivity for centralized logging
Debug Mode
Section titled âDebug Modeâ# Enable debug logging$env:FILEVAULT_DEBUG = "true"Start-FileVaultKeyRotation -TestMode -VerboseRecovery Procedures
Section titled âRecovery Proceduresâ- Lost Keys: Use backup keys from secure storage
- Failed Rotation: Manual intervention may be required
- System Issues: Reboot and retry rotation process
Conclusion
Section titled âConclusionâThis FileVault key rotation script provides automated security enhancement for macOS devices in enterprise environments. By implementing regular key rotation with proper backup and audit procedures, organizations can maintain strong security posture while ensuring operational continuity.
The scriptâs modular design allows for customization based on specific organizational requirements while maintaining security best practices and compliance with regulatory requirements.
Regular monitoring and maintenance of the rotation process ensures continued effectiveness and helps identify potential issues before they impact security operations.