← Professional Cloud Security Engineer: controls and evidence
18 / 23 · 135 MIN

Governance: approvals, scope and evidence coverage

Interpret approval mechanisms, signing dependencies, migration, exceptions and evidence gaps in APS handover.

1. Turn a control claim into evidence

A fictional team is preparing a funds reconciliation service for operational acceptance. The project presents a configured cloud folder, a change approval and a dashboard without violations. APS must decide whether those items support the claims made in the handover. This case does not describe internal BNP Paribas procedures. It also does not establish a legal obligation: exercise requirements are defined for learning and must be replaced by the approved requirements of a real project. Start by writing the claim precisely: which resource, which control, which revision is being assessed and over what period. Add the type of evidence required. If the requirement says a control operated from 09:00 to 11:00, a screenshot at 11:00 does not automatically establish the previous two hours. A decision allowing a change can exist while technical observation of that period remains incomplete. Create a matrix containing requirement, object, environment, revision, window, observation, limitation and owner. The owner need not personally produce every record, but should know who can obtain it and what it demonstrates. In a committee meeting, the matrix supports an explicit decision about gaps. Avoid a green cell that combines intended configuration, authorization and observed outcome. The objective is to let another person reconstruct the conclusion from the same records without guessing omitted assumptions or silently expanding the claim.

2. Interpret the approval mechanism actually used

An ordinary request requiring manual approval remains unapproved while it is pending. Check that context before interpreting silence as consent. In request history, policy-approved identifies a policy decision and auto-approved identifies the documented urgent-access mechanism. Neither label alone shows that someone at the customer individually reviewed the occurrence. Recording only approved loses information that can matter in a later review. Transparency mode permits observation without the additional approval intended in the manual flow. Removing local settings can also make inherited configuration effective. In the fictional case, the project documented manual review but the operator found another effective mode. Begin by comparing intent and configuration, identifying the source of the difference and assessing its effect on the requirement. Adding email addresses to the notification form does not resolve that behavioral mismatch. Prepare an occurrence table containing identifier, mechanism, resource, justification, window and available evidence. If the team uses a mode that streamlines support, describe that choice and its limits in the approved design. If the requirement changes, retain the decision and the date from which it applies. Do not relabel earlier events as manual to make them match the newest document. Communication should explain which assurance is demonstrated in each period and where the effective state still requires confirmation. Assign that confirmation to an owner with access to the relevant configuration evidence.

3. Trace the signing identity and key version

The team chooses a custom signing key for Access Approval requests. An operator can inspect the key and perform a test under their own identity, but the service reports a signing error. Investigation should follow the Access Approval service account and configured key version, including permission to sign. Operator success is not evidence of that account's access. A signature protects request integrity; it does not turn the request contents into a sound business decision. For the knowledge-transfer exercise, draw the dependency chain: approval configuration, key version, identity using it and team responsible for its lifecycle. Add who receives an out-of-hours error and where they find diagnostic evidence. A cryptographic dependency without an operational owner can delay support while the business service is already degraded. Define a change check comparing intended configuration with observed identity and the expected result. Do not use real keys or data in the exercise. A documentary simulation can reveal an incomplete plan but does not establish that IAM is correct in the environment. Before proposing a permission change, identify the resource and missing action. Avoid resolving an identity problem by granting broad privileges to everyone on the team. In the handover, preserve the boundary between a local rehearsal and the actual environment verification still required from the platform owner.

4. Correlate authorization and observed activity

A report says an object was read at 14:10, but the only attached reference is a request approved until 15:00. Approval establishes an opportunity within the applicable scope, not the time of every operation. Access Transparency provides information about Google personnel actions; that evidence serves a different purpose from an authorization decision. Customer activity records may also be needed to reconstruct an incident. Always check the services and scope actually covered by the consulted sources. To practice analysis, create three fictional rows: request received, decision recorded and action observed. Keep their identifiers distinct. Ask a colleague to reconstruct the sequence without treating the ticket title as proof of a read. If an event cannot be correlated, mark the connection as unproven. Do not invent an event from the authorization window or conclude that access was improper merely because the supplied export is incomplete. In APS work, this distinction matters when several teams ask for quick conclusions about a provider intervention. Explain what the available records establish and which source is needed next. If the relevant period is missing, request evidence from its owner and retain the limitation in the report. Pressure to close an incident does not justify converting a plausible hypothesis into a factual statement. A timeline with visible gaps supports decisions better than an apparently complete sequence assembled from unsupported inference.

5. Assess migrations without expanding report assurances

