Download the complete Azure Files AD DS workshop
Download the separate SHA-256 receipt. Extract the complete ZIP to run the lab; the controller needs its adjacent infrastructure, guest script and helper modules.

A user can open a file but cannot save a new one. Another cannot establish a session at all. The private endpoint is healthy in both cases. Which layer should you investigate first?
Build a disposable Active Directory forest and use its identities to access Azure Files over private endpoints. Establish a working, encrypted SMB path. Then diagnose three separate incidents: a missing Kerberos service principal, a restrictive account-level share gate, and a directory ACL that permits reading but prevents new file creation.
Azure Workshopz · Windows Server 2025 · Private SMB · Terraform · PowerShell
Level: intermediate to advanced. Allow: 3–5 hours including Windows promotion/restarts, policy propagation, ingestion and cleanup. Region: West Europe. Operator: Windows with PowerShell 7.6.9, Azure CLI 2.88.0 and Terraform 1.15.8. Provider: AzureRM 5.8.0 with its committed lockfile.
Verification status: PASSED
All 22 required checks passed, including all three isolated faults and first/repeated repairs and two exact cleanup checks preserving the unrelated baseline. The final four checks were evaluated after cleanup from genuine retained live observations collected at 14:49–14:57 UTC, the historical DC fault event at 13:11 UTC and the actual zero-change plan completed before deletion. This was a later offline evaluation, not a new Azure execution after cleanup. All 20 genuine privacy-reviewed captures are included. See the validation record and capture inventory. The target is twenty genuine evidence images: six Portal, eleven CLI and three guest Windows. The synthwave cover is an illustration and does not count as environment evidence.
CLI captions distinguish fresh command execution from a later display of retained operator or guest output. Retained reports keep their original execution timestamps and an explicit label; displaying them in Cloud Shell does not mean Terraform, tests or guest actions ran there again. New commands beneath a retained report are labeled separately.
What you will learn
- Separate name resolution, Kerberos authentication, share-level authorization and NTFS authorization.
- Use an independent storage account and a sibling folder to establish the scope of a failure.
- Distinguish AES256 Kerberos tickets from AES-256-GCM SMB channel encryption.
- Test with fresh, named domain-user sessions rather than a management agent's SYSTEM account.
- Correlate real file operations with client, AD and resource-specific storage logs.
- Repair the smallest owned setting, repeat the repair safely and verify exact cleanup.
Block 1 — Scenario and architecture
Objective: understand what a successful request must pass, and which controls distinguish the three incidents.
You support a small team share. Synthetic Reader, Writer and Outsider users belong to a disposable lab domain. LabAdmin creates and owns a protected workshop directory. Two separate Azure storage accounts provide an exercise path and an independent control path. All files contain synthetic data and a run-specific marker.
The storage account's AD DS identity configuration enables Kerberos authentication. Its default share-level permission grants the first authorization gate to authenticated domain identities. The protected directory's Windows ACL supplies the second gate. Azure Files enforces the more restrictive permission at the share and directory/file layers. Microsoft's permission model explains their interaction.
Traffic path and protected namespace
Windows operator: PowerShell 7 + Azure CLI + Terraform
| management API / private guest commands / Bastion Developer
v
Private Windows Server 2025 client --------> private AD DS + DNS VM
| | Kerberos KDC / lab directory
| SMB 3.1.1, encrypted AES-256-GCM |
+--> exercise.file.core.windows.net --+ exact exercise CIFS SPN
| private endpoint --> exercise storage account
+--> control.file.core.windows.net ---+ exact control CIFS SPN
private endpoint --> control storage account
Both shares: workshop-<run-id>\Control and workshop-<run-id>\Exercise
Diagnostics: StorageFileLogs + retained client/AD observations
VM egress: explicit NAT; guest desktop: Bastion Developer
Use each account's normal <account>.file.core.windows.net name for SMB. AD DNS forwards storage queries to Azure DNS, where the linked privatelink.file.core.windows.net zone resolves the private endpoint. Do not mount by IP or substitute the privatelink name: the normal name is also part of the CIFS service principal. Private endpoint DNS guidance describes this resolution path.
The protected root is named workshop-<run-id> on both accounts. It contains Control and Exercise. LabAdmin owns it; its intended permissions are LabAdmin Full Control, Reader Read and Execute, Writer Modify and Outsider no access. Inheritance is protected so broad share-root defaults cannot silently grant access inside this namespace. These claims do not describe the account's share-root defaults or other directories.
Both accounts start with Storage File Data SMB Share Elevated Contributor as the default share-level permission. This is an account-wide default for authenticated identities on every share in that storage account. It avoids adding disposable identities to Microsoft Entra ID or synchronizing the new forest. Production designs should choose identity-specific share permissions where appropriate. This workshop demonstrates AD DS, not Entra Kerberos or Microsoft Entra Domain Services. Default share permissions document that scope.
| Check | Healthy baseline | Intended failure | Control that must remain healthy |
|---|---|---|---|
| Kerberos | Fresh AES256 CIFS ticket and SMB operations on both accounts | Exact exercise CIFS SPN removed; fresh exercise authentication fails | Independent control account, DNS and TCP445 |
| ShareGate | Elevated Contributor plus Writer Modify permits a new file | Exercise account default becomes Reader: read succeeds, new write fails | Independent control account; exercise NTFS stays Modify |
| Ntfs | Writer can create a new file in the protected Exercise folder | Only its Writer ACE changes from Modify to RX | Sibling Control folder and independent account; share gate unchanged |
This is a single-DC troubleshooting environment. It teaches diagnosis and evidence, rather than production AD availability, migration, capacity or disaster recovery.
Portal checkpoint: identify the two storage accounts, private endpoints, private Windows client and AD/DNS VM in the reviewed architecture.
Expected evidence: the plan and later healthy baseline match this traffic path and scope. Recovery: resolve design or ownership mismatches before injecting any fault.
Block 2 — Preflight, cost and private state
Objective: prove deployment, capture and cleanup prerequisites before creating paid resources.
Extract the complete download. Open PowerShell 7 in its top-level folder. Select the subscription explicitly and use a new private state directory outside the package and every Git repository:
$private = Join-Path $env:LOCALAPPDATA ('AzureWorkshopz-Files-' + [guid]::NewGuid().ToString('N'))
$state = Join-Path $private 'state.json'
az login
az account set --subscription '<your-subscription-id>'
$subscriptionId = (az account show --query id --output tsv).Trim()
# Pass this flag only after verifying usable Portal and CLI capture access.
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Preflight -StatePath $state -SubscriptionId $subscriptionId -CaptureAccessVerified
Preflight checks the exact tenant/subscription, permissions, providers, regional VM/image availability and quota, network overlap and current prices. It records policies/locks, the existing Azure resource baseline and your capture-access attestation privately. Review applicable governance constraints before applying the plan. Both Standard_B2s Windows Server 2025 VMs use a resolved, pinned image. The supplied full subscription and tenant bind subsequent stages to the same context.
Operating allowance: €20 total, reserving €5 for cleanup. Before deployment, a conservative estimate for six hours of active resources must be no more than €15. Include two Windows VMs and disks, NAT/public IP, two storage accounts/shares and their transactions, private endpoints, DNS, diagnostics and expected traffic. Check Bastion Developer availability and pricing assumptions explicitly. This is an operational limit, not an Azure-enforced spending cap.
Start cleanup by 4½ hours from the first Preflight; target verified absence before six hours. Repeating preflight does not restart the operating clock. Keep time for asynchronous deletion, a second absence check and baseline comparison. Stop troubleshooting and start cleanup when the deadline requires it.
Prove that actual Azure Portal and CLI screenshots are available, and check Bastion Developer's regional and connection prerequisites before paid deployment. After Foundation exists, verify the real guest desktop before starting fault exercises. The client and DC have no public IP or public RDP exposure. Guest management uses Azure VM Run Command; the browser desktop path uses Bastion Developer. The three required Windows images must come from the real guest applications.
Portal checkpoint: selected subscription/region, approved screenshot access and Bastion Developer availability. Expected evidence: every prerequisite passes, rates are current and no workshop resource exists yet. Recovery: resolve a prerequisite or cost failure before Plan/Deploy; do not silently change region, SKUs or allowance.

