From 4220b8f57c8d2666edd9f819ac82c313548b1436 Mon Sep 17 00:00:00 2001 From: Howard Enos Date: Fri, 17 Apr 2026 15:05:28 -0700 Subject: [PATCH] sync: auto-sync from ACG-TECH03L at 2026-04-17 15:05:26 Author: Howard Enos Machine: ACG-TECH03L Timestamp: 2026-04-17 15:05:26 --- .../scripts/setup-audit-drop-share.ps1 | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 clients/cascades-tucson/scripts/setup-audit-drop-share.ps1 diff --git a/clients/cascades-tucson/scripts/setup-audit-drop-share.ps1 b/clients/cascades-tucson/scripts/setup-audit-drop-share.ps1 new file mode 100644 index 0000000..3341c1c --- /dev/null +++ b/clients/cascades-tucson/scripts/setup-audit-drop-share.ps1 @@ -0,0 +1,156 @@ +<# +.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