
Sommaire
Présentation
Vous trouverez ici, quelques petits bouts de code (Powershell) de mon cru ou glanés sur le net. Bien que perfectibles, et sans aucune prétention, j’espère qu’à défaut d’une solution, ils vous apporteront un minimum d’inspiration.
Verrouillage permanent du pavé numérique
Cet exemple particulier permet d’imposer l’activation permanente du pavé numérique. Autrement dit, en cas d’extinction volontaire ou accidentelle, le pavé est automatiquement réactivé 😉
param ( $Interval = 3 )
do {
while (-not ([console]::NumberLock)) {
$wsh = New-Object -ComObject WScript.Shell
$wsh.SendKeys('{NUMLOCK}')
}
sleep -Seconds $Interval
} until ($null)
Ou sous la forme d’une commande unique pour ne pas utiliser de script !
PowerShell -Command "& { do { while (-not ([console]::NumberLock)) { $wsh = New-Object -ComObject WScript.Shell; $wsh.SendKeys('{NUMLOCK}')} sleep -Seconds 3 } until ($null) }" -NonInteractive -NoProfile -Nologo
Lire l’attribut homedirectory d’un utilisateur AD et mapper son lecteur
Cette besoin récurrent peut être traité par le biais des GPO/GPP (Redirection + Mappage via Preferences) mais on peut aussi exécuter un script Powershell au logon 😀
En 1er lieu, pour une lecture “rapide” de l’attribut homeDirectory de l’utilisateur courant j’utilise la petite astuce suivante :
([ADSI]"LDAP://$(whoami /fqdn)").Properties["homeDirectory"].Value
Ensuite pour le mappage du lecteur personnel, il faut que le dossier existe et dans le cas contraire, le créer (sous réserve des droits suffisants sur le parent) puis ensuite créer un PSDrive avec l’option –Persist (donc PSv3 ou + coté poste client) ou à l’ancienne via “net use …” si la version Powershell est antérieure 🙁
Ce qui pourrait donner un code du genre :
$homedir = ([ADSI]"LDAP://$(whoami /fqdn)").Properties["homeDirectory"].Value
if (-not (Test-Path $homedir)) {
New-Item -Path $homedir -ItemType Directory -Force -ErrorAction Stop
}
if ($PSVersionTable.PSVersion.Major -ge 3) {
New-PSDrive -Name P -PSProvider FileSystem -Root $homedir -Persist
}
else { # si Powershell v2
& { Net Use P: $homedir}
}
Exemple de Fonction Write-Log
Les techniques de journalisation d’activité sont nombreuses. L’idée ici est de vous proposer une petite fonction basique, offrant quelques options pour afficher un message en couleur et/ou alimenter un journal le cas échéant.
function Write-Log {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$False)]
[ValidateSet("INFO","WARN","ERROR", "DEBUG", "SUCCESS")]
[String]$Level = "INFO",
[Parameter(Mandatory=$True)]
[string]$Message,
[Parameter(Mandatory=$False)]
[string]$logfile
)
$Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss")
$Line = "$Stamp $Level $Message"
If ($logfile) {
$String = [Text.Encoding]::ASCII.GetString([Text.Encoding]::GetEncoding("Cyrillic").GetBytes($Line))
Add-Content $logfile -Value $String
}
If ($Verbose) {
switch ($Level) {
"INFO" { $Color = 'white' }
"SUCCESS" { $Color = 'green' }
"WARN" { $Color = 'yellow' }
"ERROR" { $Color = 'red' }
"DEBUG" { $Color = 'cyan' }
}
Write-Host -Fore $Color $Line
}
}
Afficher le jour de la semaine en français
Cet exemple n’apportera probablement pas un grand intérêt pour les sysadmin mais je pense qu’il constitue une réflexion intéressante sur le plan pédagogique et l’apprentissage de Powershell.
En premier lieu, nous constatons que les commandes ci-après nous renvoient bien un affichage détaillé, dont le jour de semaine, en français.
$PSCulture # doit renvoyer fr-FR
Get-Date '31/12/2020' # format français jj/mm/aaaa
[datetime] '01/31/2020' # format anglais mm/dd/yyyy
Donc il est légitime d’en déduire que la propriété “.DayOfWeek” affiche le jour de la semaine. Mais, contrairement à l’invocation de l’affichage détaillé, l’appel à la seule propriété ne semble pas prendre la langue ($PSCulture) en considération, Cette subtilité vient du type “Enum” appliqué sur l’affichage de la propriété.
(Get-Date $Date).DayOfWeek # renvoie le nom du jour (en Anglais)
((Get-Date $Date).DayOfWeek ).GetType() # type = [System.Enum]
En forçant le type de l’objet résultat sur [int], on obtient le numéro du jour de la semaine au lieu du nom. (0=Sunday/Dimanche)
# [int](Get-Date $Date).DayOfWeek # renvoie la valeur du jour
On peut alors contourner cette particularité, en écrivant une fonction chargée de cette correction spécifique. La plus “accessible” à un néophyte pourrait ressembler à ceci :
function Get-DayOfWeekFR {
Param (
[datetime]$Date = $(Get-Date) # = now
)
switch ([int](Get-Date $Date).DayOfWeek) {
0 { $Day = 'Dimanche' ;break }
1 { $Day = 'Lundi' ;break }
2 { $Day = 'Mardi' ;break }
3 { $Day = 'Mercredi' ;break }
4 { $Day = 'Jeudi' ;break }
5 { $Day = 'Vendredi' ;break }
6 { $Day = 'Samedi' }
}
return $Day
}
La seconde approche consiste à s’appuyer sur un objet de gestion des spécificités de culture (aka “paramètres régionaux“). Ce qui donnerait dans la version française :
$fr=New-Object system.globalization.cultureinfo("fr-FR")
$jour=$fr.DateTimeFormat.getdayname([int](Get-Date '01/12/2020').dayofweek)
Faites une pause 🙂
Là encore, rien de transcendant pour un sysadmin mais ce genre de lecture et de réflexion ne nuit jamais à l’apprentissage de Powershell. (Disons que c’est un lubrifiant pour neurones 😀 )
Pour marquer un point d’arrêt dans un script Powershell, on utilise la fonction “pause” (intégrée par défaut dans le noyau de base ) .Vous pouvez afficher le contenu de cette fonction
# Obtenir le contenu de la fonction "Pause" originale
Get-Content Function:\Pause
Ce qui donne quelque chose du genre
$null = Read-Host 'Cliquez sur Entrée pour continuer...'
ou en anglais
$null = Read-Host 'Press Enter to continue...'
Au risque de paraitre un brin tatillon 😉 notez que pour fr-FR, la traduction par “Appuyez” aurait été plus appropriée que “Cliquez”
1er exercice, je corrige la fonction par défaut et la stocke dans mon profil personnel (afin d’en disposer dans mes prochaines sessions)
$Code = @'
Function Pause {
$null = Read-Host 'Appuyez sur Entrée pour continuer...'
}
'@
If (-not (Test-Path $profile)) {
New-Item -Path $profile -ItemType File -Force
}
Add-Content -Value $Code -Path $profile
2ème exercice, j’écris une nouvelle fonction, sensiblement différente, annulant une pause par l’appui d’une touche quelconque.
function PressAnyKey ($Message="Appuyez sur une touche (quelconque) pour continuer...")
{
Write-Host -NoNewLine $Message
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Write-Host ''
}
PressAnyKey
Attention, la fonction ne marche pas sous ISE. Au besoin, ajoutez un test préalable, comme par exemple :
if ($Host.Name -like '*ISE*') { exit }
Vous pouvez aussi stocker cette fonction dans votre profil, le cas échéant.
RunAs via Powershell = pas toujours évident 😉
La réflexion proposée ici, concerne la transformation en Powershell d’un “batch” destiné à ouvrir une console d’administration (*.msc) avec une identité alternative. En l’occurrence, celle d’un Admin. du domaine.
Le batch pourrait ressembler à cela :
@echo off
color 0E
cls
C:\windows\system32\runas.exe /netonly /user:LABO\Administrateur "mmc %systemRoot%\system32\dsa.msc /domain=LABO.local"
Le 1er écueil sous Powershell porte sur la gestion de l’identité alternative (aka “credential“) – Donc il faut déjà choisir une technique appropriée à votre usage – Voici 3 exemples capables de générer un objet d’identification ($CredN).
$username = 'LABO\Administrateur'
# Ouvre une interface graphique de logon
$Cred1 = Get-Credential $username
# Demande de mot de passe "protégé"
$pass = Read-Host "Mot de passe" -AsSecureString
$Cred2 = New-Object System.Management.Automation.PSCredential($username, $pass)
# Création d'un identifiant sans intervention utilisateur
# avec mot de passe en clair !! Vivement déconseillé
$password = 'Pa$$w0rd'
$Cred3 = New-Object System.Management.Automation.PSCredential($username, (ConvertTo-SecureString $password -AsPlainText -Force))
Une fois l’objet Credential généré, il reste encore à appeler l’exécutable en question en conservant le niveau de privilèges. Et pour cela il faut encapsuler une invocation de processus Powershell élevé (-verb RunAs) dans un processus Powershell parent. Ce qui donnerait quelque chose du genre :
$User = "LABO\Administrateur"
$SecPass = Read-Host "Mot de passe " -AsSecureString | ConvertFrom-SecureString
$Credential=New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, ($SecPass | ConvertTo-SecureString)
Start-Process powershell.exe -Credential $Credential -ArgumentList "Start-Process -FilePath $env:SystemRoot\System32\mmc.exe -ArgumentList $env:SystemRoot\System32\dsa.msc -Verb RunAs"
Il y a peut être plus simple, mais à ce jour, je n’ai pas mieux 😉 – Pour ce cas de figure, le batch peux paraitre plus facile que Powershell. Et sous Windows, pour lancer une console avec un compte d’administration, il suffit d’utiliser “Maj”+”Clic droit” sur le raccourci de l’outil et le tour est joué 😀