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

Recovery decisions and boundaries

Analyze approvals, effective policies, location and exceptions without confusing technical capability with authorization or compliance.

Prepare a recovery decision that can be checked

An APS team at a fictional bank needs to extend operation in the recovery environment. The initial incident has ended, but some controls still need restoration, support access needs closure and a location restriction limits alternatives. This case does not describe internal BNP Paribas procedures. The manager’s task is to present a concrete decision: what can continue, under which conditions, for how long and with whom responsible for outstanding work. Build a matrix containing requirement, resource, revision, evidence and decision. If a test covers the earlier revision or only the primary environment, do not present it as validation of the recovery destination. If an exception is approved, preserve the control’s technical state separately from the decision to accept the gap temporarily. The word “approved” needs an object: it might describe a change, an access request, a risk or expenditure. Those acts are not automatically equivalent, and the record should identify which one actually occurred. In the guided exercise, the business wants position reconciliation to continue until day end. The technical team demonstrates application operation, but existing authorization ends earlier and covers another destination. Write two statements: observed technical capability and the outstanding decision. Define information the approver needs, including impact, alternatives and limits. Do not edit the ticket date to make the original decision appear to cover the new request. The result should let the next shift understand the commitment without relying on verbal explanations.

Close approvals without overlooking active paths

At the end of a support intervention, the operator finds two Access Approval requests for the same resource. One is pending and the other approved. Dismiss treats the pending request as ignored; it does not prevent access granted by the other request. Invalidate applies to the approved request and revokes access based on that request. If other active approvals exist, access can continue. Reporting should identify exactly which request changed and which paths still need analysis. The precondition matters too. Sending invalidate to a still-pending request returns FAILED_PRECONDITION. That error establishes neither revocation nor a state transition. Retain the response, read effective state and use the operation appropriate to the authorized objective. Do not turn an execution attempt into a successful result merely because the command appears in terminal history. Attach the affected request identifier to the outcome so another analyst can reproduce the scope of the review. For the exercise, draw three requests associated with the resource: one pending, one approved and one already invalid. The objective is to end access no longer needed. Ask the learner to identify each action’s effect and evidence still needed for the overall conclusion. A valid answer distinguishes inventory, decision and result. It should also avoid promising that this Access Approval review removes every possible access mechanism. The analysis covers access based on the approvals considered; other controls, service exceptions and identity paths need their own assessment when relevant to the intended conclusion.

Review key defaults and effective configuration

The team creates a default Key Access Justifications policy to standardize new keys. The manager receives a report marking the whole inventory updated. Default configuration does not apply to keys that already existed. Separate evidence of creation defaults from evidence for each key used by the recovered service. An older key protecting backups still needs review even when the current default is correct. Queries also have scope. Reading the policy set directly on a project can return empty when a default is inherited from a folder or organization. Querying effective policy permits analysis of applicable inheritance. An empty local result does not mean absence of policy throughout the hierarchy. Record which query was performed, against which resource and when. The information should distinguish “not configured here” from “no effective configuration.” Include the relevant ancestor reference when explaining an inherited result to the control owner. In the guided case, the environment has an earlier key, a new key following the default and another created with its own policy. Ask the learner for an evidence table covering all three. Do not infer one key’s policy from another’s, or use primary-version rotation as proof of policy alignment. The aim is to identify the rule governing each recovery operation, who owns correction of a difference and which test will demonstrate its effect. Before changing policy, assess legitimate operations that could stop working and prepare a decision explicitly including that impact.

Interpret justifications within their technical boundary

A record containing GOOGLE_INITIATED_SYSTEM_OPERATION does not necessarily establish an engineer’s manual read. The code can cover system operations serving the workload, including backups. To interpret the event, associate it with the resource, expected operation and investigation time. The code supplies context about key use; it is not, by itself, proof that backup completed or functional restoration succeeded. KAJ also has a control boundary: it governs the transition from data at rest to data in use. Denying new key requests must not be described as instantly removing every copy already decrypted in memory. If the incident objective requires addressing data already in use, identify relevant processes and additional mechanisms, then collect evidence about them. Do not replace that analysis with a universal claim about CMEK. The recovery record should make this boundary visible to both technical and decision-making readers. Capabilities depend on the control package and supported integrations. Transparency logs should not be confused with every approval or denial option being available in every configuration. When Access Approval uses a custom signing key, that key’s KAJ policy can also constrain processing of the signed request. In the exercise, ask the learner to draw the chain between decision, signing and cryptographic operation. Then ask which link was actually observed. Avoid concluding that human authorization makes a technical key denial irrelevant, or that a cryptographic event establishes every organizational decision needed to proceed.

