Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 45 additions & 4 deletions ADMF.Core/ADMF.Core.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
RootModule = 'ADMF.Core.psm1'

# Version number of this module.
ModuleVersion = '1.2.12'
ModuleVersion = '1.4.20'

# ID used to uniquely identify this module
GUID = '11e2d894-33d7-4020-a65e-f13c2f1893aa'
Expand All @@ -21,13 +21,14 @@
Description = 'Central Tooling used across available across all ADMF Project modules'

# Minimum version of the Windows PowerShell engine required by this module
PowerShellVersion = '5.0'
PowerShellVersion = '5.1'

# Modules that must be imported into the global environment prior to importing
# this module
RequiredModules = @(
@{ ModuleName = 'PSFramework'; ModuleVersion = '1.13.416' }

# @{ ModuleName = 'Principal'; ModuleVersion = '1.0.0' }
# @{ ModuleName = 'ResolveString'; ModuleVersion = '1.0.0' }
)

Expand All @@ -43,25 +44,65 @@
# Functions to export from this module
FunctionsToExport = @(
'Clear-AdcConfiguration'
'Compare-AdcAccessRules'
'Compare-AdcProperty'
'Convert-AdcAccessRuleIdentity'
'Convert-AdcPrincipal'
'Convert-AdcSchemaGuid'
'ConvertFrom-AdcAccessRuleConfiguration'
'Find-AdcObjectCategoryItem'
'Get-AdcAccessRuleMode'
'Get-AdcCategoryBasedRules'
'Get-AdcDomain'
'Get-AdcDomainCredential'
'Get-AdcExchangeVersion'
'Get-AdcObjectCategory'
'Get-AdcObjectDefaultPermission'
'Get-AdcPrincipal'
'Get-AdcPrivilegedGroupSet'
'Get-AdcRemotingConfig'
'New-AdcChange'
'New-AdcPSSession'
'Register-AdcAccessRuleMode'
'Register-AdcBuiltInSID'
'Register-AdcObjectCategory'
'Register-AdcPrivilegedGroupSet'
'Resolve-AdcPrivilegedGroupSet'
'Remove-AdcRemotingConfig'
'Reset-AdcDomainCredential'
'Resolve-AdcAccessRuleMode'
'Resolve-AdcAceIdentity'
'Resolve-AdcObjectCategory'
'Set-AdcDomainCredential'
'Set-AdcRemotingConfig'
'Sync-AdcObject'
'Test-AdcAccessRuleEquality'
'Unregister-AdcAccessRuleMode'
'Unregister-AdcObjectCategory'
'Write-AdcChangeLog'
)

# Cmdlets to export from this module
# CmdletsToExport = ''
CmdletsToExport = @()

# Variables to export from this module
# VariablesToExport = ''

# Aliases to export from this module
# AliasesToExport = ''
AliasesToExport = @(
'Get-DMDomainCredential'
'Get-DMObjectDefaultPermission'
'Reset-DMDomainCredential'
'Set-DMDomainCredential'

'Find-DMObjectCategoryItem'
'Get-DMObjectCategory'
'Register-DMObjectCategory'
'Resolve-DMObjectCategory'
'Unregister-DMObjectCategory'

'Register-DMBuiltInSID'
)

# List of all modules packaged with this module
# ModuleList = @()
Expand Down
11 changes: 11 additions & 0 deletions ADMF.Core/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Changelog

## 1.4.20 (2026-06-19)

- New: Tool: Privileged Group Set - support tool for ACL, allowing multiple acceptable owners for a resource
- Upd: Component AccessRuleMode - moved over from DomainManagement
- Upd: Component AccessRules - partially moved over from DomainManagement
- Upd: Component DomainCredentials - moved over from DomainManagement
- Upd: Component ObjectCategories - moved over from DomainManagement
- Upd: Tools - moved over most of the Identity resolution tools from DomainManagement
- Upd: Get-AdcObjectDefaultPermission - moved over from DomainManagement and Get-DMObjectDefaultPermission
- Upd: New-AdcPSSession - added `Server` alias for parameter `ComputerName`, to allow using the same connection hashtable for AD and PSRemoting commands.

## 1.2.12 (2025-10-31)