The fictional service will move to another folder and the team runs analyzeWorkloadMove. The result helps assess compatibility with the destination but does not mean the move has occurred. Policy analysis considers constraints relevant to the destination control package; it does not demonstrate comparison of every project policy. Another significant limit is that relaxing restrictions to permit an unsupported service does not automatically include it in background checks. No violations can mean no coverage. Prepare the plan around three stages: advance analysis, authorized execution and observation of the resulting state. For each stage identify inputs, decision and output evidence. For a real migration, the owner must determine which policies, services and resources need additional checks. In the exercise, make no cloud changes; write down the information missing before recommending progression to the next stage. A concrete example is an auxiliary file service used by the funds application only at month end. Daily inventory might not highlight it, but its use remains relevant. If the service is outside package coverage, record the dependency and request its own assessment. A silent dashboard does not remove the need to address the requirement. In executive reporting, separate what was analyzed, what changed and what remains unproven. This distinction helps estimate effort and assign work without claiming an assurance the tool did not provide or treating a proposed move as completed work.

6. Review exceptions when context changes

A violation previously accepted as an exception returns to unresolved after another noncompliant change. The team should not restore the old classification as a routine action. The state can reopen when new conditions arise. Nor should applying an exception to existing child-resource violations be interpreted as unlimited approval of future violations. Compare scope, assumptions and justification. The same owner remaining on the project does not make the situations technically equivalent. In an environment already admitted to the frameworks feature, distinguish Monitor from Monitor and prevent. The first does not establish prevention. Detection can take time after deployment, so an empty dashboard minutes later does not prove complete assessment. The consulted documentation requires allowlist admission for this feature; the exercise does not assume availability in every project or turn an approximate delay into a detection SLA. In the practical case, an exception allowed a particular configuration during migration. A new release adds a resource and changes the control revision. Prepare a committee decision containing observed differences, impact and options: remediate, obtain more evidence or request an exception decision for the new scope. Keep technical state explicit even if management temporarily accepts the risk. The record should show which condition was assessed and which event requires another review. Do not use ticket date alone as evidence of continuing validity across later changes.

7. Guided exercise: coverage of declared intervals

Save the code as run.py and run python3 run.py locally with Python 3.13. The program uses fictional data. Each requirement identifies resource, control, revision and interval. Each observation declares the period it covers; it is not a point sample automatically extended over time. Intervals include the start and exclude the end. Two adjacent intervals can therefore cover a window without a gap, but favorable endpoints do not authorize filling the space between them. Predict middle-gap before reading the output. Observations cover 0–5 and 15–20; uncoveredIntervals should retain 5–15. Compare adjacent-spans with overlap: both can cover the window, but overlap does not duplicate time. Then replace the observation with approval or exception. The program retains the record and preserves the technical gap. Add a failure within a favorable interval: coverageComplete can be true while supportedUnderDeclaredEvidence remains false. Complete temporal coverage does not resolve contradictory evidence. Change an observation's revision or resource and explain why it cannot satisfy the original requirement. Finally mark inventory incomplete. The result should retain that uncertainty even when listed requirements have sufficient observations. Submit your interpretation with what must be collected, who can supply it and which decision depends on it. The exercise compares declarations; it does not authenticate records, evaluate real controls or decide legal compliance. To analyze two successive revisions, explicitly divide the work into coherent windows.

8. Prepare acceptance another team can maintain

Bring together the reconciliation service results. Approval mode was confirmed, the key dependency has an owner and move analysis was documented. One hour still lacks suitable evidence for control C7. The acceptance recommendation must preserve that gap. Management may need to decide about timing and risk, but the technical conclusion should not change merely because the delivery date has arrived. Practice a short English update: identify the required window, demonstrated periods and next action. An appropriate answer says that the intervening interval remains unobserved and assigns evidence collection to an owner. An inadequate answer says that change approval makes every check satisfied. Add the next review time and the criterion that would allow the conclusion to change. This practice supports both a project committee and an international shift handover, where the next team must understand what remains to be established. The block maps to objective 5.1 of the consulted PCSE guide, focusing on scope and the relationship between requirement, service and control. It does not cover the whole domain alone. The local exercise does not approve production or verify that the chosen requirement is legally sufficient. Learning evidence demonstrates reasoning about the presented cases. Actual acceptance needs environment evidence, agreed scope and a decision by the appropriate owners. Finish by stating what is demonstrated, what remains unknown and the specific action that reduces that uncertainty.

"""Offline coverage worksheet for declared synthetic intervals, not a compliance verifier."""
import copy
import hashlib
import itertools
import json
from pathlib import Path


def require(ok,message):
 if not ok: raise ValueError(message)


def text(value):
 return isinstance(value,str) and bool(value.strip)


def minute(value):
 return type(value) is int and value>=0


def scope(row):
 require(isinstance(row,dict),'record object')
 require(all(text(row.get(k))for k in ('resource','control','revision')),'scope fields')
 return tuple(row[k]for k in ('resource','control','revision'))


