Skip to main content

Test builder

Every Maester test declares what it is about, how urgent a failure is and what it needs to run, in a [MaesterTest()] attribute. Pick the values here. Each field explains what it means and shows the rule reviewers apply, and the code updates as you go.

The lists and rules on this page are generated from the files Maester itself uses, so they match what the unit tests and the review step enforce. The full text is in the attribute rubric.

Who is the test for?

Name it

Required

The suite decides the ID prefix and the folder. New checks written by the community go in Maester.

IDs start with MT. or MT1060. or AZDO.. Files go in tests/maester.

IdRequired

Stable identifier. The file is Test.<Id>.ps1. An ID never changes once the test has shipped, because results, configuration and history refer to it.

Not in the list?

A new Maester check takes the next free MT. number. Reserve it on issue 697 before you open the pull request, so two contributors do not pick the same one.

TitleRequired

One line, shown in the report. Say what should be true, so a passing result reads as a fact: “Guest invitations are restricted to administrators.”

Optional

The file holds one function. Leave empty to use Test-MT1234.

What is it about?

Rate the test, not your tenant

Rate every value for a typical organization that fails the test, using the fix the test's own Markdown file recommends. Do not rate for the best case, the worst case or one tenant you know. When two values fit, choose the lower one for Severity, UserImpact and ImplementationCost. A reader should be able to see the reason for each value in the test's description and remediation text.

ProductRequired

Key of the product where the fix is made, for example entra. An alias such as Entra ID is stored as the key. Groups console progress and the run summary; written to the result as Product.

How to choose: the rubric

The product where the fix is made. One value.

  • Use the product whose admin surface holds the setting, not every product the test reads from.
  • Tests in a product folder (tests/maester/entra, tests/cisa/exchange) use that folder's product.
  • A product the test only reads from, or a licence it needs, is not a second product. Service and License already say that.
Not in the list?

Search the list first. A product is a deliberate addition, because every key becomes a filter in the report. If nothing fits, choose Not in the list above, and add the entry to powershell/assets/MaesterTaxonomy.psd1 in the same pull request as the first test that needs it.

A product entry has a Key, a Name, Aliases and its Features. If the test also needs a connection Maester does not have yet, that is a larger change to powershell/assets/MaesterServiceRegistry.psd1: open an issue first.

FeatureRequired

Key of the part of the product the test is about, for example conditional-access. One of the features of the test's product.

How to choose: the rubric

The part of the product the test is about. One key from the product's Features list.

  • Choose the feature whose settings page or object type the remediation sends the admin to.
  • When a test spans two features, choose the one that fails most directly. A Conditional Access policy that requires a compliant device is conditional-access, not devices.
  • Tests that check the same thing get the same feature, whatever folder or suite they are in.
  • A feature that would hold one test only is usually too narrow. Prefer an existing feature.
Not in the list?

Search the list first. A feature is a deliberate addition, because every key becomes a filter in the report. If nothing fits, choose Not in the list above, and add the entry to powershell/assets/MaesterTaxonomy.psd1 in the same pull request as the first test that needs it.

Add one line to the product's Features list. A feature that would hold one test only is usually too narrow, so prefer an existing one.

RelatedProductOptional

Keys of other products the test also checks or depends on. Never the same key as Product.

How to choose: the rubric

Other products the test also checks or depends on. Optional list, never the same key as Product.

  • Add one when an admin of that product would expect to find the test under it. An Exchange Online test about an anti-phishing policy that is managed in the Defender portal has RelatedProduct = 'defender'.
  • Add one when the fix is made in that product's portal although the test lives in another product's folder.
  • Do not add a product only because its API is called.
AreaRequired

Key of the security theme, whatever the product, for example authentication.

How an identity proves who it is: MFA, authentication methods, passwords, legacy protocols, Kerberos and NTLM hardening, session and token lifetime.

How to choose: the rubric

The security theme, whatever the product. One key.

