---
title: "CloudSecOps"
slug: cloudsecops
category: security
term_status: emerging
evidence_status: verified
summary: "CloudSecOps is running security for a cloud estate at the provider-API layer: guardrails that block unsafe configuration, continuous posture checks, complete audit logs, and detection and response for control-plane activity. The label is emerging and loosely defined."
audience: [Cloud security engineers, Cloud platform teams, Security architects, SOC leads, Engineering leaders, Beginners]
last_verified: 2026-10-17
key_takeaways:
  - "CloudSecOps is an emerging trade-press label (Dave Shackleford called it an 'emerging buzzword' in TechTarget SearchSecurity in 2022) that no standards body defines. This page centres it on the provider control plane, which is an editorial distinction."
  - "Configurations, identities and data stay with the customer in every service model, so even fully managed services leave you control-plane security work."
  - "Use preventive, detective and responsive guardrails together, and learn each one's preconditions: AWS SCPs and RCPs do not apply to the management account or to service-linked roles."
  - "Logs you did not collect before an incident cannot be recovered after it. AWS data events and Google Data Access logs are off by default; Entra ID keeps sign-in logs for 7 days (Free) or 30 days (P1/P2)."
  - "A posture score is a ratio of passed controls, and suppressing failed findings raises it. Track exposure dwell time with the age of open findings, exception debt and logging coverage instead."
  - "Tool support lags benchmarks: in October 2026 CIS publishes AWS Foundations 7.1.0, while AWS Security Hub CSPM supports versions up to 5.0.0."
related: [cloudops, iamops, kubernetessecops, devsecops, complianceops, soc-ops, secdataops, secops]
---

## What CloudSecOps is

### In 30 seconds

**CloudSecOps** (cloud security operations) is the continuous work of keeping a cloud estate secure through the provider's own
control plane: guardrails that stop unsafe configuration before it exists, posture checks that find drift, audit logs that record
every administrative action, and detection and response for suspicious control-plane activity.

**For** cloud security and platform engineers, security architects, SOC leads, and leaders who decide what to enforce. Facts last
checked 2026-10-17.

::: note Term status and this page's emphasis
"CloudSecOps" is an emerging label. Dave Shackleford (Voodoo Security), writing in TechTarget SearchSecurity in 2022, noted "an
emerging buzzword: CloudSecOps" and defined it as "an evolution of SecOps that focuses on building controls, implementing
monitoring and enacting security response activities in cloud environments"; he stresses guardrails and integration with DevOps
and cloud engineering.[^tt-cso] A Data Center Knowledge commentary the same year covers similar ground but centres it on a SOC
whose aim is to "identify, react to, and recover from attacks".[^dck] ISC2 uses the spaced form, "Cloud Security Operations", for
one of the six domains of its CCSP exam, which also covers IT service management processes and forensics.[^isc2] No standards
body defines the term. This page centres it on the **provider control plane** (identity, configuration and logging at the API
layer); that emphasis and the boundaries below are an **editorial distinction**.
:::

### At a glance

