Skip to content

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.perez was confirmed able to browse, create, and delete files in \\\\HQ-FS01\\Public.
  • ana.garcia remained 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-T06 and P03-T07.
  • Confirm HQ-FS01 resolves 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.ps1 has already been executed with the default PublicWriteGroupSamAccountName or 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:

.\Create-HQ-TestUsers.ps1
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.'
  1. Prepare the exact validation subjects before touching the clients:

  2. allowed write test:

  3. user: miguel.perez
  4. expected allowed path: \\HQ-FS01\Public
  5. expected result: can create and delete a test file
  6. denied write test:
  7. user: ana.garcia
  8. expected denied path: \\HQ-FS01\Public
  9. expected result: can browse and read if allowed, but cannot create a file

  10. Sign in to HQ-CL02 as miguel.perez.

  11. 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.

  1. 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-CL01 as ana.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 Users read 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-FS01 before changing ACLs:

Get-SmbShareAccess -Name Public
Get-ADGroupMember 'DL-HQ-FS01-Public-RW'
  1. 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

  • whoami
  • whoami /groups
  • dir \\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 /groups output for the test user

Rollback

  • Correct share or NTFS ACLs on HQ-FS01 if the access model is wrong.
  • Do not patch around incorrect group design on the client side.