Make a location conflict explicit

The fictional requirement in this case limits storage and processing to an authorized location. The team keeps backups there but proposes running recovery elsewhere. Meeting the at-rest condition does not establish the separate processing condition. Put both into the requirements matrix and identify components that store, process or enable access. These conditions are supplied by the exercise; they are not presented as universal legal rules. Another scenario authorizes only one region but requires rapid recovery from complete regional loss. If the design does not demonstrate that outcome, counting multiple zones within the same region does not resolve the contradiction. Present observed capability and alternatives to responsible owners: revise the design within permitted locations, revise requirements through an authorized process where possible or acknowledge the remaining untreated risk. Do not remove the restriction from reporting to manufacture an RTO promise. Keep the chosen failure scenario visible when comparing options and measured recovery results. Encryption can be a relevant mitigation in some designs, but does not automatically change an approved condition. The manager should obtain a supported decision on what is permissible before selecting recovery architecture. For the guided exercise, offer two alternatives: one respects location but recovers more slowly; the other is faster but outside authorized scope. Ask for a comparison of gaps, evidence and owners. The answer should neither silently select a violation nor promise that the slower alternative meets a deadline not yet demonstrated.

Include execution and usage dependencies

Recovery architecture depends on more than replicated data. If critical failover needs VM creation or IAM changes during a failure also affecting management APIs, the sequence might miss the agreed objective. Automating those calls reduces manual work but does not remove the services on which they depend. Analyze what can be prepared in advance and which operations remain on the critical path during the selected outage scenario. The same care applies to people’s access. The runbook should identify who can enter the recovery environment, perform steps and observe results when the usual path is affected. A rehearsal performed only by the automation author does not establish RUN autonomy. Include the people who will actually be on duty and record difficulties encountered. The plan needs concrete tasks and execution evidence without distributing credentials through project documents. Assign each unresolved dependency an owner and a next observation that would change its status. There is also a difference between technical capability and usage rights. A commercial application starting on a recovery VM does not prove agreed licensing covers that environment. Confirm conditions with the supplier and retain the decision relevant to the actual destination. In the exercise, the team has an approved functional test but has not confirmed licensing for the second environment. Ask the learner to retain both states in reporting. The test does not replace contractual confirmation, and licensing confirmation does not establish that the service can achieve RTO.

Apply the fictional exception contract

This lesson’s Python exercise uses a fictional internal policy. Each control has a resource, revision, observed state, exception eligibility, authorized approvers and required compensations. An exception identifies the same scope, a time window, approver, justification and compensation evidence. These fields are declarations; the program authenticates no signatures and interprets no legislation. The exercise rule allows a failed control to be accounted for through a valid exception while keeping technicallySatisfied false. An unknown control is not treated as a known and accepted failure: it remains blocked for investigation. A control marked nonwaivable cannot be bypassed by a ticket either. Exception validity requires coverage of the whole proposed window. Two partial decisions are not automatically combined because this exercise’s contract requires one decision covering the complete commitment. This is a deliberately stated teaching rule rather than an assertion about every organization’s risk process. In the guided case, the exception ends at noon and proposed operation runs until two. Resource and revision have also changed. Even with the same incident number, the fields do not match. Ask the learner to explain the difference between requesting a new decision and changing presentation of an old one. If a technically viable, already covered alternative exists, compare it. Otherwise the gap remains explicit. The program makes reasoning inspectable rather than replacing the people authorized to accept risk or approve recovery.

Run the analysis and present residual risk

Run python3 run.py locally without credentials. Before reading results, predict the effect of a valid exception, an unauthorized approver, unknown compensation and expiry before the window ends. Compare predictions with outcome, acceptedExceptionIds and reasons shown for each candidate. The exception-only state recognizes the declared decision without confusing it with control-met. Keep a short explanation of any prediction you revise after inspecting the output. Tests cover combinations of state, eligibility, authority, compensation and window, as well as order permutations and invalid inputs. The program preserves input. A satisfied control can remain satisfied without an exception; a second resource does not automatically inherit the first resource’s decision. If inventory is incomplete, reporting retains that limitation even when every listed control is accounted for. The hash identifies executed code rather than authenticity of supplied approvals. No network service is contacted and no live policy is changed. To finish, prepare a short committee note: requested commitment, satisfied controls, applicable exceptions, gaps, review deadline and owners. Explain which observation would close each outstanding item and who should decide a scope change. Do not present program output as legal compliance or production authorization. Solve the final case and explain why urgency, the earlier ticket and compensation alone do not satisfy all supplied conditions. Result quality depends on clarity of the decision and its limits rather than the number of green cells in a table.

