157 lines
6.4 KiB
PowerShell
157 lines
6.4 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Creates write-only audit drop share with service account and scheduled archive.
|
|
|
|
.DESCRIPTION
|
|
Provisions on CS-SERVER:
|
|
- AD service account svc-audit-upload (no interactive logon)
|
|
- AD group AuditUploaders (members get write-only access)
|
|
- D:\Shares\AuditDrop folder with NTFS write-only ACL
|
|
- Hidden SMB share \\CS-SERVER\AuditDrop$ (Change for AuditUploaders, Full for Domain Admins)
|
|
- Daily 3am scheduled task that moves yesterday's drops into D:\Shares\AuditDrop_Archive\YYYY-MM-DD\
|
|
|
|
Idempotent — safe to re-run.
|
|
|
|
.NOTES
|
|
Run as Domain Admin on CS-SERVER (or any system with AD tools + SMB management installed).
|
|
Verify the OUs in $svcOuPath and $groupOuPath exist before running, or change them.
|
|
#>
|
|
|
|
# === VARIABLES ===
|
|
$domainDN = "DC=cascades,DC=local"
|
|
$svcOuPath = "OU=ServiceAccounts,$domainDN"
|
|
$groupOuPath = "OU=Groups,$domainDN"
|
|
$svcUser = "svc-audit-upload"
|
|
$group = "AuditUploaders"
|
|
$drop = "D:\Shares\AuditDrop"
|
|
$archiveRoot = "D:\Shares\AuditDrop_Archive"
|
|
$scriptPath = "D:\Shares\Scripts\Archive-AuditDrop.ps1"
|
|
|
|
# === 0. Pre-flight: confirm target OUs exist ===
|
|
foreach ($ou in @($svcOuPath, $groupOuPath)) {
|
|
try {
|
|
Get-ADOrganizationalUnit -Identity $ou -ErrorAction Stop | Out-Null
|
|
Write-Host "[OK] OU exists: $ou"
|
|
} catch {
|
|
Write-Host "[ERROR] OU not found: $ou" -ForegroundColor Red
|
|
Write-Host " Create it first, or edit `$svcOuPath / `$groupOuPath in this script." -ForegroundColor Yellow
|
|
Write-Host " Example: New-ADOrganizationalUnit -Name 'ServiceAccounts' -Path '$domainDN' -ProtectedFromAccidentalDeletion `$true" -ForegroundColor Yellow
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
# === 1. Create the dedicated upload service account ===
|
|
$svcPwd = Read-Host -AsSecureString -Prompt "Enter password for $svcUser (store in 1Password / SOPS vault)"
|
|
|
|
if (-not (Get-ADUser -Filter "SamAccountName -eq '$svcUser'" -ErrorAction SilentlyContinue)) {
|
|
New-ADUser `
|
|
-Name $svcUser `
|
|
-SamAccountName $svcUser `
|
|
-DisplayName "Service: Audit JSON Upload" `
|
|
-Description "Write-only access to \\CS-SERVER\AuditDrop$. Used by Syncro audit upload script. Do not use interactively." `
|
|
-Path $svcOuPath `
|
|
-AccountPassword $svcPwd `
|
|
-PasswordNeverExpires $true `
|
|
-CannotChangePassword $true `
|
|
-Enabled $true
|
|
Write-Host "[OK] Created service account: $svcUser"
|
|
} else {
|
|
Write-Host "[OK] Service account already exists: $svcUser"
|
|
}
|
|
|
|
# === 2. Create the AD group ===
|
|
if (-not (Get-ADGroup -Filter "SamAccountName -eq '$group'" -ErrorAction SilentlyContinue)) {
|
|
New-ADGroup `
|
|
-Name $group `
|
|
-SamAccountName $group `
|
|
-GroupScope Global `
|
|
-GroupCategory Security `
|
|
-Path $groupOuPath `
|
|
-Description "Members can WRITE-ONLY to \\CS-SERVER\AuditDrop$. No read/list."
|
|
Write-Host "[OK] Created group: $group"
|
|
} else {
|
|
Write-Host "[OK] Group already exists: $group"
|
|
}
|
|
|
|
# Add service account to group (idempotent)
|
|
Add-ADGroupMember -Identity $group -Members $svcUser -ErrorAction SilentlyContinue
|
|
Write-Host "[OK] Membership confirmed: $svcUser in $group"
|
|
|
|
# === 3. Create the drop folder ===
|
|
New-Item -Path $drop -ItemType Directory -Force | Out-Null
|
|
Write-Host "[OK] Drop folder ready: $drop"
|
|
|
|
# === 4. NTFS write-only permissions ===
|
|
icacls $drop /inheritance:r | Out-Null
|
|
icacls $drop /grant:r "CASCADES\Domain Admins:(OI)(CI)F" | Out-Null
|
|
icacls $drop /grant:r "SYSTEM:(OI)(CI)F" | Out-Null
|
|
icacls $drop /grant:r "CASCADES\${group}:(WD,AD,WA,WEA,RA,REA,RC,S,X)" | Out-Null
|
|
|
|
Write-Host "[OK] NTFS permissions applied. Listing:"
|
|
icacls $drop
|
|
|
|
# === 5. Create the hidden SMB share ===
|
|
if (-not (Get-SmbShare -Name "AuditDrop$" -ErrorAction SilentlyContinue)) {
|
|
New-SmbShare `
|
|
-Name "AuditDrop$" `
|
|
-Path $drop `
|
|
-FullAccess "CASCADES\Domain Admins" `
|
|
-ChangeAccess "CASCADES\${group}" `
|
|
-Description "Audit JSON drop-box. Hidden. Write-only for AuditUploaders group."
|
|
Write-Host "[OK] Created SMB share: AuditDrop$"
|
|
} else {
|
|
Write-Host "[OK] SMB share already exists: AuditDrop$"
|
|
}
|
|
|
|
Write-Host "[OK] Share access:"
|
|
Get-SmbShareAccess -Name "AuditDrop$"
|
|
|
|
# === 6. Generate the archive script ===
|
|
# IMPORTANT: backtick-escape any $ that must remain literal in the GENERATED file.
|
|
# - $drop and $archiveRoot expand NOW (they're fixed paths set above)
|
|
# - $src, $archive, $_, $(Get-Date ...) must be backtick-escaped so they expand at TASK RUN TIME
|
|
$archiveScript = @"
|
|
`$src = "$drop"
|
|
`$archive = "$archiveRoot\`$(Get-Date -Format yyyy-MM-dd)"
|
|
New-Item -Path `$archive -ItemType Directory -Force | Out-Null
|
|
Get-ChildItem -Path `$src -File | Where-Object { `$_.LastWriteTime -lt (Get-Date).AddHours(-2) } |
|
|
Move-Item -Destination `$archive -Force
|
|
"@
|
|
|
|
New-Item -ItemType Directory -Path (Split-Path $scriptPath) -Force | Out-Null
|
|
$archiveScript | Out-File $scriptPath -Encoding UTF8 -Force
|
|
Write-Host "[OK] Archive script written: $scriptPath"
|
|
Write-Host " Contents:"
|
|
Get-Content $scriptPath | ForEach-Object { Write-Host " $_" }
|
|
|
|
# === 7. Create scheduled task (daily 3am) ===
|
|
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
|
|
-Argument "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`""
|
|
$trigger = New-ScheduledTaskTrigger -Daily -At 3am
|
|
|
|
if (-not (Get-ScheduledTask -TaskName "Archive-AuditDrop" -ErrorAction SilentlyContinue)) {
|
|
Register-ScheduledTask `
|
|
-TaskName "Archive-AuditDrop" `
|
|
-Action $action `
|
|
-Trigger $trigger `
|
|
-RunLevel Highest `
|
|
-User "SYSTEM"
|
|
Write-Host "[OK] Scheduled task created: Archive-AuditDrop (daily 3am)"
|
|
} else {
|
|
Write-Host "[OK] Scheduled task already exists: Archive-AuditDrop"
|
|
}
|
|
|
|
# === 8. Test instructions ===
|
|
Write-Host ""
|
|
Write-Host "=== TEST FROM A WORKSTATION ===" -ForegroundColor Cyan
|
|
Write-Host "Run these manually to verify write-only behavior:"
|
|
Write-Host ""
|
|
Write-Host " `$cred = Get-Credential cascades\$svcUser"
|
|
Write-Host " New-PSDrive -Name T -PSProvider FileSystem -Root '\\CS-SERVER\AuditDrop$' -Credential `$cred"
|
|
Write-Host " 'hello' | Out-File T:\test_`$(hostname).txt # SHOULD SUCCEED"
|
|
Write-Host " Get-ChildItem T:\ # SHOULD FAIL (Access Denied)"
|
|
Write-Host " Get-Content T:\test_`$(hostname).txt # SHOULD FAIL (Access Denied)"
|
|
Write-Host " Remove-PSDrive T"
|
|
Write-Host ""
|
|
Write-Host "If all three SHOULD-FAIL lines actually return Access Denied, the drop-box is correctly write-only." -ForegroundColor Green
|