Passwords, storage Kerberos keys, domain identifiers, state/plans, raw logs and unredacted images stay in the private run directory. Terraform state and saved plans can hold secrets despite console redaction. The public ZIP contains reviewed source and sanitized evidence only.
Block 3 — Review and deploy the Foundation
Objective: create only the isolated resources needed to establish the domain and private storage paths.
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Plan -Phase Foundation -StatePath $state
# Inspect the exact saved plan and ownership summary.
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Deploy -Phase Foundation -StatePath $state -Execute -AcknowledgeCost -ReviewedPlan
Review the dedicated resource group, West Europe location, VNet/subnets, two private B2s Windows VMs, explicit NAT egress, Bastion Developer, two storage accounts, file private endpoints, DNS and dedicated Log Analytics workspace. Storage public network access must be disabled. The plan must not change or adopt an existing resource.
Foundation deliberately precedes storage AD identity configuration: the new forest's SID/GUID and storage-representing AD object information do not exist before bootstrap. Identity configuration gets a second saved, reviewed Terraform plan in Block 4. Keep both phases in the same private run ledger.
Portal checkpoint: the deployed resources match the reviewed plan; Windows VM NICs have private addresses, file endpoint connections are approved, private DNS is linked and public storage access is disabled.


Expected evidence: Foundation applies successfully with only recorded resources. Provisioning success is a prerequisite; it does not yet prove domain authentication or SMB access.
Recovery: a local timeout does not cancel Azure's accepted operation. Confirm the original process has ended, inspect the private journal and live resource state, then reconcile before the supported -Resume of the interrupted stage. Preserve the ledger, state and plan. Do not start a competing apply or remove a live lock.
Block 4 — Establish identity, permissions and the healthy baseline
Objective: prove the complete file-access path under named domain identities before introducing a failure.
Bootstrap, then review the Identity phase
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Bootstrap -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Plan -Phase Identity -StatePath $state
# Review the exact AD domain and storage identity properties.
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Deploy -Phase Identity -StatePath $state -Execute -AcknowledgeCost -ReviewedPlan
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Configure -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -StatePath $state -Execute
Bootstrap promotes the disposable DC, configures AD DNS/forwarding, joins the client and prepares synthetic users/groups and the storage-representing AD computer objects. Promotion and join require restarts, so bounded waits and resumed guest checkpoints matter. Configure creates the protected namespace and synthetic content, preserves the intended ACLs and establishes the healthy actor matrix.
Sign in to the genuine Windows checkpoints
After Configure, use the original Windows operator account and machine that created this run's current-user DPAPI store. From the extracted package directory, with $state still pointing to this run's private ledger, copy the generated LabAdmin password without printing it:
Import-Module ./scripts/AzureFiles.Common.psm1
$secretFile = Join-Path (Split-Path $state -Parent) 'secrets.dpapi'
$labSecrets = Unprotect-AzureFilesSecrets -Path $secretFile
try { Set-Clipboard -Value $labSecrets.labAdminPassword }
finally { $labSecrets = $null }
In Azure Portal, open the owned client VM → Connect → Bastion → Bastion Developer. Choose password authentication, enter AWZFILES\LabAdmin, and paste into the password field. Use LabAdmin for the administrative Windows checkpoints; open the owned DC through the same route for ADUC. After pasting, clear the operator clipboard:
Set-Clipboard -Value ''
For the Writer Explorer checkpoint, sign out of the client session and reconnect as AWZFILES\Writer. Repeat the copy block using $labSecrets.writerPassword, then clear the clipboard after pasting. ConfigureClient places Writer in the client's Remote Desktop Users group, while keeping it out of local Administrators. Inside that Writer desktop, run whoami before opening the recorded normal-FQDN share path. Administrative-session Explorer access does not establish Writer's access. Keep passwords and the clipboard out of screenshots, transcripts and shared clipboard history; neither the DPAPI file nor decrypted values belong in the package.
The operator uses PowerShell 7. Guest AD commands run in Windows PowerShell 5.1 through the self-contained scripts/guest/AzureFiles.Guest.ps1. Its pinned dependencies are Az.Accounts 5.5.3 and Az.Storage 9.7.2, verified by exact package SHA-256 and valid Microsoft signatures before import. The DC signs in with its selected user-assigned managed identity and verifies both fresh Kerb1 setup keys against the protected operator payload before manual AD registration. The identity has Reader and Storage Account Key Operator Service Role on exactly the two lab accounts; the latter role also permits key regeneration, although this guest helper only reads keys. It receives no subscription-wide role. Microsoft's AD DS registration workflow documents the manual AD-cmdlet route. Reader, Writer and Outsider always authenticate with their domain identities; setup keys are never used for their SMB tests.

