P03-T07 - HQ-FS01 File Services
Objective
Deploy a dedicated file server on VLAN 20, join it to the domain, and prepare SMB resources for workstation resource-access validation.
Validated lab status
Validated against the live lab on Friday, July 17, 2026:
HQ-FS01was deployed and joined successfully.- The base shares
Departments,Public, andBackupswere created successfully. - The access model used later by workstation testing was validated from real domain-joined clients.
\\HQ-FS01\Publicwas validated with:- read access for general domain users
- modify access through
DL-HQ-FS01-Public-RW - successful write/delete by
miguel.perez - denied write by
ana.garcia
Validated boundary:
- real client-side validation currently exists for
\\HQ-FS01\Public Departments\IT,Departments\Finance, andBackupscurrently remain validated here as server-side deployed baselines unless a later client runbook captures effective user validation for those paths
Inputs
| Key | Value |
|---|---|
| Source template | 4000 |
| Target VM | HQ-FS01 |
| Target VLAN | 20 |
| Static IP | 172.20.20.21/24 |
| Gateway | 172.20.20.1 |
| DNS | 172.20.20.11 |
Target State
| Property | Value |
|---|---|
| Role | File server |
| Domain membership | Joined to corp.gntech.me |
| Shares | Departmental shares plus backup staging |
Prechecks
- Complete
P03-T01,P03-T02,P03-T03,P03-T04,P02-T07, andP03-T06. - Verify DNS resolution to
HQ-DC01. - Confirm the server can reach
corp.gntech.me.
Execution
- On
HQ-DC01, create the file-service access groups by running the script locally:
Show full FS01 group-creation script
param(
[string]$GroupsOuDn,
[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 Resolve-Inputs {
$script:GroupsOuDn = if ([string]::IsNullOrWhiteSpace($GroupsOuDn)) {
Read-RequiredValue 'Groups OU DN' 'OU=Groups,OU=GNTECH,DC=corp,DC=gntech,DC=me'
} else {
$GroupsOuDn.Trim()
}
}
function Ensure-Module {
param([string]$Name)
Import-Module $Name
}
function Ensure-Group {
param(
[string]$Name,
[string]$Description
)
$existing = Get-ADGroup -LDAPFilter "(cn=$Name)" -SearchBase $script:GroupsOuDn -ErrorAction SilentlyContinue
if ($existing) {
return [pscustomobject]@{ Name = $Name; Action = 'Exists' }
}
if (-not $ValidateOnly) {
New-ADGroup `
-Name $Name `
-SamAccountName $Name `
-GroupScope DomainLocal `
-GroupCategory Security `
-Path $script:GroupsOuDn `
-Description $Description | Out-Null
}
[pscustomobject]@{ Name = $Name; Action = $(if ($ValidateOnly) { 'Planned' } else { 'Created' }) }
}
Resolve-Inputs
Ensure-Module -Name ActiveDirectory
$definitions = @(
@{ Name = 'DL-HQ-FS01-Public-RW'; Description = 'Modify access to HQ-FS01 Public share' },
@{ Name = 'DL-HQ-FS01-Departments-IT-RW'; Description = 'Modify access to HQ-FS01 Departments IT folder' },
@{ Name = 'DL-HQ-FS01-Departments-Finance-RW'; Description = 'Modify access to HQ-FS01 Departments Finance folder' },
@{ Name = 'DL-HQ-FS01-Backups-RW'; Description = 'Modify access to HQ-FS01 Backups share' }
)
$summary = $definitions | ForEach-Object {
[pscustomobject]@{
Name = $_.Name
Description = $_.Description
Path = $script:GroupsOuDn
}
}
$summary | Format-Table -AutoSize | Out-String | Write-Output
$results = foreach ($definition in $definitions) {
Ensure-Group -Name $definition.Name -Description $definition.Description
}
$results | Format-Table -AutoSize | Out-String | Write-Output
if ($ValidateOnly) {
Write-Output 'Validation mode only. HQ-FS01 groups were not created.'
} else {
Write-Output 'HQ-FS01 groups completed successfully.'
}
Full reference:
- Script Catalog
-
Clone
4000toHQ-FS01onH1, attach the NIC toVLAN 20, and power on the VM. -
Sign in directly to
HQ-FS01as the local built-inAdministrator, then run the stage 1 script locally:
Show full FS01 Stage 1 script
param(
[string]$TargetName,
[string]$IPv4,
[Nullable[int]]$PrefixLength,
[string]$Gateway,
[string[]]$DnsServers,
[string]$DomainName,
[string]$OuPath,
[string]$JoinCredentialUser,
[switch]$RestartIfNeeded
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
function Assert-Elevated {
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw 'Run this script from an elevated PowerShell session.'
}
}
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-RequiredInt {
param([string]$Prompt,[int]$DefaultValue)
while ($true) {
$value = Read-RequiredValue -Prompt $Prompt -DefaultValue $DefaultValue
$parsed = 0
if ([int]::TryParse($value, [ref]$parsed)) { return $parsed }
}
}
function Read-DnsServerList {
param([string[]]$DefaultServers)
while ($true) {
$value = Read-RequiredValue -Prompt 'DNS servers (comma-separated)' -DefaultValue ($DefaultServers -join ',')
$servers = @($value.Split(',') | ForEach-Object { $_.Trim() } | Where-Object { $_ })
if ($servers.Count -gt 0) { return $servers }
}
}
function Resolve-Inputs {
$script:TargetName = if ([string]::IsNullOrWhiteSpace($TargetName)) { Read-RequiredValue 'Target hostname' 'HQ-FS01' } else { $TargetName.Trim() }
$script:IPv4 = if ([string]::IsNullOrWhiteSpace($IPv4)) { Read-RequiredValue 'IPv4 address' '172.20.20.21' } else { $IPv4.Trim() }
$script:PrefixLength = if ($null -eq $PrefixLength) { Read-RequiredInt 'IPv4 prefix length' 24 } else { [int]$PrefixLength }
$script:Gateway = if ([string]::IsNullOrWhiteSpace($Gateway)) { Read-RequiredValue 'Default gateway' '172.20.20.1' } else { $Gateway.Trim() }
$script:DnsServers = if ($DnsServers.Count -eq 0) { Read-DnsServerList -DefaultServers @('172.20.20.11') } else { @($DnsServers | ForEach-Object { $_.Trim() } | Where-Object { $_ }) }
$script:DomainName = if ([string]::IsNullOrWhiteSpace($DomainName)) { Read-RequiredValue 'Domain FQDN' 'corp.gntech.me' } else { $DomainName.Trim() }
$script:OuPath = if ([string]::IsNullOrWhiteSpace($OuPath)) { Read-RequiredValue 'Target server OU DN' 'OU=Servers,OU=GNTECH,DC=corp,DC=gntech,DC=me' } else { $OuPath.Trim() }
$script:JoinCredentialUser = if ([string]::IsNullOrWhiteSpace($JoinCredentialUser)) { Read-RequiredValue 'Domain join user' 'corp.gntech.me\\svc.join.hq' } else { $JoinCredentialUser.Trim() }
}
function Get-PrimaryAdapter {
$adapter = Get-NetAdapter | Where-Object Status -eq 'Up' | Sort-Object ifIndex | Select-Object -First 1
if (-not $adapter) { throw 'No active network adapter was found.' }
return $adapter
}
function Test-DomainReadiness {
param([int]$InterfaceIndex)
$dnsServers = (Get-DnsClientServerAddress -InterfaceIndex $InterfaceIndex -AddressFamily IPv4).ServerAddresses
if (-not $dnsServers -or $dnsServers.Count -eq 0) {
throw 'No IPv4 DNS servers are configured on the active adapter.'
}
try {
Resolve-DnsName $script:DomainName -ErrorAction Stop | Out-Null
}
catch {
throw "DNS validation failed for domain '$($script:DomainName)'."
}
}
function Set-ExactDnsServers {
param([int]$InterfaceIndex,[string[]]$DesiredServers)
$current = (Get-DnsClientServerAddress -InterfaceIndex $InterfaceIndex -AddressFamily IPv4).ServerAddresses
if (@($current) -join ',' -ne @($DesiredServers) -join ',') {
Set-DnsClientServerAddress -InterfaceIndex $InterfaceIndex -ServerAddresses $DesiredServers
}
}
function Set-ExactIPv4Configuration {
param([int]$InterfaceIndex,[string]$DesiredIPv4,[int]$DesiredPrefixLength,[string]$DesiredGateway)
$existing = Get-NetIPAddress -InterfaceIndex $InterfaceIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue
foreach ($address in $existing) {
if ($address.IPAddress -eq $DesiredIPv4 -and $address.PrefixLength -eq $DesiredPrefixLength) { continue }
if ($address.IPAddress -like '169.254.*') { continue }
Remove-NetIPAddress -InterfaceIndex $InterfaceIndex -AddressFamily IPv4 -IPAddress $address.IPAddress -Confirm:$false -ErrorAction SilentlyContinue
}
$desiredAddress = Get-NetIPAddress -InterfaceIndex $InterfaceIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue | Where-Object { $_.IPAddress -eq $DesiredIPv4 -and $_.PrefixLength -eq $DesiredPrefixLength } | Select-Object -First 1
if (-not $desiredAddress) {
New-NetIPAddress -InterfaceIndex $InterfaceIndex -IPAddress $DesiredIPv4 -PrefixLength $DesiredPrefixLength | Out-Null
}
$defaultRoutes = Get-NetRoute -InterfaceIndex $InterfaceIndex -DestinationPrefix '0.0.0.0/0' -AddressFamily IPv4 -ErrorAction SilentlyContinue
foreach ($route in $defaultRoutes) {
if ($route.NextHop -ne $DesiredGateway) {
Remove-NetRoute -InterfaceIndex $InterfaceIndex -DestinationPrefix '0.0.0.0/0' -NextHop $route.NextHop -Confirm:$false -ErrorAction SilentlyContinue
}
}
$desiredRoute = Get-NetRoute -InterfaceIndex $InterfaceIndex -DestinationPrefix '0.0.0.0/0' -AddressFamily IPv4 -ErrorAction SilentlyContinue | Where-Object NextHop -eq $DesiredGateway | Select-Object -First 1
if (-not $desiredRoute) {
New-NetRoute -InterfaceIndex $InterfaceIndex -DestinationPrefix '0.0.0.0/0' -NextHop $DesiredGateway -RouteMetric 256 | Out-Null
}
}
Resolve-Inputs
Assert-Elevated
$adapter = Get-PrimaryAdapter
Set-ExactIPv4Configuration -InterfaceIndex $adapter.ifIndex -DesiredIPv4 $script:IPv4 -DesiredPrefixLength $script:PrefixLength -DesiredGateway $script:Gateway
Set-ExactDnsServers -InterfaceIndex $adapter.ifIndex -DesiredServers $script:DnsServers
Set-DnsClient -InterfaceIndex $adapter.ifIndex -ConnectionSpecificSuffix $script:DomainName -RegisterThisConnectionsAddress $true -UseSuffixWhenRegistering $true
Test-DomainReadiness -InterfaceIndex $adapter.ifIndex
$computerSystem = Get-CimInstance Win32_ComputerSystem
$renameRequired = $env:COMPUTERNAME -ne $script:TargetName
$joinRequired = -not $computerSystem.PartOfDomain
$restartRequired = $false
if ($renameRequired -or $joinRequired) {
$credential = Get-Credential -UserName $script:JoinCredentialUser -Message 'Enter domain join credential for HQ-FS01'
if ($renameRequired) {
Add-Computer -DomainName $script:DomainName -Credential $credential -OUPath $script:OuPath -NewName $script:TargetName -Force
$restartRequired = $true
Write-Output 'The server was renamed and joined to the domain.'
} else {
Add-Computer -DomainName $script:DomainName -Credential $credential -OUPath $script:OuPath -Force
$restartRequired = $true
Write-Output 'The server was joined to the domain.'
}
} else {
Write-Output 'No rename or domain join action is required.'
}
Write-Output "TargetName: $($script:TargetName)"
Write-Output "IPv4: $($script:IPv4)/$($script:PrefixLength)"
Write-Output "Gateway: $($script:Gateway)"
Write-Output "DnsServers: $($script:DnsServers -join ', ')"
Write-Output "DomainValidation: OK"
if ($restartRequired -and $RestartIfNeeded) {
Restart-Computer -Force
} elseif ($restartRequired) {
Write-Output 'A restart is required to complete HQ-FS01 stage 1.'
}
Full reference:
This script:
- prompts for hostname, IP, gateway, DNS, and domain join values if not passed as parameters
- renames the server, applies the static
VLAN 20network baseline, and validates DNS reachability - prompts securely for join credentials instead of storing them in the repository
- joins the server to the domain and can restart automatically if requested
Operational note:
- this script must be run from an elevated PowerShell session
-
the script now fails before the join if domain DNS validation is not working
-
After the domain join reboot, sign in with a domain admin account and run the share-configuration script locally on
HQ-FS01:
Full reference:
This script installs the file server role if needed, creates the validated base share paths, creates the Departments, Public, and Backups SMB shares, and applies the tested NTFS/share ACL baseline.
Show full share-configuration script
param(
[string]$ShareRoot,
[string]$DomainNetbios,
[switch]$ValidateOnly
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
function Assert-Elevated {
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($identity)
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw 'Run this script from an elevated PowerShell session.'
}
}
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 Resolve-Inputs {
$script:ShareRoot = if ([string]::IsNullOrWhiteSpace($ShareRoot)) { Read-RequiredValue 'Share root path' 'C:\Shares' } else { $ShareRoot.Trim() }
$script:DomainNetbios = if ([string]::IsNullOrWhiteSpace($DomainNetbios)) { Read-RequiredValue 'Domain NetBIOS name' 'GNTECH' } else { $DomainNetbios.Trim() }
}
function Ensure-Directory {
param([string]$Path)
if (-not (Test-Path -LiteralPath $Path)) {
if (-not $ValidateOnly) { New-Item -Path $Path -ItemType Directory -Force | Out-Null }
if ($ValidateOnly) { return 'Planned' }
return 'Created'
}
'Exists'
}
function Set-FolderAcl {
param([string]$Path,[array]$Rules)
$acl = New-Object System.Security.AccessControl.DirectorySecurity
$acl.SetAccessRuleProtection($true, $false)
$inheritanceFlags = [System.Security.AccessControl.InheritanceFlags]'ContainerInherit, ObjectInherit'
$propagationFlags = [System.Security.AccessControl.PropagationFlags]::None
foreach ($rule in $Rules) {
$fsRights = [System.Security.AccessControl.FileSystemRights]$rule.Rights
$accessType = [System.Security.AccessControl.AccessControlType]::Allow
$aclRule = New-Object System.Security.AccessControl.FileSystemAccessRule($rule.Identity,$fsRights,$inheritanceFlags,$propagationFlags,$accessType)
$acl.AddAccessRule($aclRule) | Out-Null
}
if (-not $ValidateOnly) { Set-Acl -Path $Path -AclObject $acl }
if ($ValidateOnly) { 'Planned' } else { 'Applied' }
}
function Ensure-SmbShareState {
param([string]$Name,[string]$Path,[string]$Description,[string[]]$FullAccess,[string[]]$ChangeAccess,[string[]]$ReadAccess,[string]$FolderEnumerationMode = 'AccessBased')
$existing = Get-SmbShare -Name $Name -ErrorAction SilentlyContinue
if ($existing -and $existing.Path -ne $Path) {
throw "SMB share '$Name' exists at '$($existing.Path)' instead of '$Path'. Correct it manually before rerunning."
}
if (-not $ValidateOnly) {
if (-not $existing) {
$splat = @{ Name = $Name; Path = $Path; Description = $Description; FolderEnumerationMode = $FolderEnumerationMode }
if ($FullAccess) { $splat.FullAccess = $FullAccess }
if ($ChangeAccess) { $splat.ChangeAccess = $ChangeAccess }
if ($ReadAccess) { $splat.ReadAccess = $ReadAccess }
New-SmbShare @splat | Out-Null
}
else {
Set-SmbShare -Name $Name -Description $Description -FolderEnumerationMode $FolderEnumerationMode -Force | Out-Null
$desiredAccess = @()
$desiredAccess += @($FullAccess | ForEach-Object { @{ Account = $_; Right = 'Full' } })
$desiredAccess += @($ChangeAccess | ForEach-Object { @{ Account = $_; Right = 'Change' } })
$desiredAccess += @($ReadAccess | ForEach-Object { @{ Account = $_; Right = 'Read' } })
foreach ($entry in $desiredAccess) {
Grant-SmbShareAccess -Name $Name -AccountName $entry.Account -AccessRight $entry.Right -Force | Out-Null
}
}
}
if ($ValidateOnly) { if ($existing) { 'Existing path validated' } else { 'Planned' } }
elseif ($existing) { 'Reconciled' } else { 'Created' }
}
function Assert-AccountResolvable {
param([string[]]$Identity)
foreach ($item in $Identity) {
try { ([System.Security.Principal.NTAccount]$item).Translate([System.Security.Principal.SecurityIdentifier]) | Out-Null }
catch { throw "Required account or group '$item' cannot be resolved on this server." }
}
}
Resolve-Inputs
Assert-Elevated
$departmentsRoot = Join-Path $script:ShareRoot 'Departments'
$publicRoot = Join-Path $script:ShareRoot 'Public'
$backupsRoot = Join-Path $script:ShareRoot 'Backups'
$itRoot = Join-Path $departmentsRoot 'IT'
$financeRoot = Join-Path $departmentsRoot 'Finance'
$domainUsers = "$($script:DomainNetbios)\Domain Users"
$domainAdmins = "$($script:DomainNetbios)\Domain Admins"
$builtinAdmins = 'BUILTIN\Administrators'
$systemAccount = 'NT AUTHORITY\SYSTEM'
Assert-AccountResolvable -Identity @(
$domainUsers,$domainAdmins,$builtinAdmins,$systemAccount,
"$($script:DomainNetbios)\DL-HQ-FS01-Departments-IT-RW",
"$($script:DomainNetbios)\DL-HQ-FS01-Departments-Finance-RW",
"$($script:DomainNetbios)\DL-HQ-FS01-Public-RW",
"$($script:DomainNetbios)\DL-HQ-FS01-Backups-RW"
)
$feature = Get-WindowsFeature FS-FileServer
if (-not $feature.Installed -and -not $ValidateOnly) { Install-WindowsFeature FS-FileServer | Out-Null }
Write-Output "FS-FileServer: $(if ($feature.Installed) { 'Installed' } elseif ($ValidateOnly) { 'Missing' } else { 'Installed now' })"
$folders = @($script:ShareRoot,$departmentsRoot,$publicRoot,$backupsRoot,$itRoot,$financeRoot)
$folderResults = foreach ($folder in $folders) {
[pscustomobject]@{ Path = $folder; Action = (Ensure-Directory -Path $folder) }
}
$folderResults | Format-Table -AutoSize | Out-String | Write-Output
Set-FolderAcl -Path $departmentsRoot -Rules @(
@{ Identity = $systemAccount; Rights = 'FullControl' },
@{ Identity = $builtinAdmins; Rights = 'FullControl' },
@{ Identity = $domainAdmins; Rights = 'FullControl' },
@{ Identity = $domainUsers; Rights = 'ReadAndExecute, Synchronize' }
)
Set-FolderAcl -Path $itRoot -Rules @(
@{ Identity = $systemAccount; Rights = 'FullControl' },
@{ Identity = $builtinAdmins; Rights = 'FullControl' },
@{ Identity = $domainAdmins; Rights = 'FullControl' },
@{ Identity = "$($script:DomainNetbios)\DL-HQ-FS01-Departments-IT-RW"; Rights = 'Modify, Synchronize' }
)
Set-FolderAcl -Path $financeRoot -Rules @(
@{ Identity = $systemAccount; Rights = 'FullControl' },
@{ Identity = $builtinAdmins; Rights = 'FullControl' },
@{ Identity = $domainAdmins; Rights = 'FullControl' },
@{ Identity = "$($script:DomainNetbios)\DL-HQ-FS01-Departments-Finance-RW"; Rights = 'Modify, Synchronize' }
)
Set-FolderAcl -Path $publicRoot -Rules @(
@{ Identity = $systemAccount; Rights = 'FullControl' },
@{ Identity = $builtinAdmins; Rights = 'FullControl' },
@{ Identity = $domainAdmins; Rights = 'FullControl' },
@{ Identity = $domainUsers; Rights = 'ReadAndExecute, Synchronize' },
@{ Identity = "$($script:DomainNetbios)\DL-HQ-FS01-Public-RW"; Rights = 'Modify, Synchronize' }
)
Set-FolderAcl -Path $backupsRoot -Rules @(
@{ Identity = $systemAccount; Rights = 'FullControl' },
@{ Identity = $builtinAdmins; Rights = 'FullControl' },
@{ Identity = $domainAdmins; Rights = 'FullControl' },
@{ Identity = "$($script:DomainNetbios)\DL-HQ-FS01-Backups-RW"; Rights = 'Modify, Synchronize' }
)
$shareResults = @(
[pscustomobject]@{
Name = 'Departments'
Action = (Ensure-SmbShareState -Name 'Departments' -Path $departmentsRoot -Description 'Departmental data root' -FullAccess @($domainAdmins) -ReadAccess @($domainUsers))
},
[pscustomobject]@{
Name = 'Public'
Action = (Ensure-SmbShareState -Name 'Public' -Path $publicRoot -Description 'Broad collaboration share' -FullAccess @($domainAdmins) -ChangeAccess @("$($script:DomainNetbios)\DL-HQ-FS01-Public-RW") -ReadAccess @($domainUsers))
},
[pscustomobject]@{
Name = 'Backups'
Action = (Ensure-SmbShareState -Name 'Backups' -Path $backupsRoot -Description 'Backup staging share' -FullAccess @($domainAdmins) -ChangeAccess @("$($script:DomainNetbios)\DL-HQ-FS01-Backups-RW"))
}
)
$shareResults | Format-Table -AutoSize | Out-String | Write-Output
if ($ValidateOnly) {
Write-Output 'Validation mode only. HQ-FS01 share configuration was not applied.'
} else {
Write-Output 'HQ-FS01 share configuration completed successfully.'
}
Preflight and re-run behavior:
- run
-ValidateOnlyfirst to resolve all required domain groups, inspect planned folders/shares, and confirm the role state without installing the role or changing ACLs - normal execution stops if an existing share name points to a different path
- a normal re-run reapplies the authoritative NTFS baseline and reconciles the required share grants
- NTFS inheritance is intentionally disabled on the managed business folders; rerunning replaces their explicit ACLs with the documented baseline, so place application-specific ACL changes in a separately reviewed extension rather than directly on these managed roots
Runbook boundary:
- this runbook validates server-side file-service preparation on
HQ-FS01 - this runbook does not by itself close end-user access validation for every share path
- effective workstation access must be validated from real domain-joined clients in the dependent client runbooks
Validated access model for the base shares:
Departments- share level:
Domain Admins = Full,Domain Users = Read - NTFS root:
Domain Users = Read - subfolders such as
ITandFinanceare writable only through the matchingDL-HQ-FS01-*groups Public- share level:
Domain Admins = Full,DL-HQ-FS01-Public-RW = Change,Domain Users = Read - NTFS:
Domain Users = Read,DL-HQ-FS01-Public-RW = Modify Backups- share level:
Domain Admins = Full,DL-HQ-FS01-Backups-RW = Change - NTFS:
DL-HQ-FS01-Backups-RW = Modify
Validation scope for this runbook:
Publicis the share path later validated from real clients inP03-T08Departments\IT,Departments\Finance, andBackupsare validated here as configured server-side baselines unless a later client-validation runbook proves effective access from a real user session- do not describe the non-
Publicshare paths as end-user validated unless that evidence is added later
Validation
HQ-FS01resolves and pingsHQ-DC01.- Domain join completes without trust errors.
Get-SmbShareshowsDepartments,Public, andBackups.Get-SmbShareAccess -Name PublicshowsDL-HQ-FS01-Public-RWwith change access andDomain Userswith read access.Get-ADGroupMember 'DL-HQ-FS01-Public-RW'confirms the approved write members.HQ-FS01is domain joined and ready for client access validation in the next runbook.- server-side share and ACL validation completes here; end-user access validation continues in
P03-T08. - evidence for this runbook must distinguish server-side deployment success from real client-side authorization success
Read-only preflight:
Evidence
Get-SmbShareGet-SmbShareAccess -Name PublicGet-ADGroupMember 'DL-HQ-FS01-Public-RW'Get-ADComputer HQ-FS01- Screenshot only if required by handoff
Rollback
- Remove the clone and rebuild from template if join or storage baseline is incorrect.
- Remove incorrect shares and ACLs only if the server baseline is otherwise sound.