Grundschutz++ against my own documentation
For anyone who maintains an ISMS and wonders whether it holds up against the new BSI Grundschutz. I checked mine against the state-of-the-art library. I answered every requirement against the running system, not against the page that claims it. And I show how the machine-readable catalogue can be compared directly with Infrastructure as Code and GitOps.
This post opens the series “Grundschutz++ as code”. The overview of all parts is further down.
01 — Where it started
My ISMS follows ISO 27001. There is a Statement of Applicability, registers for risks, assets and suppliers, and evidence for every control. Everything is versioned as Markdown in a repository. The setup is small: one business, one platform, a few customers.
With Grundschutz++ the BSI rebuilds its IT-Grundschutz. The requirements are published machine-readable in the Stand-der-Technik-Bibliothek. The question was simple. Does my ISMS hold up, and where does it not?
02 — Bringing Grundschutz++ and ISO 27001 together
Grundschutz++ no longer orders requirements by modules the way IT-Grundschutz used to. They are grouped by practices, for example DET for detection, KONF for configuration or NOT for contingency. Every requirement has an ID, a text and a level. The level is normal or elevated and follows the protection need.
The biggest change is the format. The BSI publishes the requirements in OSCAL, an open standard format, in the Stand-der-Technik-Bibliothek (state-of-the-art library). No custom parser is needed any more. A script can read the whole list, and no requirement gets lost in transfer.
The bridge to ISO 27001
An ISO 27001 ISMS already has a Statement of Applicability. Clause 6.1.3 d requires it. It lists the 93 controls of Annex A in the 2022 edition, each with the reason why it applies or not. The library contains a mapping from these controls to Grundschutz++.
That mapping produces the checklist. Every applied Annex A control brings its mapped Grundschutz++ requirements. In my case that was 280. Anyone running an ISO 27001 ISMS does not start from zero. The Statement of Applicability defines what to check.
03 — Normal and elevated: where the technical difference lies
The normal level is the baseline for any operation. The elevated level applies where the protection-need assessment shows a high need. The library states the level of each requirement. Whether it applies to your operation depends on your own protection need. The mapping to ISO 27001 does not make that decision for you.
The difference by example
The pairs below show how far apart the levels are. They are paraphrased summaries, not the BSI wording.
- Logging: At the normal level, security-relevant events are collected centrally and retained. Elevated adds access to individual objects: who read which backup, and when? Plus rules on which data never ends up in a log.
- Detection: Normal means detecting and reporting attacks at the network edge and on hosts. Elevated adds anomalies in the application itself and dedicated detection of data exfiltration.
- Capacity: Normal is an alert when a disk fills up. Elevated plans capacity with trends and reserves before an alert is needed.
- Vulnerabilities: Normal tracks advisories for the software in use. Elevated adds regular penetration tests and an active search of public sources for your own attack surface, for example certificate transparency logs.
- Contingency: Normal means backups that can be restored and are tested. Elevated adds a copy in a separate location and deletion protection that administrator rights cannot bypass.
How I treated the elevated level
I answered all requirements at the normal level. At the elevated level there were three classes:
- Already met: the control was running, often as a by-product of another one.
- Low-hanging: doable in a day at most, without a new component. I implemented these right away.
- Deliberately open: the effort does not match the protection need. These are marked, with a reason. They do not sit in a plan nobody executes.
The third class matters most. An open requirement with a reason is a decision. An open requirement without one is a gap. In ISO 27001 that decision belongs in the risk treatment plan, approved by the risk owner.
04 — The method: configuration, not documents
The most important rule of the check: a requirement counts as met when the system shows it. A page claiming it is not enough.
Three kinds of evidence
- Configuration: a setting in the repository that enforces the behaviour. For example a rule that only maintainers may create release tags.
- Measurement: a query against the running system, with a date. For example whether an alert rule is loaded and whether its metric actually arrives.
- Document: only where nothing else works, such as organisational rules or contracts.
The kind of evidence and the date of measurement sit on every line. That sounds pedantic. It is the difference between a check and comparing two texts. Whoever lays documents against documents checks whether the documentation agrees with itself. It almost always does.
Clear rating rules
- Met: the requirement is fully implemented and evidenced.
- Partly: the control exists, but a part is missing. An alert that reaches nobody, or a process without evidence. The missing half is written on the line.
- Not met: there is no control, or only a claim.
- Not applicable: the requirement does not fit the operation. The reason is stated, as in the Statement of Applicability.
One rule turned out to be especially important: a control that only exists on paper counts as not met, not as partly. Otherwise the middle of the scale becomes a hiding place.
05 — The catalogue as a data model: comparing it with IaC and GitOps
Grundschutz++ is the first BSI framework published in OSCAL, a NIST standard format for compliance data. It can therefore be processed without a custom parser. Every requirement is a JSON object with fixed fields. If you run your infrastructure as code, you now have two datasets that can be compared. That is what makes the check reproducible.
Anatomy of a requirement
A requirement is not free text with room for interpretation. It is a sentence with a fixed grammar, and the library splits it into fields. Shortened, it looks like this:
{
"id": "KONF.3.2",
"title": "Speicherverschlüsselung",
"props": [
{ "name": "sec_level", "value": "normal-SdT" },
{ "name": "effort_level", "value": "3" },
{ "name": "confidentiality", "value": "2" },
{ "name": "threats", "value": "G 0.19, G 0.14" }
],
"parts": [{
"name": "statement",
"props": [
{ "name": "target_object_categories", "value": "IT-Systeme" },
{ "name": "documentation", "value": "Konfigurationshistorie" },
{ "name": "result", "value": "integrierte Festspeichermedien" },
{ "name": "action_word", "value": "verschlüsseln" },
{ "name": "modal_verb", "value": "SOLLTE" }
],
"prose": "Konfiguration für IT-Systeme SOLLTE integrierte Festspeichermedien verschlüsseln."
}]
}
Source: BSI, Stand-der-Technik-Bibliothek, CC BY-SA 4.0, shortened. The catalogue is in German: the requirement says IT systems SHOULD encrypt built-in storage.
Five fields matter for a technical check:
- Target object: what the requirement applies to, here IT systems. It tells you where to look.
- Action word: what has to be done, for example enable, restrict, encrypt, review or anchor. It tells you which kind of evidence fits.
- Result: what the action applies to, here the built-in storage.
- Modal verb: MUSS, SOLLTE or KANN, that is MUST, SHOULD or MAY. It tells you how much a gap weighs.
- Documentation: where the evidence is supposed to live, here the configuration history.
On top come the level, an effort rating, the security objectives affected and references to the BSI's elementary threats. You can filter on all of that before reading a single line of text.
The action word decides the evidence
The action word is the most useful field. It separates the requirements you can check in a repository from those that need a record or an appointment. For my 280 requirements that gave four groups:
- 110 configuration: enable, disable, restrict, install, block, encrypt, log, assign, authorise, authenticate, place, prohibit, delete. The evidence is a place in the IaC or GitOps repository, plus a measurement that it is in effect.
- 49 checks: review, test, monitor, execute. Code is not enough here. The evidence is a dated run: a CI job, an alert that fired, a restore record.
- 100 documents: anchor, document, define, agree. The evidence is a text, a contract or an approval.
- 21 people: raise awareness, instruct, inform. The evidence is a dated record of who learned what, and when.
So almost 40 percent of the checklist can be answered directly against code. Another sixth against pipeline and monitoring data that exists anyway. Only the rest needs manual work on documents.
From target object to repository path
The target object tells you which part of the code to search. I defined the mapping once and used it for every requirement:
- Configuration of IT systems: OpenTofu modules and server templates. KONF.3.2 is a single resource there that enforces encryption for new volumes. Add a measurement that all existing volumes are encrypted as well.
- Configuration of applications and web servers: Helm values and manifests in the GitOps repository, for example security headers at the ingress.
- Network architecture: security groups, NetworkPolicies and firewall rules.
- Authorisation: IAM policies, Kubernetes RBAC, protected branches and tags.
- Detection: alert rules, log pipelines and scanner configuration.
- Changes and tests: CI definitions, merge rules and signature verification.
The configuration history is Git
The most common documentation type in the whole catalogue is the configuration history. It appears on 183 of 1,000 requirements. If you work with GitOps, you already have it. Every change is a commit, every commit belongs to a merge request, every approval to a pipeline. Nothing needs to be written down after the fact. You only have to point at the right commit.
That is why every line of the check names a repository and a commit. A statement like “encryption is on” is a claim without a commit. With a commit it can be verified, even a year later.
Desired, actual and claimed
IaC describes the desired state. That is not enough, because the running system can drift. For every configuration requirement I therefore compared three sources:
- Desired: what the repository says, at a fixed commit.
- Actual: what the running system reports. A
tofu planwithout changes, an ArgoCD status of “Synced”, an API call, acurlagainst the headers. - Claimed: what the ISMS documentation says.
A requirement only counts as met when desired and actual agree. The interesting findings are where the claim disagrees with both. That happens more often than expected, and in both directions.
Making it reproducible
The library is republished continuously. A check against “the current version” cannot be repeated. Three rules solved that:
- Pinned to a commit: catalogue, mapping and your own repositories are fixed to a commit. The script refuses branch names.
- Nothing drops out silently: the mapping pointed at 299 IDs. 19 of them do not exist in the catalogue at the pinned commit, because the mapping lags behind. The script lists them at the end instead of discarding them.
- Diff instead of starting over: when the library gets a new commit, you compare the IDs. Only new or changed requirements are rated again.
Whatever can be measured then becomes a check in the pipeline itself. A nightly job verifies, for example, that all release tags are protected. A daily tofu plan reports drift. A requirement that is met stays met, or someone finds out.
The tool
I have published the script as grundschutz-worksheets on GitHub, under the MIT licence. It is a Node program with no dependencies and nothing to install. It loads catalogue and mapping at a fixed commit directly from the library. It resolves nested requirements, fills in parameters and groups by practice. The output is one line per requirement with ID, level, modal verb, action word, target object, documentation type and the Annex A controls that lead to it. As a Markdown checklist or as CSV for a spreadsheet.
git clone github.com/ohartwig/grundschutz-worksheets
cd grundschutz-worksheets
C=367d775010abee641b258926bb482fcd05270059 # commit of the BSI library
CAT=control_layer/Grundschutz++/Grundschutz++-resolved_catalog.json
MAP=control_layer/Mappings/ISO-27001-zu-GSpp/ISO27001-AnnexA-to-GS++-mapping_collection.json
# Worksheet for your own Annex A controls, as CSV
node worksheet.mjs $CAT --mapping $MAP --sources @applicable-controls.txt \
--format csv --commit $C > worksheet.csv
# Where is the work? Counts by practice, level, modal verb and action word
node worksheet.mjs $CAT --mapping $MAP --summary --commit $C
It does not answer anything for you. It only makes sure no requirement is missing. It does not copy the BSI texts into your own documentation either. That holds only IDs and your own assessment, partly because of the licence.
Second level: from the running system back to the code
An ISO 27001 auditor I showed the approach to named the weak spot right away. So far the comparison runs in one direction, from code to system. It checks whether what the repository says is actually running. It does not find what is running although it is written down nowhere. A server that should have been gone long ago. An open port nobody planned. A resource someone created by hand.
A drift check only helps halfway. tofu plan only compares the resources the code knows about. Whatever was created without code does not show up. The second level reverses the direction and asks the running system what exists:
- Inventory against state: every resource the cloud API lists, against the resources in the state. Whatever only the API knows is shadow IT or a leftover.
- Outside view against expectation: an external port scan of your own addresses, compared with the list of services that are meant to be reachable.
- Names against expectation: certificate transparency logs show every hostname a certificate was issued for. An unknown name is a lead.
- Traffic against architecture: flow logs show which connections actually happen. Every connection the network architecture does not intend is a finding.
A requirement is only met when both directions agree. The code describes the right thing, and the system contains nothing beyond it.
Limits of the approach
- ISO 27001 leaves room. The standard does not prescribe a specific control, it asks for an adequate one. A control can be far from Grundschutz++ and still be effective. Then the deviation needs a reason, not a fix. Grundschutz++ stays a yardstick, not a rulebook.
- Not everything is code. In many networks, switches, firewalls and VLANs are not rolled out through IaC. There is no repository to check against. What remains is the second level: asking the system itself.
- Some things need sensors. Whether network separation works is not visible in a configuration file. It takes measuring points in every zone. If you split your DMZ into one VLAN per service, you need as many.
- Absence is hard to prove. “Restrict directory listing” is not met by a single line. You have to query every web server and show that none breaks the rule.
- Runtime is not configuration. Which rights a pod actually has is not in the repository, but in the cluster.
- The judgement stays with people. The structure tells you where to look. It does not tell you whether what you find is adequate.
My setup is small and almost entirely described as code. That made the approach easy. In a grown environment I would start the same way, with a small area that already exists as code. There you quickly see whether the comparison holds. Bigger and more complex is always possible afterwards. Some things have to be checked several times and from different directions anyway.
06 — The figures
Of the 280 requirements, at the end of the check (26 September 2026):
- 113 met,
- 115 partly met,
- 29 not met,
- 23 not applicable.
The large middle is typical for an ISMS that is technically in good shape. The control runs, but one link of the chain is missing. Typical examples are an alert that fires but reaches nobody, or a test that checks an older setup. Only someone who measures the whole chain sees such gaps.
The 29 gaps are unevenly spread. Technical requirements, on logging, backups or configuration, could often be closed the same day. What stayed open were mostly organisational requirements. Those need appointments, not merge requests.
07 — The actual finding
I expected missing controls. What I mostly found was documentation that no longer matched. In both directions.
Patterns that repeated
- Planned, never built: the documentation described a component that never existed. It was meant as the next step and stayed as a fact.
- Evidence for the wrong architecture: a record described a setup that had long been replaced. Formally complete, worthless in substance.
- Alert without a signal: a rule checked a metric that was filtered out before. Such a rule raises no error. It simply stays silent.
- The other way round: controls were already running that the documentation listed as missing. That is an error too. Whoever reads the documentation plans work that is already done.
In small organisations this is almost inevitable. The documentation is written once, carefully and completely. Then the system moves on and the documentation does not. Nobody lies. Nobody checks.
08 — What I am changing
The changes start where ISO 27001 already asks for something. They make the requirement checkable.
- Measured on, per 7.5: documented information must be controlled and current. Every statement about the state of the system carries the date it was measured. A statement without a date is a claim.
- Evidence only via dated amendment, per 7.5.3: a record is not rewritten when it turns out wrong. It gets a dated amendment. What was known when stays visible.
- Alerts that can fire, per Annex A 8.16: a new rule only counts as done once it is established that its metric arrives.
- Detecting drift automatically, per Annex A 8.9: a daily
tofu planreports when the infrastructure deviates from the code. The first run found a real error right away. - The check as an internal audit, per 9.2: it repeats instead of staying a one-off. Findings go into the improvement register as corrective actions under 10.2.
09 — What this means for an ISO 27001 audit
An auditor does not check whether the documentation looks nice. They check whether the documented controls are effective. They sample: show me this setting, this alert, this restore test.
That is exactly where outdated documentation surfaces. A control that only exists on paper is a nonconformity. If the pattern repeats, a minor nonconformity quickly becomes a major one, because control of documented information under 7.5 is then in question as a whole.
What the check does beforehand
- It finds the samples an auditor would draw, before the auditor does.
- It provides dated evidence for every Annex A control.
- It shows openly what is deliberately not implemented. That belongs in the risk treatment plan under 6.1.3 and needs the risk owner's approval.
- It gives the management review under 9.3 a solid basis instead of a feeling.
Grundschutz++ does not replace a certification. It is, however, a very precise yardstick for what the state of the art is. Whoever checks their Statement of Applicability against it goes into the audit better prepared.
10 — My assessment of Grundschutz++
After one full pass my verdict is mixed. Overall it is clearly positive.
What convinces
- Machine-readable: the requirements can be read in full, filtered and versioned. If your evidence already lives in a repository, check and documentation come together.
- The bridge to ISO 27001: the mapping saves double bookkeeping. The Statement of Applicability stays the leading document; Grundschutz++ adds the depth.
- More concrete than Annex A: where ISO 27001 only names a control, Grundschutz++ often says what it means in operation. That makes rating less a matter of interpretation.
- Levels instead of all-or-nothing: separating normal and elevated forces a conscious decision per requirement.
What takes work
- The mapping is not one-to-one. One Annex A control pulls in several requirements, and some requirements hang on several controls. You merge duplicate lines yourself.
- Appropriate stays interpretation. Many requirements ask for something appropriate. What is appropriate follows your own protection need. Without a documented protection-need assessment every rating can be challenged.
- No tooling for the rating. The library provides the requirements, not worksheets or evidence handling. Everyone has to build that. I have published a starting point for the worksheets as grundschutz-worksheets.
- Small businesses have to cut. Some requirements assume an organisation with several roles. With one person in operations you need a justified adaptation, not blind adoption.
My conclusion: Grundschutz++ works as a yardstick for an ISO 27001 ISMS, especially in small businesses. It pays off most for those who treat the check as a measurement, not as a form.
11 — The series at a glance
So far the check only reads OSCAL. The catalogue arrives as data, but the answers are still prose in the handbook. The series turns that around step by step: the operation describes itself in OSCAL, generated from what the repositories already hold.
- Grundschutz++ against my own documentation: the method, the catalogue as a data model, the comparison with IaC and GitOps. This post.
- Applicability as data: your own selection from Grundschutz++ as an OSCAL profile, generated from the Statement of Applicability.
- Containers that say which requirements they meet: a signed component definition attached to every image the pipeline builds.
- Measurement as evidence: nightly checks write their results as OSCAL. Plus the second level, which finds in the running system what no repository knows about.
- The ISMS as a build product: a security plan that is assembled rather than written, with an honest account of the effort.
A part appears only once the step it describes is running and measured. The code for it lands in grundschutz-worksheets first. The titles are linked here as soon as each post is online.
Frequently asked questions
How often should you check?+
At least once a year as part of the internal audit under ISO 27001 clause 9.2, and after larger changes. Findings go into the improvement register as corrective actions.
Is this worth it for small businesses?+
Yes, if you use the requirements as a checklist and not as a specification. The machine-readable library makes matching much easier than the old Grundschutz with its modules. The biggest effort is measuring, not reading.
What do normal and elevated mean?+
It is the level of a requirement, derived from the protection need. The normal level is the baseline. The elevated level applies where an outage or a data leak weighs particularly heavily. It pays to take the easily reachable elevated requirements along right away.
How do you get from Annex A to the Grundschutz++ requirements?+
Through the mapping in the state-of-the-art library. It assigns the matching Grundschutz++ requirements to the Annex A controls. The starting point is your own Statement of Applicability: every applied control brings its requirements.
Does Grundschutz++ replace an ISO 27001 certification?+
No. Grundschutz++ is a catalogue of requirements, not a certification. It describes very precisely what the state of the art is. Checking your ISO 27001 Statement of Applicability against it finds the places an auditor would sample.
Conclusion
The check against Grundschutz++ found fewer missing controls than expected. Above all it showed where the documentation no longer fits the system. That is the part that costs most in an ISO 27001 audit. The Statement of Applicability provides the checklist, the library's mapping provides the requirements. Because every requirement is a record with a target object and an action word, a large part can be answered directly against IaC and GitOps repositories. The rest follows from one simple rule: only count what the system shows, and date every statement.
Next up: part 2, applicability as data.
I check your ISMS against Grundschutz++. Measured on the running system, not on the documentation.
Matching your Statement of Applicability against the state-of-the-art library, rating every requirement against configuration and measurement, and a clear list of gaps with effort and order.
Platform operations instead of paper consulting: on request I close the technical gaps myself and deliver the evidence for your ISO 27001 audit.
About the author

Kai Ole Hartwig
Programming since 2002 – self-taught, set up my own business with KO-Web in 2012. Over 100 projects, with a focus on security, performance, automation and quality. Today freelance: DevSecOps consulting, training and software development.
