Public/Set-AdLocalAdminHousekeeping.ps1
Function Set-AdLocalAdminHousekeeping { <# .SYNOPSIS Manage local administrative groups for servers in a domain. .DESCRIPTION This function performs housekeeping for local administrative groups on servers within a domain. It will: A) Retrieve all servers in the domain. B) Ensure each server has a corresponding local admin group named 'Admin_<HostName>'. If such a group does not exist, it will be created at the specified LDAP path. C) Check if each 'Admin_<HostName>' group corresponds to an existing server. If the server does not exist in AD, the group will be deleted. .PARAMETER Domain Specifies the domain to perform the operations on. .PARAMETER LDAPPath Specifies the LDAP path where the 'Admin_<HostName>' groups should be created. Example: "OU=SpecialGroups,DC=example,DC=com" .EXAMPLE Set-AdLocalAdminHousekeeping -Domain "example.com" -LDAPPath "OU=SpecialGroups,DC=example,DC=com" .NOTES Used Functions: Name | Module ---------------------------------------|-------------------------- Get-ADComputer | ActiveDirectory Get-ADGroup | ActiveDirectory New-ADGroup | ActiveDirectory Remove-ADGroup | ActiveDirectory Get-ADDomainController | ActiveDirectory Import-Module | Microsoft.PowerShell.Core Write-Verbose | Microsoft.PowerShell.Utility Write-Error | Microsoft.PowerShell.Utility Get-FunctionToDisplay | EguibarIT.DelegationPS & EguibarIT.HousekeepingPS Test-IsValidDN | EguibarIT.DelegationPS & EguibarIT.HousekeepingPS .NOTES Version: 1.0 DateModified: 10/May/2024 LasModifiedBy: Vicente Rodriguez Eguibar vicente@eguibar.com Eguibar Information Technology S.L. http://www.eguibarit.com #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'low')] param ( [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $false, HelpMessage = 'Specifies the domain to perform the operations on.', Position = 0)] [PSDefaultValue(Help = 'Use current domain from $Env:USERDNSDOMAIN if parameter value is not provided.')] [string] $Domain = $Env:USERDNSDOMAIN, [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $false, HelpMessage = 'Admin Groups OU Distinguished Name.', Position = 1)] [ValidateScript({ Test-IsValidDN -ObjectDN $_ }, ErrorMessage = 'DistinguishedName provided is not valid! Please Check.')] [Alias('DN', 'DistinguishedName')] [String] $LDAPpath ) begin { $txt = ($Variables.HeaderHousekeeping -f (Get-Date).ToShortDateString(), $MyInvocation.Mycommand, (Get-FunctionDisplay -Hashtable $PsBoundParameters -Verbose:$False) ) Write-Verbose -Message $txt # Verify the Active Directory module is loaded Import-MyModule ActiveDirectory -Verbose:$false ############################## # Variables Definition # explicit type declaration of HashTable [hashtable]$Splat = [hashtable]::New([StringComparer]::OrdinalIgnoreCase) # Find a domain controller in the specified domain $domainController = Get-ADDomainController -Discover -DomainName $PsBoundParameters['Domain'] if (-not $domainController) { Write-Error 'No domain controllers found for domain {0}' -f $PsBoundParameters['Domain'] return } #end If Write-Verbose -Message ('Using domain controller {0} for domain operations.' -f $($domainController.HostName)) # Get all computer objects categorized as servers, excluding 'Domain Controllers' $Splat = @{ Filter = '( (OperatingSystem -Like "*Server*") -and (PrimaryGroupID -ne 516) )' Server = $domainController.HostName Property = 'Name' } $servers = Get-ADComputer @Splat Write-Verbose -Message ('Retrieved {0} servers from the domain.' -f $servers.Count) } #end Begin process { try { # Ensure each server has a corresponding Admin_<HostName> group foreach ($server in $servers) { # Find the corresponding Administrative group name $groupName = 'Admin_{0}' -f $server.Name # Get the group. If NotFound exception variable will be Null $group = Get-ADGroup -Filter { Name -eq $groupName } -Server $($domainController.HostName) -ErrorAction SilentlyContinue if (-not $group) { if ($PSCmdlet.ShouldProcess($groupName, 'Create group')) { $Splat = @{ Name = $groupName GroupCategory = 'Security' GroupScope = 'Global' DisplayName = '{0} Local Administrators members' -f $server.Name Path = $PsBoundParameters['LDAPPath'] Description = 'Local Admin group for {0}' -f $server.Name Server = $($domainController.HostName) } New-ADGroup @Splat Write-Verbose -Message ('Created group {0} at {1}.' -f $groupName, $PsBoundParameters['LDAPPath']) } #end If } #end If } #end ForEach # Check all groups on given LDAPPAth for obsolete entries $adminGroups = Get-ADGroup -Filter * -Server $($domainController.HostName) -SearchBase $PsBoundParameters['LDAPPath'] -Properties Members #iterate all found groups foreach ($group in $adminGroups) { # Exclude groups that do not follow the naming convention if ($group.Name -match '^Admin_[A-Za-z0-9_-]+$') { # Extract server name from group name $hostName = $group.Name -replace '^Admin_', '' # Find the corresponding server object $server = $servers | Where-Object { $_.Name -eq $hostName } if (-not $server) { if ($PSCmdlet.ShouldProcess($group.Name, 'Delete group')) { Remove-ADGroup -Identity $group -Confirm:$false -Server $($domainController.HostName) Write-Verbose -Message ('Deleted group {0} because the corresponding server no longer exists.' -f $group.Name) } #end If } #end If } else { # Remove this group because it does not follow naming convention, or it does not belongs to this OU. Remove-ADGroup -Identity $group -Confirm:$false -Server $($domainController.HostName) Write-Verbose -Message ('Deleted group {0} because does not follow the naming conventions.' -f $group.Name) }#end If } #end ForEach } catch { Write-Error -Message ('An error occurred: {0}' -f $_) } #end Try-Catch } #end Process end { $txt = ($Variables.FooterHousekeeping -f $MyInvocation.InvocationName, 'setting group for Local Administrators.' ) Write-Verbose -Message $txt } #end End } #end function Set-AdLocalAdminHousekeeping |