Keep ticket encryption and transport encryption separate
Both storage accounts explicitly require Kerberos authentication, AES256 ticket encryption, SMB 3.1.1 and AES-256-GCM as the sole allowed channel cipher. The client also permits only AES_256_GCM. Fresh user connections must actually report dialect 3.1.1 and Encrypted=True. Together, the sole client/server cipher settings and the observed encrypted session establish AES-256-GCM; the report labels that evidence rather than claiming a direct connection-cipher getter. Azure Files SMB settings describe the independent protocol and encryption controls.

The exact service ticket must target cifs/<account>.file.core.windows.net and use AES256. The workshop does not rely on RC4 or a HOST service-principal mapping fallback. Microsoft's AES256 troubleshooting guidance explains why storage configuration and the AD object's encryption/key material must agree.
Test the real actor, path and operation
Each validation uses a fresh named-user logon/session, closes its owned SMB connections and verifies the requested file operation. A successful Run Command under SYSTEM, a cached ticket or a stored storage-account-key connection cannot satisfy the actor check. Record identity, exact server/share, marker, ticket encryption, observed SMB dialect/encryption, sole configured client/server cipher and operation result.
The client grants only the local Log on as a batch job right needed by the three synthetic test users. They remain non-administrators. The runner verifies the registered task's Password logon type and user SID, then records the new logon ID. Existing batch grants and denial policies are preserved. A scheduler error such as Win32 1385 concerns Windows logon rights; it is not evidence of a share or NTFS denial. Client SMB diagnostic channels are enabled without clearing events; final collection retains actual matching event metadata where emitted.
The following are read-only inspection commands inside the relevant fresh guest user session. Replace the placeholders from this run's recorded paths. Run them in the client VM; the operator workstation is not joined to the disposable forest.
$exerciseServer = '<exercise-account>.file.core.windows.net'
$controlServer = '<control-account>.file.core.windows.net'
whoami
Resolve-DnsName $exerciseServer
Test-NetConnection $exerciseServer -Port 445
klist
Get-SmbConnection | Select-Object ServerName, ShareName, UserName, Dialect, Encrypted
The acceptance report uses the label sole configured cipher + observed encrypted session. Encrypted=True alone is insufficient: verify both the current server's sole AES-256-GCM setting and the client's sole AES_256_GCM setting, plus the fresh observed SMB3.1.1 connection. The write probe creates a new unique filename, reads the synthetic marker, verifies content and removes only its own file. File ownership and existing handles can otherwise disguise a permission defect.


