266 lines
12 KiB
PowerShell
266 lines
12 KiB
PowerShell
<#
|
|
Join-Domain-USB.ps1
|
|
Usage: placer ce script sur la clé USB avec (optionnel) key.bin + pwd.sec
|
|
- key.bin : clé symétrique (base64)
|
|
- pwd.sec : mot de passe chiffré (ConvertFrom-SecureString -Key)
|
|
Logs -> %SystemRoot%\Logs (ex: C:\Windows\Logs)
|
|
#>
|
|
|
|
# -----------------------
|
|
# Configuration utilisateur
|
|
# -----------------------
|
|
$user_migration_edu = "EDU\ajoutpc"
|
|
$secteur = "NRD2"
|
|
|
|
# Noms de fichiers
|
|
$keyFileName = "key.bin"
|
|
$pwdFileName = "pwd.sec"
|
|
$logPrefix = "migration-log"
|
|
|
|
# -----------------------
|
|
# Déterminer le dossier du script (fonctionne si lancé depuis clé ou ailleurs)
|
|
# -----------------------
|
|
# $PSScriptRoot fonctionne quand le script est exécuté depuis un fichier,
|
|
# sinon on essaie de récupérer via MyInvocation; sinon on prend le répertoire courant.
|
|
$scriptDir = $null
|
|
if ($PSScriptRoot) {
|
|
$scriptDir = $PSScriptRoot
|
|
} else {
|
|
try {
|
|
$scriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
|
|
} catch {
|
|
$scriptDir = (Get-Location).Path
|
|
}
|
|
}
|
|
if (-not $scriptDir) { $scriptDir = (Get-Location).Path }
|
|
|
|
# Emplacements des fichiers sur la clé (ou dossier du script)
|
|
$keyPath = Join-Path -Path $scriptDir -ChildPath $keyFileName
|
|
$pwdPath = Join-Path -Path $scriptDir -ChildPath $pwdFileName
|
|
|
|
# Emplacement des logs : dossier "Logs" de Windows
|
|
$windowsLogsDir = Join-Path -Path $env:windir -ChildPath "Logs"
|
|
if (-not (Test-Path -Path $windowsLogsDir)) {
|
|
New-Item -Path $windowsLogsDir -ItemType Directory -Force | Out-Null
|
|
}
|
|
$logFilePath = Join-Path -Path $windowsLogsDir -ChildPath ("$logPrefix-$(Get-Date -Format 'yyyy-MM-dd').log")
|
|
|
|
# -----------------------
|
|
# Helpers
|
|
# -----------------------
|
|
|
|
function Assert-RunAsAdministrator {
|
|
$currentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
|
$principal = New-Object System.Security.Principal.WindowsPrincipal($currentIdentity)
|
|
$isAdmin = $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
|
$isSystem = $currentIdentity.Name -eq 'NT AUTHORITY\SYSTEM'
|
|
|
|
Write-LogMessage "Identité courante : $($currentIdentity.Name). IsAdmin=$isAdmin IsSystem=$isSystem"
|
|
|
|
if (-not ($isAdmin -or $isSystem)) {
|
|
throw "Le script doit être exécuté avec des privilèges élevés. Exécution automatique MDT non interactive attendue."
|
|
}
|
|
}
|
|
|
|
function Write-LogMessage {
|
|
param([string]$Message, [string]$Type = "INFO")
|
|
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
|
|
$entry = "[$timestamp] [$Type] $Message"
|
|
try { Add-Content -Path $global:logFilePath -Value $entry -ErrorAction Stop } catch { Write-Output "ERREUR LOG: $($_)"; }
|
|
Write-Output $entry
|
|
}
|
|
|
|
function Set-RestrictiveAcl {
|
|
param([string]$Path)
|
|
try {
|
|
$acl = Get-Acl -Path $Path
|
|
$acl.SetAccessRuleProtection($true, $false)
|
|
# Remove existing explicit rules (best-effort)
|
|
$acl.Access | ForEach-Object { $acl.RemoveAccessRule($_) }
|
|
$admin = New-Object System.Security.Principal.NTAccount("BUILTIN\Administrators")
|
|
$system = New-Object System.Security.Principal.NTAccount("NT AUTHORITY\SYSTEM")
|
|
$ruleAdmin = New-Object System.Security.AccessControl.FileSystemAccessRule($admin,"FullControl","None","Allow")
|
|
$ruleSystem = New-Object System.Security.AccessControl.FileSystemAccessRule($system,"FullControl","None","Allow")
|
|
$acl.AddAccessRule($ruleAdmin)
|
|
$acl.AddAccessRule($ruleSystem)
|
|
Set-Acl -Path $Path -AclObject $acl
|
|
} catch {
|
|
# sur certaines clés USB, Set-Acl échoue; on ignore mais logguer
|
|
Write-LogMessage "Impossible d'appliquer ACL restrictive sur $Path : $_" -Type "WARNING"
|
|
}
|
|
}
|
|
|
|
# -----------------------
|
|
# Elevation
|
|
# -----------------------
|
|
Assert-RunAsAdministrator
|
|
|
|
# Créer fichier log si absent
|
|
if (-not (Test-Path -Path $logFilePath)) { New-Item -Path $logFilePath -ItemType File -Force | Out-Null }
|
|
Write-LogMessage "Dossier script : $scriptDir"
|
|
Write-LogMessage "Chemin key : $keyPath"
|
|
Write-LogMessage "Chemin mot de passe : $pwdPath"
|
|
|
|
# -----------------------
|
|
# Gestion clé et mot de passe (dans dossier du script / clé USB)
|
|
# -----------------------
|
|
function New-SymmetricKeyFile {
|
|
param([string]$OutPath)
|
|
$rng = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
|
|
$bytes = New-Object byte[] 32
|
|
$rng.GetBytes($bytes)
|
|
$b64 = [Convert]::ToBase64String($bytes)
|
|
Set-Content -Path $OutPath -Value $b64 -Force
|
|
Set-RestrictiveAcl -Path $OutPath
|
|
return $bytes
|
|
}
|
|
|
|
function Get-SymmetricKeyBytes {
|
|
param([string]$KeyFile)
|
|
if (-not (Test-Path -Path $KeyFile)) { return $null }
|
|
try {
|
|
$b64 = Get-Content -Path $KeyFile -Raw
|
|
return [Convert]::FromBase64String($b64.Trim())
|
|
} catch {
|
|
return $null
|
|
}
|
|
}
|
|
|
|
function New-EncryptedPasswordFile {
|
|
param([string]$KeyFile, [string]$PwdFile, [string]$UserName)
|
|
Write-Host "Fichier mot de passe chiffré absent. Initialisation..."
|
|
$plain = Read-Host -AsSecureString "Saisir le mot de passe pour $UserName (sera chiffré et stocké sur la clé)"
|
|
$keyBytes = Get-SymmetricKeyBytes -KeyFile $KeyFile
|
|
if (-not $keyBytes) { $keyBytes = New-SymmetricKeyFile -OutPath $KeyFile }
|
|
$enc = $plain | ConvertFrom-SecureString -Key $keyBytes
|
|
Set-Content -Path $PwdFile -Value $enc -Force
|
|
Set-RestrictiveAcl -Path $PwdFile
|
|
Write-LogMessage "Fichier mot de passe chiffré créé sur la clé : $PwdFile"
|
|
}
|
|
|
|
# Si absent, le script ne peut pas fonctionner en mode automatique
|
|
if (-not (Test-Path -Path $pwdPath) -or -not (Test-Path -Path $keyPath)) {
|
|
Write-LogMessage "Fichiers requis manquants : $keyPath ou $pwdPath" -Type "ERROR"
|
|
throw "Les fichiers $keyFileName et $pwdFileName sont obligatoires pour une exécution automatique."
|
|
}
|
|
|
|
# Lire et déchiffrer
|
|
$keyBytes = Get-SymmetricKeyBytes -KeyFile $keyPath
|
|
if (-not $keyBytes) {
|
|
Write-LogMessage "Impossible de lire la clé symétrique ($keyPath)." -Type "ERROR"
|
|
throw "Clé symétrique manquante ou illisible."
|
|
}
|
|
try {
|
|
$encString = Get-Content -Path $pwdPath -Raw
|
|
$securePassword = ConvertTo-SecureString $encString -Key $keyBytes
|
|
} catch {
|
|
Write-LogMessage "Erreur lors du déchiffrement du mot de passe : $_" -Type "ERROR"
|
|
throw "Impossible de déchiffrer le mot de passe."
|
|
}
|
|
$cred_edu = New-Object System.Management.Automation.PSCredential -ArgumentList $user_migration_edu, $securePassword
|
|
|
|
# -----------------------
|
|
# OU / UAI (inchangé)
|
|
# -----------------------
|
|
$OuSites = @{
|
|
"009" = "009-Labbe-LaMadeleine"; "027" = "027-VertesFeuilles-StAndreLille"; "073" = "073-StExupery-Halluin";
|
|
"074" = "074-Magny-LyslezLannoy"; "075" = "075-Mongy-MarcqenBaroeul"; "076" = "076-Europeenne-MarcqenBaroeul";
|
|
"077" = "077-Kernanec-MarcqenBaroeul"; "078" = "078-Loucheur-Roubaix"; "079" = "079-Baudelaire-Roubaix";
|
|
"080" = "080-Rostand-Roubaix"; "081" = "081-Moulin-Roubaix"; "082" = "082-Turgot-Roubaix";
|
|
"083" = "083-Lavoisier-Roubaix"; "084" = "084-Meersch-Roubaix"; "085" = "085-Esaat-Roubaix";
|
|
"087" = "087-Corbusier-Tourcoing"; "089" = "089-Gambetta-Tourcoing"; "090" = "090-Colbert-Tourcoing";
|
|
"091" = "091-Sevigne-Tourcoing"; "092" = "092-Derycke-VilleneuvedAscq"; "093" = "093-Queneau-VilleneuvedAscq";
|
|
"094" = "094-Cousteau-Wasquehal"; "095" = "095-Zola-Wattrelos"; "096" = "096-Savary-Wattrelos"
|
|
}
|
|
$OuUAI = @{
|
|
"009" = "0590122M"; "027" = "0592832H"; "073" = "0592850C"; "074" = "0594380R"; "075" = "0590144L";
|
|
"076" = "0597100X"; "077" = "0590143K"; "078" = "0590187H"; "079" = "0590182C"; "080" = "0590184E";
|
|
"081" = "0590185F"; "082" = "0590186G"; "083" = "0590189K"; "084" = "0590181B"; "085" = "0594391C";
|
|
"087" = "0590216P"; "089" = "0590212K"; "090" = "0590214M"; "091" = "0590215N"; "092" = "0594375K";
|
|
"093" = "0594424N"; "094" = "0590249A"; "095" = "0590233H"; "096" = "0595787V"
|
|
}
|
|
|
|
# -----------------------
|
|
# Détection hostname / site et jointure domaine (inchangé mais robuste)
|
|
# -----------------------
|
|
$hostname = $env:COMPUTERNAME
|
|
if ($hostname -match '^s20(\d{2})') {
|
|
$siteNumber = $matches[1]
|
|
$siteNumberFormatted = "{0:D3}" -f [int]$siteNumber
|
|
if ($OuSites.ContainsKey($siteNumberFormatted)) {
|
|
$Site = "02-$siteNumberFormatted"
|
|
$Ou_site = $OuSites[$siteNumberFormatted]
|
|
$uai = $OuUAI[$siteNumberFormatted]
|
|
$Ou = "OU=Pedagogie,OU=$uai,OU=Parc-Informatique,OU=$Ou_site,OU=$secteur,OU=HDF,DC=EDU,DC=HDF"
|
|
# $dc = "$Site-dc.EDU.HDF"
|
|
$dc = "999-dc01.EDU.HDF"
|
|
Write-LogMessage "Traitement du site : $Site avec l'OU : $Ou_site"
|
|
|
|
try {
|
|
$netConfigs = Get-NetIPConfiguration -All -ErrorAction Stop
|
|
} catch {
|
|
Write-LogMessage "Impossible de récupérer la configuration IP : $_" -Type "ERROR"
|
|
throw $_
|
|
}
|
|
|
|
$joined = $false
|
|
foreach ($cfg in $netConfigs) {
|
|
foreach ($addr in $cfg.IPv4Address) {
|
|
$ipv4 = $addr.IPAddress
|
|
if (-not $ipv4) { continue }
|
|
Write-LogMessage "IP détectée : $ipv4"
|
|
$parts = $ipv4.Split('.')
|
|
if ($parts.Length -lt 4) { Write-LogMessage "IP $ipv4 non conforme." -Type "WARNING"; continue }
|
|
try { $thirdOctet = [int]$parts[2] } catch { Write-LogMessage "Impossible de parser $ipv4" -Type "WARNING"; continue }
|
|
|
|
if ($thirdOctet -ge 192 -and $thirdOctet -le 224) {
|
|
$isDomain = $false
|
|
try {
|
|
$dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
|
|
if ($dom -and $dom.Name -eq "EDU.HDF") { $isDomain = $true }
|
|
} catch { $isDomain = $false }
|
|
|
|
if (-not $isDomain) {
|
|
try {
|
|
$envKey = "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
|
|
$siteExists = Get-ItemProperty -Path $envKey -Name "site" -ErrorAction SilentlyContinue
|
|
if (-not $siteExists) {
|
|
New-ItemProperty -Path $envKey -Name "site" -Value $Site -PropertyType String -Force | Out-Null
|
|
Write-LogMessage "Variable d'environnement 'site' créée : $Site"
|
|
}
|
|
} catch { Write-LogMessage "Impossible d'écrire la variable d'environnement 'site' : $_" -Type "WARNING" }
|
|
|
|
try {
|
|
Remove-Computer -UnjoinDomaincredential $cred_edu -WorkgroupName "WORKGROUP" -Force -ErrorAction SilentlyContinue
|
|
} catch { Write-LogMessage "Remove-Computer NON requis / échoué : $_" -Type "INFO" }
|
|
|
|
try {
|
|
Add-Computer -DomainName "edu.hdf" -Credential $cred_edu -OUPath $Ou -Force -ErrorAction Stop
|
|
Write-LogMessage "La machine a été ajoutée au domaine $dc avec succès."
|
|
$joined = $true
|
|
break
|
|
} catch {
|
|
Write-LogMessage "Erreur lors de l'ajout au domaine (interface $ipv4) : $_" -Type "ERROR"
|
|
}
|
|
} else {
|
|
Write-LogMessage "Machine déjà membre du domaine EDU.HDF."
|
|
$joined = $true
|
|
break
|
|
}
|
|
} else {
|
|
Write-LogMessage "IP $ipv4 hors des plages autorisées (3ème octet = $thirdOctet)."
|
|
}
|
|
}
|
|
if ($joined) { break }
|
|
}
|
|
|
|
if (-not $joined) { Write-LogMessage "Aucune interface IP valide trouvée pour joindre le domaine." -Type "ERROR" }
|
|
} else {
|
|
Write-LogMessage "Le numéro de site $siteNumberFormatted n'est pas dans le tableau d'OU définies." -Type "ERROR"
|
|
}
|
|
} else {
|
|
Write-LogMessage "Hostname ne correspond pas au format attendu (ex: s2009, s2027, s2073 à s2096)." -Type "ERROR"
|
|
}
|
|
|
|
Write-LogMessage "Traitement terminé."
|