2026-01-29 05:01:26 -05:00
<#
. SYNOPSIS
Configure strict ACLs for a directory so only Administrators , Domain Admins ( optional )
and the specified Icinga service user ( s ) have FullControl .
. DESCRIPTION
This function validates the provided accounts , disables inheritance on the target
directory , clears existing explicit ACL entries , sets the directory owner and
grants FullControl recursively to the local Administrators group , an optional
Domain Admins group and one or more Icinga service user accounts .
. PARAMETER Directory
The path to the target directory to update ACLs for . Must be a directory path .
. PARAMETER Owner
The account to set as owner of the directory . Default is 'Administrators' .
. PARAMETER IcingaUser
One or more accounts ( string or string [ ] ) which should receive FullControl on
the directory . Default is the value returned by ` Get-IcingaServiceUser ` .
. PARAMETER DomainName
Optional domain name . When provided the function will also grant FullControl to
'<DomainName>\Domain Admins' .
. OUTPUTS
None . This function writes progress and errors to the Icinga console helpers .
. NOTES
- Requires administrative privileges to set ownership and modify ACLs on system
directories .
- Intended for use on Windows hosts only .
#>
2019-09-29 12:25:40 -04:00
function Set-IcingaAcl ( )
{
2026-01-29 05:01:26 -05:00
param (
[ string ] $Directory = $null ,
[ string ] $Owner = 'NT AUTHORITY\SYSTEM' ,
[ string[] ] $IcingaUser = ( Get-IcingaServiceUser ) ,
[ string ] $DomainName = ( $env:USERDOMAIN ) . ToLower ( )
2019-09-29 12:25:40 -04:00
) ;
2026-01-29 05:01:26 -05:00
# First check if the directory exists
if ( -not ( Test-Path -Path $Directory -PathType Container ) ) {
Write-IcingaConsoleWarning -Message 'The folder does not exist: {0}' -Objects $Directory ;
2019-09-29 12:25:40 -04:00
return ;
}
2026-01-29 05:01:26 -05:00
# Create the owner account object and validate
try {
$ownerAccount = New-Object System . Security . Principal . NTAccount ( $Owner ) ;
$ownerAccount . Translate ( [ System.Security.Principal.SecurityIdentifier ] ) | Out-Null ;
} catch {
Write-IcingaConsoleError -Message 'The owner account does not exist or is invalid: {0}' -Objects $Owner ;
2024-03-14 12:16:09 -04:00
return ;
}
2026-01-29 05:01:26 -05:00
# Create and validate IcingaUser accounts
foreach ( $user in $IcingaUser ) {
try {
$userAccount = New-Object System . Security . Principal . NTAccount ( $user ) ;
$userAccount . Translate ( [ System.Security.Principal.SecurityIdentifier ] ) | Out-Null ;
} catch {
Write-IcingaConsoleError -Message 'The user account does not exist or is invalid: {0}' -Objects $user ;
return ;
}
}
2019-09-29 12:25:40 -04:00
2026-01-29 05:01:26 -05:00
# Validate if the local Administrators group exists (shouldn't happen anyway)
try {
$adminGroup = New-Object System . Security . Principal . NTAccount ( 'Administrators' ) ;
$adminGroup . Translate ( [ System.Security.Principal.SecurityIdentifier ] ) | Out-Null ;
} catch {
Write-IcingaConsoleError -Message 'The local Administrators group does not exist or is invalid' -Objects $null ;
return ;
}
[ bool ] $AddDomainAdmins = $TRUE ;
# Validate if the Domain Admins group exists (if DomainName is provided)
if ( -not [ string ] :: IsNullOrEmpty ( $DomainName ) ) {
try {
$domainAdminGroup = New-Object System . Security . Principal . NTAccount ( ( [ string ] :: Format ( '{0}\Domain Admins' , $DomainName ) ) ) ;
$domainAdminGroup . Translate ( [ System.Security.Principal.SecurityIdentifier ] ) | Out-Null ;
} catch {
# Continue in this case, just warn the user
Write-IcingaConsoleWarning -Message 'The Domain Admins group does not exist or is invalid: {0}' -Objects ( [ string ] :: Format ( '{0}\Domain Admins' , $DomainName ) ) ;
$AddDomainAdmins = $FALSE ;
2021-08-06 12:12:27 -04:00
}
}
2026-01-29 05:01:26 -05:00
try {
# Get the ACL for the directory
$acl = Get-Acl -Path $Directory ;
# Now disable inheritance for the parent folder
$acl . SetAccessRuleProtection ( $true , $false ) | Out-Null ;
# Update the owner of the folder to "Administrators" first, to ensure we don't
# run into any exceptions
$acl . SetOwner ( ( New-Object System . Security . Principal . NTAccount ( 'Administrators' ) ) ) | Out-Null ;
Write-IcingaConsoleNotice -Message 'Disabled inheritance for directory {0}' -Objects $Directory ;
# Clear all existing ACL entries to ensure we start fresh
$acl . Access | ForEach-Object {
$acl . RemoveAccessRule ( $_ ) | Out-Null ;
} ;
Write-IcingaConsoleNotice -Message 'Cleared existing ACL entries for directory {0}' -Objects $Directory ;
# Add the permission for each defined user with Full Control
# Only add Icinga user permissions if we are not removing them
foreach ( $user in $IcingaUser ) {
$rule = New-Object System . Security . AccessControl . FileSystemAccessRule (
$user ,
'FullControl' ,
'ContainerInherit, ObjectInherit' ,
'None' ,
'Allow'
) ;
$acl . AddAccessRule ( $rule ) | Out-Null ;
}
# Add local Administrators group (Full Control)
$adminRule = New-Object System . Security . AccessControl . FileSystemAccessRule (
'Administrators' ,
'FullControl' ,
'ContainerInherit, ObjectInherit' ,
'None' ,
'Allow'
) ;
$acl . AddAccessRule ( $adminRule ) | Out-Null ;
# We need to ensure we add Domain Admins, as most likely those will require to have access as well
# and allow them to configure Icinga for Windows
if ( $AddDomainAdmins ) {
$domainAdminRule = New-Object System . Security . AccessControl . FileSystemAccessRule (
( [ string ] :: Format ( '{0}\Domain Admins' , $DomainName ) ) ,
'FullControl' ,
'ContainerInherit, ObjectInherit' ,
'None' ,
'Allow'
) ;
$acl . AddAccessRule ( $domainAdminRule ) | Out-Null ;
}
Write-IcingaConsoleNotice -Message 'Configured new ACL entries for directory {0}' -Objects $Directory ;
# Update the ACL on the directory
Set-Acl -Path $Directory -AclObject $acl | Out-Null ;
# Let's now enable inheritance for all subfolders and files, ensuring they inherit the correct permissions
# from the parent folder and we only require to configure permissions there
Get-ChildItem -Path $Directory -Recurse -Force | ForEach-Object {
try {
$childAcl = Get-Acl -Path $_ . FullName ;
$childAcl . SetAccessRuleProtection ( $false , $true ) | Out-Null ;
# As our parent or current Acl might be owned by SYSTEM,
# we need to set the owner to Administrators here as well to fix exceptions
# for SYSTEM user not being allowed to own this file
$childAcl . SetOwner ( ( New-Object System . Security . Principal . NTAccount ( 'Administrators' ) ) ) | Out-Null ;
Set-Acl -Path $_ . FullName -AclObject $childAcl | Out-Null ;
} catch {
Write-IcingaConsoleWarning -Message 'Failed to enable inheritance for directory {0}: {1}' -Objects $_ . FullName , $_ . Exception . Message ;
}
}
# Ensure we set the owner of the directory and all sub-items correctly
# Set-Acl cannot do this for the SYSTEM user and ProgramData folders
# properly, so we need to use icacls for this task
$Result = & icacls $Directory / setowner $Owner / T / C | Out-Null ;
if ( $LASTEXITCODE -ne 0 ) {
Write-IcingaConsoleError -Message 'Failed to set owner "{0}" for directory {1} and its sub-items using icacls. Output: {2}' -Objects $Owner , $Directory , $Result ;
}
$StringBuilder = New-Object System . Text . StringBuilder ;
$StringBuilder . Append ( 'Permissions for directory "' ) . Append ( $Directory ) . Append ( '" successfully configured for owner "' ) . Append ( $Owner ) . Append ( '"' ) | Out-Null ;
$StringBuilder . Append ( ' and full access users (' ) . Append ( ( $IcingaUser -join ', ' ) ) . Append ( ')' ) | Out-Null ;
$StringBuilder . Append ( ' and groups (Administrators' ) | Out-Null ;
if ( $AddDomainAdmins ) {
$StringBuilder . Append ( ', ' ) . Append ( ( [ string ] :: Format ( '{0}\Domain Admins)' , $DomainName ) ) ) | Out-Null ;
} else {
$StringBuilder . Append ( ')' ) | Out-Null ;
} ;
2021-08-06 12:12:27 -04:00
2026-01-29 05:01:26 -05:00
Write-IcingaConsoleNotice -Message $StringBuilder . ToString ( ) ;
} catch {
Write-IcingaConsoleError -Message 'Failed to Update ACL for directory {0}: {1}' -Objects $Directory , $_ . Exception . Message ;
2021-08-06 12:12:27 -04:00
}
2019-09-29 12:25:40 -04:00
}