Prepare an attempt that reveals learning gaps

Reserve an uninterrupted session and choose one of the three mocks. Each form contains 60 questions with a continuous 120-minute limit. Two minutes per question is only a time-management reference: a policy decision may be quick, while a case with several constraints needs more reading. These are original DR questions that also appear in this course’s lessons or cases. The forms share no questions with each other, but recognizing an answer from study can improve your score without showing that you can solve a new situation. Record that familiarity in your own analysis. Do not interpret a high-scoring repeat as a prediction of passing the official exam. Before selecting options, identify the actor, resource, credential and relevant time. During an access incident, revoking a key, disabling an account and ending a session are different actions. During restoration, recovering bytes, recovering a key and recovering access boundaries are separate criteria too. Keep the explicit requirement in mind and find the option supported by available evidence. For multiple-response cases, check how many choices are requested; a partly correct selection earns no partial credit on this platform. This is a local training rule, not a description of official scoring.

Solve a case without adding assumptions

Consider this original reasoning exercise outside the scored questions: after rollback, a service responds to an administrator’s test. The consumer-access report belongs to the previous revision and a snooze closed the alerts. The committee asks for a RUN handover decision. The administrator’s response establishes only that this identity performed that test at that moment. It does not establish customer isolation or notification availability. The old report remains useful historical evidence, but its applicability to the recovered revision is unproven. Alert closure through suppression is not a health measurement. A supported decision requests functional and access validation on the current revision, plus confirmation of the notification path after suppression ends. If the committee deadline arrives first, communicate the gap and decision owner; do not change the meaning of results to obtain a green status. Use the same method in the mocks. If two options look defensible, compare the condition each requires. One might depend on a grant the question never confirms; another might propose gathering that evidence before acting. Distinguish the best next action from a complete solution. When failure evidence exists, more documentation does not remove it. When evidence is missing, do not turn uncertainty into success or a universal denial. Flag the question if it needs another look and preserve time for the remaining decisions.

Turn the result into a practice plan

After finishing, read the explanation for the correct answer and each rejected alternative. For every error, write one sentence identifying the failed assumption: wrong execution identity, excessive scope, evidence from another revision, an omitted dependency or a misread operation state. Add the question’s domain and task, the reference to revisit and a concrete practice action. For example, if you confused endpoint acceptance with application delivery, draw the request path and identify where you would collect DNS, connection and functional-response evidence. If you confused approval with control execution, separate the authorized decision from the observation still needed. Return to the corresponding lesson and complete its local exercise where available. Those exercises use fictional data and bounded contracts; passing one does not prove that a Google Cloud service is configured correctly. Then explain, without consulting the answer, why your selected alternative fails and under what conditions it could be appropriate. Use another form to explore different gaps before repeating the first. Compare reasoning and error types, not just percentages. The three forms have the same approximate domain allocation, but this does not demonstrate equal psychometric difficulty or exhaustive subtopic coverage. DR accuracy has no official passing threshold, awards no certification and does not replace practical experience or independent specialist review.

"""Original offline exception worksheet using a fictional internal policy.

A permitted exception never changes a failed control into a satisfied control.
This program neither interprets law nor authenticates approvals or evidence.
"""
from copy import deepcopy
from itertools import product, permutations
from hashlib import sha256
from pathlib import Path
import json


def label(x):return isinstance(x,str) and bool(x.strip) and x==x.strip
def integer(x):return type(x)is int and x>=0
def names(x):return isinstance(x,list) and all(label(v)for v in x) and len(x)==len(set(x))