| Item | Summary |
|---|---|
| Term status | **Emerging.** In use in trade press by 2022; the practices are documented by providers, CIS, CSA, NSA/CISA and MITRE. |
| Goals | Unsafe configurations blocked or found quickly; every administrative action logged where attackers cannot erase it; control-plane threats detected and contained. |
| In scope | Shared responsibility for security controls, security guardrails and drift, CSPM/CWPP/CNAPP, cloud IAM misconfigurations, keys, audit logging, public storage, CIS cloud benchmarks, cloud detection and response basics. |
| Out of scope | See [What CloudSecOps is not](#what-cloudsecops-is-not). |
| Prerequisites | [CloudOps](../cloudops/) (hierarchy, accounts, Regions); basic IAM; one provider's CLI. |
| Key outputs | A versioned security guardrail set with exceptions, an organisation-wide audit log archive, a triaged posture backlog with owners, tested control-plane detections and runbooks. |

### The problem it solves

In the cloud, a bucket becomes public, a key is created or a log is switched off with one API call by anyone holding the right
permission, including an attacker holding someone else's. Three patterns recur:

- **Configuration.** The Cloud Security Alliance's 2024 Top Threats ranking, built from a poll of more than 500 industry experts,
  put misconfiguration and inadequate change control first and identity and access management second.[^csa-threats] It is an
  expert ranking, not incident data.
- **Credentials.** Mandiant traced every incident it handled in the 2024 Snowflake customer campaign "back to compromised customer
  credentials", on accounts without multi-factor authentication.[^mandiant]
- **Missing logs.** In the 2023 Exchange Online intrusion reviewed by the US Cyber Safety Review Board, one victim agency "could not
  view past activity as these logs only captured data from the time that Microsoft enabled the advanced logging".[^csrb]

| Operating reality | Without CloudSecOps | With CloudSecOps |
|---|---|---|
| A risky setting (public bucket, open admin port) | Found by an outsider | Blocked, or found and assigned within hours |
| Audit logs | A 90-day built-in history, plus account-level trails or exports that account admins can stop or delete | An organisation trail or export into an account workload admins cannot change |
| A suspicious API call | Nobody is looking | A tested detection pages an on-call engineer with a runbook |
| Exceptions | Permanent, undocumented | Owner, reason, expiry date, review |

### What CloudSecOps is not

- **Not SecOps with a cloud connector.** In this Atlas, [SecOps](../secops/) is the collaboration hub between security and IT
  operations and governs hardening baselines; [SOC Ops](../soc-ops/) runs triage. CloudSecOps supplies them with cloud-specific
  controls, telemetry and runbooks.
- **Not DevSecOps.** [DevSecOps](../devsecops/) catches misconfigurations in the pull request; CloudSecOps owns the deployed
  state, including changes made outside the pipeline.
- **Not CloudOps.** [CloudOps](../cloudops/) owns the account structure, operational guardrails (Regions, tags, budgets) and teaches
  the shared responsibility model; CloudSecOps decides which **security** guardrails to enforce and watches them drift.
- **Not identity engineering.** How policies are written and evaluated → [IAMOps](../iamops/); accounts and SSO →
  [IdentityOps](../identityops/); just-in-time elevation → [AccessOps](../accessops/).
- **Not a product, not compliance.** A posture tool gives findings, not owners or fixes; audit programmes belong to
  [ComplianceOps](../complianceops/), and CloudSecOps produces their evidence.

## Where CloudSecOps comes from and where it sits

### Origins and evolution

No founding document exists. These dated milestones can be verified:

| Date | Milestone | What it shows |
|---|---|---|
| 2021 | A Wiz-authored post on the CSA blog says Gartner coined "CNAPP"[^csa-cnapp] | Posture and workload tools start to converge |
| 2022 | TechTarget SearchSecurity and Data Center Knowledge articles use "CloudSecOps"[^tt-cso][^dck] | The label is in use, with different emphases |
| 2024 | CISA and NSA cloud information sheets (March);[^cisa-csi] the Snowflake customer campaign (June);[^mandiant] CSRB calls for free default audit logging;[^csrb] CISA BOD 25-01 (December)[^bod2501] | Credentials and logging become policy topics |
| 2026 | Google closes its Wiz acquisition (March);[^wiz-close] MITRE ATT&CK v20 regroups cloud log tampering under a new technique ID[^attack-log] | Tools consolidate; vocabulary still moves |

### Glossary and mental model

| Term | Meaning on this page |
|---|---|
| **Control plane / data plane** | Management operations that create, change and delete resources, versus operations inside a resource. Azure's activity log records the first; its resource logs record the second.[^az-activity] |
| **Posture** | The security-relevant configuration of the estate at a point in time, measured against a baseline. |
| **Guardrail** | A control that runs continuously in the background;[^tt-cso] this page distinguishes **preventive** (blocks the call), **detective** (reports drift) and **responsive** (alerts or remediates). |
| **SCP / RCP** | AWS organisation policies that cap what principals (SCP) or resources (RCP) can do. Neither grants permissions; effective permissions are the intersection with identity and resource policies.[^scp][^rcp] |
| **OU, service-linked role** | An organisational unit groups AWS accounts so that one attached policy covers them all. A service-linked role is predefined by an AWS service and used on your behalf; SCPs and RCPs do not restrict it.[^scp] |
| **IMDSv2** | The session-oriented version of the EC2 instance metadata service, which AWS links to defence in depth against SSRF (see Use case 1).[^imds] |
| **Data perimeter** | AWS: "a set of preventive controls you can use to help ensure that only your trusted identities access your trusted resources from expected networks".[^rcp-ex] |
| **CSPM, CWPP, CNAPP** | Posture management, workload protection, and the platform that combines them. Microsoft describes Defender for Cloud as a CNAPP with CSPM, DevSecOps and CWPP components.[^ms-dfc] |

**The mental model: four questions at the API.** Every cloud action is an authenticated API call. CloudSecOps keeps four answers
current: **who can call** (the identity perimeter), **what they may configure** (preventive guardrails), **what the estate looks
like now** (posture), and **what happened** (logs and detections). A perfect posture score says nothing about an attacker using
valid keys. This model is this Atlas's teaching device, not an industry framework.

### How CloudSecOps relates to other Ops

| Page | Shared ground | CloudSecOps focus | Their focus |
|---|---|---|---|
| [CloudOps](../cloudops/) | Organisation policies | Security guardrails, posture, CIS benchmarks | Hierarchy, Regions, tags, budgets, quotas |
| [IAMOps](../iamops/) | SCPs, Azure Policy | Which guardrails to enforce and their drift | Writing and evaluating policy language |
| [DevSecOps](../devsecops/) | IaC findings | Deployed state and out-of-band change | The pull request and pipeline |
| [KubernetesSecOps](../kubernetessecops/) | Workload identity | Cloud accounts | Admission and runtime in clusters |
| [SecOps](../secops/), [SOC Ops](../soc-ops/) | Alerts, baselines | Cloud context, telemetry and runbooks | Coordination; 24×7 triage |
| [DetectionOps](../detectionops/), [SecDataOps](../secdataops/) | Cloud logs | Which logs exist and where | Detection logic; telemetry pipelines |
| [ComplianceOps](../complianceops/) | Benchmarks | Producing evidence | Audit programmes |
| [VulnerabilityOps](../vulnerabilityops/) | Findings | Configuration findings | Software vulnerabilities and patch priority |

## Principles and operating model

### Charter and principles

This charter is **this Atlas's proposal**; no official CloudSecOps manifesto exists.

**Mission:** make unsafe cloud configuration hard to create, quick to find and hard to hide.

1. **Prevent centrally what must never happen; detect the rest.** Organisation-level denies for a short list of catastrophic
   actions, detective checks for everything that has legitimate exceptions.
2. **Every guardrail has an owner, a test, an exception path and a measurement.** A rule without an exception path creates
   shadow accounts.
3. **Collect logs before you need them, where workload administrators cannot change them.**
4. **Identity is the perimeter.** Long-lived keys are debt with interest; federated, short-lived credentials are the default.
5. **Know each control's preconditions.** Name the accounts, Regions and principals a control does not cover.
6. **Fix in code.** The next deployment reverts a console fix; remediate in the infrastructure as code
   ([InfraOps](../infraops/)).

**Non-goals:** writing application code, running the SOC, designing permission models for each workload.

### Fundamentals: shared responsibility applied to controls

[CloudOps](../cloudops/#shared-responsibility-what-moves-with-the-service-model) teaches the shared responsibility model; this
table applies Microsoft's matrix to security controls.[^az-srm]

| Control area | IaaS | PaaS and serverless | SaaS |
|---|---|---|---|
| Identities, access, configuration settings, data | Customer | Customer | Customer |
| Network controls | Customer | Shared | Provider |
| Operating system and patching | Customer | Provider | Provider |

The building blocks follow the four questions: an **identity perimeter** of federated, short-lived credentials (designed in
[IAMOps](../iamops/) and [IdentityOps](../identityops/)); **preventive guardrails** such as AWS SCPs, RCPs and declarative
policies, Azure Policy `deny` and Google Organization Policy constraints;[^scp][^rcp][^declarative][^az-effects][^gcp-orgpol]
**detective posture**, including analysis of resources shared outside a "zone of trust";[^iaa] and **audit logs with
detections**. [Keys and encryption](#keys-secrets-and-encryption) cut across all four.

Defaults decide where the work starts (as of October 2026):

| Log | AWS | Azure | Google Cloud |
|---|---|---|---|
| Control-plane audit log | Event history keeps 90 days of management events; a trail stores more[^ct-intro] | Activity log kept 90 days; export with a diagnostic setting[^az-activity] | Admin Activity logs "always written"; cannot be disabled[^gcp-audit] |
| Data-plane audit log | Data events off by default, charged extra[^ct-data] | Resource logs need a diagnostic setting[^az-activity] | Data Access logs off by default except BigQuery[^gcp-audit] |
| Identity sign-ins and directory changes | Console sign-ins are CloudTrail events[^ct-signin] | Entra ID sign-in and audit logs kept 7 days (Free) or 30 days (P1/P2); route them out with Azure Monitor[^entra-ret] | Cloud Identity and Workspace login logs reach Cloud Logging only once you enable sharing[^gws-audit] |

If identity is the perimeter, the identity row matters most, and Entra retention is not retroactive: after an upgrade from Free,
"only data still within the free retention period (up to seven days) is available".[^entra-ret]

Two other defaults matter. On AWS, instances accept both IMDSv1 and IMDSv2 unless a newer default requires
IMDSv2.[^imds][^ec2-pol] On Google Cloud, organisations created on or after 3 May 2024 start with baseline constraints such as
disabled service-account key creation and uniform bucket-level access; older organisations may not have them.[^gcp-sbd]

### Keys, secrets and encryption

Most services encrypt at rest by default, so the work is deciding **who controls each key** and **who may use it**. AWS owned
keys cost nothing, but "you cannot change their policies, you cannot audit activities on these keys, and you cannot delete them";
customer managed keys give "full control over the lifecycle and usage" for a monthly fee.[^kms-concepts] Azure and Google Cloud
call these customer-managed keys (CMK, CMEK). Pay for one when you must see every use in your own logs, decide who may use it, or
make data unreadable by disabling it. Three controls then matter more than [rotation](#anti-patterns-and-myths):

- **Separate administering from using.** AWS's default key policy lets key administrators manage a key but not use it; because
  they can edit the policy, they "can give themselves and others" more access, so treat key administration as privileged.[^kms-policy]
- **Guard deletion.** Deleting a KMS key "is destructive and potentially dangerous": the data it protects "becomes
  unrecoverable", so each provider adds a delay. AWS KMS waits 7–30 days (default 30);[^kms-delete] AWS's sample SCPs reserve
  `kms:ScheduleKeyDeletion` for a privileged role or forbid waiting periods under 30 days.[^scp-ex] Azure Key Vault keeps deleted
  keys recoverable for 7–90 days; add purge protection, which services such as Azure Storage require.[^kv-soft] Cloud KMS waits 30
  days by default, and an organisation policy can enforce a minimum.[^gcp-kms-destroy]
- **Keep key-use logs.** CloudTrail logs all AWS KMS actions by default, but a trail can exclude them all to cut volume;[^kms-ct]
  never exclude them from the trail covering accounts that hold sensitive data.

**Secrets** belong in the provider's secret store (AWS Secrets Manager, Azure Key Vault, Google Secret Manager), never in code or
instance user data; how pipelines fetch them belongs to [DevSecOps](../devsecops/).

### Roles, ownership and RACI

Microsoft's Cloud Adoption Framework describes posture management as "the operational security team that focuses on security
enablement for technical operations teams".[^caf-roles] The RACI below is this Atlas's proposal (R = responsible, A = accountable,
C = consulted, I = informed).

| Activity | Cloud security | Cloud platform | Workload team | SOC | Risk owner / GRC |
|---|---|---|---|---|---|
| Choose the security guardrail set and baseline | A/R | C | C | I | C |
| Deploy organisation policies through the pipeline | A | R | I | I | I |
| Organisation audit log, archive account, retention | A/R | R | I | C | C |
| Triage and route posture findings | A/R | C | I | I | I |
| Fix a misconfiguration in a workload | C | C | A/R | I | I |
| Approve an exception | R | C | R (requests) | I | A |
| Cloud detection use cases and runbooks | A/R | C | I | R | I |
| Contain a cloud incident | R | R | C | A | I |

**Skills:** IAM evaluation logic, policy as code, logging architecture, audit-log queries, incident handling. Small organisations
combine these roles (see [When to use](#when-to-use-and-when-not-to-use)).

### Lifecycle: Day 0, Day 1, Day 2

- **Day 0, design.** Pick a baseline (a CIS Foundations benchmark or the provider's), list the few actions to deny everywhere,
  design the log archive and its retention, decide the exception process.
- **Day 1, roll out.** Turn on organisation-wide logging and posture scanning first, then trial guardrails, then enforce OU by OU.
  Azure's `DoNotEnforce` mode exists "when you want to evaluate the policy assignment before enforcement",[^az-assign] and Google
  supports dry-run organisation policies.[^gcp-orgpol] AWS SCPs and RCPs have no documented evaluate-only mode, so on AWS the trial
  is a test OU and a review of its access-denied events.[^scp]
- **Day 2, run.** The cadences below.

### Operating model and governance

| Cadence | Activity | Output |
|---|---|---|
| Continuous | Guardrail enforcement, posture scans, control-plane detections | Denied calls, findings, alerts |
| Daily | Triage of new critical findings | Owner and due date on each |
| Weekly | Exception and overdue-finding review | Renewed, closed or escalated exceptions |
| Monthly | Guardrail and benchmark review | Changes to the guardrail set, staged by OU |
| Quarterly | Game day and metrics review (scorecard in [Templates](#templates)) | Tested detections, trend report to leadership |

Release guardrails like software, from a test OU to ever broader OUs; [CloudOps](../cloudops/#operating-model-and-governance)
teaches that staged rollout.[^scp-ex] Exceptions use the [exception request](#templates), capped at 90 days (this page's
suggestion).

## Architecture and workflow

### Reference architecture: guardrail and evidence layers

[CloudOps](../cloudops/) shows the landing zone; this view adds the security layers in AWS terms, and the table after it gives
the Azure and Google Cloud counterparts.

::: figure Security layers in a multi-account cloud estate
```mermaid
flowchart TB
  MGMT["Management account: no workloads, outside SCPs and RCPs"] -->|"attaches"| POL
  POL["Organisation policies: SCP, RCP, declarative"]
  POL --> MEM["Member accounts: workloads and sandboxes"]
  MEM -->|"organisation trail"| LOG["Log archive account"]
  MEM -->|"findings"| SEC["Security tooling account"]
  LOG --> SIEM["Query store or SIEM"]
  SEC --> TRI["Triage queue"]
  TRI -->|"misconfiguration"| OWN["Workload owner: fix in IaC"]
  TRI -->|"threat"| SOC["SOC: incident"]
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029
  classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
  class POL,LOG,SEC,TRI core
  class MEM,SIEM,OWN,SOC neutral
  class MGMT risk
```
Text equivalent: The management account, marked as a risk, holds no workloads because SCPs and RCPs do not limit it; it attaches
the organisation policies (SCP, RCP, declarative), which apply to the member accounts holding workloads. Member accounts send audit
events through the organisation trail to a log archive account, which feeds a query store or SIEM, and findings to a security
tooling account and its triage queue: misconfigurations go to the workload owner to fix in code, threats to the SOC.
:::

Design choices, with their evidence:

- **Keep the management account empty.** "SCPs don't affect users or roles in the management account", nor RCPs its
  resources.[^scp][^rcp]
- **Log through the organisation.** Member-account users "do not have sufficient permissions to delete organization trails, turn
  logging on or off, change what types of events are logged".[^ct-org] Only the security team administers the archive account.
- **Cover every enabled Region.** Security Hub CSPM "only receives and processes findings in the Region where you enabled" it, and
  AWS recommends enabling GuardDuty "in all AWS Regions", including ones you do not use, because attackers can create resources
  there.[^sh-intro][^guardduty]

| Layer | AWS | Azure | Google Cloud |
|---|---|---|---|
| Hierarchy for policy | Organization, OUs, accounts | Management groups, subscriptions | Organization, folders, projects |
| Preventive guardrails | SCP, RCP, declarative policies | Azure Policy (`deny`, `denyAction`)[^az-effects] | Organization Policy constraints[^gcp-orgpol] |
| Control-plane log | CloudTrail organisation trail[^ct-org] | Activity log via diagnostic settings[^az-activity] | Admin Activity audit logs[^gcp-audit] |

### End-to-end workflow: from change to verified fix

::: figure From a configuration change to a verified fix, with the exception path
```mermaid
flowchart TB
  CHG["Change: pipeline, console, script or attacker"] --> PRE{"Preventive guardrail matches?"}
  PRE -->|"yes"| DENY["Call denied and logged"]
  DENY --> EXC{"Legitimate need?"}
  EXC -->|"yes"| REQ["Exception request: owner, reason, expiry"]
  EXC -->|"no"| STOP["Requester changes the design"]
  PRE -->|"no"| RES["Resource created or changed"]
  RES --> SCAN["Posture scan or detection"]
  SCAN --> TRI{"Finding type?"}
  TRI -->|"misconfiguration"| FIX["Owner fixes in IaC within SLA"]
  TRI -->|"suspicious activity"| INC["SOC incident and runbook"]
  FIX --> VER["Rescan confirms closure"]
  FIX -->|"SLA missed"| ESC["Escalate, or exception with expiry"]
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
  class CHG,RES,SCAN,FIX,VER,REQ core
  class PRE,EXC,TRI decision
  class DENY,INC,ESC,STOP risk
```
Text equivalent: A change arrives from a pipeline, console, script or attacker. If a preventive guardrail matches, the call is
denied and logged; a legitimate need becomes an exception request with owner, reason and expiry, otherwise the design changes. If
not, the change happens and a posture scan or detection evaluates it: misconfigurations go to the owner to fix in infrastructure as
code, confirmed by a rescan (a missed target escalates or becomes a time-limited exception); suspicious activity becomes a SOC
incident with a runbook.
:::

**Acceptance criteria (this page's suggestion):** every finding has an owner within one working day, and every closure is
confirmed by a rescan, not a ticket comment.

## Practices, frameworks and tools

### Core practices and playbooks

| Practice | Trigger | Owner | Output | Verification |
|---|---|---|---|---|
| Guardrail release | New risk, incident, benchmark change | Cloud security, with platform | Policy as code, staged by OU | No unexpected denials in the test OU |
| Logging baseline | New account or Region | Platform | Organisation trail or exports | A test event reaches the archive |
| Posture triage | New critical finding | Cloud security | Owner and due date, or false-positive record | Finding closes on rescan |
| Credential hygiene | Long-lived key detected | Workload owner | Key replaced by role or federation | Unused-access findings fall |
| Detection game day | Quarterly, new detection | Cloud security with SOC | Alert received, runbook followed | Time to alert within target |

### Methods, standards and frameworks

Separate what binds you from what guides you:

| Framework | Type | Use it for | Caveat |
|---|---|---|---|
| CIS Foundations Benchmarks (AWS 7.1.0, Azure 6.0.0, Google Cloud 5.0.0 as of October 2026)[^cis-aws][^cis-azure][^cis-gcp] | Consensus guidance, free PDF for non-commercial use | A starting baseline with assessment procedures | Tools lag versions; some checks are manual |
| Microsoft cloud security benchmark (v1; v2 in preview)[^mcsb] | Vendor benchmark, multicloud | Azure-first estates; pre-mapped to CIS, NIST, PCI DSS | v2 baselines not yet published |
| CSA Cloud Controls Matrix v4.1[^csa-ccm] | Control framework, 197 objectives in 17 domains | Assigning each control to provider or customer | Control objectives, not settings |
| CISA/NSA cloud information sheets (2024)[^cisa-csi] | Government guidance | Identity, keys, segmentation, data, MSP risks | Advisory outside US federal mandates |
| CISA BOD 25-01[^bod2501] | **Mandatory** for US federal civilian agencies | SaaS tenant baselines (SCuBA: Secure Cloud Business Applications) | Binding only in its jurisdiction |
| MITRE ATT&CK Cloud matrix (v20)[^attack-cloud] | Threat knowledge base | Choosing and testing control-plane detections | Coverage maps are not protection |

**Tools lag benchmarks.** CIS lists AWS Foundations 7.1.0 as current,[^cis-aws] while Security Hub CSPM "supports CIS AWS
Foundations Benchmark versions 5.0.0, 3.0.0, 1.4.0, and 1.2.0" and leaves some requirements, such as metric-filter alarms, as
manual checks.[^sh-cis] A green dashboard means *passes the automated subset of an earlier version*; record the version.

### Common IAM and exposure misconfigurations

This page maps the most consequential recurring mistakes to Security Hub CSPM control IDs and to a preventive option (AWS, as of
October 2026).[^sh-controls]

| Misconfiguration | Check | Preventive option |
|---|---|---|
| A policy allowing `*` actions on `*` resources | IAM.1 | Validation gate and permission boundaries ([IAMOps](../iamops/)) |
| Root user access keys | IAM.4 | Centralised root access, then delete member-account root credentials[^root-central] |
| User credentials unused for 45 days | IAM.22 | Federation for people; unused-access findings[^iaa] |
| Trust or resource policies open to other accounts | IAM.28 (analyzer on) | External-access findings; data-perimeter RCP[^rcp-ex] |
| Publicly restorable EBS snapshots | EC2.1, EC2.182 | Snapshot and image block public access (EC2 declarative policy)[^ec2-pol] |
| Admin ports open to `0.0.0.0/0` or `::/0` | EC2.53, EC2.54 | Detective, routed to the owner; VPC Block Public Access where no ingress is needed[^ec2-pol] |
| Public buckets | S3.1, S3.8 | Organisation-level S3 block[^s3-org] |
| IMDSv1 still accepted | EC2.8 | Instance metadata defaults (EC2 declarative policy)[^ec2-pol] |
| KMS key scheduled for deletion | KMS.3 | SCP on `kms:ScheduleKeyDeletion`[^scp-ex] |

### Tooling landscape

| Category | Job | Verified examples (October 2026) | Limits and selection criteria |
|---|---|---|---|
| Native preventive | Block calls or settings | AWS SCP, RCP, declarative policies; Azure Policy; Google Organization Policy | Provider-specific; know exempt principals |
| Native posture | Check configuration against standards | Security Hub CSPM (needs AWS Config, Regional; also checks Azure);[^sh-intro][^sh-azure] Defender for Cloud;[^ms-dfc] Security Command Center[^gcp-scc] | Attack paths only in paid tiers (Defender CSPM, SCC Premium) |
| Access analysis | Find external, internal and unused access | IAM Access Analyzer[^iaa] | External analyzers are Regional |
| Threat detection | Flag suspicious control-plane and network activity | GuardDuty;[^guardduty] Defender for Cloud plans; Security Command Center Premium | Detections are vendor-defined; test them |
| Multicloud CNAPP | One posture and risk view across providers | Defender for Cloud (AWS, GCP connectors); Security Hub CSPM (Azure); commercial CNAPPs | Consolidation risk: SCC Enterprise shuts down on 21 May 2027;[^gcp-scc] Google closed its Wiz acquisition in March 2026[^wiz-close] |
| Open source | Scan or enforce as code | Prowler (Apache 2.0, many providers);[^prowler] Cloud Custodian (CNCF incubating)[^custodian] | You run, update and tune them |

**Native tools now cross providers (as of October 2026).** AWS runs two services. Security Hub CSPM, cited throughout this page,
also scores Azure against CIS Azure Foundations v4.0 (CIS lists 6.0.0). The newer AWS Security Hub correlates CSPM, Inspector,
Macie and GuardDuty findings into exposure findings with an attack path graph, in OCSF (Open Cybersecurity Schema Framework). AWS
recommends enabling both.[^sh-azure][^sh-v2]

**Selection criteria:** coverage of the benchmark *version* you need; organisation-wide and cross-Region aggregation; export in an
open format (Prowler writes CSV and JSON-OCSF by default; its SARIF output, a static-analysis format, is for IaC scans only);[^prowler-out] routing to owners
rather than to one security queue; cost model (per check, per resource, per role); and an exit plan if the vendor changes hands.

### Automation and AI

Automate **prevention** freely and **remediation** carefully: only what is reversible, high-confidence and narrow, such as
re-enabling a public access block on a bucket tagged as private; never auto-delete data or rotate credentials in use. Azure
Policy's `modify` and `deployIfNotExists` effects remediate inside the platform;[^az-effects] elsewhere, run automation as a
dedicated, logged role. Language models can summarise findings and draft runbook steps, but their suggestions are changes that
need review; see [AIOps for Security](../aiops-for-security/).

## CloudSecOps in practice

### Reference implementation

**Assumptions:** an AWS organisation with all features enabled, a security tooling account, a log archive account, policies kept
in Git and deployed by a pipeline role. The worked artefacts are AWS-first: for Azure and Google Cloud, item 3 gives the storage
equivalent and the [layer table](#reference-architecture-guardrail-and-evidence-layers) names the policy and log mechanisms, without
worked examples.

**1. A service control policy that protects security telemetry.** Adapted from AWS's samples: member accounts cannot leave the
organisation, stop or narrow audit and posture recording, disable detectors, or suppress findings (archive rules, filters,
trusted-IP lists, or setting a Security Hub CSPM finding to `SUPPRESSED`), and only the deployer can alter the two exempt
roles.[^scp-ex][^sh-bu] [IAMOps](../iamops/#reference-implementation)
teaches how such policies are written; its example protects the trail and access keys, while this one covers posture, detection
and the break-glass path.

_Example status: JSON syntax-checked by the validator; not executed (requires an AWS organisation). Action names and the Security
Hub condition key checked against AWS's machine-readable service reference on 2026-10-17._

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyLeaveOrganization",
      "Effect": "Deny",
      "Action": "organizations:LeaveOrganization",
      "Resource": "*"
    },
    {
      "Sid": "ProtectAuditAndPosture",
      "Effect": "Deny",
      "Action": [
        "cloudtrail:DeleteTrail",
        "cloudtrail:StopLogging",
        "cloudtrail:UpdateTrail",
        "cloudtrail:PutEventSelectors",
        "config:StopConfigurationRecorder",
        "config:DeleteConfigurationRecorder",
        "config:PutConfigurationRecorder",
        "config:DeleteDeliveryChannel",
        "config:PutDeliveryChannel",
        "securityhub:DisableSecurityHub",
        "securityhub:BatchDisableStandards",
        "securityhub:UpdateStandardsControl",
        "securityhub:BatchUpdateStandardsControlAssociations",
        "securityhub:DisassociateFromAdministratorAccount",
        "securityhub:DisassociateFromMasterAccount",
        "access-analyzer:DeleteAnalyzer",
        "access-analyzer:CreateArchiveRule",
        "access-analyzer:UpdateArchiveRule",
        "access-analyzer:ApplyArchiveRule",
        "access-analyzer:UpdateFindings"
      ],
      "Resource": "*",
      "Condition": {
        "ArnNotLike": {
          "aws:PrincipalARN": [
            "arn:aws:iam::*:role/security-breakglass",
            "arn:aws:iam::*:role/org-baseline-deployer"
          ]
        }
      }
    },
    {
      "Sid": "DenyFindingSuppression",
      "Effect": "Deny",
      "Action": "securityhub:BatchUpdateFindings",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "securityhub:ASFFSyntaxPath/Workflow.Status": "SUPPRESSED"
        },
        "ArnNotLike": {
          "aws:PrincipalARN": [
            "arn:aws:iam::*:role/security-breakglass",
            "arn:aws:iam::*:role/org-baseline-deployer"
          ]
        }
      }
    },
    {
      "Sid": "ProtectThreatDetection",
      "Effect": "Deny",
      "Action": [
        "guardduty:DeleteDetector",
        "guardduty:UpdateDetector",
        "guardduty:DisassociateFromAdministratorAccount",
        "guardduty:DisassociateFromMasterAccount",
        "guardduty:ArchiveFindings",
        "guardduty:CreateFilter",
        "guardduty:UpdateFilter",
        "guardduty:CreateIPSet",
        "guardduty:UpdateIPSet",
        "guardduty:CreateTrustedEntitySet",
        "guardduty:UpdateTrustedEntitySet"
      ],
      "Resource": "*",
      "Condition": {
        "ArnNotLike": {
          "aws:PrincipalARN": [
            "arn:aws:iam::*:role/security-breakglass",
            "arn:aws:iam::*:role/org-baseline-deployer"
          ]
        }
      }
    },
    {
      "Sid": "ProtectExemptRoles",
      "Effect": "Deny",
      "Action": [
        "iam:CreateRole",
        "iam:DeleteRole",
        "iam:UpdateRole",
        "iam:UpdateAssumeRolePolicy",
        "iam:AttachRolePolicy",
        "iam:DetachRolePolicy",
        "iam:PutRolePolicy",
        "iam:DeleteRolePolicy",
        "iam:PutRolePermissionsBoundary",
        "iam:DeleteRolePermissionsBoundary",
        "iam:TagRole",
        "iam:UntagRole"
      ],
      "Resource": [
        "arn:aws:iam::*:role/security-breakglass",
        "arn:aws:iam::*:role/org-baseline-deployer"
      ],
      "Condition": {
        "ArnNotLike": {
          "aws:PrincipalARN": "arn:aws:iam::*:role/org-baseline-deployer"
        }
      }
    }
  ]
}
```

**Preconditions and limits:**

- **Scope.** It binds only **member** accounts, and not service-linked roles.[^scp] It does **not** cover Macie, Inspector, the
  EventBridge rules behind your detections, or the log archive's key and bucket.
- **Exempt roles.** Each exemption matches a role *name* in any account, so both names are protected: create the roles while
  vending an account, before it joins a governed OU, and alert on every use.
- **A second layer.** Member accounts cannot change an organisation trail anyway,[^ct-org] so the CloudTrail actions protect
  account-level trails. Manage GuardDuty and Security Hub CSPM through Organizations, where only the administrator manages
  GuardDuty's lists and central configuration stops centrally managed accounts from disabling standards or
  controls.[^gd-lists][^sh-central]
- **Finding suppression.** `DenyFindingSuppression` closes the scoring trap under [Metrics](#metrics-and-kpis): a member account
  "can update findings only for their own account". AWS documents this deny as an IAM policy, so validate it as an SCP (Lab 2,
  step 1) before attaching.[^sh-bu]

**2. A resource control policy that requires TLS.** A shortened version of AWS's sample "Restrict access to only HTTPS connections
to your resources" (the sample covers more services).[^rcp-ex] RCPs apply to a listed subset of services and not to resources in
the management account.[^rcp]

_Example status: JSON syntax-checked; not executed._

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "EnforceSecureTransport",
      "Effect": "Deny",
      "Principal": "*",
      "Action": ["s3:*", "sts:*", "kms:*", "sqs:*", "secretsmanager:*"],
      "Resource": "*",
      "Condition": {
        "BoolIfExists": {
          "aws:SecureTransport": "false"
        }
      }
    }
  ]
}
```

**3. The same intent on Google Cloud:** prevent public Cloud Storage access across the organisation. When enforced, "no one can make
data in applicable buckets public through IAM policies or ACLs"; existing public grants stay in place but are overridden.[^gcs-pap]

_Example status: YAML syntax-checked; not executed. Replace the organisation ID._

```yaml
name: organizations/123456789012/policies/storage.publicAccessPrevention
spec:
  rules:
    - enforce: true
```

Apply it with `gcloud org-policies set-policy policy.yaml`; the same command accepts a dry-run specification for testing
first.[^gcp-orgpol] On Azure, assign a `deny` policy for the same intent with `enforcementMode` set to `DoNotEnforce`, review
compliance, then switch to `Default`.[^az-assign]

**4. Validate before you attach.** IAM Access Analyzer validates SCPs and RCPs against grammar and best practice and returns
`ERROR`, `SECURITY_WARNING`, `WARNING` and `SUGGESTION` findings.[^aa-validate] Make an empty `ERROR` and `SECURITY_WARNING` list a
pipeline gate.

### Hands-on labs

Lab 1 runs anywhere with Python and was executed. Labs 2 and 3 need an AWS organisation and were **not executed**; their expected
outputs come from the cited documentation.

#### Lab 1 (beginner): find public storage exposure and expired exceptions

**Objective:** learn how layered public-access settings combine, why a "fixed value" condition matters, and why exceptions need
expiry dates. **Prerequisites:** Python 3.8 or later; no cloud account, cost or risk; 40 minutes.

The script is a teaching approximation of the Amazon S3 rules for what counts as "public":[^s3-bpa] a statement that allows
`Principal: "*"` is public unless a listed condition key has a fixed value, and an `aws:SourceIp` range broader than /8 still counts
as public. It treats negated operators (`StringNotEquals`, `NotIpAddress`) as not narrowing, and ignores `aws:userid`, access
points and the `ForAnyValue`/`ForAllValues` set operators. Use IAM Access Analyzer or S3's own policy status for real decisions.

1. Save the three files below as `exposure_check.py`, `inventory.json` and `exceptions.json`.
2. Run `python3 exposure_check.py inventory.json exceptions.json 2026-10-16`, then `echo $?`.
3. In `inventory.json`, set the account's `IgnorePublicAcls` to `false` and run again.
4. Restore it, set `"org_bpa": {"enforced": true}` and run again. Ask what happens to `marketing-site`, which serves a website.

_Example status: verified by execution (Python 3.8.10, 2026-10-16; re-run after the r1 change, 2026-10-17); outputs below are
verbatim._

```python
"""Teaching approximation of S3 public-exposure evaluation (not a replacement
for IAM Access Analyzer or S3's own policy-status check)."""
import ipaddress
import json
import sys
from datetime import date

FIXED_KEYS = {  # condition keys that can make a policy non-public (AWS S3 docs)
    "aws:principalorgid", "aws:sourceip", "aws:sourcearn", "aws:sourcevpc",
    "aws:sourcevpce", "aws:sourceowner", "aws:sourceaccount",
    "s3:dataaccesspointarn", "s3:dataaccesspointaccount",
}
BPA = ["BlockPublicAcls", "IgnorePublicAcls", "BlockPublicPolicy",
       "RestrictPublicBuckets"]
PUBLIC_GROUPS = ("AllUsers", "AuthenticatedUsers")


def effective_bpa(org, account, bucket):
    """Most restrictive combination: a setting is on if any level turns it on."""
    org_all = org.get("enforced", False)  # org level is all-or-nothing
    return {k: org_all or account.get(k, False) or bucket.get(k, False) for k in BPA}


def is_fixed(value):
    values = value if isinstance(value, list) else [value]
    return all(isinstance(v, str) and not any(c in v for c in "*?$") for v in values)


RFC1918 = [ipaddress.ip_network(n) for n in
           ("10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16")]


def too_broad(cidrs):
    """Broader than /8 (IPv4) or /32 (IPv6), RFC 1918 ranges excepted."""
    cidrs = cidrs if isinstance(cidrs, list) else [cidrs]
    for c in cidrs:
        net = ipaddress.ip_network(c, strict=False)
        if net.version == 4 and any(net.subnet_of(p) for p in RFC1918):
            continue
        if net.prefixlen < (8 if net.version == 4 else 32):
            return True
    return False


def statement_is_public(stmt):
    if stmt.get("Effect") != "Allow":
        return False
    principal = stmt.get("Principal")
    if isinstance(principal, dict):
        principal = principal.get("AWS")
    principals = principal if isinstance(principal, list) else [principal]
    if "*" not in principals:
        return False
    for op_name, operator in stmt.get("Condition", {}).items():
        if "Not" in op_name:  # negated operators never narrow a grant
            continue
        for key, value in operator.items():
            if key.lower() not in FIXED_KEYS or not is_fixed(value):
                continue
            if key.lower() == "aws:sourceip" and too_broad(value):
                continue
            return False  # one fixed-value key narrows the grant
    return True


def assess(bucket, org, account):
    bpa = effective_bpa(org, account, bucket.get("bpa", {}))
    stmts = bucket.get("policy", {}).get("Statement", [])
    policy_public = any(statement_is_public(s) for s in stmts)
    acl_public = any(g in PUBLIC_GROUPS for g in bucket.get("acl_grantees", []))
    exposed = []
    if policy_public and not bpa["RestrictPublicBuckets"]:
        exposed.append("bucket policy")
    if acl_public and not bpa["IgnorePublicAcls"]:
        exposed.append("ACL")
    return exposed, bpa


def main(inventory_path, exceptions_path, today):
    inv = json.load(open(inventory_path))
    exceptions = {e["bucket"]: e for e in json.load(open(exceptions_path))}
    failures = 0
    for b in inv["buckets"]:
        exposed, _ = assess(b, inv.get("org_bpa", {}), inv.get("account_bpa", {}))
        exc = exceptions.get(b["name"])
        if not exposed:
            status = "OK"
        elif exc and date.fromisoformat(exc["expires"]) >= today:
            status = f"PUBLIC, excepted to {exc['expires']} ({exc['owner']})"
        elif exc:
            status = f"PUBLIC, exception EXPIRED {exc['expires']}"
            failures += 1
        else:
            status = "PUBLIC, no exception"
            failures += 1
        via = f" via {', '.join(exposed)}" if exposed else ""
        print(f"{b['name']:<18} {status}{via}")
    print(f"summary: {len(inv['buckets'])} buckets, {failures} need action")
    return 1 if failures else 0


if __name__ == "__main__":
    as_of = date.fromisoformat(sys.argv[3]) if len(sys.argv) > 3 else date.today()
    sys.exit(main(sys.argv[1], sys.argv[2], as_of))
```

```json
{
  "org_bpa": {"enforced": false},
  "account_bpa": {"BlockPublicAcls": true, "IgnorePublicAcls": true},
  "buckets": [
    {"name": "app-logs-prod",
     "policy": {"Statement": [{"Effect": "Allow",
       "Principal": {"Service": "logging.s3.amazonaws.com"},
       "Action": "s3:PutObject", "Resource": "*"}]}},
    {"name": "partner-drop",
     "policy": {"Statement": [{"Effect": "Allow", "Principal": "*",
       "Action": "s3:GetObject", "Resource": "*",
       "Condition": {"StringEquals": {"aws:PrincipalOrgID": "o-exampleorg1"}}}]}},
    {"name": "marketing-site",
     "policy": {"Statement": [{"Effect": "Allow", "Principal": "*",
       "Action": "s3:GetObject", "Resource": "*"}]}},
    {"name": "legacy-exports",
     "acl_grantees": ["AllUsers"],
     "policy": {"Statement": [{"Effect": "Allow", "Principal": {"AWS": "*"},
       "Action": "s3:GetObject", "Resource": "*",
       "Condition": {"IpAddress": {"aws:SourceIp": "0.0.0.0/1"}}}]}},
    {"name": "data-science-tmp",
     "bpa": {"RestrictPublicBuckets": true},
     "policy": {"Statement": [{"Effect": "Allow", "Principal": "*",
       "Action": "s3:GetObject", "Resource": "*"}]}}
  ]
}
```

```json
[
  {"bucket": "marketing-site", "owner": "web-team", "reason": "static site",
   "expires": "2026-12-31"},
  {"bucket": "legacy-exports", "owner": "finance-it", "reason": "vendor migration",
   "expires": "2026-09-30"}
]
```

**Expected output (step 2), exit code 1:**

```text
app-logs-prod      OK
partner-drop       OK
marketing-site     PUBLIC, excepted to 2026-12-31 (web-team) via bucket policy
legacy-exports     PUBLIC, exception EXPIRED 2026-09-30 via bucket policy
data-science-tmp   OK
summary: 5 buckets, 1 need action
```

**Validation.** Step 3 changes one line to `legacy-exports     PUBLIC, exception EXPIRED 2026-09-30 via bucket policy, ACL`
(exit code 1): with `IgnorePublicAcls` off, the public ACL counts again. Step 4 prints `OK` for all five buckets and
`summary: 5 buckets, 0 need action` (exit code 0), because the organisation level applies all four settings together.[^s3-bpa]
**What to notice:** `partner-drop` is not public because its condition uses a fixed `aws:PrincipalOrgID`; `legacy-exports` is
public because `0.0.0.0/1` is broader than /8; `data-science-tmp` relies on a bucket setting that its editors could remove. In
step 4 the website stops serving the public, the trade-off examined in
[A judgement call](#a-judgement-call-block-public-storage-across-the-organisation-now-or-fix-first).
**Troubleshooting:** `JSONDecodeError` usually means a trailing comma; `AttributeError: … subnet_of` means Python older than 3.7.
**Teardown:** delete the three files.

#### Lab 2 (intermediate): stage two guardrails in a test OU (AWS)

**Objective:** release the reference SCP and RCP to one test OU and prove they work.[^orgs-cli] **Prerequisites:**

- an AWS organisation with all features;
- a pipeline role limited to Organizations policy actions, or a member account made a delegated administrator for policies, not
  management-account administrator credentials;[^orgs-deleg]
- a test OU with one sandbox account holding a test role, an account-level trail named `sandbox-trail` and a bucket with one
  throw-away object.

**Cost and risk:** negligible cost; a wrong attachment can lock users out, so attach only to the test OU.[^scp] Time: 60 minutes.

_Example status: syntax-checked; not executed (no AWS organisation available). IDs such as `r-exam` and `ou-exam-test1234` are
placeholders._

```bash
# 1. Validate both policies (expect no ERROR or SECURITY_WARNING findings).
aws accessanalyzer validate-policy --policy-type SERVICE_CONTROL_POLICY \
  --policy-document file://scp-protect.json
aws accessanalyzer validate-policy --policy-type RESOURCE_CONTROL_POLICY \
  --policy-document file://rcp-tls.json

# 2. Enable RCPs once for the organisation root.
aws organizations enable-policy-type --root-id r-exam \
  --policy-type RESOURCE_CONTROL_POLICY

# 3. Create the policies and attach them to the test OU only.
SCP_ID=$(aws organizations create-policy --type SERVICE_CONTROL_POLICY \
  --name protect-security-telemetry --description "Lab 2 SCP" \
  --content file://scp-protect.json --query Policy.PolicySummary.Id --output text)
RCP_ID=$(aws organizations create-policy --type RESOURCE_CONTROL_POLICY \
  --name require-tls --description "Lab 2 RCP" \
  --content file://rcp-tls.json --query Policy.PolicySummary.Id --output text)
aws organizations attach-policy --policy-id "$SCP_ID" --target-id ou-exam-test1234
aws organizations attach-policy --policy-id "$RCP_ID" --target-id ou-exam-test1234

# 4. From the sandbox account's test role: both calls should be denied.
#    The second sends a signed request over plain HTTP on purpose; use the
#    bucket's own Region in the endpoint URL.
aws cloudtrail stop-logging --name sandbox-trail
aws s3api get-object --bucket example-lab-bucket --key test.txt out.txt \
  --endpoint-url http://s3.eu-west-1.amazonaws.com

# 5. Confirm the trail is still logging and HTTPS access still works.
aws cloudtrail get-trail-status --name sandbox-trail --query IsLogging
aws s3api get-object --bucket example-lab-bucket --key test.txt out.txt
```

**Expected output:** step 1 returns an empty or suggestion-only `findings` list; step 4 returns two access-denied errors (the first
should name an explicit deny in a service control policy); step 5 prints `true` and downloads the object. **Validation:** the denied
calls appear in the sandbox account's event history with an access-denied error code. **Troubleshooting:** if step 4 succeeds,
check that the account really sits in the test OU and that the role has neither exempt name; if step 3 fails with a
policy-type error, step 2 has not completed. **Teardown:** `aws organizations detach-policy` for both IDs, then
`aws organizations delete-policy`; disable the RCP type if nothing else uses it.

#### Lab 3 (advanced): detect logging tampering and test the alert

**Objective:** alert within minutes when anyone stops or changes a trail, and measure the time to alert. **Prerequisites:** a
sandbox account with an active trail, permission to manage EventBridge and SNS, and a way to run `cloudtrail stop-logging` there
(the break-glass role, or detach the Lab 2 SCP first). **Cost:** negligible. Time: 45 minutes.

To receive these events in EventBridge "you must enable a CloudTrail trail with logging"; they reach the account's default event
bus, and rules in the default `ENABLED` state match write management events.[^eb-ct] ATT&CK v20 files this behaviour as T1685.002,
Disable or Modify Cloud Log.[^attack-log]

_Example status: JSON syntax-checked; not executed._

```json
{
  "source": ["aws.cloudtrail"],
  "detail-type": ["AWS API Call via CloudTrail"],
  "detail": {
    "eventSource": ["cloudtrail.amazonaws.com"],
    "eventName": ["StopLogging", "DeleteTrail", "UpdateTrail", "PutEventSelectors"]
  }
}
```

Save the pattern above as `pattern.json` and the topic policy below as `topic-policy.json`, with your Region and account ID. AWS
documents this EventBridge statement without a condition, so keep the topic for this rule alone.[^eb-res]

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublishEventsToMyTopic",
      "Effect": "Allow",
      "Principal": {"Service": "events.amazonaws.com"},
      "Action": "sns:Publish",
      "Resource": "arn:aws:sns:eu-west-1:111122223333:cloud-log-tamper"
    }
  ]
}
```

```bash
TOPIC_ARN=$(aws sns create-topic --name cloud-log-tamper --query TopicArn --output text)
aws sns set-topic-attributes --topic-arn "$TOPIC_ARN" --attribute-name Policy \
  --attribute-value file://topic-policy.json
aws sns subscribe --topic-arn "$TOPIC_ARN" --protocol email \
  --notification-endpoint secops-oncall@example.com
aws events put-rule --name cloud-log-tamper --event-pattern file://pattern.json
aws events put-targets --rule cloud-log-tamper --targets "Id=sns,Arn=$TOPIC_ARN"

# Game day: note the time, stop logging, wait for the e-mail, restart logging.
date -u +%H:%M:%S
aws cloudtrail stop-logging --name sandbox-trail
aws cloudtrail start-logging --name sandbox-trail
```

**Expected output:** an SNS e-mail containing `"eventName": "StopLogging"` and the caller's ARN. **Validation:** record the minutes
between the `date` output and the e-mail; `get-trail-status` shows `IsLogging` `true` after the restart. **Troubleshooting:** no
e-mail usually means an unconfirmed subscription, a missing topic policy, or a rule created in a different Region from the call.
**The lesson:** if the stopped trail were the account's only trail, later calls might never reach EventBridge; the organisation
trail and GuardDuty, whose consumption your CloudTrail settings do not affect, cover that gap.[^guardduty] **Teardown:**
`aws events remove-targets --rule cloud-log-tamper --ids sns`, `aws events delete-rule --name cloud-log-tamper`,
`aws sns delete-topic --topic-arn "$TOPIC_ARN"`.

### Use cases and scenarios

For the sourced cases, the lessons drawn are this page's mapping, not the investigators' findings.

| Scenario | Context | CloudSecOps response | Success measure |
|---|---|---|---|
| **1. Over-permissioned role (sourced)** | Capital One, 2019: "The intrusion occurred through a misconfigured web application firewall that enabled access to the data" (DOJ).[^doj-c1] Krebs reported request forgery against the metadata service and a role "assigned too many permissions".[^krebs] | IMDSv2 by default through an EC2 declarative policy;[^ec2-pol] roles scoped to named buckets; data events on sensitive buckets[^ct-data] | Instances requiring IMDSv2; sensitive buckets logged |
| **2. Valid stolen credentials (sourced)** | Snowflake customers, 2024: no MFA, credentials "had not been rotated or updated", no network allow lists.[^mandiant] A SaaS platform, but the failure, identity at the tenant API, transfers to any provider ([IdentityOps](../identityops/#use-cases-and-scenarios) teaches the case as its Case 3) | MFA and federation for people; long-lived keys replaced; access from trusted networks only; alerts on abnormal access | Long-lived credentials trending to zero |
| **3. Missing logs (sourced)** | Exchange Online, 2023: the detecting agency relied on a premium-licence log ([SOC Ops](../soc-ops/#use-cases-and-scenarios) teaches the detection); the CSRB asked for default logs "without additional charges, with a minimum default retention of six months".[^csrb] | Inventory default logs per provider and licence; enable what investigations need *before* an incident | Logging coverage matches the written standard |
| **4. A 40-account scale-up turns on CSPM (illustrative)** | Thousands of findings on day one; teams ignore the dashboard | Exposure findings first; organisation-level blocks where no legitimate use exists; owners and SLAs for the rest | Critical dwell time on target within a quarter |

## Measure, govern, pay

### Metrics and KPIs

| KPI | Definition and formula | Data source | Pitfall | Cadence |
|---|---|---|---|---|
| Guardrail coverage | Accounts under the current guardrail set ÷ active accounts | Organisation policy attachments | Management account always excluded; report it separately | Monthly |
| Logging coverage | (Account, enabled Region) pairs whose control-plane events reach the organisation archive ÷ all pairs | Trail and export status | Opt-in Regions enabled later are missed | Weekly |
| Detection coverage | Pairs with threat detection on ÷ all pairs | Detector status | Detectors off in newly enabled Regions | Weekly |
| Critical exposure dwell time | Median of (closed − first seen) for public-exposure and admin-credential findings | Posture tool timestamps | Counts closed findings only, so leaving the hardest open improves it; closure by suppression | Weekly |
| Open critical finding age | p50 and p90 of (now − first seen) for open critical findings | Posture tool | Report it beside dwell time, never instead | Weekly |
| Exception debt | Open exceptions past expiry ÷ open exceptions | Exception register | Silent renewals; count renewals too | Weekly |
| Long-lived credentials | Active user access keys + service-account keys | IAM reports, Access Analyzer | Counting keys, not their privilege | Monthly |
| Detection test pass rate | Control-plane tests that alerted within target ÷ tests run | Game-day log | Testing only easy detections | Quarterly |
| Tool control pass rate | Passed ÷ (passed + failed + unknown) controls | Posture tool | Suppressing failed findings raises it | Monthly, as context only |

The last row is a trap. AWS defines its security score as "the proportion of **Passed** controls to enabled controls"
and warns that "if you suppress all failed findings for a control, its status becomes **Passed**".[^sh-score] A score is a lagging,
gameable view of an automated subset; dwell time, open-finding age and exception debt show whether the operating model works.

::: example Illustrative scenario: a monthly calculation
An estate has 48 active member accounts; 45 carry guardrail set v3, so **guardrail coverage = 45 ÷ 48 = 93.8%**. It has 48 accounts
× 4 enabled Regions = 192 pairs. The organisation trail covers all of them (**logging coverage 100%**), but threat detection is off
in one Region of 6 accounts: **detection coverage = (192 − 6) ÷ 192 = 96.9%**. Nine public-exposure findings closed this month
after 2, 3, 5, 6, 8, 20, 30, 70 and 140 hours: the **median dwell time is the fifth value, 8 hours** (the mean, 31.6 hours, is
pulled up by two slow fixes, so report both). Three critical findings are still open, aged 10, 50 and 400 hours: **open age p50 =
50 hours, p90 = 400 hours** (nearest rank), which the closed-only median hides. Of 25 open exceptions, 4 are past expiry:
**exception debt = 16%**. Suggested targets: coverage 100%, median dwell under 24 hours, no open critical finding older than a
week, exception debt 0%.
:::

### Quality, security, privacy and compliance

The main quality risk is self-inflicted: a deny at the wrong level locks out a team or a pipeline, which is why AWS "strongly
recommends" testing SCPs before attaching them to the root.[^scp] Regulation is jurisdictional: BOD 25-01 binds only US federal
civilian agencies.[^bod2501] Audit logs contain personal data such as user names and IP addresses, so set their retention with
[PrivacyOps](../privacyops/) and their evidence use with [ComplianceOps](../complianceops/). Accepting a residual risk is its
owner's decision ([RiskOps](../riskops/)), not the finder's. This page is not legal advice.

### Cost, capacity and sustainability

Logging and posture costs shape design (as of October 2026):

- CloudTrail delivers one copy of management events without a CloudTrail charge (storage is billed); scope the extra-cost data
  events to sensitive data stores.[^ct-intro][^ct-data]
- Azure charges no ingestion for activity logs in Log Analytics, only retention beyond 90 days.[^az-activity] Google leaves Data
  Access logs off "because they can generate large volumes of data".[^gcp-audit]
- Security Hub CSPM charges per check and finding after a 30-day trial; Access Analyzer charges for unused and internal access
  analysis; Defender's foundational CSPM and SCC Standard cost nothing extra.[^sh-intro][^iaa][^ms-dfc][^gcp-scc]

Price logging by the investigations it enables: a missing log cannot be bought during an incident. Cost allocation belongs to
[FinOps](../finops/).

### Assurance of the operating model

Assure the guardrails themselves: run the Lab 3 game day quarterly for each critical detection; diff attached organisation
policies against Git weekly; enable a new benchmark version before disabling the old one, as AWS advises.[^sh-cis] **Risk
indicators:** denials that teams work around by moving accounts between OUs, exceptions renewed rather than closed, findings
closed without a rescan, detectors off in newly enabled Regions, and growing counts of long-lived keys.

## When things go wrong

### Common failures and troubleshooting

| Symptom | Likely cause | Diagnosis | Remediation | Prevention |
|---|---|---|---|---|
| A team is locked out after a policy change | Deny attached too high, missing exception | Access-denied events naming a service control policy | Detach or narrow; add exception | Staged rollout by OU |
| Dashboard green, auditor finds gaps | Tool covers an older benchmark or only automated checks | Compare tool version with CIS list[^sh-cis] | Add manual checks | Record benchmark version |
| Findings never close | No owner, or fix made in console and reverted by IaC | Finding reopens after deploy | Fix in code | Route by owner tag |
| Alert expected, none arrived | Trail stopped, rule in wrong Region, detector off in that Region | Trail status, rule Region, detector list | Restore; extend coverage | Logging-coverage KPI |
| A public bucket reappears | Bucket-level block removed by someone with bucket rights[^s3-bpa] | Event history for the setting change | Account or organisation block | Organisation-level block |
| Investigation lacks data | Data events or Data Access logs never enabled | Logging standard vs reality | Enable now; accept the historical gap | Logging standard reviewed with responders |

### Worked runbook: logging stopped and an unknown access key is active

**Trigger:** the Lab 3 rule reports `StopLogging` on a production account trail, and threat detection reports API calls from an
access key in a country where the team does not work. **Severity:** high until scoped. Incident command follows
[IncidentOps](../incidentops/); evidence and eradication depth follow [ResponseOps](../responseops/).

::: figure Containment sequence for a control-plane alert
```mermaid
sequenceDiagram
  participant S as SOC lead
  participant E as On-call engineer
  participant P as Cloud APIs and log archive
  Note over E: Alert: StopLogging and odd key use
  E->>P: Query activity since key creation
  P-->>E: Calls and resources touched
  E->>S: Declare incident with scope
  E->>P: Disable key, deny user, revoke sessions
  P-->>E: Confirmed
  E->>P: Remove persistence, restart logging
  E->>S: Hand over evidence and timeline
```
Text equivalent: On the alert, the on-call engineer queries the log archive for the key's activity since it was created, gets
back the calls and resources touched, and declares a scoped incident to the SOC lead. Through the cloud APIs the engineer disables
the key, denies all actions to the user and revokes role sessions (confirmed), removes persistence, restarts logging, and hands
evidence and a timeline to the SOC lead.
:::

1. **Confirm and preserve.** Check that the organisation trail is still logging (member accounts cannot stop it).[^ct-org] Do not
   delete anything; snapshot evidence before changing resources.
2. **Scope.** In the log archive, list every call made with the access key ID, every role it assumed, and every `GetSessionToken`
   or `GetFederationToken` call: those return temporary keys (IDs starting `ASIA`) that keep working after the long-term key is
   disabled, so follow their key IDs too.[^aws-ids] Note created users, keys, roles, trust-policy changes and data reads.
3. **Contain.** Deactivate the key (`aws iam update-access-key --user-name <user> --access-key-id <key-id> --status Inactive`),
   then attach the AWS managed policy `AWSDenyAll` to the user. Temporary credentials "are valid until they expire" (up to 36
   hours), and AWS says "you must also change permissions for the IAM user or role" to stop them; sessions the user created are
   evaluated against the user's own policies, so the deny ends them. Root-user sessions cannot be stopped this way, one more
   reason for IAM.4.[^temp-disable] For roles the key assumed, revoke sessions: AWS attaches a deny on `aws:TokenIssueTime`,
   which does **not** stop sessions assumed afterwards, so first remove the attacker's ability to re-assume the role.[^revoke]
4. **Eradicate.** Delete persistence found in step 2; check for new resources in Regions you do not use.
5. **Recover and verify.** Restart the trail, confirm `IsLogging` is `true`, rerun the posture scan, watch for the key ID for 30 days.
6. **Learn.** Why did a long-lived key exist, and why could its holder stop logging? Usually both lead to a guardrail (the
   reference SCP) and a credential-hygiene backlog item.

### Anti-patterns and myths

- **"The provider secures the cloud for us."** The provider secures its infrastructure; configurations, identities and data stay
  with you in every model.[^az-srm]
- **Dashboard-driven security.** Chasing a score invites suppression, which raises it.[^sh-score] Manage dwell time and exceptions.
- **Deny everything at the root on day one.** It locks out teams and produces exception sprawl; stage by OU.[^scp-ex]
- **Exempting a role name without protecting it.** Anyone who can create a role with that name inherits the bypass.
- **"Key rotation protects our data."** AWS states that rotation "has no effect on the data that the KMS key protects" and "will
  not mitigate the effect of a compromised data key"; KMS keys are mostly wrapping keys "almost never reused enough to risk key
  exhaustion".[^kms-rot] Rotate where rules require it, but spend effort on who can *use* the key.
- **"GuardDuty keeps our logs."** It extracts fields "and then discards these logs"; investigations need your own archive.[^guardduty]

## Deciding and adopting

### When to use and when not to use

**Use a deliberate CloudSecOps practice when** several hold: more than a handful of accounts or subscriptions; production or
regulated data in the cloud; many teams with API access; a security team that cannot review every change; auditors who expect
continuous control evidence.

**Do not build a separate function when** the estate is one or two accounts run by one small team. For small organisations
Microsoft's guidance is to "Combine cloud platform engineering and security responsibilities into a single function";[^caf-roles]
free native posture tiers and an organisation-wide log are enough to start. **Do not buy a CNAPP first** if nobody owns findings:
it will produce a backlog without a workflow. **Hidden responsibilities:** maintaining exceptions, benchmark upgrades, detector
coverage in new Regions, and the on-call rota for control-plane alerts.

### Decision framework

Most decisions in CloudSecOps are about where a control should live. The tree below is this Atlas's proposal.

::: figure Choosing preventive or detective placement for a proposed control
```mermaid
flowchart TB
  Q1{"Could this action cause severe harm in any account?"}
  Q1 -->|"no"| D1["Detective check, routed to owner"]
  Q1 -->|"yes"| Q2{"Any legitimate use in some accounts?"}
  Q2 -->|"yes"| Q3{"Can those accounts sit in their own OU?"}
  Q2 -->|"no"| P1["Preventive at organisation root, staged"]
  Q3 -->|"yes"| P2["Preventive on other OUs; detective there"]
  Q3 -->|"no"| Q4{"Can a condition express the exception?"}
  Q4 -->|"no"| D2["Detective with fast SLA and auto-alert"]
  Q4 -->|"yes"| P3["Preventive with tested condition"]
  D1 --> R["Review quarterly: promote or retire"]
  D2 --> R
  classDef decision fill:#fdf3e3,stroke:#c9800f,color:#122029
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef neutral fill:#f1f4f6,stroke:#5b7a87,color:#122029
  class Q1,Q2,Q3,Q4 decision
  class P1,P2,P3 core
  class D1,D2,R neutral
```
Text equivalent: If the action cannot cause severe harm in any account, use a detective check routed to the owner. If it can and
has no legitimate use, make it preventive at the organisation root, staged. If some accounts need it and can sit in their own OU,
prevent it elsewhere and detect it there. Otherwise prevent with a tested policy condition if one can express the exception, or
detect with a fast service-level target and automatic alerts. Detective controls are reviewed quarterly and promoted or retired.
:::

**Assumptions:** preventive controls can be tested in a non-production OU, and the people a condition constrains cannot set it
themselves; if a tag drives the exception, protect the tag or it becomes a bypass.

### Maturity model

This maturity model is **this Atlas's proposal**, not an industry standard.

| Level | Observable behaviour | Exit criteria |
|---|---|---|
| 0 Ad hoc | Per-account defaults; findings seen only after incidents | Organisation-wide audit log and posture scan enabled |
| 1 Visible | Findings exist but few have owners | Every critical finding has an owner within a day |
| 2 Baselined | Written baseline, guardrails in audit mode or a test OU, exception register | Benchmark version recorded; exceptions all have expiry dates |
| 3 Enforced | Preventive guardrails by OU, logging and detection coverage complete | Coverage 100%; median critical dwell under target for a quarter |
| 4 Engineered | Guardrails as tested code; detections game-dayed; metrics change the baseline | Each critical detection passes its quarterly test |

::: figure Maturity progression and the usual regressions
```mermaid
flowchart TB
  L0["0 Ad hoc"] -->|"turn on org logging and scans"| L1["1 Visible"]
  L1 -->|"owners and SLAs"| L2["2 Baselined"]
  L2 -->|"enforce by OU"| L3["3 Enforced"]
  L3 -->|"test guardrails and detections"| L4["4 Engineered"]
  L3 -.->|"exceptions renewed, not closed"| L2
  L2 -.->|"triage stops, backlog grows"| L1
  classDef core fill:#e8f3f2,stroke:#16756f,color:#122029
  classDef risk fill:#fbeceb,stroke:#b4332b,color:#122029
  class L0,L1,L2,L3,L4 core
```
Text equivalent: The five levels of the table in order, each step labelled with its main action (organisation logging and scans,
owners and SLAs, enforcement by OU, tested guardrails and detections). Two dotted arrows show regressions: level 3 falls back to 2
when exceptions are renewed rather than closed, and level 2 to 1 when triage stops and the backlog grows.
:::

### Adoption roadmap: 30/60/90 days

| Window | Actions | Milestone | Metric |
|---|---|---|---|
| Days 1–30 | Organisation-wide logs into a locked archive; posture scans and threat detection in all enabled Regions; one benchmark version; long-lived key inventory | Level 1 | Logging and detection coverage measured |
| Days 31–60 | Owners and SLAs for critical findings; exception register with expiry; reference SCP and storage block in a test OU; Lab 3 detection live | Level 2 | Median critical dwell time measured |
| Days 61–90 | Enforce the first preventive guardrails OU by OU; quarterly scorecard to leadership; first game day | Start of level 3 | Guardrail coverage; exception debt |

**Quick wins:** storage blocks on OUs with no public buckets; no root access keys; threat detection in unused Regions.
**Dependency:** an IaC pipeline for organisation policies ([InfraOps](../infraops/)).

## Toolkit

### Checklists

**Quick start**
- [ ] Organisation-wide control-plane logging into an account workload admins cannot change
- [ ] Posture scanning and threat detection in every enabled Region
- [ ] One benchmark version chosen and recorded
- [ ] Critical-finding routing to owners, with a service-level target

**Guardrail preflight**
- [ ] Policy validated (no `ERROR` or `SECURITY_WARNING` findings)
- [ ] Exempt principals listed and protected; management account and service-linked roles noted as not covered
- [ ] Attached to a test OU for at least one week with access-denied events reviewed
- [ ] Rollback is a one-line detach in the pipeline

**Logging readiness**
- [ ] Data-plane logs enabled for sensitive data stores
- [ ] Identity-provider sign-in and directory logs exported beyond their default retention
- [ ] Retention set with privacy and compliance owners
- [ ] A test event found in the archive in under one hour

**Quarterly review**
- [ ] Each critical detection passed its game day
- [ ] Exceptions past expiry: zero
- [ ] Benchmark and tool versions re-checked

### Templates

**Guardrail record** (one per guardrail, kept next to the policy code):

```yaml
id: GR-007
intent: Member accounts cannot disable, narrow or suppress audit, posture or detection
type: preventive (second layer)
mechanism: AWS SCP protect-security-telemetry (incl. DenyFindingSuppression)
preconditions: [GuardDuty and Security Hub CSPM managed via Organizations]
scope: all OUs except Sandbox (detective there)
not_covered: [management account, service-linked roles, Macie, Inspector, detection rules]
exempt_principals: [role/security-breakglass, role/org-baseline-deployer]
owner: cloud-security
test: Lab 3 game day, quarterly
exceptions: none
last_reviewed: 2026-10-16
```

**Exception request:**

```text
Exception ID:        EX-2026-031
Guardrail:           GR-012 (no public object storage)
Resource / account:  marketing-site / web-prod
Reason:              Static website; CDN migration planned
Compensating control: Bucket holds public assets only; data events logged
Risk owner:          Head of Web (accepts the residual risk)
Expires:             2026-12-31 (max 90 days; renewal needs a new review)
```

**Quarterly scorecard:** guardrail, logging and detection coverage, median and mean critical dwell time, open critical finding
age, exception debt, long-lived credential count, detection test pass rate, benchmark version assessed, top three risks and
decisions needed.

### Deep comparisons

| Approach | Strength | Weakness | Choose when |
|---|---|---|---|
| Native tools per provider | Free or cheap tiers; deep integration | Partial cross-provider coverage; attack paths in paid tiers | One or two main providers |
| Multicloud CNAPP | One workflow and risk model across providers | Licence cost; vendor consolidation risk | Three or more providers, small team |
| Open source (Prowler, Cloud Custodian) | Transparent checks; runs in your pipeline | You maintain and tune it | Strong engineering team; need for custom checks |

### FAQ

**Is CloudSecOps a job title or a team?** Both appear; the label is not standardised. What matters is that someone owns
guardrails, posture, logging and cloud detections.

**Do we need a CNAPP?** Not to start: the free tiers and trial under [Cost](#cost-capacity-and-sustainability) are enough to
begin. Attack-path analysis no longer needs one: AWS
Security Hub, the paid Defender CSPM plan and SCC Premium offer it.[^sh-v2][^ms-dfc][^gcp-scc] A CNAPP earns its cost through one
workflow across several providers.

**Should every CIS check be enforced?** No. Your baseline chooses among the recommendations and records exceptions.

**Does SaaS belong here?** It is a close neighbour: CISA's BOD 25-01 sets baselines for Microsoft 365 tenants.[^bod2501] This
page covers infrastructure and platform providers, but the guardrail-and-evidence pattern carries over.

**What if the provider itself is compromised?** You cannot prevent it, but your logs, keys you control and detections on your own
tenancy are what let you see it. The CSRB report describes a customer whose own logs first revealed an intrusion that turned out to
stem from a provider key compromise.[^csrb]

## Going further

### Advanced topics and emerging direction

- **Data perimeters:** combining SCPs, RCPs and network conditions so only trusted identities reach trusted resources.[^rcp-ex]
- **Configuration that survives new APIs:** AWS declarative policies are enforced "in the service's control plane", so the baseline
  holds "even when the service introduces new features or APIs".[^declarative]
- **AI workloads:** AI posture management in Defender for Cloud and an AI domain in the Microsoft benchmark v2 preview are early
  signs.[^ms-dfc][^mcsb]
- **Provider logging norms:** the CSRB asked cloud providers for default audit logs without extra charge, kept at least six
  months.[^csrb] Microsoft raised Purview Audit (Standard) retention from 90 to 180 days for records from 17 October 2023;[^purview]
  this page found no cross-provider standard for the wider ask (as of October 2026).
- **Shifting vocabulary:** ATT&CK v20 moved cloud log tampering to T1685.002 under a "Defense Impairment" tactic;[^attack-log]
  update detection mappings that use the old ID.

### Learning path

- **Prerequisites:** [CloudOps](../cloudops/) for hierarchy and accounts; one provider's IAM evaluation logic ([IAMOps](../iamops/)).
- **One day:** Lab 1; read your provider's SCP or Azure Policy documentation and its audit-log defaults.
- **One week:** Labs 2 and 3 in a sandbox organisation; run Prowler with `--compliance` against a test account and triage the
  output.[^prowler][^prowler-comp]
- **30 days:** write a baseline mapped to one CIS benchmark, an exception process and the scorecard; run one game day.
- **Specialise:** cluster security ([KubernetesSecOps](../kubernetessecops/)), detections ([DetectionOps](../detectionops/)),
  telemetry ([SecDataOps](../secdataops/)) or response ([ResponseOps](../responseops/)).
- **Portfolio:** a public repository with tested guardrail policies, a logging standard and a game-day report.

## Wrap-up

### Executive recap and cheat sheet

| Topic | Remember |
|---|---|
| What it is | Security enforced, measured and investigated at the provider API |
| Decide | Which few actions to deny everywhere; which logs to keep and for how long; who owns findings |
| Practices | Staged guardrails, organisation-wide logging, owner-routed triage, expiring exceptions, game days |
| Metrics | Guardrail, logging and detection coverage; critical dwell time with open-finding age; exception debt; long-lived credentials |
| Watch-outs | Management account and service-linked roles sit outside SCPs and RCPs; tools lag benchmarks; scores can be gamed |
| Next action | Run Lab 1 on a real inventory export and count your expired exceptions |

### A judgement call: block public storage across the organisation now, or fix first?

**Situation (illustrative):** an AWS estate of 60 accounts has 14 publicly readable buckets. Nine hold website assets; five are
unexplained. The CISO asks for an organisation-level S3 block "today".

**Option A, enforce at the root now.** Organisation-level S3 policies apply all four block settings together and override account
settings.[^s3-org] Every unexplained exposure closes at once, but the nine website buckets stop serving too, and the pressure to
restore them can produce exemptions by moving accounts out of the OU, weakening every guardrail for those accounts.

**Option B, detect and fix first.** Triage all 14 within days, move websites behind a CDN with private origins, then enforce. The
estate stays exposed during the work, and "temporary" can become permanent.

**Assumptions that decide it:** whether the five unexplained buckets hold sensitive data (unknown until triaged), and whether OUs
can be split so website accounts sit apart. **A reasoned choice:** close the five unexplained exposures now with account- or
bucket-level blocks, enforce the organisation-level block today on every OU without a public bucket (Lab 1's step 4 shows the
effect), and give the website OU a dated plan to move behind a CDN, with a detective rule meanwhile. Option A looks safest, but
breaking production on day one is how guardrails lose their sponsors. **Uncertainty:** if an unexplained bucket holds personal
data, that account moves to Option A and to incident handling.

### Learning objectives and mastery check

You can now **explain** what CloudSecOps covers and where its label is unsettled; **design** a guardrail set with exceptions;
**operate** logging, posture triage and control-plane detections; and **evaluate** posture metrics and tools critically.

1. An SCP denies `cloudtrail:StopLogging`, yet a trail was stopped. Name two ways this can happen.

::: answer Show answer for question 1
The call came from the management account, which SCPs do not affect, or from a principal matching the exemption (for example, a
role created with the break-glass name in an account that does not protect it).[^scp] A service-linked role is also outside SCPs.
:::

2. Your posture score rose from 71% to 90% in a week with no deployments. What do you check first?

::: answer Show answer for question 2
Suppressions and archived findings. AWS notes that suppressing all failed findings for a control makes it "Passed".[^sh-score]
Also check whether a standard was disabled or a Region removed from aggregation.
:::

3. Why does `BlockPublicPolicy` alone not close an existing public bucket?

::: answer Show answer for question 3
It rejects new public bucket policies but "doesn't affect existing" ones. `RestrictPublicBuckets` limits access granted by an
existing public policy.[^s3-bpa] Lab 1's `data-science-tmp` shows the effect.
:::

4. During an investigation you need object reads from six months ago on AWS and Google Cloud. What do you probably lack, and why?

::: answer Show answer for question 4
Data-plane logs. CloudTrail data events are off by default and Google's Data Access logs are off by default except for
BigQuery.[^ct-data][^gcp-audit] Logs not collected cannot be recovered; the CSRB case shows the cost.[^csrb]
:::

5. A team revoked active sessions on a compromised role, yet the attacker is back an hour later. Why? And what else must you do
   when the stolen credential is an IAM user's access key?

::: answer Show answer for question 5
Revocation denies sessions issued before the revocation time; a session assumed afterwards is not affected.[^revoke] The attacker
still held whatever let them assume the role. Remove that source (a key, a trust-policy entry) first. For a user key, deactivation
leaves sessions from `GetSessionToken` or `GetFederationToken` valid until they expire, up to 36 hours; attach a deny-all policy
to the user as well.[^temp-disable]
:::

6. Your tool reports full CIS compliance on AWS. What two caveats belong in the report?

::: answer Show answer for question 6
The benchmark version (Security Hub CSPM supports up to 5.0.0, while CIS lists 7.1.0) and the requirements the tool marks as manual
checks.[^sh-cis][^cis-aws]
:::

### Conclusion

CloudSecOps is a young name for an old problem in a new place. The name will keep shifting; trade press and a certification body
already use it differently. The place will not: every cloud resource, permission and log setting passes through a
provider API, and the cases that teach most (an over-permissioned role, valid stolen passwords, logs nobody switched on) were
failures of configuration, identity and evidence at that layer. This page's proposal is simple to state: enforce centrally a short
list of things that must never happen, let owners fix everything else in code against a clock, and test guardrails and detections
like applications. Start with logs you can trust; then make dangerous things impossible, one OU at a time.

## Sources and further reading

Claim-by-claim evidence: `research/cloudsecops-ledger.md`. Re-verify dated facts (versions, retention defaults, prices) quarterly or
when CIS, AWS, Microsoft or Google announce changes, and re-check neighbour anchors on each rebuild.

[^tt-cso]: [SecOps vs. CloudSecOps: What does a CloudSecOps team do?](https://www.techtarget.com/searchsecurity/tip/SecOps-vs-CloudSecOps-What-does-a-CloudSecOps-team-do) — Dave Shackleford, TechTarget, 2022-07-14. Checked 2026-10-16.
[^dck]: [CloudSecOps: When SecOps Meets Cloud Security](https://www.datacenterknowledge.com/cloud/cloudsecops-when-secops-meets-cloud-security) — Gilad David Maayan, Data Center Knowledge, 2022-02-16. Checked 2026-10-16.
[^isc2]: [CCSP Certification Exam Outline](https://www.isc2.org/certifications/ccsp/ccsp-certification-exam-outline) — ISC2, page dated 2026-08-01. Checked 2026-10-16.
[^csa-cnapp]: [What is a Cloud Native Application Protection Platform (CNAPP)?](https://cloudsecurityalliance.org/articles/what-is-a-cloud-native-application-protection-platform-cnapp) — Josh Dreyfuss (Wiz), Cloud Security Alliance blog, 2021-10-25. Checked 2026-10-16.
[^ms-dfc]: [Microsoft Defender for Cloud Overview](https://learn.microsoft.com/en-us/azure/defender-for-cloud/defender-for-cloud-introduction) — Microsoft Learn, updated 2026-08-10. Checked 2026-10-16.
[^wiz-close]: [Google Completes $32 Billion Acquisition of Wiz](https://www.clearygottlieb.com/news-and-insights/news-listing/google-completes-32-billion-acquisition-of-wiz) — Cleary Gottlieb, 2026-03-11. Checked 2026-10-16.
[^gcp-scc]: [Security Command Center service tiers](https://docs.cloud.google.com/security-command-center/docs/service-tiers) — Google Cloud, updated 2026-10-15. Checked 2026-10-16.
[^az-srm]: [Shared responsibility in the cloud](https://learn.microsoft.com/en-us/azure/security/fundamentals/shared-responsibility) — Microsoft Learn, updated 2026-08-24. Checked 2026-10-16.
[^gcp-sbd]: [Managing secure-by-default organization resources](https://docs.cloud.google.com/resource-manager/docs/secure-by-default-organizations) — Google Cloud, updated 2026-10-13. Checked 2026-10-16.
[^gcs-pap]: [Public access prevention](https://docs.cloud.google.com/storage/docs/public-access-prevention) — Google Cloud. Checked 2026-10-16.
[^gcp-orgpol]: [Creating and managing organization policies](https://docs.cloud.google.com/resource-manager/docs/organization-policy/using-constraints) — Google Cloud; see also [gcloud org-policies set-policy](https://docs.cloud.google.com/sdk/gcloud/reference/org-policies/set-policy). Checked 2026-10-16.
[^imds]: [Use the Instance Metadata Service to access instance metadata](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html) — AWS. Checked 2026-10-16.
[^scp]: [Service control policies (SCPs)](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html) — AWS Organizations. Checked 2026-10-16.
[^rcp]: [Resource control policies (RCPs)](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_rcps.html) — AWS Organizations. Checked 2026-10-16.
[^rcp-ex]: [Resource control policy examples](https://github.com/aws-samples/resource-control-policy-examples) and [Data perimeter policy examples](https://github.com/aws-samples/data-perimeter-policy-examples) — AWS samples on GitHub. Checked 2026-10-16.
[^scp-ex]: [Service control policy examples](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps_examples.html) — AWS Organizations, with the linked [sample repository](https://github.com/aws-samples/service-control-policy-examples). Checked 2026-10-16.
[^declarative]: [Declarative policies in AWS Organizations](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_declarative.html) — AWS. Checked 2026-10-16.
[^ec2-pol]: [EC2 policies](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_ec2.html) — AWS Organizations. Checked 2026-10-16.
[^orgs-cli]: [aws organizations create-policy](https://docs.aws.amazon.com/cli/latest/reference/organizations/create-policy.html) — AWS CLI Command Reference. Checked 2026-10-16.
[^orgs-deleg]: [Delegated administrator for AWS Organizations](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_delegate_policies.html) — AWS Organizations. Checked 2026-10-17.
[^aa-validate]: [aws accessanalyzer validate-policy](https://docs.aws.amazon.com/cli/latest/reference/accessanalyzer/validate-policy.html) — AWS CLI Command Reference. Checked 2026-10-16.
[^s3-bpa]: [Blocking public access to your Amazon S3 storage](https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html) — AWS. Checked 2026-10-16.
[^s3-org]: [Amazon S3 policies](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_s3.html) — AWS Organizations. Checked 2026-10-16.
[^az-effects]: [Azure Policy definitions effect basics](https://learn.microsoft.com/en-us/azure/governance/policy/concepts/effect-basics) — Microsoft Learn, updated 2025-12-01. Checked 2026-10-16.
[^az-assign]: [Details of the policy assignment structure](https://learn.microsoft.com/en-us/azure/governance/policy/concepts/assignment-structure) — Microsoft Learn, updated 2026-09-18. Checked 2026-10-16.
[^cis-aws]: [CIS Amazon Web Services Benchmarks](https://www.cisecurity.org/benchmark/amazon_web_services) — Center for Internet Security. Checked 2026-10-16.
[^cis-azure]: [CIS Microsoft Azure Benchmarks](https://www.cisecurity.org/benchmark/azure) — Center for Internet Security. Checked 2026-10-16.
[^cis-gcp]: [CIS Google Cloud Computing Platform Benchmarks](https://www.cisecurity.org/benchmark/google_cloud_computing_platform) — Center for Internet Security. Checked 2026-10-16.
[^sh-cis]: [CIS AWS Foundations Benchmark in Security Hub CSPM](https://docs.aws.amazon.com/securityhub/latest/userguide/cis-aws-foundations-benchmark.html) — AWS. Checked 2026-10-16.
[^sh-intro]: [Introduction to AWS Security Hub CSPM](https://docs.aws.amazon.com/securityhub/latest/userguide/what-is-securityhub.html) — AWS. Checked 2026-10-16.
[^sh-score]: [Calculating security scores](https://docs.aws.amazon.com/securityhub/latest/userguide/standards-security-score.html) — AWS Security Hub CSPM. Checked 2026-10-16.
[^sh-controls]: [Security Hub CSPM controls reference](https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-controls-reference.html) — AWS. Checked 2026-10-17.
[^sh-azure]: [Integrating Security Hub CSPM with Microsoft Azure](https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-azure.html) — AWS. Checked 2026-10-17.
[^sh-v2]: [What are Security Hub and Security Hub CSPM?](https://docs.aws.amazon.com/securityhub/latest/userguide/what-are-securityhub-services.html) and [Introduction to AWS Security Hub](https://docs.aws.amazon.com/securityhub/latest/userguide/what-is-securityhub-v2.html) — AWS. Checked 2026-10-17.
[^sh-bu]: [Using BatchUpdateFindings to update a finding](https://docs.aws.amazon.com/securityhub/latest/userguide/finding-update-batchupdatefindings.html) and [BatchUpdateFindings API](https://docs.aws.amazon.com/securityhub/1.0/APIReference/API_BatchUpdateFindings.html) — AWS Security Hub CSPM. Checked 2026-10-17.
[^sh-central]: [Understanding central configuration in Security Hub CSPM](https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html) — AWS. Checked 2026-10-17.
[^mcsb]: [Microsoft cloud security benchmark introduction](https://learn.microsoft.com/en-us/security/benchmark/azure/introduction) — Microsoft Learn, updated 2026-02-03. Checked 2026-10-16.
[^csa-ccm]: [Cloud Controls Matrix](https://cloudsecurityalliance.org/research/cloud-controls-matrix) — Cloud Security Alliance (version 4.1). Checked 2026-10-16.
[^csa-threats]: [Cloud Security Alliance Releases Top Threats to Cloud Computing 2024 Report](https://cloudsecurityalliance.org/press-releases/2024/08/06/cloud-security-alliance-releases-top-threats-to-cloud-computing-2024-report) — CSA, 2024-08-06. Checked 2026-10-16.
[^cisa-csi]: [CISA and NSA Release Cybersecurity Information Sheets on Cloud Security Best Practices](https://www.cisa.gov/news-events/alerts/2024/03/07/cisa-and-nsa-release-cybersecurity-information-sheets-cloud-security-best-practices) — CISA, 2024-03-07. Checked 2026-10-16.
[^bod2501]: [BOD 25-01: Implementing Secure Practices for Cloud Services](https://www.cisa.gov/news-events/directives/bod-25-01-implementing-secure-practices-cloud-services) — CISA, 2024-12-17. Checked 2026-10-16.
[^attack-cloud]: [Cloud Matrix](https://attack.mitre.org/matrices/enterprise/cloud/) — MITRE ATT&CK, content v20. Checked 2026-10-16.
[^attack-log]: [Disable or Modify Tools: Disable or Modify Cloud Log, T1685.002](https://attack.mitre.org/techniques/T1685/002/) — MITRE ATT&CK, modified 2026-05-12. Checked 2026-10-16.
[^caf-roles]: [Security Teams, Roles, and Functions](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/secure/teams-roles) — Microsoft Cloud Adoption Framework, updated 2026-10-01. Checked 2026-10-16.
[^ct-intro]: [What Is AWS CloudTrail?](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-user-guide.html) — AWS. Checked 2026-10-16.
[^ct-signin]: [AWS Management Console sign-in events](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-aws-console-sign-in-events.html) — AWS CloudTrail. Checked 2026-10-17.
[^ct-data]: [Logging data events](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/logging-data-events-with-cloudtrail.html) — AWS CloudTrail. Checked 2026-10-16.
[^ct-org]: [Creating a trail for an organization](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/creating-trail-organization.html) — AWS CloudTrail. Checked 2026-10-16.
[^eb-ct]: [AWS service events delivered via AWS CloudTrail](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-service-event-cloudtrail.html) — Amazon EventBridge. Checked 2026-10-16.
[^eb-res]: [Using resource-based policies for Amazon EventBridge](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-use-resource-based.html) — Amazon EventBridge. Checked 2026-10-17.
[^guardduty]: [GuardDuty foundational data sources](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_data-sources.html) — AWS. Checked 2026-10-16.
[^gd-lists]: [Working with trusted lists and threat lists](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_upload-lists.html) — Amazon GuardDuty User Guide. Checked 2026-10-17.
[^gcp-audit]: [Cloud Audit Logs overview](https://docs.cloud.google.com/logging/docs/audit) — Google Cloud, updated 2026-10-16. Checked 2026-10-16.
[^az-activity]: [Activity Log in Azure Monitor](https://learn.microsoft.com/en-us/azure/azure-monitor/platform/activity-log) — Microsoft Learn, updated 2026-10-15. Checked 2026-10-16.
[^entra-ret]: [Microsoft Entra data retention](https://learn.microsoft.com/en-us/entra/identity/monitoring-health/reference-reports-data-retention) — Microsoft Learn, updated 2026-03-25. Checked 2026-10-17.
[^purview]: [Learn about auditing solutions in Microsoft Purview](https://learn.microsoft.com/en-us/purview/audit-solutions-overview) — Microsoft Learn, updated 2026-07-08. Checked 2026-10-17.
[^gws-audit]: [Audit logs for Google Workspace](https://docs.cloud.google.com/logging/docs/audit/configure-gsuite-audit-logs) — Google Cloud, updated 2026-10-16. Checked 2026-10-17.
[^iaa]: [Using AWS IAM Access Analyzer](https://docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html) — AWS. Checked 2026-10-16.
[^revoke]: [Revoke IAM role temporary security credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_revoke-sessions.html) — AWS. Checked 2026-10-16.
[^temp-disable]: [Disabling permissions for temporary security credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_disable-perms.html) and [Permissions for GetFederationToken](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_getfederationtoken.html) — AWS IAM User Guide. Checked 2026-10-17.
[^aws-ids]: [IAM identifiers](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html) — AWS IAM User Guide. Checked 2026-10-17.
[^root-central]: [Centralize root access for member accounts](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-enable-root-access.html) — AWS IAM User Guide. Checked 2026-10-17.
[^kms-rot]: [Rotate AWS KMS keys](https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html) — AWS. Checked 2026-10-16.
[^kms-concepts]: [AWS KMS keys](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html) — AWS KMS Developer Guide. Checked 2026-10-17.
[^kms-policy]: [Default key policy](https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-default.html) — AWS KMS Developer Guide. Checked 2026-10-17.
[^kms-delete]: [Delete an AWS KMS key](https://docs.aws.amazon.com/kms/latest/developerguide/deleting-keys.html) — AWS KMS Developer Guide. Checked 2026-10-17.
[^kms-ct]: [Logging AWS KMS API calls with AWS CloudTrail](https://docs.aws.amazon.com/kms/latest/developerguide/logging-using-cloudtrail.html) — AWS KMS Developer Guide. Checked 2026-10-17.
[^kv-soft]: [Azure Key Vault soft-delete overview](https://learn.microsoft.com/en-us/azure/key-vault/general/soft-delete-overview) — Microsoft Learn, updated 2026-06-12. Checked 2026-10-17.
[^gcp-kms-destroy]: [Destroy and restore key versions](https://docs.cloud.google.com/kms/docs/destroy-restore) — Google Cloud, updated 2026-10-13. Checked 2026-10-17.
[^doj-c1]: [United States v. Paige Thompson](https://www.justice.gov/usao-wdwa/united-states-v-paige-thompson) — US Attorney's Office, Western District of Washington, updated 2022-01-13. Checked 2026-10-16.
[^krebs]: [What We Can Learn from the Capital One Hack](https://krebsonsecurity.com/2019/08/what-we-can-learn-from-the-capital-one-hack/) — Brian Krebs, Krebs on Security, 2019-08-02. Checked 2026-10-16.
[^mandiant]: [UNC5537 Targets Snowflake Customer Instances for Data Theft and Extortion](https://cloud.google.com/blog/topics/threat-intelligence/unc5537-snowflake-data-theft-extortion) — Mandiant, Google Cloud blog, 2024-06-10. Checked 2026-10-16.
[^csrb]: [Review of the Summer 2023 Microsoft Exchange Online Intrusion](https://www.cisa.gov/sites/default/files/2025-03/CSRBReviewOfTheSummer2023MEOIntrusion508.pdf) — Cyber Safety Review Board, report dated 2024-03-20. Checked 2026-10-16.
[^prowler]: [prowler-cloud/prowler](https://github.com/prowler-cloud/prowler) — Prowler, GitHub. Checked 2026-10-16.
[^prowler-comp]: [Compliance](https://docs.prowler.com/user-guide/cli/tutorials/compliance) — Prowler documentation. Checked 2026-10-16.
[^prowler-out]: [Reporting in Prowler](https://docs.prowler.com/user-guide/cli/tutorials/reporting) — Prowler documentation. Checked 2026-10-17.
[^custodian]: [Cloud Custodian](https://www.cncf.io/projects/cloud-custodian/) — CNCF. Checked 2026-10-16.

**Changelog.** 2026-10-16, r0: first version; Lab 1 executed, Labs 2–3 not executed. 2026-10-17, r1: revised after independent
review `reviews/cloudsecops-r1.md` (item-by-item response in `reviews/cloudsecops-r1-response.md`): TechTarget definition,
stolen-key containment, telemetry SCP, identity-log defaults, keys and misconfiguration sections, Security Hub split; Lab 1 re-run.
2026-10-17, r1.1: score follow-ups S1, S2, S5, S6 (`reviews/cloudsecops-score-superseded-20261017T0519.md`): the
`DenyFindingSuppression` statement, IdentityOps link, decision-tree title, re-check cadence. 2026-10-17, post-score polish
(`reviews/cloudsecops-polish.md`): worked artefacts labelled AWS-first; SCP action names and condition key re-checked against AWS's
service reference; dense passages split into lists; decision tree narrowed; restatements trimmed.
