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
This commit is contained in:
156
clients/cascades-tucson/scripts/setup-audit-drop-share.ps1
Normal file
156
clients/cascades-tucson/scripts/setup-audit-drop-share.ps1
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user