KeyUse it for
authenticationHow an identity proves who it is: MFA, authentication methods, passwords, legacy protocols, Kerberos and NTLM hardening, session and token lifetime.
privileged-accessAdministrative roles and accounts: who holds them, just-in-time elevation, emergency access, admin tiering, delegated admin rights.
access-controlWho may reach which resource under which conditions: device, location and risk conditions, permissions on objects, user defaults.
applicationsApp registrations, service principals and their credentials, consent and API permissions, OAuth apps, add-ins, connectors.
external-accessGuests, cross-tenant access, federation and trusts, external sharing and external communication.
devicesDevice compliance, enrollment and configuration, endpoint protection, device identity.
email-protectionAnti-phishing, anti-spam and anti-malware, Safe Links and Safe Attachments, SPF, DKIM and DMARC, mail flow rules, meeting and messaging protections.
data-protectionData loss prevention, sensitivity labels, encryption, retention, backup.
logging-and-detectionAudit logs, diagnostic settings, alert policies, sensors and the health of detection tooling.
identity-lifecycleStale and dormant accounts, ownership, access reviews, entitlement management, group membership hygiene.
infrastructureThe directory and platform itself: domain controllers, DNS, replication, sites, schema, Group Policy health, protocol hardening, subscriptions, tenant-wide platform settings.
engineering-systemsDevOps organizations, repositories, pipelines, build identities, the software supply chain.
aiAI agents and copilots: who can use and share them, how they authenticate, what data and tools they reach.

When more than one area fits, choose by what the control is for, in this order:

  1. It protects administrators or admin rights: privileged-access.
  2. It decides how sign-in is proven: authentication.
  3. It is about an application, service principal or agent as the subject: applications, or ai for agents.
  4. It is about people or data outside the organization: external-access.
  5. Otherwise the area that names the mechanism: access-control, devices, email-protection, data-protection, logging-and-detection, identity-lifecycle, engineering-systems, infrastructure.

A product never decides the area on its own. An Azure DevOps test about who is a Project Collection Administrator is privileged-access, not engineering-systems.

Inventory and health tests with no security theme of their own are infrastructure. When no area fits, choose the closest and say so in the pull request, so the list can be reviewed.

Not in the list?

Search the list first. An area is a deliberate addition, because every key becomes a filter in the report. If nothing fits, choose Not in the list above, and add the entry to powershell/assets/MaesterTaxonomy.psd1 in the same pull request as the first test that needs it.

The thirteen areas are meant to cover every product. When none fits, choose the closest and say so in the pull request, or propose a new one with its description and a row in the rubric's Area table.

ResourceTypeRequired

What a failing result points at, for example Tenant or User.

A tenant-, organization-, forest- or domain-wide setting. The result is about the environment, not a list of objects.

How to choose: the rubric

What a failing result points at. One key.

  • Tenant when the test evaluates one tenant-, organization-, forest- or domain-wide setting. The result is about the environment.
  • Tenant when the test checks that at least one policy of a kind exists, even when it lists the policies that satisfy it. A failing result has nothing to list.
  • Tenant when the test evaluates the single default or global policy of a kind, because there is one of it and it applies to everyone.
  • Otherwise the kind of object a failing result lists or counts: User, Application, Device, ConditionalAccessPolicy, GroupPolicy and so on. A test that evaluates every policy and lists the ones that fail uses the policy type.
  • A test that counts objects for review (an inventory test) uses the type it counts. When no key names that kind of object and it is a one-off, use Tenant.
  • A family test, which yields one result per instance, uses the type of the instance.
Not in the list?

Search the list first. A resource type is a deliberate addition, because every key becomes a filter in the report. If nothing fits, choose Not in the list above, and add the entry to powershell/assets/MaesterTaxonomy.psd1 in the same pull request as the first test that needs it.

Add one line to ResourceTypes with a one-line description. For a one-off inventory with no matching type, the rubric says to use Tenant.

What should be fixed first?

SeverityRequired

The risk to the organization while the test fails. Not the effort to fix it and not the disruption of fixing it.

How to choose: the rubric

The risk to the organization while the test fails. Not the effort to fix it and not the disruption of fixing it.

