Map engineering controls to ISO 27001 Annex A without busywork
For software engineers who need to turn real technical safeguards into ISO 27001 Annex A evidence, not spreadsheet theater. This guide shows how to map concrete controls like SSO, logging, encryption, backups, and CI policy checks to Annex A themes, with examples you can adapt and audit.
TL;DR — ISO 27001 Annex A mapping is not "implement every control"; it is "show which engineering controls reduce which risks, and produce evidence that they actually run." The fastest path is to build a control inventory from systems you already operate—IdP, CI/CD, cloud IAM, logging, backups, endpoint management—then map each control to Annex A objectives, owner, evidence source, and test method. Reading time: ~7 min
What it is and where it sits
For an engineering team, mapping technical controls to ISO 27001 Annex A is the translation layer between what you actually run and what an auditor, risk owner, or security lead needs to see.
The useful unit is not the Annex A control text by itself. The useful unit is:
- risk or policy requirement
- technical control that reduces it
- system that enforces it
- evidence that proves it is active
- owner who can answer when it breaks
Annex A sits downstream of your ISMS decisions and upstream of audit evidence. Engineering usually does not "implement Annex A" directly. Engineering implements things like:
- SSO with MFA on admin apps
- branch protection and required reviews
- encrypted object storage and database volumes
- centralized audit logs
- vulnerability scanning in CI
- backups with restore tests
- least-privilege IAM roles
- secrets in a vault or cloud secret manager
Those controls live in the delivery path and data path of your systems, not in a compliance tool.
View as text
[Developer] ---- 1. git push ----> [CI/CD] [CI/CD] ---- 2. build/test/scan ----> [Artifact Registry] [CI/CD] ---- 3. deploy ----> [Cloud Runtime] [User] ---- 4. HTTPS ----> [App] [App] ---- 5. auth ----> [IdP] [App] ---- 6. read/write ----> [DB] [Cloud Runtime] ---- 7. logs ----> [Log Store] [DB] ---- 8. backups ----> [Backup Storage] [Security/Compliance] ---- 9. evidence pull ----> [IdP] [Security/Compliance] ---- 10. evidence pull ----> [CI/CD] [Security/Compliance] ---- 11. evidence pull ----> [Cloud Runtime] [Security/Compliance] ---- 12. evidence pull ----> [Log Store]
What it replaces: ad hoc spreadsheets that say "access control = yes" with no owner, no system, and no proof.
What talks to it: your source control, CI platform, cloud IAM, identity provider, ticketing system, logging stack, backup tooling, endpoint tooling, and vulnerability scanners. The mapping itself can live in Git as YAML/Markdown, in a GRC tool, or in a database. For most engineering teams, Git is enough if the schema is disciplined.
A practical architecture is:
controls/directory in a repo- one file per control implementation
- links to scripts or commands that collect evidence
- a scheduled job that refreshes evidence artifacts
- a review cadence tied to risk review or internal audit prep
How it actually works
Walk one realistic example: you want to map "admin access to production is strongly controlled and auditable" to Annex A.
Suppose your stack is:
- GitHub or GitLab for code
- cloud provider IAM for infrastructure
- an IdP for workforce auth
- Kubernetes for runtime
- centralized logs in CloudWatch, Stackdriver, ELK, or similar
You do not start from Annex A text and brainstorm. Start from the actual enforcement points.
Step 1: Identify the technical control
Concrete control statement:
- Production admin access requires SSO via IdP.
- MFA is enforced by the IdP.
- Access is granted only through a
prod-admingroup. - Kubernetes admin actions are logged to the audit log sink.
- Membership changes require a ticket.
This likely supports Annex A themes around identity management, authentication, access restriction, logging, and privileged access. Exact numbering can vary by edition and your statement of applicability, so map to your adopted Annex A structure, not a random blog post.
Step 2: Record the implementation, owner, and evidence source
A control record should include fields like:
control_id: internal ID likeAC-PRD-001annex_a_refs: your selected Annex A referencessystem: IdP, Kubernetes, cloud IAMowner: team or personimplementation: exact enforcement configevidence: command/API/report pathtest: how you verify it still worksexceptions: break-glass path, if any
Step 3: Pull evidence from the live systems
Examples of evidence you can actually collect:
- IdP group policy showing MFA required for admins
- current members of
prod-admin - cloud IAM role trust policy
- Kubernetes audit log entries for
create,patch,delete - ticket references for recent access changes
If your evidence requires screenshots, you are already paying a tax. Prefer commands and exported JSON.
For example, if your Kubernetes API audit logs are in JSON lines, a quick verification might look like:
jq 'select(.user.groups[]? == "prod-admin") | {verb, user: .user.username, resource: .objectRef.resource, ns: .objectRef.namespace}' audit.log | head
Typical output shape:
{
"verb": "patch",
"user": "alice@example.com",
"resource": "deployments",
"ns": "production"
}
{
"verb": "delete",
"user": "bob@example.com",
"resource": "pods",
"ns": "production"
}
If that returns nothing for a period where you know changes happened, you may have a logging gap, a filter issue, or logs routed somewhere else.
Step 4: Define the test, not just the control
A mapped control without a test is documentation, not assurance.
Good test:
- weekly job exports current
prod-adminmembership - compare against approved access list
- alert if a user lacks MFA or if membership changed without a ticket
- monthly sample one admin action from audit logs and trace it to a person and change record
Step 5: Handle the edge case auditors will ask about
Break-glass access.
If production can be accessed outside SSO during an outage, document:
- where emergency credentials live
- who can retrieve them
- how use is logged
- how the password or key is rotated after use
If you skip this, your "all admin access requires SSO" statement is false in the one scenario everyone cares about.
When to use it (and when not to)
Use Annex A mapping when you need a repeatable bridge from engineering reality to compliance evidence.
| Scenario | Recommendation |
|---|---|
| You are preparing for ISO 27001 certification or surveillance audit | Do it now. Keep mappings in version control and tie each to evidence and owner. |
| You already have security controls but cannot prove they are active | Do it. Focus on evidence automation before writing more policy text. |
| You are a 5-person startup with one product and no formal ISMS yet | Start lighter: inventory systems, risks, and top 10 controls first. Full Annex A mapping can wait. |
| You want a giant one-to-one matrix from every firewall rule to every Annex A item | Don’t. Map at control capability level, not every low-level setting. |
| You have no owners for IAM, logging, backups, or endpoints | Fix ownership first. Mapping without owners becomes stale immediately. |
| You only need customer security questionnaire answers | You probably don’t need full Annex A mapping yet; a control inventory may be enough. |
You probably do not need a dedicated GRC platform if:
- your control count is under roughly 50-80
- evidence can be exported by scripts
- reviews can happen in pull requests
- your auditors accept Git-based evidence packages
Trade-offs
Every benefit costs something.
- Better audit readiness
- Cost: someone must maintain control records and evidence collection jobs.
- Clear ownership
- Cost: uncomfortable decisions about who owns shared systems like IAM and logging.
- Less duplicate work across questionnaires, audits, and customer reviews
- Cost: you need a normalized schema for controls, evidence, and exceptions.
- Faster gap analysis
- Cost: initial inventory work is tedious, especially around legacy systems.
- More trustworthy security claims
- Cost: weak controls become visible. For example, "encrypted backups" means little if restores are never tested.
- Easier engineering prioritization
- Cost: some controls add friction—MFA prompts, stricter branch protection, slower CI due to scans, more logging spend.
Operationally, the common hidden costs are:
- log retention bills
- CI minutes for SAST/dependency scans
- staff time to review access recertifications
- breakage from overly strict policy-as-code checks
- lock-in if evidence collection depends heavily on one cloud or IdP API
In practice
Below are examples you can adapt today.
Example 1: Control mapping as YAML in Git
id: AC-PRD-001
name: Production admin access is SSO-protected and audited
annex_a_refs:
- "Identity management"
- "Authentication information"
- "Access rights"
- "Logging"
owner: platform-team
systems:
- idp
- kubernetes
- cloud-iam
implementation:
idp_group: prod-admin
mfa_required: true
sso_required: true
break_glass: "stored in sealed secret process; post-use rotation required"
evidence:
- type: api-export
source: idp-group-membership
path: evidence/idp/prod-admin-members.json
- type: log-query
source: k8s-audit
path: evidence/k8s/prod-admin-audit-sample.json
- type: ticket-sample
source: access-requests
path: evidence/tickets/prod-admin-changes.csv
test:
frequency: weekly
method: "Export group membership, verify MFA flag, sample one privileged action log"
exceptions:
- "Emergency access allowed only via break-glass runbook"
review_every_days: 90
This gives you a durable control record with enough structure to automate checks. Gotcha: keep Annex A references aligned to the edition and naming your ISMS actually uses; don’t mix labels from different templates.
Example 2: Simple evidence collection script
#!/usr/bin/env bash
set -euo pipefail
OUTDIR="evidence/$(date +%F)"
mkdir -p "$OUTDIR"
# Example placeholders: replace with your IdP / cloud CLI commands.
# 1) Export admin group membership
idpctl groups members list prod-admin --format json > "$OUTDIR/prod-admin-members.json"
# 2) Check that all members have MFA enabled
jq -e 'all(.members[]; .mfa_enabled == true)' "$OUTDIR/prod-admin-members.json" > /dev/null
# 3) Pull a sample of privileged Kubernetes audit events from the last 7 days
jq 'select(.verb == "create" or .verb == "patch" or .verb == "delete") | select(.user.groups[]? == "prod-admin")' /var/log/kubernetes/audit.log | head -n 20 > "$OUTDIR/prod-admin-audit-sample.json"
echo "evidence written to $OUTDIR"
This fails fast if any admin lacks MFA because jq -e exits non-zero.
Typical failure shape:
$ ./collect-evidence.sh
jq: error (at evidence/2026-10-30/prod-admin-members.json:122): false and null cannot be used as a boolean
That usually means your export schema changed or some users are missing the mfa_enabled field. Treat schema drift as a control failure until you update the check.
Example 3: CI policy gate for branch protection and scans
⚠️ Blocking merges on new policy checks can stop releases. Roll this out in report-only mode first, then switch to required status checks after one or two sprints.
name: policy-checks
on:
pull_request:
push:
branches: [main]
jobs:
controls:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Verify control files are valid YAML
run: |
python - <<'PY'
import sys, yaml, glob
required = {"id","name","annex_a_refs","owner","evidence","test"}
bad = False
for f in glob.glob("controls/*.yml") + glob.glob("controls/*.yaml"):
data = yaml.safe_load(open(f))
missing = required - set(data)
if missing:
print(f"{f}: missing {sorted(missing)}")
bad = True
sys.exit(1 if bad else 0)
PY
- name: Dependency vulnerability scan
run: |
npm audit --audit-level=high
This enforces that control records stay minimally complete and pairs governance with a real technical check. Gotcha: npm audit can be noisy or flaky for transitive issues; define an exception process or baseline, or teams will bypass it.
Further reading
- ISO/IEC 27001 Annex A and Statement of Applicability guidance
- ISO/IEC 27002 information security controls guidance
- NIST SP 800-53 Control Baselines and Control Enhancements
- CIS Controls v8 Implementation Groups
- The "Security logging and monitoring" sections of the OWASP Cheat Sheet Series
This article was written by an AI system and published pending human review. Verify anything you intend to act on.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI