P03-T08 - Workstation Resource Access Validation
Objective
Validate that domain-joined Windows 11 clients can access approved SMB resources on HQ-FS01 and are denied where expected.
Validated lab status
Validated against the live lab on Friday, July 17, 2026:
miguel.perezwas confirmed able to browse, create, and delete files in\\\\HQ-FS01\\Public.ana.garciaremained denied for write access to\\\\HQ-FS01\\Public.- The validation was executed from real joined clients, not from the server console.
Inputs
| Key | Value |
|---|---|
| Clients | HQ-CL01, HQ-CL02 |
| File server | HQ-FS01 |
| VLAN | 30 |
| Domain | corp.gntech.me |
Target State
| Property | Value |
|---|---|
| Client state | Already domain joined |
| Server state | HQ-FS01 already joined and configured |
| Resource access | Allowed shares work, denied paths fail |
Prechecks
- Complete
P03-T06andP03-T07. - Confirm
HQ-FS01resolves in DNS and responds to SMB. - Confirm at least one allowed and one denied permission path are documented in the client overlay.
- Confirm
Create-HQ-TestUsers.ps1has already been executed with the defaultPublicWriteGroupSamAccountNameor an equivalent validated membership model.
Execution
If the validation identities or their approved Public membership do not exist yet, run the preparation script from elevated PowerShell on HQ-DC01 before signing in to a client:
Show full test-user preparation script
param(
[string]$DomainDn,
[string]$UsersOuDn,
[string]$AdminsOuDn,
[string]$UserUpnSuffix,
[string]$AdminUpnSuffix,
[string]$PublicWriteGroupSamAccountName,
[switch]$ValidateOnly
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
function Read-RequiredValue {
param(
[string]$Prompt,
[string]$DefaultValue
)
$fullPrompt = $Prompt
if ($DefaultValue) { $fullPrompt = "$Prompt [$DefaultValue]" }
while ($true) {
$value = Read-Host -Prompt $fullPrompt
if (-not [string]::IsNullOrWhiteSpace($value)) { return $value.Trim() }
if (-not [string]::IsNullOrWhiteSpace($DefaultValue)) { return $DefaultValue }
}
}
function Read-RequiredSecret {
param([string]$Prompt)
try {
return Read-Host -Prompt $Prompt -AsSecureString
}
catch {
throw "A secure value for '$Prompt' is required when running non-interactively."
}
}
function Resolve-Inputs {
$script:DomainDn = if ([string]::IsNullOrWhiteSpace($DomainDn)) { Read-RequiredValue 'Domain DN' 'DC=corp,DC=gntech,DC=me' } else { $DomainDn.Trim() }
$script:UsersOuDn = if ([string]::IsNullOrWhiteSpace($UsersOuDn)) { Read-RequiredValue 'Users OU DN' 'OU=Users,OU=GNTECH,DC=corp,DC=gntech,DC=me' } else { $UsersOuDn.Trim() }
$script:AdminsOuDn = if ([string]::IsNullOrWhiteSpace($AdminsOuDn)) { Read-RequiredValue 'Admins OU DN' 'OU=Admins,OU=GNTECH,DC=corp,DC=gntech,DC=me' } else { $AdminsOuDn.Trim() }
$script:UserUpnSuffix = if ([string]::IsNullOrWhiteSpace($UserUpnSuffix)) { Read-RequiredValue 'Standard user UPN suffix' 'gntech.me' } else { $UserUpnSuffix.Trim() }
$script:AdminUpnSuffix = if ([string]::IsNullOrWhiteSpace($AdminUpnSuffix)) { Read-RequiredValue 'Admin user UPN suffix' 'gntech.me' } else { $AdminUpnSuffix.Trim() }
$script:PublicWriteGroupSamAccountName = if ([string]::IsNullOrWhiteSpace($PublicWriteGroupSamAccountName)) { Read-RequiredValue 'Public write group SamAccountName' 'DL-HQ-FS01-Public-RW' } else { $PublicWriteGroupSamAccountName.Trim() }
if (-not $ValidateOnly) {
$script:DefaultUserPassword = Read-RequiredSecret -Prompt 'Standard user password'
$script:DefaultAdminPassword = Read-RequiredSecret -Prompt 'Admin user password'
}
}
function Ensure-Module {
param([string]$Name)
Import-Module $Name
}
function Ensure-TestUser {
param(
[string]$Name,
[string]$GivenName,
[string]$Surname,
[string]$DisplayName,
[string]$UserPrincipalName,
[string]$SamAccountName,
[string]$EmployeeId,
[string]$Path,
[securestring]$Password
)
$existing = Get-ADUser -Filter "SamAccountName -eq '$SamAccountName'" -ErrorAction SilentlyContinue
if (-not $existing) {
New-ADUser `
-Name $Name `
-GivenName $GivenName `
-Surname $Surname `
-DisplayName $DisplayName `
-UserPrincipalName $UserPrincipalName `
-SamAccountName $SamAccountName `
-EmployeeID $EmployeeId `
-Path $Path `
-AccountPassword $Password `
-Enabled $true `
-ChangePasswordAtLogon $false
}
}
function Ensure-GroupMembership {
param(
[string]$GroupSamAccountName,
[string]$MemberSamAccountName
)
$group = Get-ADGroup -Filter "SamAccountName -eq '$GroupSamAccountName'" -ErrorAction SilentlyContinue
if (-not $group) {
throw "The group '$GroupSamAccountName' was not found."
}
$member = Get-ADUser -Filter "SamAccountName -eq '$MemberSamAccountName'" -ErrorAction SilentlyContinue
if (-not $member) {
throw "The user '$MemberSamAccountName' was not found."
}
$isMember = Get-ADGroupMember -Identity $group.DistinguishedName -Recursive | Where-Object SamAccountName -eq $MemberSamAccountName
if ($isMember) {
return [pscustomobject]@{
GroupSamAccountName = $GroupSamAccountName
MemberSamAccountName = $MemberSamAccountName
Action = 'Exists'
}
}
if (-not $ValidateOnly) {
Add-ADGroupMember -Identity $group.DistinguishedName -Members $member.DistinguishedName
}
[pscustomobject]@{
GroupSamAccountName = $GroupSamAccountName
MemberSamAccountName = $MemberSamAccountName
Action = $(if ($ValidateOnly) { 'Planned' } else { 'Added' })
}
}
Resolve-Inputs
Ensure-Module -Name ActiveDirectory
$definitions = @(
@{
Name = 'Miguel Perez'
GivenName = 'Miguel'
Surname = 'Perez'
DisplayName = 'Miguel Perez'
UserPrincipalName = "miguel.perez@$($script:UserUpnSuffix)"
SamAccountName = 'miguel.perez'
EmployeeId = 'GNT-000123'
Path = $script:UsersOuDn
IsAdmin = $false
},
@{
Name = 'Ana Garcia'
GivenName = 'Ana'
Surname = 'Garcia'
DisplayName = 'Ana Garcia'
UserPrincipalName = "ana.garcia@$($script:UserUpnSuffix)"
SamAccountName = 'ana.garcia'
EmployeeId = 'GNT-000124'
Path = $script:UsersOuDn
IsAdmin = $false
},
@{
Name = 'Admin Miguel Perez'
GivenName = 'Miguel'
Surname = 'Perez'
DisplayName = 'Admin Miguel Perez'
UserPrincipalName = "adm.miguel.perez@$($script:AdminUpnSuffix)"
SamAccountName = 'adm.miguel.perez'
EmployeeId = 'GNT-A000123'
Path = $script:AdminsOuDn
IsAdmin = $true
},
@{
Name = 'Admin Gerlin Nolasco'
GivenName = 'Gerlin'
Surname = 'Nolasco'
DisplayName = 'Admin Gerlin Nolasco'
UserPrincipalName = "adm.gerlin.nolasco@$($script:AdminUpnSuffix)"
SamAccountName = 'adm.gerlin.nolasco'
EmployeeId = 'GNT-A000124'
Path = $script:AdminsOuDn
IsAdmin = $true
}
)
$summary = $definitions | ForEach-Object {
[pscustomobject]@{
Name = $_.Name
UserPrincipalName = $_.UserPrincipalName
SamAccountName = $_.SamAccountName
EmployeeId = $_.EmployeeId
Path = $_.Path
}
}
$summary | Format-Table -AutoSize | Out-String | Write-Output
[pscustomobject]@{
PublicWriteGroupSamAccountName = $script:PublicWriteGroupSamAccountName
PublicWriteMemberSamAccountName = 'miguel.perez'
PublicDeniedMemberSamAccountName = 'ana.garcia'
} | Format-List | Out-String | Write-Output
if ($ValidateOnly) {
Write-Output 'Validation mode only. Test users and group memberships were not created.'
return
}
foreach ($definition in $definitions) {
$password = if ($definition.IsAdmin) { $script:DefaultAdminPassword } else { $script:DefaultUserPassword }
Ensure-TestUser `
-Name $definition.Name `
-GivenName $definition.GivenName `
-Surname $definition.Surname `
-DisplayName $definition.DisplayName `
-UserPrincipalName $definition.UserPrincipalName `
-SamAccountName $definition.SamAccountName `
-EmployeeId $definition.EmployeeId `
-Path $definition.Path `
-Password $password
}
$membershipResult = Ensure-GroupMembership `
-GroupSamAccountName $script:PublicWriteGroupSamAccountName `
-MemberSamAccountName 'miguel.perez'
$membershipResult | Format-Table -AutoSize | Out-String | Write-Output
Write-Output 'Test users completed successfully.'
-
Prepare the exact validation subjects before touching the clients:
-
allowed write test:
- user:
miguel.perez - expected allowed path:
\\HQ-FS01\Public - expected result: can create and delete a test file
- denied write test:
- user:
ana.garcia - expected denied path:
\\HQ-FS01\Public -
expected result: can browse and read if allowed, but cannot create a file
-
Sign in to
HQ-CL02asmiguel.perez. -
From a standard user PowerShell session, validate identity and network first:
whoami
whoami /groups
Resolve-DnsName hq-fs01.corp.gntech.me
Test-NetConnection hq-fs01.corp.gntech.me -Port 445
Do not continue until name resolution and TCP 445 are successful.
- Validate allowed SMB access for
miguel.perez:
dir \\HQ-FS01\Public
'resource access validation' | Out-File \\HQ-FS01\Public\miguel-write-test.txt
Get-Item \\HQ-FS01\Public\miguel-write-test.txt
Remove-Item \\HQ-FS01\Public\miguel-write-test.txt
Expected result:
- share browse succeeds
- file create succeeds
-
file delete succeeds
-
Sign out and sign in to
HQ-CL01asana.garcia. -
From a standard user PowerShell session, validate denied write behavior:
whoami
whoami /groups
dir \\HQ-FS01\Public
'resource access validation' | Out-File \\HQ-FS01\Public\ana-write-test.txt
Expected result:
- directory listing succeeds if
Domain Usersread access is still part of the baseline -
file creation fails with access denied
-
If the denied user receives an unexpected result, validate the effective access model from
HQ-FS01before changing ACLs:
- Record the result as pass only if the allowed user can write and the denied user cannot write.
Validation
- Allowed SMB write access works for the approved user.
- Denied SMB write access fails cleanly for the non-member user.
- The client can still resolve external DNS and reach the internet.
Evidence
whoamiwhoami /groupsdir \\HQ-FS01\Public- output or screenshot of the successful write as
miguel.perez - output or screenshot of the denied write as
ana.garcia - Optional
whoami /groupsoutput for the test user
Rollback
- Correct share or NTFS ACLs on
HQ-FS01if the access model is wrong. - Do not patch around incorrect group design on the client side.