def validate(m):
 if not isinstance(m,dict)or set(m)!={'start','end','inventoryComplete','controls','exceptions'}:
 raise ValueError('Expected exact model fields')
 if not integer(m['start'])or not integer(m['end'])or m['start']>=m['end']or type(m['inventoryComplete'])is not bool:
 raise ValueError('Valid half-open window and inventory flag required')
 if not isinstance(m['controls'],list)or not m['controls']or not isinstance(m['exceptions'],list):
 raise ValueError('Nonempty controls and exception list required')
 controls={}
 for c in m['controls']:
 if not isinstance(c,dict)or set(c)!={'id','resource','revision','state','waivable','approvers','compensations'}:
 raise ValueError('Expected exact control fields')
 if not all(label(c[k])for k in ['id','resource','revision'])or c['id']in controls or c['state']not in ['pass','fail','unknown']or type(c['waivable'])is not bool or not names(c['approvers'])or not names(c['compensations']):
 raise ValueError('Invalid control')
 controls[c['id']]=c
 ids=set
 for e in m['exceptions']:
 if not isinstance(e,dict)or set(e)!={'id','control','resource','revision','start','end','approver','status','reason','compensations'}:
 raise ValueError('Expected exact exception fields')
 if not all(label(e[k])for k in ['id','control','resource','revision','approver','reason'])or e['id']in ids or e['control']not in controls:
 raise ValueError('Invalid exception identity')
 if not integer(e['start'])or not integer(e['end'])or e['start']>=e['end']or e['status']not in ['approved','revoked']:
 raise ValueError('Invalid exception interval or state')
 if not isinstance(e['compensations'],dict)or any(not label(k)or v not in ['pass','fail','unknown']for k,v in e['compensations'].items):
 raise ValueError('Invalid compensation evidence')
 ids.add(e['id'])
 return controls


def analyze(m):
 controls=validate(m);out=[]
 for id,c in sorted(controls.items):
 candidates=[]
 for e in sorted((e for e in m['exceptions']if e['control']==id),key=lambda e:e['id']):
 reasons=[]
 if not c['waivable']:reasons.append('nonwaivable')
 if e['resource']!=c['resource']:reasons.append('resource-mismatch')
 if e['revision']!=c['revision']:reasons.append('revision-mismatch')
 if e['start']>m['start']or e['end']<m['end']:reasons.append('window-not-covered')
 if e['approver']not in c['approvers']:reasons.append('unauthorized-approver')
 if e['status']!='approved':reasons.append('revoked')
 for required in c['compensations']:
 if e['compensations'].get(required)!='pass':reasons.append('compensation-unproven:'+required)
 candidates.append(dict(id=e['id'],reasons=sorted(reasons),fitsDeclaredExceptionContract=not reasons))
 accepted=sorted(e['id']for e in candidates if e['fitsDeclaredExceptionContract'])if c['state']=='fail'else[]
 outcome='control-met'if c['state']=='pass'else'exception-only'if accepted else'blocked'
 out.append(dict(control=id,resource=c['resource'],revision=c['revision'],observedState=c['state'],
 technicallySatisfied=c['state']=='pass',observationGap=c['state']=='unknown',
 acceptedExceptionIds=accepted,candidates=candidates,outcome=outcome))
 all_accounted=all(r['outcome']!='blocked'for r in out)
 return dict(controls=out,allTechnicallySatisfied=all(r['technicallySatisfied']for r in out),
 allAccountedForUnderDeclaredPolicy=all_accounted,
 inventoryCoverageUnproven=not m['inventoryComplete'],
 supportedUnderDeclaredContract=all_accounted and m['inventoryComplete'],
 approvalAuthenticityVerified=False,compensationEffectivenessVerified=False,
 legalComplianceEstablished=False,productionRecoveryAuthorized=False)


def model:
 return dict(start=100,end=200,inventoryComplete=True,
 controls=[dict(id='c1',resource='funds-dr',revision='v3',state='fail',waivable=True,approvers=['risk-owner'],compensations=['extra-observation'])],
 exceptions=[dict(id='ex1',control='c1',resource='funds-dr',revision='v3',start=100,end=200,approver='risk-owner',status='approved',reason='Fictional temporary recovery decision',compensations={'extra-observation':'pass'})])