ValueA failing result means
CriticalAn attacker with no special position can take over the tenant, the domain or a privileged identity, or a secret is already exposed. Directly exploitable and wide in reach.
HighA primary control is missing or weakened for many users or for privileged identities, and common techniques such as phishing, password spray or consent phishing are enough to use it.
MediumA defence-in-depth control is missing, or the exposure is limited to a few objects or one workload, or using it needs another weakness first.
LowHardening or hygiene with a small security effect on its own.
InfoThe test reports a fact for review and makes no risk judgement. Info tests are left out of scores.

Decide in this order:

  1. Reach. Privileged identities or the whole tenant move the value up. A single workload or a few objects move it down.
  2. Directness. Usable from the internet or by any signed-in user moves it up. Needing an earlier compromise moves it down. A password taken by phishing, spray or stuffing is not an earlier compromise: those are the common techniques High already assumes.
  3. Role of the control. The main control for a threat rates higher than a second layer behind another control.

Critical is rare. If the description cannot say in one sentence how an attacker gets from the failing state to admin rights or exposed secrets, the value is High or lower. A benchmark's own wording does not set the value: a SHALL policy can be Medium.

Five cases come up often:

  • A test that passes whenever its data can be read is Info, whatever the data is about. If the fact deserves a severity, the test should fail on it.
  • A failure that is only an outage or untidiness, such as replication health, an expiring token or an unused object, is Low. It is Medium or higher only when the outage switches off a security control, and then it is rated as the loss of that control.
  • Alerts and notifications are a second layer behind the control they watch, so they are Medium at most.
  • The same check in two suites gets the same value in both.
  • The main control against an attack that starts from a stolen password is High. A second layer behind that control is Medium. Number matching against MFA fatigue is High; showing the app name and location in the prompt is Medium.

Anchors from existing tests:

TestValueWhy
No Group Policy object contains a cpasswordCriticalAny domain user can read and decrypt the stored password.
No domain admin account has a service principal nameCriticalAny domain user can request a ticket and crack an admin password offline.
Legacy authentication is blockedHighPassword spray bypasses MFA for every user.
Number matching is required for Authenticator push notificationsHighMFA fatigue works against any user once a password is phished.
Authenticator shows the app name and locationMediumA second layer behind number matching.
Users cannot register applicationsMediumNeeds a signed-in user and a further step to cause harm.
MailTips are enabled for end usersLowA small aid against mis-sent mail.
Disabled user count should be retrievableInfoInventory for review.

Microsoft's Zero Trust Assessment rates many of the same Microsoft Entra and Intune settings. Where it has a test for the same setting, compare your value with its risk level and give the reason in the pull request if the two differ.

Not in the list?

The five values are fixed. The report, the scores and the -Severity selector depend on them.

UserImpactRequired

How much the recommended fix disrupts end users. Omitted when Severity is Info.

How to choose: the rubric

How much the recommended fix disrupts end users. Administrators and the owners of applications are not end users here. Developers are the end users of Azure DevOps and GitHub.

ValueApplying the fix
LowEnd users notice nothing, or only administrators are affected.
MediumSome users change how they work or meet a new prompt: a group loses a capability, users register a method once, a legacy client stops working for a subset.
HighMost users change a daily habit, or a mistake locks people out or stops mail or an app for many. Needs communication and a staged rollout.

Omit UserImpact when Severity is Info.

ImplementationCostRequired

How much administrator work the recommended fix takes. Omitted when Severity is Info.

How to choose: the rubric

How much administrator work the recommended fix takes.

ValueThe fix is
LowOne setting or one policy in one portal. Under an hour, with no dependency on another team, a licence purchase or a rollout.
MediumSeveral steps: a policy that needs exclusions and a pilot, clean-up of a handful of objects, coordination with one other team. Hours to a few days.
HighA project: new licences, infrastructure or process, a migration of users or apps, or clean-up across many objects. Weeks or more.