def interval(row,as_of):
 require(minute(row.get('start'))and minute(row.get('end')),'interval numbers')
 require(row['start']<row['end']<=as_of,'ordered nonfuture interval')
 return row['start'],row['end']


def gaps(start,end,spans):
 cursor=start;missing=[]
 for left,right in sorted(spans):
 left=max(start,left);right=min(end,right)
 if right<=left:continue
 if left>cursor:missing.append([cursor,left])
 cursor=max(cursor,right)
 if cursor<end:missing.append([cursor,end])
 return missing


def inspect(data):
 require(isinstance(data,dict)and minute(data.get('asOf')),'input and asOf')
 require(type(data.get('inventoryComplete'))is bool,'inventory flag')
 require(isinstance(data.get('requirements'),list)and data['requirements'],'requirements')
 require(isinstance(data.get('evidence'),list),'evidence')
 identifiers=set;pairs=set
 for req in data['requirements']:
 k=scope(req);interval(req,data['asOf'])
 require(k[:2]not in pairs,'one revision per resource and control');pairs.add(k[:2])
 for row in data['evidence']:
 scope(row);interval(row,data['asOf'])
 require(text(row.get('id'))and row['id']not in identifiers,'unique evidence id');identifiers.add(row['id'])
 require(row.get('kind')in('observation','approval','exception'),'evidence kind')
 require(row.get('outcome')in('pass','fail','unknown'),'evidence outcome')
 rows=[];used=set
 for req in sorted(data['requirements'],key=scope):
 key=scope(req);matching=[];ignored=[]
 for evidence in data['evidence']:
 if scope(evidence)!=key:
 if scope(evidence)[:2]==key[:2]:ignored.append({'id':evidence['id'],'reason':'different-revision'})
 continue
 if evidence['end']<=req['start']or evidence['start']>=req['end']:
 ignored.append({'id':evidence['id'],'reason':'outside-window'});continue
 matching.append(evidence);used.add(evidence['id'])
 positive=[r for r in matching if r['kind']=='observation'and r['outcome']=='pass']
 failed=sorted(r['id']for r in matching if r['kind']=='observation'and r['outcome']=='fail')
 unknown=sorted(r['id']for r in matching if r['kind']=='observation'and r['outcome']=='unknown')
 missing=gaps(req['start'],req['end'],[(r['start'],r['end'])for r in positive])
 rows.append(dict(resource=key[0],control=key[1],revision=key[2],window=[req['start'],req['end']],
 uncoveredIntervals=missing,positiveEvidenceIds=sorted(r['id']for r in positive),
 failedEvidenceIds=failed,unknownEvidenceIds=unknown,
 approvalIds=sorted(r['id']for r in matching if r['kind']=='approval'),
 exceptionIds=sorted(r['id']for r in matching if r['kind']=='exception'),
 ignored=sorted(ignored,key=lambda x:(x['id'],x['reason'])),
 coverageComplete=not missing,contradictoryOrUnknown=bool(failed or unknown)))
 return dict(requirements=rows,inventoryCoverageUnproven=not data['inventoryComplete'],
 unassessedEvidenceIds=sorted(identifiers-used),
 supportedUnderDeclaredEvidence=data['inventoryComplete']and all(r['coverageComplete']and not r['contradictoryOrUnknown']for r in rows),
 evidenceAuthenticityVerified=False,actualControlEvaluated=False,productionAcceptanceAuthorized=False)


def sample:
 return dict(asOf=30,inventoryComplete=True,
 requirements=[dict(resource='training/r1',control='C7',revision='v3',start=0,end=20)],
 evidence=[dict(id='e1',resource='training/r1',control='C7',revision='v3',start=0,end=20,kind='observation',outcome='pass')])


def checks:
 fixtures=[]
 def check(name,data,**expected):
 before=copy.deepcopy(data);actual=inspect(data);assert data==before
 row=actual['requirements'][0]
 for k,v in expected.items:assert row[k]==v,(name,k,row[k],v)
 fixtures.append(dict(id=name,**actual));return actual
 check('full-window',sample,uncoveredIntervals=[],coverageComplete=True)
 x=sample;x['evidence'][0]['end']=10;x['evidence'].append(dict(x['evidence'][0],id='e2',start=10,end=20));check('adjacent-spans',x,uncoveredIntervals=[])
 x=sample;x['evidence'][0]['end']=5;x['evidence'].append(dict(x['evidence'][0],id='e2',start=15,end=20));check('middle-gap',x,uncoveredIntervals=[[5,15]])
 x=sample;x['evidence'][0]['end']=15;x['evidence'].append(dict(x['evidence'][0],id='e2',start=10,end=20));check('overlap',x,uncoveredIntervals=[])
 x=sample;x['evidence'][0].update(start=20,end=25);check('outside-half-open-window',x,uncoveredIntervals=[[0,20]],ignored=[dict(id='e1',reason='outside-window')])
 x=sample;x['evidence'][0]['revision']='v2'check('different-revision',x,uncoveredIntervals=[[0,20]],ignored=[dict(id='e1',reason='different-revision')])
 x=sample;x['evidence'][0]['resource']='training/r2'a=check('different-resource',x,uncoveredIntervals=[[0,20]]);assert a['unassessedEvidenceIds']==['e1']
 x=sample;x['evidence'].append(dict(x['evidence'][0],id='e2',start=5,end=6,outcome='fail'));a=check('failure-inside-pass',x,coverageComplete=True,failedEvidenceIds=['e2']);assert not a['supportedUnderDeclaredEvidence']
 x=sample;x['evidence'].append(dict(x['evidence'][0],id='e2',outcome='unknown'));a=check('unknown-inside-pass',x,unknownEvidenceIds=['e2']);assert not a['supportedUnderDeclaredEvidence']
 x=sample;x['evidence'][0]['kind']='approval'check('approval-only',x,uncoveredIntervals=[[0,20]],approvalIds=['e1'])
 x=sample;x['evidence'][0]['kind']='exception'check('exception-only',x,uncoveredIntervals=[[0,20]],exceptionIds=['e1'])
 x=sample;x['inventoryComplete']=False;a=check('incomplete-inventory',x,coverageComplete=True);assert a['inventoryCoverageUnproven']and not a['supportedUnderDeclaredEvidence']
 x=sample;x['evidence']=[];check('no-evidence',x,uncoveredIntervals=[[0,20]])
 combinations=0
 for start,end,kind,outcome in itertools.product((0,5,10),(15,20),('observation','approval','exception'),('pass','fail','unknown')):
 x=sample;x['evidence'][0].update(start=start,end=end,kind=kind,outcome=outcome)
 actual=inspect(x);assert actual['supportedUnderDeclaredEvidence']==(start==0 and end==20 and kind=='observation'and outcome=='pass');combinations+=1
 x=sample;x['evidence']=[dict(x['evidence'][0],id='e'+str(i),start=i*5,end=(i+1)*5)for i in range(3)];reference=inspect(x);permutations=0
 for order in itertools.permutations(x['evidence']):
 y=copy.deepcopy(x);y['evidence']=list(order);assert inspect(y)==reference;permutations+=1
 invalid=[]
 for k,v in [('asOf',True),('asOf',-1),('inventoryComplete',1),('requirements',[]),('evidence',None)]:
 x=sample;x[k]=v;invalid.append(x)
 for k,v in [('resource',''),('control',''),('revision',''),('start',True),('start',20),('end',31)]:
 x=sample;x['requirements'][0][k]=v;invalid.append(x)
 for k,v in [('id',''),('kind','snapshot'),('outcome','green'),('start',1.5),('end',0),('resource',None),('revision','')]:
 x=sample;x['evidence'][0][k]=v;invalid.append(x)
 x=sample;x['evidence'].append(dict(x['evidence'][0]));invalid.append(x)
 x=sample;x['requirements'].append(dict(x['requirements'][0],revision='v2'));invalid.append(x)
 for x in invalid:
 try:inspect(x)
 except ValueError:pass
 else:raise AssertionError('invalid accepted')
 return dict(fixtures=fixtures,stateCombinations=combinations,inputPermutations=permutations,invalidInputs=len(invalid),
 inputPreserved=True,orderIndependent=True,network=False,cloudExecuted=False,persistentWrites=False,
 scriptSha256=hashlib.sha256(Path(__file__).read_bytes).hexdigest)


if __name__=='__main__':print(json.dumps(checks,ensure_ascii=False,indent=2))
IN PRACTICE

Two favorable observations leave an hour without evidence; a change approval does not establish technical state during that interval.

Common pitfalls

Counting policy as manual decision, confusing a signature with observed action, expanding monitoring coverage or interpolating success across a gap.

Related topics: Control ownership · Approval lifecycle · Handover and operational evidence

Take this idea with you

A control conclusion covers only its supported scope and period. Approvals, exceptions and observations must retain their distinct meaning.

Create account

Reference: Approving Access Approval requests · Current linked guide; edition date unconfirmed (2026-09-30 inspection)

Google Cloud is a trademark of Google LLC. bigsavant.com is an independent preparation platform and is not affiliated with, associated with, sponsored, authorised or endorsed by Google. Content and questions are original, are not official exam questions, and completing our tests does not award or guarantee any certification. Names are used only to identify the subject. All other trademarks belong to their respective owners.