Expected actor matrix inside both protected workshop namespaces: Reader can read but cannot create; Writer can create/read/verify/delete; Outsider is denied; LabAdmin retains ownership and administration. The control and exercise directories both pass before a fault. Default share-root permissions remain outside these claims.
Portal/Windows checkpoint: AD DS is the selected storage identity source; the correct default share permission is displayed; Explorer shows the synthetic marker under the intended Writer identity; the protected ACL has no broad inherited grant.
Recovery: resolve clock skew, DNS, client domain membership, exact SPNs, AD key/encryption or guest-agent errors before proceeding. Do not weaken encryption, mount by key, add broad ACL grants or treat a successful network test as a successful file operation.
Block 5 — Kerberos fault: the service principal is missing
Objective: distinguish authentication failure from network reachability and file permission failures.
Begin with fresh healthy validation. The fault removes only the exact exercise account CIFS SPN from its owned AD object. Its key, DNS, private endpoint, share gate and NTFS ACL stay fixed. The independent control account retains its exact CIFS SPN. The validation must rule out a matching HOST SPN fallback and obtain a genuinely fresh user session/ticket.
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Fault -Scenario Kerberos -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -Scenario Kerberos -StatePath $state -Execute
Expected evidence: normal-name DNS still returns the exercise private endpoint and TCP445 is reachable. Each fresh actor fails to obtain the exercise CIFS ticket while its independent control ticket, encrypted SMB session and file operations remain healthy. Combine that result with the exact missing SPN and the DC's service-ticket failure event.
Read the client and DC diagnostics separately
The calibrated Windows Server 2025 run produced this local klist get diagnostic:
Error calling API LsaCallAuthenticationPackage (GetTicket substatus): 0x6fb
klist failed with 0xc000018b/-1073741429: The SAM database on the Windows Server does not have a computer account for this workstation trust relationship.
The local report retains diagnosticCode=0xc000018b and getTicketSubstatus=0x6fb. The DC separately recorded Security event 4769, Status 0x7, for the exact exercise CIFS name, with TicketEncryptionType=0xffffffff. That DC status is KDC_ERR_S_PRINCIPAL_UNKNOWN. Together with the healthy control and physical SPN readback, these observations identify the intended missing-service-principal fault. The local trust-related message alone needs that context.
The failed net use attempt prompts for another username. The runner closes native stdin so it finishes without user input. In this run, it returned native exit 2 and system error 1223: local cancellation at EOF. Record that client cancellation separately from the ticket and DC error. Acceptance requires mount.timedOut=false and a completed nonzero native mount result; status 1460 or a killed timeout fails validation.
Enable the specific DC audit evidence
Bootstrap and the Kerberos fault helper preserve the existing KdcExtraLogLevel bits and add 0x01 directly under HKLM\SYSTEM\CurrentControlSet\Services\Kdc. A new DC's documented default is 2, so 2 -bor 1 gives 3. This enables unknown-SPN failure auditing while retaining the default PKINIT logging bit. The helper journals the actual before/after readback. Microsoft's 4769 reference explains why unknown-SPN errors need this flag; the KDC registry reference specifies its location and default.
On the disposable DC, use Windows PowerShell for this read-only checkpoint and keep the event details private:
Get-ItemProperty -LiteralPath 'HKLM:\SYSTEM\CurrentControlSet\Services\Kdc' -Name KdcExtraLogLevel |
Select-Object KdcExtraLogLevel
Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4769; StartTime=(Get-Date).AddMinutes(-10)} -MaxEvents 20 |
Format-List TimeCreated, Id, Message
Find the event for the exact exercise CIFS name and the fault's UTC window; verify its Status/Failure Code 0x7. Successful control tickets use AES256 (0x12). The client raw ticket journals retain the original stdout/stderr and actor logon identity for the same probe nonce. Cleanup in Block 8 deletes this disposable DC and its logging setting; the private journal preserves what changed.
Windows checkpoint: use Bastion to view the exercise storage object's servicePrincipalName in ADUC Attribute Editor. Capture the missing exact CIFS entry; command evidence must also establish no HOST fallback and an intact control SPN.


Restore the exact recorded SPN and test a new session. Repeat repair and validate again:
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Repair -Scenario Kerberos -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -Scenario Kerberos -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Repair -Scenario Kerberos -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -Scenario Kerberos -StatePath $state -Execute
Recovery evidence: require a fresh AES256 CIFS ticket, completed mount, correct negotiated SMB security and successful synthetic file operation after each repair. Re-read the exact SPN on the recorded object. The repeated repair must preserve a single CIFS entry and the control account. Keep each recovery's nonce, actor logon IDs and timestamps separate; the command sequence itself does not establish that both recovery windows passed.

Block 6 — Share gate fault: read works, new writes fail
Objective: recognize account-level share authorization even when the folder grants Modify.
This fault changes only the exercise storage account's default share-level permission from Elevated Contributor to Reader. The change affects all shares and both workshop folders in that account. The separate control storage account provides the unaffected control. A sibling folder in the same exercise account is not an independent control for this fault.
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Fault -Scenario ShareGate -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -Scenario ShareGate -StatePath $state -Execute
Portal checkpoint: Azure Files identity-based access shows the exercise account's default permission as Reader. Expected evidence: Writer still obtains its ticket, negotiates encrypted SMB and reads the marker, but new file creation is denied. Its protected-directory Modify ACE is unchanged. Writer can still create a new file on the independent control account. Record the actual storage/Windows denial status and timing after propagation.


Restore the account's recorded default, allow bounded propagation and create a fresh session:
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Repair -Scenario ShareGate -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -Scenario ShareGate -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Repair -Scenario ShareGate -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -Scenario ShareGate -StatePath $state -Execute
Recovery evidence: new Writer file creation succeeds after each repair validation; Reader stays read-only and Outsider remains denied inside the protected namespace. The control account and NTFS owner/ACL remain unchanged. Do not grant extra domain permissions to compensate for a storage share gate.