Clean-up that one administrator can finish alone in a sitting is Low. Clean-up that needs the owner of each object is Medium. Turning on a plan that is billed by use is Medium: the click is easy, the budget approval is not. Count the work of the fix, including exclusions and rollout, but not the time to decide.

Omit ImplementationCost when Severity is Info.

What does it map to?

MitreRequired

The attack techniques the checked control prevents or detects. You pick technique IDs; the tactics are looked up from ATT&CK 19.2 and shown in the report.

  • T1078.004 Cloud Accounts · Stealth, Persistence, Privilege Escalation, Initial Access
  • T1528 Steal Application Access Token · Credential Access
  • T1566 Phishing · Initial Access
How to choose: the rubric

The MITRE ATT&CK techniques that the checked control prevents or detects. A list of technique IDs, or None.

  • IDs only, in the form T1110 or T1110.003. Tactic names are looked up from the pinned ATT&CK release (powershell/assets/MaesterAttackTechniques.json) and are never typed.
  • Use a sub-technique when the control addresses only that sub-technique. When three or more sub-techniques of one technique apply, list the technique instead. Do not list a technique together with one of its own sub-techniques.
  • List a technique only if the test's description explains, or plainly implies, how the control stops or reveals it. At most five.
  • A test that implements a benchmark which publishes a mapping, such as CISA SCuBA, starts from that mapping and then applies these rules. An ID the pinned release has retired is replaced by its successor in that release, found by name.
  • The description means the comment-based help and the Markdown file together.
  • Use None for a test with no attacker behaviour behind it, such as replication health or an inventory count. An inventory test lists a technique only when the fact it reports is itself the precondition of that technique, such as accounts that do not require Kerberos pre-authentication.
  • A retired or unknown ID fails the unit tests.
Not in the list?

The list is the ATT&CK release Maester pins, in powershell/assets/MaesterAttackTechniques.json. A technique that is missing was retired, or is newer than the pinned release. Use its successor, or update the pinned release with ./build/attack/Update-MtAttackTechniques.ps1 in a pull request of its own.

ControlOptional

Requirements of CIS, CISA, NIST SP 800-53 or any other framework that this test implements or supports. Look one up, or type your own as framework-key:requirement-id. This is the only place a test is mapped to a framework.

  1. The requirement the test is written for. A CIS or CISA test lists its own requirement first, such as cis-m365:5.1.5.1.
  2. A CIS or CISA requirement that asks for the same setting. Search for the setting or a test ID you know.
  3. NIST SP 800-53 controls, at most three, when a passing result is direct evidence for them. Skip this when you already listed a cisa-scuba requirement: CISA publishes its NIST mapping and the result gets it from there.
How to choose: the rubric

Requirements of frameworks the test implements or supports, written <framework-key>:<requirement-id>, for example cis-m365:5.1.5.1, cisa-scuba:MS.AAD.3.1 or nist-800-53-r5:IA-2(1). Optional list. Any test can use it: one shipped with Maester or one of your own.

  • Control is the only way a test is mapped to a framework. A CIS test carries Control = 'cis-m365:5.1.5.1' and a CISA test carries Control = 'cisa-scuba:MS.AAD.3.1'. Every test in tests/cis and tests/cisa must name a requirement of its suite's framework. The unit tests check this.
  • The first value that names a requirement in a framework file becomes the Benchmark of the result. Put the requirement the test is written for first, and any other requirement it supports after it.
  • Use the same attribute for a test that exists for its own reasons and also supports a requirement: a Maester test that checks the setting a CISA policy asks for, or a CIS test that also supports a NIST control.
  • When the key is a framework shipped with Maester (cis-m365, cis-github, cisa-scuba), the requirement must exist in that framework's file. If it is new, add it to the file with its facts. The module build and the unit tests fail otherwise.
  • Do not repeat the Related controls of a requirement. The framework file lists them and Maester adds them to the result.
  • List a requirement only when a passing result is evidence for it. A test that checks one part of a requirement may list it; a test that is merely on the same topic may not.

A reference says the test supports the requirement. It never says the organization complies with it.

NIST SP 800-53