def evidence:
 fixtures=[]
 def record(id,m):
 old=deepcopy(m);r=analyze(m);assert m==old;fixtures.append({'id':id,'result':r});return r
 m=model;m['controls'][0]['state']='pass'm['exceptions']=[];assert record('control-already-met',m)['allTechnicallySatisfied']
 m=model;m['exceptions']=[];assert not record('no-exception',m)['supportedUnderDeclaredContract']
 r=record('valid-bounded-exception',model);assert r['supportedUnderDeclaredContract']and not r['allTechnicallySatisfied']
 m=model;m['controls'][0]['state']='unknown'assert not record('unknown-not-waived',m)['supportedUnderDeclaredContract']
 m=model;m['controls'][0]['waivable']=False;assert not record('nonwaivable-control',m)['supportedUnderDeclaredContract']
 for field,value,id in [('resource','other-dr','other-resource'),('revision','v2','other-revision'),('approver','operator','wrong-approver'),('end',199,'expires-too-soon'),('start',101,'starts-too-late'),('status','revoked','revoked-decision')]:
 m=model;m['exceptions'][0][field]=value;assert not record(id,m)['supportedUnderDeclaredContract']
 m=model;m['exceptions'][0]['compensations']={};assert not record('missing-compensation',m)['supportedUnderDeclaredContract']
 m=model;m['exceptions'][0]['compensations']['extra-observation']='unknown'assert not record('unknown-compensation',m)['supportedUnderDeclaredContract']
 m=model;m['exceptions'][0]['end']=150;e=deepcopy(m['exceptions'][0]);e.update(id='ex2',start=150,end=200);m['exceptions'].append(e);assert not record('partial-decisions-not-combined',m)['supportedUnderDeclaredContract']
 m=model;m['inventoryComplete']=False;r=record('incomplete-inventory',m);assert r['allAccountedForUnderDeclaredPolicy']and not r['supportedUnderDeclaredContract']
 m=model;c=deepcopy(m['controls'][0]);c.update(id='c2',resource='payments-dr');m['controls'].append(c);r=record('independent-control-not-covered',m);assert r['controls'][0]['outcome']=='exception-only'and r['controls'][1]['outcome']=='blocked'
 combinations=0
 for state,waivable,authority,compensation,window in product(['pass','fail','unknown'],[False,True],[False,True],[False,True],[False,True]):
 m=model;m['controls'][0].update(state=state,waivable=waivable);e=m['exceptions'][0]
 if not authority:e['approver']='operator'
 if not compensation:e['compensations']['extra-observation']='fail'
 if not window:e['end']=199
 expected=state=='pass'or(state=='fail'and waivable and authority and compensation and window)
 assert analyze(m)['supportedUnderDeclaredContract']==expected;combinations+=1
 m=model
 for id in ['ex2','ex3']:
 e=deepcopy(m['exceptions'][0]);e['id']=id;e['approver']='operator'm['exceptions'].append(e)
 expected=analyze(m);orders=0
 for order in permutations(m['exceptions']):
 v=deepcopy(m);v['exceptions']=list(order);assert analyze(v)==expected;orders+=1
 bad=[]
 def altered(fn):
 m=model;fn(m);bad.append(m)
 altered(lambda m:m.update(start=True))
 altered(lambda m:m.update(end=100))
 altered(lambda m:m.update(inventoryComplete=1))
 altered(lambda m:m.update(controls=[]))
 altered(lambda m:m['controls'].append(deepcopy(m['controls'][0])))
 altered(lambda m:m['controls'][0].update(state='green'))
 altered(lambda m:m['controls'][0].update(waivable='yes'))
 altered(lambda m:m['controls'][0].update(approvers=['owner','owner']))
 altered(lambda m:m['controls'][0].update(compensations=['x','x']))
 altered(lambda m:m['exceptions'].append(deepcopy(m['exceptions'][0])))
 altered(lambda m:m['exceptions'][0].update(control='absent'))
 altered(lambda m:m['exceptions'][0].update(resource=[]))
 altered(lambda m:m['exceptions'][0].update(start=200))
 altered(lambda m:m['exceptions'][0].update(end=-1))
 altered(lambda m:m['exceptions'][0].update(status='pending'))
 altered(lambda m:m['exceptions'][0].update(reason=' '))
 altered(lambda m:m['exceptions'][0].update(compensations=[]))
 altered(lambda m:m['exceptions'][0].update(compensations={'x':'green'}))
 altered(lambda m:m['exceptions'][0].update(extra=True))
 altered(lambda m:m.update(extra=True))
 bad.extend([None,[]])
 for m in bad:
 try:analyze(m)
 except ValueError:pass
 else:raise AssertionError('Invalid input accepted')
 return dict(scriptSha256=sha256(Path(__file__).read_bytes).hexdigest,fixtures=fixtures,
 policyCombinations=combinations,inputPermutations=orders,invalidInputs=len(bad),
 inputPreserved=True,orderIndependent=True,cloudExecuted=False,network=False,persistentWrites=False)


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

The exception covers R1/v2 until noon, but recovery needs R2/v3 until two. The existing ticket does not automatically cover the new commitment.

Common pitfalls

Treating one invalidated request as universal revocation; applying defaults retroactively; confusing residency with processing; treating an exception as a satisfied control.

Related topics: Operational recovery and evidence for RUN · Recover data, keys and AI controls · Trust recovery and access rollback

Take this idea with you

Every decision needs its own scope and evidence; a valid exception keeps the temporarily accepted technical gap visible.

Create account

Reference: Method: projects.approvalRequests.dismiss · 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.