Tests¶
The tests are Pester 5 tests, in CI/tests, run against the built module.
pwsh -File CI/03_Build.ps1
pwsh -File CI/Get-AdmxSource.ps1 # once: the ADMX files of Windows the tests read
$Config = New-PesterConfiguration
$Config.Run.Path = 'CI/tests'
$Config.Output.Verbosity = 'Detailed'
Invoke-Pester -Configuration $Config
| File | Covers |
|---|---|
PSGPOTools.Model.tests.ps1, GPOToolsUtility.tests.ps1 |
ADMX, ADML, categories, policies |
PSGPOTools.RegistryPol.tests.ps1, PSGPOTools.Setting.tests.ps1 |
Registry.pol, the state of every policy of the ADMX set, settings reports |
PSGPOTools.AccessControl.tests.ps1 |
security descriptors, security filtering, SYSVOL ACL |
PSGPOTools.Backup.tests.ps1 |
backups, comparison, restore, notes, drift, recycle bin, templates |
PSGPOTools.Configuration.tests.ps1 |
the configuration of the domain |
PSGPOTools.Hardening.tests.ps1 |
what a hostile file, attribute, tool or domain could do (hardening) |
PSGPOTools.Workflow.tests.ps1 |
the approval workflow |
PSGPOTools.Delegation.tests.ps1 |
the rights read from the directory |
PSGPOTools.Ldap.tests.ps1 |
LDAP, gPLink, samba-tool, RSAT (mocked), and against a domain controller |
PSGPOTools.PasswordPolicy.tests.ps1 |
the fine-grained password policies: the values of the directory, the checks, the PSO that applies and why, the commands |
PSGPOTools.Gui.tests.ps1 |
the real windows of both consoles (-OnShown), an error no handler caught |
PSGPOTools.WindowCode.tests.ps1 |
the code of the GPO console read as a syntax tree: each variable it reads is assigned or a control of its XAML |
PSGPOTools.Health.tests.ps1 |
the health of the GPOs and their replication between domain controllers (three simulated) |
PSGPOTools.Posture.tests.ps1 |
the security posture: the rights too wide in the directory and in SYSVOL, the Tier 0 and its groups, the findings to the log of the system |
PSGPOTools.Inheritance.tests.ps1 |
the links of a domain or an OU by link order, and the GPOs that apply there by precedence (Get-PSGPOInheritance) |
PSGPOTools.AvaloniaInstall.tests.ps1 |
the Avalonia runtime: its pinned packages and files, where they come from, the checks before it is loaded |
PSGPOTools.WellKnownSid.tests.ps1 |
the well-known accounts: their table, the names typed in English or French, the suggestions while typing and the completion of the commands |
PSGPOTools.Release.tests.ps1 |
the build reproducible, the package of a release (SHA256SUMS, signature, catalog), the rules of the workflows |
PSGPOTools.Public.tests.ps1 |
the commands exported |
PSGPOTools.Update.tests.ps1 |
the versions of HGPM and Test-PSGPOUpdate (the Gallery mocked) |
Against a domain controller¶
The integration tests run when a domain controller is given, with a Kerberos ticket of an administrator of the domain:
kinit administrator@HARDENBOX.LABO
export PSGPOTOOLS_TEST_LDAP_SERVER=dc.hardenbox.labo # reads the domain
export PSGPOTOOLS_TEST_LDAP_WRITE=1 # also creates, links, writes and deletes
pwsh -c 'Invoke-Pester CI/tests'
With PSGPOTOOLS_TEST_LDAP_WRITE=1, they create an OU and GPOs named PSGPOTools test ..., link
them, write their settings, delete and restore one, and delete everything they created at the end,
whatever failed. For the password policies (Linux, samba-tool), they create a user, a global and
a domain local security group, and PSOs named PSO test ...: protected from accidental deletion,
renamed, applied directly and through the groups, a change made meanwhile refused, the PSO the
console finds compared with msDS-ResultantPSO; then they delete them.
With PSGPOTOOLS_TEST_LAB_SCRIPTS=1 too (Linux), they run the Linux scripts of the
lab, New-PSGPOTierLab.ps1 and Grant-PSGPOTierDelegation.ps1, twice
each: the OUs, groups and accounts of the tiers, the password of the lab (a Kerberos ticket of
tiers1), the delegation of the links and of the creation of GPOs (the workflow enabled for the test). They change the domain for
good: the job test-samba of the CI sets it, on its throwaway domain only.
The writes of an approval (PSGPOTools.Hardening.tests.ps1) are tested with mocks: their order
(SYSVOL, the objects the GPO holds, then the GPO), the GPO left pending when SYSVOL fails, the
permissions removed and reported. The whole workflow, with its two accounts, runs against the domain
controller too (Approval written to a domain controller), after the scripts of the lab:
tiers1, in a process of its own with its Kerberos ticket, creates a GPO: it waits, nobody applies it;tiers1adds a permission of its own (Domain Users, full control);- the administrator approves it: Domain Admins owns the GPO and its SYSVOL folder, the permission
tiers1added and its own rights are gone (and reported), Authenticated Users applies it; tiers1cannot write it anymore;- a GPO
tiers1changes after its review is not approved, and stays pending.
The configuration of the domain is kept and written back byte for byte. On a lab of your own,
PSGPOTOOLS_TEST_TIER1_CCACHE gives the ticket of a creator (FILE:/tmp/krb5cc_tiers1) instead of
the password of the lab.
What a job must run¶
CI/Invoke-Tests.ps1 -RequireRun names the blocks (Describe, Context, wildcards) a run is
there for: a test of them skipped with its block (a runtime of Avalonia missing, a variable of the
domain misnamed) or not run, or no test of them at all, fails the run. A test that skips itself for
another platform (-Skip:$IsWindows) does not. Each run writes a table of its files (passed,
failed, skipped, not run) to the summary of its job on GitHub (GITHUB_STEP_SUMMARY).
CI¶
.github/workflows/ci.yml runs four jobs on each push to master and main, and on each pull request
(with the rights to read the repository only; the actions pinned to a commit, Pester, PSScriptAnalyzer
and the site generator to a version):
| Job | Runs |
|---|---|
build |
builds the module once (CI/03_Build.ps1), checks its manifest is the one of the repository, and gives it to the other jobs (the artifact module): they test this one, and the release publishes it |
test |
builds the module again on Windows and checks it is the same bytes (the build is reproducible), PSScriptAnalyzer on Code/ (PSScriptAnalyzerSettings.psd1: no error, no warning), then every test on windows-latest; the windows must run (-RequireRun) |
test-linux |
every test on ubuntu-latest (Avalonia on Xvfb); the windows must run |
test-samba |
the tests that write to a domain, against a Samba AD DC provisioned on the runner (CI.TEST), with a Kerberos ticket of its administrator; the reading of the domain, the scripts of the lab, the approval, the changes and the password policies must run |
The page of each public command (docs/<language>/reference/commands/, and its entry in the menu) is
written from its help by CI/Update-CommandReference.ps1, after CI/03_Build.ps1; the first .LINK of
the help of a command is its page, which Get-Help <command> -Online opens. A test checks that the pages
are those of the help.
The repository keeps no file of Microsoft. The tests read the ADMX files of Windows from
sources/PolicyDefinitions, which is not in Git: CI/Get-AdmxSource.ps1 installs them there with
Install-PSGPOAdmx -AllLanguages, from the package of Microsoft the module pins, kept in
sources/package (the CI caches it). Install-PSGPOAdmx installs only the package that
HGPM/Resources/PolicyDefinitions.json pins (its address, size and SHA-256), and from it only the
files pinned (each set, the .admx files and each language: its number of files and a SHA-256, the
case of the names aside). For a new package of Microsoft, CI/Update-AdmxCatalog.ps1 -MsiPath
writes the catalog again; a test checks it.
.github/workflows/release.yml publishes the module on the PowerShell Gallery for a tag of a version
(v0.1.3-alpha) only, never for a commit. It runs the jobs of the CI on the tagged commit, then, on
Windows, with the rights it needs and those only:
CI/04_Publish.ps1checks the version (the tag is the version of the manifest,CHANGELOG.mdhas its section, the gallery does not have it yet);CI/05_Package.ps1makes the package from the module the tests ran (the artifactmodule, never a build of its own): signed with Authenticode, with its catalogHGPM.cat, when the secretsCODE_SIGNING_PFX(the PFX in Base64) andCODE_SIGNING_PASSWORDare given; its archive, the offline archives with the Avalonia runtime of each platform, andSHA256SUMS;actions/attestattests every file ofSHA256SUMS(gh attestation verify);CI/04_Publish.ps1publishes the package with the secretPSGALLERY_API_KEY, and the release of GitHub is created with the notes of the version, the archives andSHA256SUMS.
The secrets are those of the environment psgallery, whose protection rules apply before they are
given.
The documentation is built and published by .github/workflows/docs.yml. It makes the Changelog page from
CHANGELOG.md, and puts the version of the module (ModuleVersion of its manifest) and the commit the
site is built from in the footer of every page.