Architecture¶
The repository¶
Code/
Classes/ PowerShell classes: the model (GPOs, links, OUs, directory), ADMX,
Registry.pol, GptTmpl.inf, preferences, security descriptors, platform
Functions/Private/ the implementation: LDAP, SYSVOL, backups, workflow, window, trees...
Functions/Public/ the commands of the module
CI/
03_Build.ps1 builds the module from Code/
tests/ the Pester tests (and their fixtures)
HGPM/
HGPM.psd1 the manifest (FunctionsToExport written by the build)
HGPM.psm1 built: the classes, then the private and public functions (never edited)
Resources/ Configuration.psd1, Preferences.json, AvaloniaPackages.json, Icons/
Avalonia/ Get-AvaloniaBinaries.ps1, and bin/ (the runtime, not committed)
Samples/ Directory.sample.json, PasswordPolicy.sample.json (the demonstration environments)
docs/ this documentation (en, fr, assets)
CI/03_Build.ps1 concatenates the classes and the functions into HGPM.psm1 and writes the
public functions in the manifest. The build is reproducible: no date, the files in the order of
their names, LF line endings, so that the same sources give the same bytes on any computer
(offline installation). The module name is always HGPM, whatever the folder of
the repository is called.
Layers¶
flowchart TB
subgraph UI[Window - Avalonia]
window[Start-PSGPOManagerWindow<br/>XAML parsed at runtime]
trees[New-PSGPODirectoryTree<br/>New-PSGPOPolicyTree<br/>Get-PSGPODirectoryDetail]
end
subgraph Commands[Public commands]
cmds[Get-PSGPODirectory, Backup-PSGPO,<br/>Restore-PSGPO, Approve-PSGPO<br/>and the other PSGPO commands]
end
subgraph Model[Model - classes]
dir[GPOToolsDirectory<br/>in memory]
ldapdir[GPOToolsLdapDirectory<br/>a domain]
end
subgraph Domain[The domain]
ldap[LDAP<br/>System.DirectoryServices.Protocols]
sysvol[SYSVOL<br/>UNC / smbclient / Python bindings]
tools[RSAT / samba-tool]
end
window --> trees --> dir
cmds --> dir
ldapdir -. inherits .-> dir
ldapdir --> ldap
ldapdir --> sysvol
ldapdir --> tools
The model¶
GPOToolsDirectory is a domain in memory: its OUs (GPOToolsSom), their links
(GPOToolsGpoLink), its GPOs (GPOToolsGpo). Its public methods are the operations of the
console (NewGpo, NewLink, RemoveLink, SetGpoStatus, NewFilteringGroups, RemoveGpo,
RestoreGpo, ApproveGpo, WritePolicyFile...): they check what is asked (rights, names,
default GPOs...), then call a hidden Save* / Load* method that does the work.
- In memory (
Import-PSGPODirectory, the demonstration environment, the tests), these methods keep the changes in memory. GPOToolsLdapDirectory(Get-PSGPODirectory) overrides them to write to the domain.
So the window and the commands are the same code for a domain and for the tests, and a change the domain refuses leaves the model as it was.
The fine-grained password policies follow the same pattern, without the GPOs:
GPOToolsPasswordPolicyStore holds the PSOs (GPOToolsPasswordPolicy), the password policy of the
domain and, in memory, the users and groups (GPOToolsPasswordAccount); its methods NewPolicy,
SetPolicy, RemovePolicy, AddSubject, RemoveSubject check, then call Save*.
GPOToolsLdapPasswordPolicyStore (Connect-PSGPOPasswordPolicyStore, used by
Show-PSOManagerAvalonia and the commands) writes them over LDAP. Resolve-PSGPOPasswordPolicy
orders the PSOs that concern an account, with the groups each domain controller counts
(LDAP).
The platform¶
Get-PSGPOPlatform detects once what the computer is: Windows or Linux, member of a domain or
domain controller (Samba smb.conf, krb5.conf), its domain, samba-tool, the LDAP library.
Each operation then takes the way of its platform:
| Operation | Windows | Linux |
|---|---|---|
| Read the directory | LDAP, the identity of the session | LDAP, Kerberos (SASL GSSAPI) |
| Read and write SYSVOL | UNC path | smbclient with Kerberos |
| SYSVOL permissions | .NET access control | Samba Python bindings (libsmb) |
| Create and delete GPOs | New-GPO, Remove-GPO (RSAT) |
samba-tool gpo create / del |
| Links, status, security filtering, workflow | LDAP | LDAP |
| Fine-grained password policies | LDAP | LDAP |
Principles¶
- No password is ever handed to a tool: Kerberos for
samba-toolandsmbclient(never a prompt: no standard input,--use-kerberos=required), never a password over plain LDAP. - The rights are those of the domain: the console reads the delegations, offers what they allow, and lets the domain refuse the rest (delegation).
- No change is lost: an LDAP value is replaced by removing the old one and adding the new one in one request, refused if someone changed it meanwhile; the version of a GPO is increased from the value the directory has.
- Read as data: the configuration files are read with
Import-PowerShellDataFile, never run. - What the console does not edit is kept: unknown attributes and item-level targeting of the preferences, the extra registry values, the ACEs it does not touch.