[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidateSet("DC1","PostDC1","DC2","FileServer")]
[string]$Role
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
function Assert-Admin {
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) { throw "Run this script in an elevated PowerShell session." }
}
function Install-Features {
param([string[]]$Names)
Import-Module ServerManager -ErrorAction Stop
$missing = $Names | Where-Object { -not (Get-WindowsFeature $_).Installed }
if ($missing) {
Install-WindowsFeature -Name $missing -IncludeManagementTools | Out-Null
}
}
function New-StrongPassword {
param([int]$Length = 20)
# Generates a random password with upper, lower, digits, special
$upper = "ABCDEFGHJKLMNPQRSTUVWXYZ"
$lower = "abcdefghijkmnopqrstuvwxyz"
$digits = "23456789"
$spec = "!@#$%^&*()-_=+[]{}:,.?"
$all = ($upper + $lower + $digits + $spec).ToCharArray()
$rand = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
function Get-RandInt([int]$max) {
$b = New-Object byte[] 4
$rand.GetBytes($b)
[Math]::Abs([BitConverter]::ToInt32($b,0)) % $max
}
$chars = @()
$chars += $upper[ (Get-RandInt $upper.Length) ]
$chars += $lower[ (Get-RandInt $lower.Length) ]
$chars += $digits[(Get-RandInt $digits.Length)]
$chars += $spec[ (Get-RandInt $spec.Length) ]
while ($chars.Count -lt $Length) { $chars += $all[(Get-RandInt $all.Length)] }
$chars = $chars | Sort-Object { Get-RandInt 100000 }
-join $chars
}
function Get-Config {
# Adjust these to your lab network
[pscustomobject]@{
DomainFqdn = "BaaS.com"
Netbios = "BAAS"
Site1 = "Montreal"
Site2 = "Quebec"
# DHCP config (update for your subnet)
DhcpScopeNetwork = "192.168.50.0"
DhcpScopeMask = "255.255.255.0"
DhcpRangeStart = "192.168.50.10"
DhcpRangeEnd = "192.168.50.254"
DhcpExclusionStart = "192.168.50.1"
DhcpExclusionEnd = "192.168.50.9"
DhcpLeaseDays = 3
DhcpRouter = "192.168.50.1"
DnsServers = @("192.168.50.10") # DC1 IP
# File services
DataRoot = "D:\DATA"
HomeRoot = "D:\USERS"
ShareHost = $env:COMPUTERNAME # set to FileServer name if separate, else DC1
SharePrefix = "" # keep empty for \\HOST\Marketing etc
# Admin account settings
BuiltinAdminNewName = "adm-rid500"
NamedAdminGivenName = "Admin"
NamedAdminSurname = "BaaS"
NamedAdminSam = "adm.baas"
# You will be prompted for the named admin password securely.
# User naming convention: first initial + surname, normalized
# Example: Linda Brown -> lbrown ; Valérie Tremblay -> vtremblay
UserUPNSuffix = "BaaS.com"
}
}
function Normalize-Sam {
param([string]$FirstName,[string]$LastName)
$raw = ($FirstName.Substring(0,1) + $LastName).ToLowerInvariant()
# remove accents and non letters
$norm = $raw.Normalize([Text.NormalizationForm]::FormD)
$sb = New-Object System.Text.StringBuilder
foreach ($c in $norm.ToCharArray()) {
$cat = [Globalization.CharUnicodeInfo]::GetUnicodeCategory($c)
if ($cat -ne [Globalization.UnicodeCategory]::NonSpacingMark) { [void]$sb.Append($c) }
}
$clean = ($sb.ToString() -replace "[^a-z0-9\.]","")
$clean
}
function Ensure-ADModules {
Import-Module ActiveDirectory -ErrorAction Stop
Import-Module GroupPolicy -ErrorAction Stop
}
function Ensure-Folder {
param([string]$Path)
if (-not (Test-Path $Path)) { New-Item -ItemType Directory -Path $Path -Force | Out-Null }
}
function Set-NTFSPermissionsExact {
param(
[string]$Path,
[bool]$DisableInheritance,
[System.Security.AccessControl.FileSystemAccessRule[]]$Rules
)
$acl = Get-Acl -Path $Path
if ($DisableInheritance) {
# disable inheritance and remove inherited rules
$acl.SetAccessRuleProtection($true, $false)
foreach ($r in $acl.Access) { $acl.RemoveAccessRule($r) | Out-Null }
}
foreach ($r in $Rules) { $acl.AddAccessRule($r) | Out-Null }
Set-Acl -Path $Path -AclObject $acl
}
function New-Rule {
param([string]$Identity,[string]$Rights,[string]$Type = "Allow",[string]$Inherit = "ContainerInherit,ObjectInherit",[string]$Prop = "None")
$inheritFlags = [System.Security.AccessControl.InheritanceFlags]$Inherit
$propFlags = [System.Security.AccessControl.PropagationFlags]$Prop
$accessType = [System.Security.AccessControl.AccessControlType]$Type
$fsr = [System.Security.AccessControl.FileSystemRights]$Rights
New-Object System.Security.AccessControl.FileSystemAccessRule($Identity,$fsr,$inheritFlags,$propFlags,$accessType)
}
function Create-GPOAndLink {
param(
[string]$Name,
[string]$LinkDn,
[int]$Order = 1
)
$gpo = Get-GPO -Name $Name -ErrorAction SilentlyContinue
if (-not $gpo) { $gpo = New-GPO -Name $Name }
$link = Get-GPInheritance -Target $LinkDn
$already = $link.GpoLinks | Where-Object { $_.DisplayName -eq $Name }
if (-not $already) {
New-GPLink -Name $Name -Target $LinkDn -LinkEnabled Yes -Order $Order | Out-Null
}
Get-GPO -Name $Name
}
function Get-GpoSysvolPath {
param([Guid]$GpoId,[string]$DomainFqdn)
$domain = $DomainFqdn
"\\$domain\SYSVOL\$domain\Policies\{$GpoId}"
}
function Write-DrivesXml {
param(
[string]$GpoSysvol,
[hashtable[]]$DriveMaps
)
$dir = Join-Path $GpoSysvol "User\Preferences\Drives"
Ensure-Folder $dir
$xmlPath = Join-Path $dir "Drives.xml"
$doc = New-Object System.Xml.XmlDocument
$doc.AppendChild($doc.CreateXmlDeclaration("1.0","utf-8",$null)) | Out-Null
$root = $doc.CreateElement("Drives")
$root.SetAttribute("clsid","{CCB7E38E-0DDB-4B3C-8B9E-000000000000}") | Out-Null
$doc.AppendChild($root) | Out-Null
foreach ($m in $DriveMaps) {
# Item
$drive = $doc.CreateElement("Drive")
$drive.SetAttribute("clsid","{C631DF4C-088F-4156-B058-4375F0853CD8}") | Out-Null
$drive.SetAttribute("name",$m.Name) | Out-Null
$drive.SetAttribute("image","2") | Out-Null
$drive.SetAttribute("changed",(Get-Date).ToString("yyyy-MM-dd HH:mm:ss")) | Out-Null
$drive.SetAttribute("uid","{" + ([guid]::NewGuid().Guid) + "}") | Out-Null
$drive.SetAttribute("userContext","1") | Out-Null
$drive.SetAttribute("removePolicy","0") | Out-Null
$props = $doc.CreateElement("Properties")
$props.SetAttribute("action","U") | Out-Null
$props.SetAttribute("thisDrive",$m.Letter) | Out-Null
$props.SetAttribute("allDrives","NOCHANGE") | Out-Null
$props.SetAttribute("userName","") | Out-Null
$props.SetAttribute("path",$m.Path) | Out-Null
$props.SetAttribute("label",$m.Label) | Out-Null
$props.SetAttribute("persistent","1") | Out-Null
$props.SetAttribute("useLetter","1") | Out-Null
$props.SetAttribute("letter",$m.Letter.TrimEnd(":")) | Out-Null
$props.SetAttribute("disconnect","0") | Out-Null
$props.SetAttribute("showAll","0") | Out-Null
$props.SetAttribute("useUNC","0") | Out-Null
$props.SetAttribute("hide","0") | Out-Null
$props.SetAttribute("noReconnect","0") | Out-Null
$drive.AppendChild($props) | Out-Null
# Optional targeting by security group
if ($m.ContainsKey("GroupSid") -and $m.GroupSid) {
$filters = $doc.CreateElement("Filters")
$fg = $doc.CreateElement("FilterGroup")
$fg.SetAttribute("clsid","{3610EDA5-77EF-11D2-8DC5-00C04FA31A66}") | Out-Null
$fg.SetAttribute("name","") | Out-Null
$fg.SetAttribute("sid",$m.GroupSid) | Out-Null
$fg.SetAttribute("userContext","1") | Out-Null
$fg.SetAttribute("not","0") | Out-Null
$fg.SetAttribute("bool","AND") | Out-Null
$filters.AppendChild($fg) | Out-Null
$drive.AppendChild($filters) | Out-Null
}
$root.AppendChild($drive) | Out-Null
}
$doc.Save($xmlPath)
}
function Write-GptTmplUserRights {
param(
[string]$GpoSysvol,
[string[]]$DenyLogonLocallySids,
[string[]]$DenyLogonRdpSids
)
$secDir = Join-Path $GpoSysvol "Machine\Microsoft\Windows NT\SecEdit"
Ensure-Folder $secDir
$infPath = Join-Path $secDir "GptTmpl.inf"
# Minimal security template for user rights assignment
$content = @()
$content += "[Unicode]"
$content += "Unicode=yes"
$content += "[Version]"
$content += "signature=`"`$CHICAGO`$`""
$content += "Revision=1"
$content += "[Privilege Rights]"
# SeDenyInteractiveLogonRight: Deny log on locally
if ($DenyLogonLocallySids.Count -gt 0) {
$content += "SeDenyInteractiveLogonRight = " + ($DenyLogonLocallySids -join ",")
}
# SeDenyRemoteInteractiveLogonRight: Deny log on through Remote Desktop Services
if ($DenyLogonRdpSids.Count -gt 0) {
$content += "SeDenyRemoteInteractiveLogonRight = " + ($DenyLogonRdpSids -join ",")
}
Set-Content -Path $infPath -Value $content -Encoding Unicode
}
function Set-DomainPasswordAndLockoutPolicy {
param([string]$DomainFqdn)
Ensure-ADModules
Set-ADDefaultDomainPasswordPolicy -Identity $DomainFqdn `
-MinPasswordLength 12 `
-ComplexityEnabled $true `
-PasswordHistoryCount 5 `
-MaxPasswordAge (New-TimeSpan -Days 180) `
-MinPasswordAge (New-TimeSpan -Days 1) `
-ReversibleEncryptionEnabled $false
# Lockout: 3 attempts, 15 minutes duration, reset counter 15 minutes
Set-ADDefaultDomainPasswordPolicy -Identity $DomainFqdn `
-LockoutThreshold 3 `
-LockoutDuration (New-TimeSpan -Minutes 15) `
-LockoutObservationWindow (New-TimeSpan -Minutes 15)
}
function Set-DnsHardening {
# Secure dynamic updates for AD integrated zones
Import-Module DnsServer -ErrorAction Stop
$zones = Get-DnsServerZone | Where-Object { $_.IsDsIntegrated -eq $true -and $_.ZoneType -eq "Primary" }
foreach ($z in $zones) {
Set-DnsServerPrimaryZone -Name $z.ZoneName -SecureSecondaries NoTransfer | Out-Null
Set-DnsServerZone -Name $z.ZoneName -DynamicUpdate Secure | Out-Null
}
}
function Configure-Dhcp {
param($cfg)
Import-Module DhcpServer -ErrorAction Stop
# Authorize DHCP in AD (requires domain admin)
try { Add-DhcpServerInDC -DnsName "$($env:COMPUTERNAME).$($cfg.DomainFqdn)" -IPAddress $cfg.DnsServers[0] | Out-Null } catch {}
# Create scope if missing
$scope = Get-DhcpServerv4Scope -ErrorAction SilentlyContinue | Where-Object { $_.ScopeId -eq $cfg.DhcpScopeNetwork }
if (-not $scope) {
Add-DhcpServerv4Scope -Name "BaaS Scope" -StartRange $cfg.DhcpRangeStart -EndRange $cfg.DhcpRangeEnd -SubnetMask $cfg.DhcpScopeMask -State Active | Out-Null
}
# Exclusions 1-9
Add-DhcpServerv4ExclusionRange -ScopeId $cfg.DhcpScopeNetwork -StartRange $cfg.DhcpExclusionStart -EndRange $cfg.DhcpExclusionEnd -ErrorAction SilentlyContinue | Out-Null
# Lease duration
Set-DhcpServerv4Scope -ScopeId $cfg.DhcpScopeNetwork -LeaseDuration (New-TimeSpan -Days $cfg.DhcpLeaseDays) | Out-Null
# Options: router and DNS
Set-DhcpServerv4OptionValue -ScopeId $cfg.DhcpScopeNetwork -Router $cfg.DhcpRouter -DnsServer $cfg.DnsServers -DnsDomain $cfg.DomainFqdn | Out-Null
}
function Build-OUAndGroupsAndUsers {
param($cfg)
Ensure-ADModules
$domainDn = (Get-ADDomain).DistinguishedName
# OU layout:
# OU=BaaS,DC=...
# OU=Montreal
# OU=Users, OU=Computers, OU=Groups
# OU=Quebec
# OU=Users, OU=Computers, OU=Groups
$rootOuName = "BaaS"
$rootOuDn = "OU=$rootOuName,$domainDn"
if (-not (Get-ADOrganizationalUnit -LDAPFilter "(distinguishedName=$rootOuDn)" -ErrorAction SilentlyContinue)) {
New-ADOrganizationalUnit -Name $rootOuName -Path $domainDn -ProtectedFromAccidentalDeletion $true | Out-Null
}
foreach ($site in @($cfg.Site1,$cfg.Site2)) {
$siteDn = "OU=$site,$rootOuDn"
if (-not (Get-ADOrganizationalUnit -LDAPFilter "(distinguishedName=$siteDn)" -ErrorAction SilentlyContinue)) {
New-ADOrganizationalUnit -Name $site -Path $rootOuDn -ProtectedFromAccidentalDeletion $true | Out-Null
}
foreach ($child in @("Users","Computers","Groups")) {
$childDn = "OU=$child,OU=$site,$rootOuDn"
if (-not (Get-ADOrganizationalUnit -LDAPFilter "(distinguishedName=$childDn)" -ErrorAction SilentlyContinue)) {
New-ADOrganizationalUnit -Name $child -Path $siteDn -ProtectedFromAccidentalDeletion $true | Out-Null
}
}
}
# Role groups (domain wide)
$groupsOuDn = "OU=Groups,OU=$($cfg.Site1),$rootOuDn" # keep groups in Montreal OU, but groups are domain wide
$roleGroups = @(
"GG_PDG",
"GG_Informatique",
"GG_Finance",
"GG_Marketing",
"GG_RH",
"GG_Production",
"GG_Direction",
"GG_ServiceClients",
"GG_TousEmployes"
)
foreach ($g in $roleGroups) {
if (-not (Get-ADGroup -Filter "SamAccountName -eq '$g'" -ErrorAction SilentlyContinue)) {
New-ADGroup -Name $g -SamAccountName $g -GroupScope Global -GroupCategory Security -Path $groupsOuDn | Out-Null
}
}
# Users list from document
$users = @(
# Montreal
@{ First="Linda"; Last="Brown"; Role="PDG"; Site=$cfg.Site1 }
@{ First="Chris"; Last="Martinez"; Role="Informatique"; Site=$cfg.Site1 }
@{ First="Paula"; Last="Rasmussen"; Role="Finance"; Site=$cfg.Site1 }
@{ First="Nicole"; Last="Dion"; Role="Marketing"; Site=$cfg.Site1 }
@{ First="Valerie"; Last="Tremblay"; Role="RH"; Site=$cfg.Site1 }
@{ First="Donald"; Last="Campbell"; Role="RH"; Site=$cfg.Site1 }
@{ First="Mark"; Last="Harlan"; Role="Production"; Site=$cfg.Site1 }
@{ First="Donna"; Last="Jewett"; Role="Finance"; Site=$cfg.Site1 }
@{ First="Freddie"; Last="Burkhardt"; Role="Direction"; Site=$cfg.Site1 }
@{ First="James"; Last="Williams"; Role="Finance"; Site=$cfg.Site1 }
@{ First="Christine"; Last="Mondou"; Role="Marketing"; Site=$cfg.Site1 }
@{ First="Charles"; Last="Montgomery"; Role="ServiceClients"; Site=$cfg.Site1 }
@{ First="Jeanie"; Last="Spencer"; Role="ServiceClients"; Site=$cfg.Site1 }
@{ First="Sandra"; Last="McCarthy"; Role="ServiceClients"; Site=$cfg.Site1 }
# Quebec
@{ First="Justine"; Last="West"; Role="Direction"; Site=$cfg.Site2 }
@{ First="Laura"; Last="Young"; Role="Finance"; Site=$cfg.Site2 }
@{ First="Sandra"; Last="Novello"; Role="ServiceClients"; Site=$cfg.Site2 }
@{ First="Tony"; Last="Brossard"; Role="ServiceClients"; Site=$cfg.Site2 }
@{ First="Kristina"; Last="Lewis"; Role="Direction"; Site=$cfg.Site2 }
@{ First="Paul"; Last="Samson"; Role="Finance"; Site=$cfg.Site2 }
@{ First="Thomas"; Last="Levesque"; Role="ServiceClients"; Site=$cfg.Site2 }
@{ First="Ronald"; Last="Turcotte"; Role="Marketing"; Site=$cfg.Site2 }
@{ First="Michael"; Last="Richard"; Role="ServiceClients"; Site=$cfg.Site2 }
@{ First="Robert"; Last="Jarrett"; Role="Marketing"; Site=$cfg.Site2 }
)
# Create users with random initial password, force change at logon
foreach ($u in $users) {
$sam = Normalize-Sam -FirstName $u.First -LastName $u.Last
$upn = "$sam@$($cfg.UserUPNSuffix)"
$path = "OU=Users,OU=$($u.Site),OU=$($rootOuName),$domainDn"
$existing = Get-ADUser -Filter "SamAccountName -eq '$sam'" -ErrorAction SilentlyContinue
if (-not $existing) {
$pwd = New-StrongPassword -Length 18
$sec = ConvertTo-SecureString $pwd -AsPlainText -Force
New-ADUser -Name "$($u.First) $($u.Last)" `
-GivenName $u.First -Surname $u.Last `
-SamAccountName $sam `
-UserPrincipalName $upn `
-Path $path `
-Enabled $true `
-AccountPassword $sec `
-ChangePasswordAtLogon $true | Out-Null
Write-Host "Created user $sam with initial password (store it securely): $pwd"
}
# Membership: everyone in GG_TousEmployes, plus role group
Add-ADGroupMember -Identity "GG_TousEmployes" -Members $sam -ErrorAction SilentlyContinue | Out-Null
switch ($u.Role) {
"PDG" { Add-ADGroupMember -Identity "GG_PDG" -Members $sam -ErrorAction SilentlyContinue | Out-Null }
"Informatique" { Add-ADGroupMember -Identity "GG_Informatique" -Members $sam -ErrorAction SilentlyContinue | Out-Null }
"Finance" { Add-ADGroupMember -Identity "GG_Finance" -Members $sam -ErrorAction SilentlyContinue | Out-Null }
"Marketing" { Add-ADGroupMember -Identity "GG_Marketing" -Members $sam -ErrorAction SilentlyContinue | Out-Null }
"RH" { Add-ADGroupMember -Identity "GG_RH" -Members $sam -ErrorAction SilentlyContinue | Out-Null }
"Production" { Add-ADGroupMember -Identity "GG_Production" -Members $sam -ErrorAction SilentlyContinue | Out-Null }
"Direction" { Add-ADGroupMember -Identity "GG_Direction" -Members $sam -ErrorAction SilentlyContinue | Out-Null }
"ServiceClients" { Add-ADGroupMember -Identity "GG_ServiceClients" -Members $sam -ErrorAction SilentlyContinue | Out-Null }
}
}
}
function Harden-BuiltinDomainAdministrator {
param($cfg)
Ensure-ADModules
$rid500 = Get-ADUser -Filter 'ObjectSID -like "*-500"' -Properties ObjectSID
if (-not $rid500) { throw "RID 500 account not found." }
# Rename (CN) and set sAMAccountName and display name
if ($rid500.SamAccountName -ne $cfg.BuiltinAdminNewName) {
$newCn = $cfg.BuiltinAdminNewName
Rename-ADObject -Identity $rid500.DistinguishedName -NewName $newCn
Set-ADUser -Identity $rid500.ObjectSID.Value `
-SamAccountName $cfg.BuiltinAdminNewName `
-DisplayName "Domain Administrator (renamed)" `
-Description "Built-in RID 500 account renamed for hardening"
}
# Set a strong password (printed once, you must store it securely)
$newPwd = New-StrongPassword -Length 24
Set-ADAccountPassword -Identity $rid500.ObjectSID.Value -Reset -NewPassword (ConvertTo-SecureString $newPwd -AsPlainText -Force)
Write-Host "RID 500 account password set (store it securely): $newPwd"
# Create a GPO that denies interactive and RDP logon for this account
$domainDn = (Get-ADDomain).DistinguishedName
$rootOuDn = "OU=BaaS,$domainDn"
$gpo = Create-GPOAndLink -Name "GPO - Deny logon for RID500" -LinkDn $rootOuDn -Order 1
$gpoSysvol = Get-GpoSysvolPath -GpoId $gpo.Id -DomainFqdn $cfg.DomainFqdn
$sid = $rid500.ObjectSID.Value
Write-GptTmplUserRights -GpoSysvol $gpoSysvol `
-DenyLogonLocallySids @("*$sid") `
-DenyLogonRdpSids @("*$sid")
}
function Create-NamedDomainAdmin {
param($cfg)
Ensure-ADModules
$domainDn = (Get-ADDomain).DistinguishedName
$path = "OU=Users,OU=$($cfg.Site1),OU=BaaS,$domainDn"
$existing = Get-ADUser -Filter "SamAccountName -eq '$($cfg.NamedAdminSam)'" -ErrorAction SilentlyContinue
if (-not $existing) {
$sec = Read-Host "Enter password for named admin $($cfg.NamedAdminSam)" -AsSecureString
New-ADUser -Name "$($cfg.NamedAdminGivenName) $($cfg.NamedAdminSurname)" `
-GivenName $cfg.NamedAdminGivenName -Surname $cfg.NamedAdminSurname `
-SamAccountName $cfg.NamedAdminSam `
-UserPrincipalName "$($cfg.NamedAdminSam)@$($cfg.UserUPNSuffix)" `
-Path $path `
-Enabled $true `
-AccountPassword $sec `
-PasswordNeverExpires $false `
-ChangePasswordAtLogon $false | Out-Null
}
Add-ADGroupMember -Identity "Domain Admins" -Members $cfg.NamedAdminSam -ErrorAction SilentlyContinue | Out-Null
Write-Host "Named domain admin ensured: $($cfg.NamedAdminSam)"
}
function Configure-FileSharesAndPermissions {
param($cfg)
Ensure-ADModules
# Folders
Ensure-Folder $cfg.DataRoot
Ensure-Folder $cfg.HomeRoot
$folders = @(
"Marketing","Finance","Direction","ServiceClients","RH","Production","Public"
)
foreach ($f in $folders) { Ensure-Folder (Join-Path $cfg.DataRoot $f) }
# Special subfolders
Ensure-Folder (Join-Path (Join-Path $cfg.DataRoot "ServiceClients") "VIP")
Ensure-Folder (Join-Path (Join-Path $cfg.DataRoot "RH") "Confidentiel")
# Shares (use simple names to match drive maps)
Install-Features -Names @("FS-FileServer")
foreach ($f in $folders) {
$shareName = $f
$path = Join-Path $cfg.DataRoot $f
if (-not (Get-SmbShare -Name $shareName -ErrorAction SilentlyContinue)) {
New-SmbShare -Name $shareName -Path $path -FullAccess "BUILTIN\Administrators" | Out-Null
}
}
if (-not (Get-SmbShare -Name "Users" -ErrorAction SilentlyContinue)) {
New-SmbShare -Name "Users" -Path $cfg.HomeRoot -FullAccess "BUILTIN\Administrators" | Out-Null
}
# Recommended approach: SMB share perms wide, enforce with NTFS
# NTFS base rules per requirements:
# - Role folder: role group Modify
# - PDG and Informatique: admin rights to all except VIP and Confidentiel
# - Direction: Finance RW, ServiceClients Modify, RH RW
# - Production: Marketing RW
# - RH: ServiceClients RW
# - Everyone (all staff): Public Modify
# Special:
# - VIP: only Direction and ServiceClients Modify
# - Confidentiel: RH + PDG Modify, Informatique Read, others none
$domain = (Get-ADDomain).DNSRoot
function DN([string]$name) { "$domain\$name" }
$admins = @(
New-Rule -Identity (DN "GG_PDG") -Rights "FullControl",
New-Rule -Identity (DN "GG_Informatique") -Rights "FullControl",
New-Rule -Identity "BUILTIN\Administrators" -Rights "FullControl",
New-Rule -Identity "SYSTEM" -Rights "FullControl"
)
# Helper to set base ACL for a folder with role Modify plus cross-access rules
function Set-RoleFolderAcl([string]$folderName) {
$path = Join-Path $cfg.DataRoot $folderName
$rules = @()
$rules += $admins
switch ($folderName) {
"Marketing" {
$rules += New-Rule -Identity (DN "GG_Marketing") -Rights "Modify"
$rules += New-Rule -Identity (DN "GG_Production") -Rights "Modify"
}
"Finance" {
$rules += New-Rule -Identity (DN "GG_Finance") -Rights "Modify"
$rules += New-Rule -Identity (DN "GG_Direction") -Rights "Modify"
}
"Direction" {
$rules += New-Rule -Identity (DN "GG_Direction") -Rights "Modify"
}
"ServiceClients" {
$rules += New-Rule -Identity (DN "GG_ServiceClients") -Rights "Modify"
$rules += New-Rule -Identity (DN "GG_Direction") -Rights "Modify"
$rules += New-Rule -Identity (DN "GG_RH") -Rights "Modify"
}
"RH" {
$rules += New-Rule -Identity (DN "GG_RH") -Rights "Modify"
$rules += New-Rule -Identity (DN "GG_Direction") -Rights "Modify"
}
"Production" {
$rules += New-Rule -Identity (DN "GG_Production") -Rights "Modify"
}
"Public" {
$rules += New-Rule -Identity (DN "GG_TousEmployes") -Rights "Modify"
}
}
Set-NTFSPermissionsExact -Path $path -DisableInheritance $true -Rules $rules
}
foreach ($f in $folders) { Set-RoleFolderAcl -folderName $f }
# Special folder: VIP
$vipPath = Join-Path (Join-Path $cfg.DataRoot "ServiceClients") "VIP"
$vipRules = @(
New-Rule -Identity (DN "GG_Direction") -Rights "Modify",
New-Rule -Identity (DN "GG_ServiceClients") -Rights "Modify",
New-Rule -Identity "BUILTIN\Administrators" -Rights "FullControl",
New-Rule -Identity "SYSTEM" -Rights "FullControl"
)
Set-NTFSPermissionsExact -Path $vipPath -DisableInheritance $true -Rules $vipRules
# Special folder: Confidentiel
$confPath = Join-Path (Join-Path $cfg.DataRoot "RH") "Confidentiel"
$confRules = @(
New-Rule -Identity (DN "GG_RH") -Rights "Modify",
New-Rule -Identity (DN "GG_PDG") -Rights "Modify",
New-Rule -Identity (DN "GG_Informatique") -Rights "ReadAndExecute",
New-Rule -Identity "BUILTIN\Administrators" -Rights "FullControl",
New-Rule -Identity "SYSTEM" -Rights "FullControl"
)
Set-NTFSPermissionsExact -Path $confPath -DisableInheritance $true -Rules $confRules
# Home folders per user (U:)
$allUsers = Get-ADUser -Filter * -SearchBase ( (Get-ADDomain).DistinguishedName ) -Properties SamAccountName,HomeDirectory,HomeDrive
foreach ($u in $allUsers) {
if ($u.SamAccountName -in @("krbtgt",$cfg.NamedAdminSam,$cfg.BuiltinAdminNewName)) { continue }
$userDir = Join-Path $cfg.HomeRoot $u.SamAccountName
Ensure-Folder $userDir
# ACL: user full control, admins full control, system full control
$homeRules = @(
New-Rule -Identity "$domain\$($u.SamAccountName)" -Rights "FullControl",
New-Rule -Identity "BUILTIN\Administrators" -Rights "FullControl",
New-Rule -Identity "SYSTEM" -Rights "FullControl"
)
Set-NTFSPermissionsExact -Path $userDir -DisableInheritance $true -Rules $homeRules
$unc = "\\$($cfg.ShareHost)\Users\$($u.SamAccountName)"
Set-ADUser -Identity $u.SamAccountName -HomeDirectory $unc -HomeDrive "U:" | Out-Null
}
Write-Host "File shares and NTFS permissions configured."
}
function Create-GPOs {
param($cfg)
Ensure-ADModules
$domainDn = (Get-ADDomain).DistinguishedName
$rootOuDn = "OU=BaaS,$domainDn"
# 1) GPO for drive mappings (M,F,I,S,R,O,P) and home drive is already in AD user profile (U:)
$gpoDrive = Create-GPOAndLink -Name "GPO - Drive mappings" -LinkDn $rootOuDn -Order 2
$sysvolDrive = Get-GpoSysvolPath -GpoId $gpoDrive.Id -DomainFqdn $cfg.DomainFqdn
$domain = (Get-ADDomain).DNSRoot
function SidOf([string]$groupSam) { (Get-ADGroup -Identity $groupSam).SID.Value }
$maps = @(
@{ Name="Marketing (M:)"; Letter="M:"; Path="\\$($cfg.ShareHost)\Marketing"; Label="Marketing"; GroupSid=(SidOf "GG_Marketing") }
@{ Name="Finance (F:)"; Letter="F:"; Path="\\$($cfg.ShareHost)\Finance"; Label="Finance"; GroupSid=(SidOf "GG_Finance") }
@{ Name="Direction (I:)"; Letter="I:"; Path="\\$($cfg.ShareHost)\Direction"; Label="Direction"; GroupSid=(SidOf "GG_Direction") }
@{ Name="ServiceClients (S:)";Letter="S:"; Path="\\$($cfg.ShareHost)\ServiceClients";Label="ServiceClients";GroupSid=(SidOf "GG_ServiceClients") }
@{ Name="RH (R:)"; Letter="R:"; Path="\\$($cfg.ShareHost)\RH"; Label="RH"; GroupSid=(SidOf "GG_RH") }
@{ Name="Production (O:)"; Letter="O:"; Path="\\$($cfg.ShareHost)\Production"; Label="Production"; GroupSid=(SidOf "GG_Production") }
@{ Name="Public (P:)"; Letter="P:"; Path="\\$($cfg.ShareHost)\Public"; Label="Public"; GroupSid=(SidOf "GG_TousEmployes") }
)
Write-DrivesXml -GpoSysvol $sysvolDrive -DriveMaps $maps
# 2) GPO for workstation standard desktop restrictions + privacy + autorun + lock screen + disable guest via startup script
$gpoWs = Create-GPOAndLink -Name "GPO - Workstations baseline" -LinkDn $rootOuDn -Order 3
# User settings (HKCU) via GPO registry values
# Remove Run menu
Set-GPRegistryValue -Name $gpoWs.DisplayName -Key "HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer" -ValueName "NoRun" -Type DWord -Value 1
# Restrict system tools (Task Manager, Registry Tools, CMD)
Set-GPRegistryValue -Name $gpoWs.DisplayName -Key "HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\System" -ValueName "DisableTaskMgr" -Type DWord -Value 1
Set-GPRegistryValue -Name $gpoWs.DisplayName -Key "HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\System" -ValueName "DisableRegistryTools" -Type DWord -Value 1
Set-GPRegistryValue -Name $gpoWs.DisplayName -Key "HKCU\Software\Policies\Microsoft\Windows\System" -ValueName "DisableCMD" -Type DWord -Value 1
# Restrict recycle bin properties
Set-GPRegistryValue -Name $gpoWs.DisplayName -Key "HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer" -ValueName "NoPropertiesRecycleBin" -Type DWord -Value 1
# Screen lock after 15 minutes (user side)
Set-GPRegistryValue -Name $gpoWs.DisplayName -Key "HKCU\Software\Policies\Microsoft\Windows\Control Panel\Desktop" -ValueName "ScreenSaveActive" -Type String -Value "1"
Set-GPRegistryValue -Name $gpoWs.DisplayName -Key "HKCU\Software\Policies\Microsoft\Windows\Control Panel\Desktop" -ValueName "ScreenSaveTimeOut" -Type String -Value "900"
Set-GPRegistryValue -Name $gpoWs.DisplayName -Key "HKCU\Software\Policies\Microsoft\Windows\Control Panel\Desktop" -ValueName "ScreenSaverIsSecure" -Type String -Value "1"
# Computer settings (HKLM)
# Disable autorun for removable media
Set-GPRegistryValue -Name $gpoWs.DisplayName -Key "HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer" -ValueName "NoDriveTypeAutoRun" -Type DWord -Value 255
# Lock screen policy (machine side backup)
Set-GPRegistryValue -Name $gpoWs.DisplayName -Key "HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System" -ValueName "InactivityTimeoutSecs" -Type DWord -Value 900
# Reduce diagnostic data (note: some editions enforce minimums)
Set-GPRegistryValue -Name $gpoWs.DisplayName -Key "HKLM\Software\Policies\Microsoft\Windows\DataCollection" -ValueName "AllowTelemetry" -Type DWord -Value 0
# Disable activity history
Set-GPRegistryValue -Name $gpoWs.DisplayName -Key "HKLM\Software\Policies\Microsoft\Windows\System" -ValueName "PublishUserActivities" -Type DWord -Value 0
Set-GPRegistryValue -Name $gpoWs.DisplayName -Key "HKLM\Software\Policies\Microsoft\Windows\System" -ValueName "UploadUserActivities" -Type DWord -Value 0
# "Disable PowerShell" (practical approach):
# - Block script execution via policy
Set-GPRegistryValue -Name $gpoWs.DisplayName -Key "HKLM\Software\Policies\Microsoft\Windows\PowerShell" -ValueName "EnableScripts" -Type DWord -Value 0
# - Optional: add Software Restriction Policy entries would be more complex; this blocks scripts but not interactive console.
# Disable local Guest account using startup script in the GPO (reliable across Pro editions)
$sysvolWs = Get-GpoSysvolPath -GpoId $gpoWs.Id -DomainFqdn $cfg.DomainFqdn
$startupDir = Join-Path $sysvolWs "Machine\Scripts\Startup"
Ensure-Folder $startupDir
$startupScript = Join-Path $startupDir "Disable-Guest.cmd"
Set-Content -Path $startupScript -Value "@echo off`r`nnet user Guest /active:no" -Encoding ASCII
# Register startup script in scripts.ini
$iniDir = Join-Path $sysvolWs "Machine\Scripts"
Ensure-Folder $iniDir
$iniPath = Join-Path $iniDir "scripts.ini"
$ini = @()
$ini += "[Startup]"
$ini += "0CmdLine=Disable-Guest.cmd"
$ini += "0Parameters="
Set-Content -Path $iniPath -Value $ini -Encoding ASCII
# 3) BitLocker GPO (store recovery keys in AD)
$gpoBl = Create-GPOAndLink -Name "GPO - BitLocker (AD recovery)" -LinkDn $rootOuDn -Order 4
# Core BitLocker policy keys (Windows Components\BitLocker Drive Encryption)
# Enforce AD backup for OS drive recovery information
Set-GPRegistryValue -Name $gpoBl.DisplayName -Key "HKLM\Software\Policies\Microsoft\FVE" -ValueName "OSActiveDirectoryBackup" -Type DWord -Value 1
Set-GPRegistryValue -Name $gpoBl.DisplayName -Key "HKLM\Software\Policies\Microsoft\FVE" -ValueName "OSRequireActiveDirectoryBackup" -Type DWord -Value 1
Set-GPRegistryValue -Name $gpoBl.DisplayName -Key "HKLM\Software\Policies\Microsoft\FVE" -ValueName "OSRecovery" -Type DWord -Value 1
Set-GPRegistryValue -Name $gpoBl.DisplayName -Key "HKLM\Software\Policies\Microsoft\FVE" -ValueName "OSManageDRA" -Type DWord -Value 0
# Require TPM (typical baseline; adjust if your VMs do not have vTPM)
Set-GPRegistryValue -Name $gpoBl.DisplayName -Key "HKLM\Software\Policies\Microsoft\FVE" -ValueName "UseTPM" -Type DWord -Value 1
Set-GPRegistryValue -Name $gpoBl.DisplayName -Key "HKLM\Software\Policies\Microsoft\FVE" -ValueName "UseTPMPIN" -Type DWord -Value 0
Set-GPRegistryValue -Name $gpoBl.DisplayName -Key "HKLM\Software\Policies\Microsoft\FVE" -ValueName "UseTPMKey" -Type DWord -Value 0
Set-GPRegistryValue -Name $gpoBl.DisplayName -Key "HKLM\Software\Policies\Microsoft\FVE" -ValueName "UseAdvancedStartup" -Type DWord -Value 1
Write-Host "GPOs created: Drive mappings, Workstations baseline, BitLocker."
}
Assert-Admin
$cfg = Get-Config
switch ($Role) {
"DC1" {
# Install AD DS + DNS + DHCP now, then create the forest/domain
Install-Features -Names @("AD-Domain-Services","DNS","DHCP","GPMC","RSAT-AD-Tools")
Import-Module ADDSDeployment -ErrorAction Stop
Write-Host "Promoting this server to new forest/domain $($cfg.DomainFqdn)."
$dsrm = Read-Host "Enter DSRM password" -AsSecureString
Install-ADDSForest `
-DomainName $cfg.DomainFqdn `
-DomainNetbiosName $cfg.Netbios `
-SafeModeAdministratorPassword $dsrm `
-InstallDNS:$true `
-Force:$true
Write-Host "DC1 promotion initiated. The server will reboot automatically."
}
"PostDC1" {
Ensure-ADModules
# Domain password and lockout policies
Set-DomainPasswordAndLockoutPolicy -DomainFqdn $cfg.DomainFqdn
# Harden built-in RID 500 admin and deny interactive/remote logon
Harden-BuiltinDomainAdministrator -cfg $cfg
# Create named Domain Admin
Create-NamedDomainAdmin -cfg $cfg
# DNS hardening for AD integrated zones and secure dynamic updates only
Set-DnsHardening
# DHCP config (lease 3 days, exclusions 1-9, range 10-254)
Configure-Dhcp -cfg $cfg
# Users, OUs, groups for Montreal and Quebec
Build-OUAndGroupsAndUsers -cfg $cfg
# File shares and permissions (DC1 acts as file server per project)
Configure-FileSharesAndPermissions -cfg $cfg
# GPOs: drive mapping, workstation baseline, BitLocker
Create-GPOs -cfg $cfg
Write-Host "Post-DC1 configuration completed."
}
"DC2" {
Install-Features -Names @("AD-Domain-Services","RSAT-AD-Tools")
Import-Module ADDSDeployment -ErrorAction Stop
$cred = Get-Credential -Message "Enter Domain Admin credentials to promote DC2 into $($cfg.DomainFqdn)"
$dsrm = Read-Host "Enter DSRM password for DC2" -AsSecureString
Install-ADDSDomainController `
-DomainName $cfg.DomainFqdn `
-Credential $cred `
-SafeModeAdministratorPassword $dsrm `
-InstallDns:$false `
-Force:$true
Write-Host "DC2 promotion initiated. The server will reboot automatically."
}
"FileServer" {
# Optional separate file server
Install-Features -Names @("FS-FileServer","GPMC","RSAT-AD-Tools")
Ensure-ADModules
Configure-FileSharesAndPermissions -cfg $cfg
Write-Host "File server configuration completed."
}
}