1. Build a release evidence chain
In a fictional APS scenario, the approved change refers to R84, but the operator sees only a file called service.zip. Its name is insufficient to connect the package to the tests presented. Record origin, selected run, artifact name, expected contents and acceptance criteria. Also distinguish manual resource selection from the decision to use it: the picker can offer a failed run. Investigate where failure occurred and which evidence remains valid. Do not assume a version used for default selection takes precedence when a completion trigger selects the resource. During preparation, ask the build owner and delivery owner to point to the same run. Resolve any discrepancy before starting the change. Another person should be able to reconstruct the decision from records without relying on the operator’s memory. Keep selection evidence with the change record.
2. Confirm presence, path and content before execution
The practical case contains plan.json but lacks R84 service.zip. Download ended without error because broad selection can find no files without failing. Explicitly confirm required files and investigate publication, names and filters. An old workspace ZIP does not close that gap. If preDeploy needs the plan, declare download in that hook; automatic injection belongs to deploy. In the exercise using the download shortcut and fundsBuild resource, service content is under Pipeline.Workspace/fundsBuild/service. Do not transfer that conclusion to every DownloadPipelineArtifact task mode: check its inputs and configured destination. At the source, review targetPath and.artifactignore location when published contents differ from expectations. Finish investigation by comparing actual contents with the release contract, including plan and binaries. A green exit code is only one part of the evidence; it does not replace the expected-file inventory.
3. Manage dependency availability and consumption
Publishing a feed version and promoting it to a view are separate actions. Define who assesses results before making the version visible to that view’s consumers. Changing the default view does not create a new publishing destination. If a promoted version is defective, the plan needs consumer containment and recovery; it cannot depend on a demotion operation the documentation does not support. Another incident involves upstreams: LibA was saved, but installation also requires LibB, which was never saved. Removing the source does not establish that the entire dependency set is available. Inventory the required graph and confirm restore in the appropriate context. When investigating an unexpected version, consider precedence of packages published directly to the feed before changing upstream order. Record affected consumers and the decision for each. Feed state alone does not describe every application already installed.
4. Use cache for acceleration and interpret its state
The lockfile changed and a run obtained inexact through a restoreKey. A condition installing dependencies only when the result is false lets this case pass without reconciliation. Ask the learner to compare restored contents with lockfile intent and justify the installation needed. Cache is not the package’s approval contract. In a different failure, literal segment deps.v2 was interpreted as a path; quoting distinguishes that text from the file whose contents should contribute to the key. If installation completed but the job later failed, check whether Post-job: Cache actually saved an entry. Also avoid using an identical key in different pipelines as guaranteed release transport. During diagnosis, separate calculated key, matching prefix, scope and save outcome. This sequence explains both legitimate misses and a partial restore that still needs work. Record the observed cache status with the dependency decision.
5. Plan batches and recover a partial deployment
In the fictional model, eight healthy VMs each support twenty requests per second. Updating one removes twenty and leaves one hundred and forty, sufficient for one hundred and twenty-five requests. Updating two leaves one hundred and twenty and no longer meets the contract. The exercise adds no safety margin, failures or capacity differences; a real decision needs those inputs. maxParallel limits batch size but does not establish that remaining service capacity can carry load. Before retrying a rolling stage, consider that retry can revisit all VMs. In the second practical case, two completed, one failed after sending an external instruction and another remains unchanged. Inventory versions, health and effects per target, confirm the uncertain instruction and choose authorized recovery. Lower concurrency does not make a script without deduplication execute an operation only once. Also document who will reconcile external state.
6. Observe slots without overlooking startup effects
In the example, the application starts a queue consumer during startup. During preview, target configuration can reach the source before HTTP traffic switches. Ask which connections become effective and whether startup can consume real messages. This consequence depends on the described application’s behavior; it is not a claim that every slot processes queues. If App Service authentication must remain enabled, check its documented incompatibility with swap with preview and choose a compatible path. For restricted access, zero percent automatic traffic does not make the hostname private: define appropriate access controls and check both allowed and disallowed networks. Finally, one hundred requests from the same browser can remain pinned to one slot by the routing cookie. Use representative session and load observations, explaining which evidence is still missing before concluding that distribution or application behavior is correct.
7. Separate technical restoration from functional acceptance
The endpoint responds again, but thirty-seven operations have unknown confirmation in an external service. Resending all of them can duplicate work; closing the incident can hide unreconciled outcomes. Use identifiers and observed states to determine each operation’s result and assign exception resolution. The previous version can restore code without reversing external effects. Release promotion also needs representative workload observation. In the exercise, the agreed criterion requires one hundred interactive transactions and a complete batch cycle. One hundred and forty afternoon transactions do not replace the night batch. Wait for that evidence or formally review the criterion with accountable owners. These numbers are fictional exercise choices, not Microsoft limits. The decision record should show what was observed, what remains outstanding, who accepts the risk and which condition permits progress or requires stopping. Retain this record for operational handover.
8. Practise integrity checks and explain evidence limits
The laboratory below creates fictional files in a temporary folder and compares them with a fixed manifest treated as trusted in this exercise. First predict outcomes when service.bin is missing, the plan changes or the expected run differs. Then execute checks and explain each failure. One deliberate case changes both file and manifest and comparison passes. That result demonstrates that hash equality neither authenticates an author nor proves approval; trust in manifest origin would need to be established through mechanisms appropriate to the real system. The laboratory does not implement that mechanism. Its second part checks batch arithmetic and shows that losing one additional VM changes the decision. Finish with a release note containing expected identity, confirmed contents, missing evidence and next owner. Connect the exercise to artifact selection, recovery of external effects and the functional criteria covered in this lesson.
# Original local exercise: trusted fixture manifest, never a production verifier.
# Fixed filenames only. Hash equality checks bytes, not authenticity or approval.
# No cloud APIs, network, subprocesses or existing application files are used.
from hashlib import sha256
from pathlib import Path
from tempfile import TemporaryDirectory
REQUIRED = ('service.bin', 'plan.json')
def digest(data):
return sha256(data).hexdigest
def inspect_bundle(folder, manifest, expected_run):
problems = []
if manifest.get('run')!= expected_run:
problems.append('run-mismatch')
declared = manifest.get('files', {})
for name in REQUIRED:
path = folder / name
if name not in declared:
problems.append('undeclared:' + name)
if not path.is_file:
problems.append('missing:' + name)
elif name in declared and digest(path.read_bytes)!= declared[name]:
problems.append('digest:' + name)
return problems
def enough_capacity(healthy, unavailable, per_vm, load):
# Fictional equal-capacity model: no failures or safety margin beyond inputs.
if not 0 <= unavailable <= healthy:
raise ValueError('invalid unavailable count')
return (healthy - unavailable) * per_vm >= load
with TemporaryDirectory(prefix='dr-artifact-lesson-') as temporary:
root = Path(temporary)
payload = {'service.bin': b'fictional-build-R84',
'plan.json': b'{"mode":"reconcile","release":"R84"}'}
for name, data in payload.items:
(root / name).write_bytes(data)
trusted = {'run': 'R84', 'files': {n: digest(v) for n, v in payload.items}}
assert inspect_bundle(root, trusted, 'R84') == []
assert inspect_bundle(root, trusted, 'R85') == ['run-mismatch']
(root / 'service.bin').unlink
assert inspect_bundle(root, trusted, 'R84') == ['missing:service.bin']
(root / 'service.bin').write_bytes(b'leftover-from-R83')
assert inspect_bundle(root, trusted, 'R84') == ['digest:service.bin']
(root / 'service.bin').write_bytes(payload['service.bin'])
assert inspect_bundle(root, trusted, 'R84') == []
(root / 'plan.json').write_bytes(b'{"mode":"other"}')
assert inspect_bundle(root, trusted, 'R84') == ['digest:plan.json']
(root / 'plan.json').write_bytes(payload['plan.json'])
incomplete = {'run': 'R84', 'files': {'service.bin': trusted['files']['service.bin']}}
assert inspect_bundle(root, incomplete, 'R84') == ['undeclared:plan.json']
(root / 'service.bin').write_bytes(b'changed-code')
changed_manifest = {'run': 'R84', 'files': dict(trusted['files'])}
changed_manifest['files']['service.bin'] = digest(b'changed-code')
# This passes: a hash cannot authenticate a manifest supplied by an attacker.
assert inspect_bundle(root, changed_manifest, 'R84') == []
assert enough_capacity(8, 0, 20, 125)
assert enough_capacity(8, 1, 20, 125)
assert not enough_capacity(8, 2, 20, 125)
assert enough_capacity(8, 2, 20, 120)
assert not enough_capacity(7, 1, 20, 125)
assert [n for n in range(9) if enough_capacity(8, n, 20, 125)] == [0, 1]
print('14 bundle and capacity assertions passed; no cloud execution or authenticity proof')
R84 is approved but its ZIP is missing. Another release was partially applied and left an external instruction unconfirmed. The learner decides how to retain identity, contain effects and recover using evidence.
Common pitfalls
Treating selection as approval; accepting old files by name; ignoring inexact; planning unsupported demotion; retrying a stage without reconciling effects; confusing zero traffic with private access; assuming hashes authenticate the manifest.
Related topics: Software provenance · Idempotency and reconciliation · Capacity and availability · Change acceptance
Deliver content whose identity and completeness you can establish. Recover from observed state and confirm functional outcomes before declaring the change complete.
Reference: Publish and download pipeline artifacts · AZ-400 objectives 2026-07-27