Block 7 — NTFS fault: one folder refuses new files
Objective: isolate directory authorization with a healthy authentication path and share gate.
The fault replaces only the owned Writer Modify ACE with Read and Execute on the protected Exercise folder of the exercise account. LabAdmin ownership, the Reader entry, protected inheritance, sibling Control folder and account-wide Elevated Contributor setting remain fixed. Outsider receives no new grant.
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Fault -Scenario Ntfs -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -Scenario Ntfs -StatePath $state -Execute
Windows checkpoint: Advanced Security on the exact recorded Exercise path shows Writer RX and the preserved owner/other entries. Avoid changing the share root or disabling inheritance on unrelated paths.
Expected evidence: a fresh Writer session can read the exercise marker but cannot create a new unique file in Exercise. Writer can create in the sibling Control folder and independent account. The exact CIFS ticket, encryption and default share permission remain healthy. Do not test only an existing Writer-owned file: its ownership or explicit ACL may produce a misleading pass.


Restore the precise recorded ACE and run both recovery validations:
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Repair -Scenario Ntfs -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -Scenario Ntfs -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Repair -Scenario Ntfs -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -Scenario Ntfs -StatePath $state -Execute
Recovery evidence: new file creation/read/hash/delete succeeds, Reader and Outsider boundaries hold, and the ACL/owner comparison shows only the intended restoration. Repeated repair must preserve unrelated ACEs, synthetic content and the account-wide default.

Block 8 — Final validation, real logs and verified cleanup
Objective: finish with observed evidence, configuration consistency and no leftover workshop resources.
After all three fault cycles and both recovery windows, run a fresh complete matrix and collect the actual diagnostic logs:
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -StatePath $state -Execute -CollectLogs
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Plan -Phase Identity -StatePath $state
# Require zero unexpected changes in the restored final configuration.
Require the restored exact SPNs, both Elevated Contributor defaults, protected ACL/owner baselines, correct named-user operations and AES256 tickets. Recheck the sole AES-256-GCM cipher settings on client/server and fresh encrypted SMB3.1.1 connections. Verify the final Identity-phase Terraform configuration has zero unexpected drift. Do not accept an unreviewed change merely because its resources belong to the lab.
Use the log fields actually emitted by this run
The dedicated workspace receives resource-specific StorageFileLogs from the file service. Inspect actual Protocol, AuthenticationType, OperationName, StatusCode, StatusText, SmbStatusCode, SmbPrimarySID, SmbSessionID, SmbTreeConnectID and CorrelationId values. Keep the exact test window and unique synthetic filenames. Preserve raw status strings and empty fields before interpreting their encoding. The StorageFileLogs schema defines these fields.
The ShareGate evidence in capture 16 correlates the exact Writer test filename. Its control rows reported Protocol='SMB' and AuthenticationType='Kerberos':
| Account and operation | SmbCommandMajor |
Raw StatusCode |
Raw SmbStatusCode |
|---|---|---|---|
| Control CREATE, READ and WRITE | 5, 8, 9 |
'0' |
'0X0' |
| Exercise CREATE | 5 |
'-1073741790' |
(empty) |
The exercise row's StatusText was also empty. Interpreting its observed StatusCode string as a signed 32-bit value gives the derived NTSTATUS 0xC0000022, named STATUS_ACCESS_DENIED in Microsoft MS-ERREF. This is an NTSTATUS interpretation of the observed SMB row. The raw SmbStatusCode and StatusText remain empty; the derived label is recorded separately.
$rawStatus = '-1073741790'
'0x{0:X8}' -f ([int64]$rawStatus -band 0xffffffffL) # Derived: 0xC0000022
Use inventory, file-operation evidence and session correlation. A Kerberos ticket request that fails at the DC may never reach the storage service. Corroborate that case with the actual client/KDC observation, not a fabricated storage denial row. Log ingestion delay is distinct from a permission failure; use bounded polling and preserve original event timestamps.
The existing -Stage Validate -Execute -CollectLogs interface uses core Azure CLI az rest to call the official workspace Logs Query API, with bounded polling. The optional log-analytics CLI extension is unnecessary for this controller path. Microsoft's query request format documents direct az rest requests.


Delete only the exact recorded deployment
Preview cleanup, inspect the exact destroy plan and ownership checks, then execute it. Preserve the private ledger until absence and baseline comparison finish.
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Cleanup -StatePath $state
# Review the exact recorded destroy target and saved plan.
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Cleanup -StatePath $state -Execute -ReviewedPlan
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Cleanup -StatePath $state -Execute -ReviewedPlan
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Export -StatePath $state
Expected evidence: the dedicated group and all exact recorded resources are absent after Azure completes deletion; repeating cleanup creates nothing and confirms absence again. The protected pre-existing resource baseline has no missing entries. An ownership mismatch or unexpected group member requires reconciliation, rather than a broader delete command.


