Bouts de code

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é 😀

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.