A test may list the NIST SP 800-53 Rev. 5 controls its setting implements, as nist-800-53-r5:<control>.

  • Use an ID from the pinned list, powershell/assets/MaesterNistControls.json: a base control (AC-2) or an enhancement (IA-2(1)). The unit tests reject anything else.
  • List a control only when a passing result is direct evidence that it is implemented for what the test checks.
  • Prefer the enhancement when the test checks exactly what it says: MFA for privileged accounts is IA-2(1). Do not list a base control together with one of its own enhancements.
  • At most three, best fit first. Never the policy-and-procedures controls (AC-1, IA-1 and so on).
  • A test that only reports a count, or checks health, licensing or tidiness, lists none, unless the count is itself what a control asks to be tracked: dormant accounts for AC-2(3).
  • A test that implements a CISA SCuBA policy does not list NIST controls. CISA publishes that mapping, the framework file carries it as Related, and the result gets it from there.
Not in the list?

The lookup lists the frameworks Maester ships a file for: tests/cis/frameworks/cis-github.json, tests/cis/frameworks/cis-m365.json, tests/cisa/frameworks/cisa-scuba.json. For any other framework, type the reference yourself with a lowercase key, for example nist-800-53-r5:IA-2(1) or iso-27001-2022:A.5.17, and use the same key every time.

The framework file is a catalogue. It holds what a key:id reference cannot: each requirement's level, profile or criticality, the controls it relates to, and the full list of requirements, so typos and gaps are found. A new CIS or CISA requirement is added to that file; the mapping itself is always this attribute.

What does it need to run?

ServiceRequired

The connections the test needs. The engine checks them before it calls your function and skips the test when one is missing, so the function needs no connection check.

Not in the list?

Services are registered in powershell/assets/MaesterServiceRegistry.psd1. A new service also needs a connection probe in the module, so open an issue before the pull request.

LicenseOptional

The service plans the tenant must have. In a tenant without them the test is skipped, not failed.

A test requires service plans, the building blocks inside a product such as Microsoft 365 E5. Plans inside one box are joined with and: the tenant needs all of them. Boxes are joined with or: any one box is enough.

In the attribute each box is one element and & joins the plans in it, so “Entra ID P1 and Intune, or Entra ID P2” is License = ('AAD_PREMIUM&INTUNE_A', 'AAD_PREMIUM_P2').

  • AAD_PREMIUM · Maester also counts its government and education variants

The test runs when the tenant has AAD_PREMIUM. Otherwise it is skipped.

Not in the list?

The list is Microsoft's product names and service plan identifiers page, refreshed every day. You can also type a name that is not listed yet: it is matched as written against the tenant's service plan names.

For the plans in powershell/assets/MaesterLicenseTable.psd1 Maester also counts government, education and volume variants as the same licence. Add a plan there when its variants should count too.

PermissionOptional

Permissions the test needs, one per element, no commas. Recorded; not enforced. One per line, for example Policy.Read.All.

TenantTypeOptional

Tenant types the test applies to. Leave empty for Workforce only.

CloudOptional

Clouds the test is valid in. Omitted means all clouds.

PlatformOptional

Operating systems the test can run on. Omitted means all platforms.

Lifecycle and credit

LifecycleOptional

Preview, Stable or Deprecated. A Preview test is not run unless -IncludePreview or any -Tag is given, or its exact ID is in -TestId.

How to choose: the rubric
  • Preview: new and still changing. Not run unless previews are included.
  • Stable: the default.
  • Deprecated: kept so history stays readable. Give ReplacedBy when another test took its place.
Optional

Leave both off unless you know the test needs them.

AuthorRequired

GitHub handles of the initial author(s). Separate several with commas. Authors are credited on the contributors page.

ContributorOptional

GitHub handles of secondary contributors, in order of first contribution.

Why is there no Tag field?

Tag holds labels the owner of a test folder finds useful. It is not part of Maester's taxonomy.

  • Built-in tests do not carry tags for product, feature, area, suite, benchmark, licence, severity or their own ID. Each of those has an attribute or is derived.
  • A custom test may tag freely.