Export allowlisted sanitized checks, UTC observations and capture provenance. Keep raw logs, keys, credentials, private plans/state and original images outside the public package. Report local test results separately from observed Azure results. Final acceptance requires all twenty genuine images and the completed evidence, not merely this expected-result narrative.
Troubleshooting reference
| Symptom | First evidence to inspect | Appropriate response |
|---|---|---|
| DNS gives a public or unexpected IP | Client resolver, AD forwarder, normal hostname, linked private zone | Restore private resolution before interpreting SMB authorization |
| TCP445 fails on both accounts | NSG/routes/endpoint state and guest/network health | Resolve connectivity; do not label it the intended SPN fault |
| Fresh exercise ticket fails; control works | Exact CIFS SPN, uniqueness, no HOST fallback, actual KDC error | Restore only the recorded exercise SPN |
| Old session still works during SPN fault | User identity, owned SMB connections, ticket cache/logon freshness | Test a new named-user logon; do not delete unrelated connections |
| Read succeeds, writes fail across exercise-account folders | Default share permission and unchanged Modify ACE | Restore the exercise account default; preserve NTFS |
| Read succeeds, new creation fails only in Exercise | Protected directory ACL/owner and sibling Control result | Restore only Writer Modify on the owned folder |
| Outsider can access protected content | Inherited broad grants, token groups, protected root DACL | Correct the protected namespace; do not generalize root defaults |
| Ticket AES256 but transport proof incomplete | Sole client/server cipher settings plus fresh connection dialect/encryption | Require AES-256-GCM on both ends and observed encrypted SMB3.1.1; record how the cipher was established |
| Guest command times out or restarts | Recorded operation, VM Agent, promotion/join checkpoint | Reconcile before Resume; this is not scenario evidence |
| Storage logs have no missing-SPN denial | Client/KDC event and storage arrival window | Record where the failure occurred; do not invent a storage row |
| Cleanup shows a residual or missing baseline object | Exact resource IDs, ownership, original baseline | Reconcile scope and verify again before declaring completion |
Block 9 — Learning pack
Attempt the independent challenge, open the progressive hints one at a time and compare your diagnosis with the worked solution. The eight knowledge questions and explanations cover scope, permissions, session freshness, encryption, logging and cleanup. The complete learning pack appears below in the illustrated HTML.
Download the controller source, source map and capture guide for inspection. Run from the extracted complete ZIP. If an individual file is served as text by the blog's download system, retain the original filename and folder structure from the ZIP for execution.
This is independent community training. Use only the disposable lab domain and owned resources created for this run.
Independent challenge
Independent challenge — Which gate denied the operation?
Start after the complete baseline passes. Keep the same private run state, isolated domain and €20 operating allowance. Leave time to begin cleanup by 4½ hours from initial Preflight.
Your assignment
An application team reports that its private share is unreliable. One engineer proposes opening the public endpoint; another wants Full Control for every domain user. You have three incident reports and must prove the failing layer before changing access.
- Establish the baseline using fresh sessions for Reader, Writer and Outsider. Record both normal storage hostnames, private resolution, exact CIFS AES256 tickets, sole AES-256-GCM client/server settings, observed encrypted SMB3.1.1 sessions, run marker and real operations. Explain why the agent's SYSTEM identity is insufficient.
- Run the Kerberos fault. Prove the exact exercise CIFS principal is missing, no HOST fallback covers it, DNS/TCP remain healthy and the independent storage account still works. Record the actual client/KDC diagnostic. Restore only the SPN, validate, repeat repair and validate again.
- Run the ShareGate fault. Prove Writer can read but cannot create a new file despite its unchanged Modify ACE. Show that the account-wide default affects both folders in the exercise account, while the independent account still works. Restore Elevated Contributor and prove both recovery windows.
- Run the Ntfs fault. Prove that a new file cannot be created in the exercise account's protected Exercise folder while its sibling Control folder and the independent account work. Preserve LabAdmin ownership and other ACEs. Restore only Writer Modify and prove both recovery windows.
- Correlate actual SMB operations with StorageFileLogs and client/AD observations. Explain why a missing-SPN failure can have no corresponding storage request row.
- Restore intended configuration, obtain zero unexpected Terraform drift, clean up exact recorded resources, repeat cleanup and compare the original unrelated-resource baseline. Export reviewed sanitized evidence.
Evidence acceptance
| Claim | Minimum evidence |
|---|---|
| Healthy access | Fresh named-user session; private normal hostname; exact AES256 CIFS ticket; sole client/server cipher plus observed encrypted SMB3.1.1; correct run marker; operation matrix |
| Kerberos incident | Exact SPN absent and no HOST fallback; real fresh-session authentication error; DNS/TCP and independent control healthy |
| ShareGate incident | Default Reader on exercise account; unchanged Modify ACL; read success/new-write denial; independent control success |
| Ntfs incident | Protected Exercise Writer RX; LabAdmin owner/other ACEs preserved; new-file denial; sibling and independent successes |
| Recovery | First and repeated repair each followed by fresh successful validation; Reader/Outsider boundaries remain |
| Completion | Real logs/AD-client correlation, no unexpected drift, twenty genuine captures, exact resources absent twice and baseline preserved |
All permission claims apply inside the protected workshop-<run-id> namespace. The default share permission applies account-wide; root-directory default ACLs do not become the protected-folder ACL automatically.
Use HINTS.md progressively. Record your diagnosis before opening SOLUTION.md. Do not use public storage, broad grants, RC4, a storage-key SMB mount or an old cached connection to manufacture a pass.
Progressive hints
Progressive hints — Follow the file operation
Open one hint at a time. Keep the same private ledger and enough time for cleanup.
Hint 1 — Identify the user and the exact operation
Who owns the logon session and SMB connection? Did the test attempt to read, create a new file, overwrite an old file or delete a file? A successful existing-handle operation may answer a different question from new file creation.
Hint 2 — Check the name before the permissions
Does the normal <account>.file.core.windows.net name resolve through AD DNS to the private endpoint? Is TCP445 reachable? The private endpoint being approved in Portal does not prove client resolution or user authentication.
Hint 3 — A configured identity is not a fresh ticket
Find the exact CIFS service principal and its AD owner. Does a fresh user logon obtain its AES256 service ticket? During the fault, could a HOST service-principal mapping or an existing ticket/session hide the missing CIFS entry? Compare with the independent control account.
Hint 4 — Separate the two encryption checks
klist describes the Kerberos ticket. The SMB connection describes the observed dialect and encryption. Encrypted=True alone does not identify its cipher. Require AES256 ticket evidence, sole AES-256-GCM settings on both client/server, and a fresh encrypted SMB3.1.1 session. The constrained settings plus actual connection establish the channel cipher; no direct cipher getter is claimed.
Hint 5 — Read success narrows the problem
If the same fresh user reads the marker but cannot create, inspect both authorization layers. An NTFS Modify ACE cannot override a Reader share gate. The account default reaches all shares in that storage account, so use the second account as the independent control.
Hint 6 — One folder can fail while its account is healthy
Compare the protected Exercise folder with its sibling Control folder. Is only Writer's ACE different? Is inheritance protected? Does LabAdmin still own the folder? Avoid replacing the whole ACL or testing only a file that Writer already owns.
Hint 7 — The point of failure determines the log source
Which service actually received the attempted request? A KDC can reject a service-ticket request before the storage service sees SMB. Storage logs can show genuine SMB denials for later gates; client/KDC observations establish an earlier authentication failure. Preserve real values instead of assuming every failure is HTTP403.
Hint 8 — A repair needs two fresh observations
Validate after the first repair, then repeat repair and validate again. Verify Reader remains read-only and Outsider remains denied. Finish with a restored full matrix and no unexpected drift, then prove exact absence twice and preservation of unrelated resources.
Worked solution
Worked solution — Prove the gate, then restore its owned setting
These are the expected reasoning and command sequence. See VALIDATION.md for separate observed Azure results. A pending release has no accepted live result simply because this solution describes it.
1. Establish the exact context and baseline
Create a private StatePath outside the package and Git, explicitly select the subscription and complete Preflight. Review/apply Foundation, Bootstrap, review/apply Identity, Configure and Validate as shown in README.md. The two-phase plan keeps discovered domain identifiers in reviewed Terraform configuration.
Use fresh per-user sessions for the protected workshop-<run-id> namespace on both accounts. Reader reads but cannot create; Writer creates, reads, verifies and deletes a new uniquely named file; Outsider cannot access protected content. LabAdmin owns the protected namespace. Normal storage hostnames resolve privately. Service tickets target the exact CIFS names and use AES256. Sole AES-256-GCM settings on client/server plus fresh observed encrypted SMB3.1.1 sessions establish transport protection; record this evidence rather than a direct cipher getter. No storage-key mount or management-agent identity counts as a user test.
2. Run each complete fault/recovery cycle
Run the following whole sequence for each scenario, in order. Do not skip the fault validation or combine the two repair windows into a single observation.
foreach ($scenario in @('Kerberos', 'ShareGate', 'Ntfs')) {
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Fault -Scenario $scenario -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -Scenario $scenario -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Repair -Scenario $scenario -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -Scenario $scenario -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Repair -Scenario $scenario -StatePath $state -Execute
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -Scenario $scenario -StatePath $state -Execute
}
Kerberos diagnosis
The missing exact exercise CIFS principal prevents a fresh exercise service ticket/session. Verify that no HOST mapping fallback applies. DNS/TCP and independent control access stay healthy, separating authentication from reachability. Capture the actual diagnostic and UTC window. Restore the exact SPN to the recorded storage-representing AD object, without adding duplicates or changing keys/ACLs. Fresh AES256 ticket and operation success establish recovery. A stale connection success proves nothing about a new ticket.
For the calibrated Windows Server 2025 image, the local klist failure was 0xc000018b (signed exit -1073741429) with GetTicket substatus 0x6fb and the SAM computer-account/trust-relationship message. Retain these actual local fields. The separate DC evidence is Security event 4769, exact exercise CIFS service, Status 0x7 (KDC_ERR_S_PRINCIPAL_UNKNOWN), and failure encryption type 0xffffffff. Healthy control tickets and the physical missing-SPN readback distinguish this controlled service lookup failure from an unrelated machine-trust problem.
Unknown-SPN failure events require KdcExtraLogLevel bit 0x01 directly under HKLM\SYSTEM\CurrentControlSet\Services\Kdc. The helper preserves existing bits; the documented default 2 becomes 3 on a new disposable DC. Verify its actual readback and the fault-time event rather than treating the logging setting as an event. See Microsoft's 4769 audit reference and KDC registry reference. Block 8 deletes this lab DC and its setting while the private before/after journal remains available.
Closing native stdin makes the failed net use credential prompt finish at EOF. Its observed native exit 2 / system error 1223 is client cancellation; use the ticket and DC observations to diagnose Kerberos. A mount timeout (1460 or timedOut=true) never passes the intended-failure gate. After repair, require a fresh successful ticket/mount and the full expected file operations, then repeat repair and validate a second fresh window. Each completed validation needs its own observed result.
ShareGate diagnosis
Writer's ticket and read work, and its protected folder still grants Modify. The exercise account's default Reader permission is therefore the limiting gate. This setting applies to every share in that account; a sibling folder cannot isolate it. The independent account's write success supplies the useful control. Restore the recorded Elevated Contributor default, allow bounded propagation and retest fresh sessions. Reader must stay read-only through NTFS, and Outsider must stay denied inside the protected namespace.
Ntfs diagnosis
The share default, ticket and transport are healthy. Writer reads Exercise but new creation fails there; sibling Control and independent account creation work. The protected Exercise folder has Writer RX instead of Modify. Restore only that owned ACE while preserving LabAdmin ownership, Reader, inheritance protection and other entries. Test a new unique filename rather than reusing an old Writer-owned file. Compare the resulting ACL/owner with the recorded intended baseline.
3. Corroborate the actual observations
Use the query pack to find real StorageFileLogs in the exact test windows. Preserve both raw StatusCode and SmbStatusCode, StatusText, session/tree/file identifiers, actor and synthetic filename. For the exact Writer ShareGate file in capture 16, control SMB/Kerberos CREATE/READ/WRITE rows (majors 5/8/9) had StatusCode='0' and SmbStatusCode='0X0'. The exercise CREATE row (major 5) had the string StatusCode='-1073741790', with empty SmbStatusCode and StatusText. Signed 32-bit interpretation yields the derived NTSTATUS 0xC0000022 / STATUS_ACCESS_DENIED, as defined by Microsoft MS-ERREF. Keep that derived interpretation separate and preserve the empty raw fields; this observed SMB value is not an HTTP status.
The unchanged -Stage Validate -Execute -CollectLogs command uses core az rest against the official workspace Logs Query API, avoiding the optional CLI extension. See the official query format. A failure at the KDC can precede storage arrival and needs the actual client/KDC evidence. Record missing or delayed observations honestly and keep ingestion polls bounded.
4. Restore, clean and export
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Validate -StatePath $state -Execute -CollectLogs
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Plan -Phase Identity -StatePath $state
# Require zero unexpected changes in the restored configuration.
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Cleanup -StatePath $state
# Read the exact saved destroy plan and ownership summary.
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Cleanup -StatePath $state -Execute -ReviewedPlan
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Cleanup -StatePath $state -Execute -ReviewedPlan
./Invoke-AzureFilesAdDsWorkshop.ps1 -Stage Export -StatePath $state
Final healthy matrix, restored identity/defaults/ACLs and zero unexpected drift precede cleanup. Require exact resource absence twice and a zero-missing comparison against the private pre-existing-resource baseline. Preserve the private ledger while reconciling residuals. Review the sanitized export and twenty genuine capture records before publication; raw logs, identifiers, secrets, state/plans and original captures stay private.
Eight knowledge questions and explanations
Eight knowledge questions and explanations
1. Why can Writer read a marker but fail to create a new file despite an NTFS Modify ACE?
Answer: the share-level gate can be more restrictive. An account default of Reader permits reads but limits writes even when the folder grants Modify. Check both layers and the exact attempted operation before changing an ACL. See Blocks 1 and 6.
2. Does changing the exercise account's default share permission affect only its Exercise folder?
Answer: no. The default applies to authenticated identities across all shares in that storage account. Both Control and Exercise folders in that account are affected. The separate storage account supplies an independent control. The protected NTFS namespace is a different scope. See Blocks 1 and 6.
3. Why can an existing share session keep working after the exact CIFS SPN is removed?
Answer: it may retain a ticket, authenticated SMB session or open handle. A new named-user logon with fresh owned connections tests ticket issuance. Also rule out a HOST SPN mapping fallback. The exercise requires the actual missing-SPN failure, not a cached success. See Block 5.
4. Does an AES256 ticket prove AES-256-GCM protects the file traffic?
Answer: no. Kerberos ticket encryption and SMB channel encryption are independent checks. Record the exact AES256 CIFS ticket, sole AES-256-GCM client/server settings and a fresh observed encrypted SMB3.1.1 session. The constrained settings plus connection establish the channel cipher; Encrypted=True alone does not, and no direct cipher getter is claimed. See Block 4.
5. Why is a successful file read under Azure VM Run Command insufficient to prove Writer's access?
Answer: Run Command normally executes under the guest management context, often SYSTEM. It may use a different token, connection or credential. Prove the synthetic user's fresh logon, exact server/share, ticket and file operation. A storage-key connection also bypasses the intended domain-user test. See Block 4.
6. Why test a new unique filename during the NTFS exercise?
Answer: an existing file may have a different owner or explicit ACL, and an open handle can retain previously granted access. New creation inside the admin-owned protected Exercise folder tests the intended directory permission. Preserve its owner/other ACEs and compare against the sibling Control folder. See Block 7.
7. Must every missing-SPN failure appear as a denied StorageFileLogs row?
Answer: no. The KDC can refuse the service-ticket request before the client sends an authenticated SMB operation to storage. Use genuine client/KDC evidence for that failure, and storage session/operation/status evidence for requests that reach Azure Files. Preserve actual emitted fields rather than assuming an HTTP 403 or a particular authentication label. See Block 8.
8. Is €20 an Azure-enforced cap, and when is cleanup complete?
Answer: it is an operating allowance: a conservative six-hour active estimate of at most €15 plus €5 reserved for cleanup. Start cleanup by 4½ hours and target removal before six hours from initial Preflight. Completion requires exact-resource absence, repeated cleanup without new resources and preservation of the original unrelated-resource baseline. Deallocation alone is insufficient. See Blocks 2 and 8.
Environment claims require real observations. Original private runtime material is excluded from this public package. Source: ARTICLE.md.