- New: Remoting config handling, to allow specifying the settings used during invocation.
Expand Down
53 changes: 43 additions & 10 deletions ADMF.Core/en-us/strings.psd1
Original file line number Diff line number Diff line change
@@ -1,18 +1,51 @@
# This is where the strings go, that are written by
# Write-PSFMessage, Stop-PSFFunction or the PSFramework validation scriptblocks
@{
'Get-LdapObject.SearchError' = 'Failed to execute ldap request.' #
'Get-LdapObject.Searchfilter' = 'Searching with filter: {0}' # $LdapFilter
'Get-LdapObject.SearchRoot' = 'Searching {0} in {1}' # $SearchScope, $searcher.SearchRoot.Path
'Compare-AdcAccessRule.Error.BadIdentity' = 'Failed to resolve Identity "{1}" configured for "{0}"' # $ADObject, $configuredRule.IdentityReference

'New-AdcPSSession.Connecting' = 'Connecting via PS Remoting to {0}' # $ComputerName
'New-AdcPSSession.Connecting.Config' = 'Remoting Config used for {0} | Target: {1} | Options: {2} | SSH: {3} | Parameters: {4}' # $ComputerName, $config.Target, $config.HasOptions, $config.UseSSH, @($config.Parameters).Count

'Sync-AdcObject.ConnectError' = 'Failed to connect to {0}' # $errorObject.TargetObject
'Convert-AdcPrincipal.Processing' = 'Converting principal: {0}' # $Name
'Convert-AdcPrincipal.Processing.InputNT' = 'Input detected as NT: {0}' # $Name
'Convert-AdcPrincipal.Processing.InputSID' = 'Input detected as SID: {0}' # $Name
'Convert-AdcPrincipal.Processing.NT.LdapFilter' = 'Resolving NT identity via AD using the following filter: {0}' # "(samAccountName=$namePart)"
'Convert-AdcPrincipal.Processing.NTDetails' = 'Resolved NT identity: Domain = {0} | Name = {1}' # $domainPart, $namePart

'Sync-LdapObject.DestinationAccessError' = 'Failed to connect to destination server {0} | {1}' # $Target, $_
'Sync-LdapObject.PerformingReplication' = 'Performing replication from {0} to {1}' # $Server, $Target
'Sync-LdapObject.SourceAccessError' = 'Failed to connect to source server {0} | {1}' # $Server, $_
'ConvertFrom-AdcAccessRuleConfiguration.Identity.ResolutionError' = 'Failed to convert identity "{0}" on "{1}. This generally means a configuration error, especially when referencing the parent as identity.' # $ruleObject.IdentityReference, $ADObject

'Find-AdcObjectCategoryItem.ADError' = 'Error retrieving AD Objects for object category {0}' # $Name
'Find-AdcObjectCategoryItem.Category.NotFound' = 'No such object category defined: {0}' # $Name

'Get-AdcCategoryBasedRules.Identity.ResolutionError' = 'Error resolving Access Rule Identity {0} for a rule configured for the Object Category {1}' # $ruleObject.IdentityReference, $resolvedCategory.Name

'Get-AdcPrincipal.Resolution.Failed' = 'Failed to resolve principal: SID {0} | Name {1} | ObjectClass {2} | Domain {3}' # $Sid, $Name, $ObjectClass, $Domain
'Get-AdcPrincipal.Resolution.FailedWithTarget' = 'Failed to resolve principal: SID {0} | Name {1} | ObjectClass {2} | Domain {3} | Target {4}' # $Sid, $Name, $ObjectClass, $Domain, $Target

'Get-LdapObject.SearchError' = 'Failed to execute ldap request.' #
'Get-LdapObject.Searchfilter' = 'Searching with filter: {0}' # $LdapFilter
'Get-LdapObject.SearchRoot' = 'Searching {0} in {1}' # $SearchScope, $searcher.SearchRoot.Path

'Get-PermissionGuidMapping.Processing' = 'Processing Permission Guids for domain: {0} (This may take a while)' # $identity

'Get-SchemaGuidMapping.Processing' = 'Processing Schema Guids for domain: {0} (This may take a while)' # $identity

'New-AdcPSSession.Connecting' = 'Connecting via PS Remoting to {0}' # $ComputerName
'New-AdcPSSession.Connecting.Config' = 'Remoting Config used for {0} | Target: {1} | Options: {2} | SSH: {3} | Parameters: {4}' # $ComputerName, $config.Target, $config.HasOptions, $config.UseSSH, @($config.Parameters).Count

'Resolve-AdcAccessRuleMode.PathResolution.Failed' = 'Unable to resolve path: {0}' # $mode.Path

'Resolve-AdcAceIdentity.ParentObject.NoSecurityPrincipal' = 'Error resolving the parent object of {0} for the purpose of access rule or owner assignment. While {1} (of type {2}) can be found, it does not have a SID and cannot be assigned any permissions' # $ADObject, $parentObject.Name, $parentObject.ObjectClass

'Resolve-AdcPrivilegedGroupSet.Error.NoDefaultSid' = 'Error resolving the Privileged Group Set "{0}" agaiunst Domain "{1}": No default SID was included in the resolution result!' # $Type, $domain.DnsRoot
'Resolve-AdcPrivilegedGroupSet.Error.NoSids' = 'Error resolving the Privileged Group Set "{0}" agaiunst Domain "{1}": No SIDs were included in the resolution result!' # $Type, $domain.DnsRoot
'Resolve-AdcPrivilegedGroupSet.Error.UnknownPGS' = 'Error resolving the Privileged Group Set "{0}". No PGS of that name was found! Check your configuration and make sure no typos were made. PGS known: {1}' # $Type, ($script:privilegedGroupSets.Keys -join ", ")
'Resolve-AdcPrivilegedGroupSet.Resolving' = 'Resolving the identities that are part of the Privileged Group Set "{0}" in domain "{1}"' # $Type, $domain.DnsRoot


'Sync-AdcObject.ConnectError' = 'Failed to connect to {0}' # $errorObject.TargetObject

'Sync-LdapObject.DestinationAccessError' = 'Failed to connect to destination server {0} | {1}' # $Target, $_
'Sync-LdapObject.PerformingReplication' = 'Performing replication from {0} to {1}' # $Server, $Target
'Sync-LdapObject.SourceAccessError' = 'Failed to connect to source server {0} | {1}' # $Server, $_

'Write-AdcChangeLog.ChangeEntry' = 'Updating {0} from {1} to {2} on {3}' # $change.Property, $change.Old, $change.New, $change.Identity
'Write-AdcChangeLog.ChangeEntry' = 'Updating {0} from {1} to {2} on {3}' # $change.Property, $change.Old, $change.New, $change.Identity
}
33 changes: 33 additions & 0 deletions ADMF.Core/functions/AccessRuleMode/Get-AdcAccessRuleMode.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
function Get-AdcAccessRuleMode {
<#
.SYNOPSIS
Retrieve registered AccessRule processing modes.

.DESCRIPTION
Retrieve registered AccessRule processing modes.
These are used to define, how AccessRules will be processed.

.PARAMETER Path
Filter by the path the AccessRule processing mode applies to.

.PARAMETER ObjectCategory
Filter by the object category the AccessRule processing mode applies to.

.EXAMPLE
PS C:\> Get-AdcAccessRuleMode

List all registered AccessRule processing modes.
#>
[CmdletBinding()]
param (
[string]
$Path = '*',

[string]
$ObjectCategory = '*'
)

process {
$script:accessRuleMode.Values | Where-Object Path -Like $Path | Where-Object ObjectCategory -Like $ObjectCategory
}
}
73 changes: 73 additions & 0 deletions ADMF.Core/functions/AccessRuleMode/Register-AdcAccessRuleMode.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
function Register-AdcAccessRuleMode {
<#
.SYNOPSIS
Register the processing mode for access rules on a specified object.

.DESCRIPTION
Register the processing mode for access rules on a specified object.
This is used by the AccessRule Component exclusively.

.PARAMETER Path
The path to the AD object to govern.
This should be a distinguishedname.
This path uses name resolution.
For example %DomainDN% will be replaced with the DN of the target domain itself (and should probably be part of everyy single path).

.PARAMETER PathMode
Whether to only target a specific path or the target path and all items beneath it.

.PARAMETER ObjectCategory
Instead of a path, define a category to apply the processing mode to.
Categories are defined using Register-DMObjectCategory.
This allows you to apply processing mode to a category of objects, rather than a specific path.
With this you could apply a processing mode to all domain controller objects, for example.

.PARAMETER Mode
Determines, how the AccessRules are applied on the target object:
- Constrained: All non-defined AccessRules will be removed.
- Defined: Only non-defined AccessRules with identities for which a configuration exists on the object will be deleted.
- Additive: Non-defined AccessRules on the targeted object will be ignored.
By default, with no AccessRuleMode defined, all objects are considered to be in Constrained mode.

.EXAMPLE
PS C:\> Register-AdcAccessRuleMode -Path 'OU=Company,%DomainDN%' -PathMode SubTree -Mode Additive

Configures the specified OU and all items beneath it to be in additive mode.
Defined AccessRules will be applied if missing, but previously existing rules remain untouched.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Path')]
[string]
$Path,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Path')]
[ValidateSet('SingleItem', 'SubTree')]
[string]
$PathMode = 'SingleItem',

[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Category')]
[string]
$ObjectCategory,

[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateSet('Constrained', 'Defined', 'Additive')]
[string]
$Mode
)

process {
$identity = 'Path:{0}:{1}' -f $PathMode, $Path
if ($ObjectCategory) { $identity = 'Category:{0}' -f $ObjectCategory }

$script:accessRuleMode[$identity] = [PSCustomObject]@{
PSTypeName = 'ADMF.Core.AccessRuleMode'
Identity = $identity
Type = $PSCmdlet.ParameterSetName
Path = $Path
PathMode = $PathMode
ObjectCategory = $ObjectCategory
Mode = $Mode
}
}
}
81 changes: 81 additions & 0 deletions ADMF.Core/functions/AccessRuleMode/Resolve-AdcAccessRuleMode.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
function Resolve-AdcAccessRuleMode {
<#
.SYNOPSIS
Resolves the AccessRule processing mode that applies to the specified ADObject.

.DESCRIPTION
Resolves the AccessRule processing mode that applies to the specified ADObject.

.PARAMETER ADObject
The AD Object for which to resolve the AccessRule processing mode.

.PARAMETER Server
The server / domain to work with.

.PARAMETER Credential
The credentials to use for this operation.

.EXAMPLE
PS C:\> Resolve-AdcAccessRuleMode @parameters -ADObject $adObject

Resolves the AccessRule processing mode that applies to the specified ADObject.
#>
[OutputType([string])]
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
$ADObject,

[PSFComputer]
$Server,

[PSCredential]
$Credential
)

begin {
$parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential
}
process {
if ($script:accessRuleMode.Count -lt 1) { return 'Constrained' }

$relevantCategories = @()
if ($script:accessRuleMode.Values.ObjectCategory) {
$relevantCategories = Resolve-AdcObjectCategory -ADObject $ADObject @parameters
}

$applicableModes = :main foreach ($mode in $script:accessRuleMode.Values) {
if ($mode.Path) {
try { $resolvedPath = $mode.Path | Resolve-String } # @parameters
catch {
Write-PSFMessage -Level Warning -String 'Resolve-AdcAccessRuleMode.PathResolution.Failed' -StringValues $mode.Path -ErrorRecord $_
$resolvedPath = $mode.Path | Resolve-String
}
switch ($mode.PathMode) {
'SingleItem' {
if ($ADObject.DistinguishedName -eq $resolvedPath) { $mode }
continue main
}
'SubTree' {
if ($ADObject.DistinguishedName -like "*$resolvedPath") { $mode }
continue main
}
}
}
if ($mode.ObjectCategory -and ($mode.ObjectCategory -in $relevantCategories.Name)) {
$mode
}
}

if ($primaryMode = $applicableModes | Where-Object { $_.Type -eq 'Path' -and $_.PathMode -eq 'SingleItem' }) {
return $primaryMode.Mode
}
if ($secondaryMode = $applicableModes | Where-Object Type -EQ 'Category' | Select-Object -First 1) {
return $secondaryMode.Mode
}
if ($tertiaryMode = $applicableModes | Where-Object { $_.Type -eq 'Path' -and $_.PathMode -eq 'SubTree' } | Sort-Object { $_.Path.Length } -Descending | Select-Object -First 1) {
return $tertiaryMode.Mode
}
return 'Constrained'
}
}
Loading