Disques RAID invisibles par le setup de 2012 R2 sur HPE ProLiant Gen10

Je devais installer un Windows Server 2012 R2 sur un serveur HPE ProLiant DL360 Gen10 flambant neuf, équipé d’un contrôleur RAID HP SmartArray P408i-a SR Gen10. Une fois le RAID configuré proprement, impossible pour le setup d’install de 2012 R2 de voir la configuration.

La carte RAID n’est plus détectée directement par le wizard d’installation ; il faut donc télécharger un driver externe, extraire le contenu de l’exécutable fraîchement téléchargé, le monter en tant que répertoire sur la carte ILO, et ensuite, spécifier lors de la recherche des disques que l’on souhaite utiliser un driver externe. En choisissant le répertoire monté plus tôt, on installe donc ces 2 pilotes :

Une fois le driver installé, la configuration RAID est visible et l’OS est installable.

A noter que le driver est également nécessaire pour installer Windows Server 2016 ; celui-ci n’étant pas disponible pour des versions plus anciennes que 2012 R2, je doute qu’il soit possible d’installer autre chose que les 2 dernières versions sur un stockage RAID.

Tous les crédits aux participants de ce thread sur les forums HPE pour la résolution de ce problème.

Création d’un réseau interne grâce au NAT sur Proxmox VE 5

Il y a quelques semaines maintenant, j’ai repensé mon cloud personnel. Auparavant basé sur ESXi, j’ai changé de technologie pour revenir sur quelque chose de plus « léger » que je connaissais déjà : Proxmox. Cet hyperviseur basé sur Debian m’avait causé dans sa version 4 pas mal de maux de tête pour réussir à monter un réseau et donner accès à internet à mes VM sans qu’elles n’utilisent d’IP globales ; la version 5 ne fait pas vraiment mieux, c’est pourquoi je vais partager dans cet article les opérations nécessaires via l’interface de Proxmox et via la modification du fichier /etc/network/interfaces.

Sur l’hôte, dans les paramètres réseau, doivent apparaître juste après l’installation la carte physique de la machine et un bridge. Il est très important de ne pas y toucher sous peine de rendre la machine injoignable après le redémarrage ou la recharge de la configuration des cartes. L’interface web ne semblant pas traduire correctement les valeurs saisies en un fichier de configuration propre, on va donc passer par la modification en direct du fichier /etc/network/interfaces pour y créer un deuxième bridge qui sera notre réseau local.

En l’ouvrant avec nano en utilisateur root, il doit ressembler à ceci (je vais appeler dans cet exemple ma carte réseau eth0 et mon IP 26.10.19.91), en gras les paramètres qu’il faut absolument vérifier afin qu’ils soient sur cette valeur :

auto lo
iface lo inet loopback


iface eth0 inet manual


auto vmbr0
iface vmbr0 inet static
  address 26.10.19.91
  netmask 255.255.255.0
  gateway 26.10.19.1
  bridge_ports eth0
  bridge_stp off
  bridge_fd 0 


auto vmbr1
iface vmbr1 inet static
  address 192.168.2.1
  netmask 255.255.255.0
  bridge_ports none
  bridge_stp off
  bridge_fd 0

Le vmbr1 est le nouveau bridge sur lequel nous allons raccorder nos VM pour leur donner accès à internet et pouvoir faire du NAT afin de les joindre depuis l’extérieur sur la même IP que l’hôte et qu’elles puissent éventuellement communiquer entre elles.

Un redémarrage plus tard (relancer les interfaces réseaux n’est pas suffisant), le nouveau pont doit apparaître dans la section réseau de l’interface web :

Par la suite, en configurant le réseau sur les VM, on indiquera une adresse IP comprise entre 192.168.2.2 et 192.168.2.254 (si vous avez laissé le sous-réseau par défaut) et 192.168.2.1 en gateway. Concernant les serveurs DNS, il est possible d’utiliser des serveurs DNS externes ou internes ; parmi les DNS publics, on peut renseigner par exemple 1.1.1.1 qui est le DNS public CloudFlare, ou le réputé 8.8.8.8 de Google.

Maintenant que les machines peuvent avoir une IP définie, afin de les joindre via certaines applications, il ne reste plus qu’à faire le NAT dans iptables. Toujours en root, on autorise le routage des paquets venant sur l’interface vmbr0 (qui est liée à eth0, la carte physique liée à internet) vers notre sous-réseau fraîchement créé :

iptables -t nat -A POSTROUTING -s ‘192.168.2.0/24’ -o vmbr0 -j MASQUERADE

Et enfin, si l’on souhaite ouvrir le port 80 sur 192.168.2.2 :

iptables -t nat -A PREROUTING -i vmbr0 -p tcp –dport 80 -j DNAT –to 192.168.2.2:80

A noter que si l’on souhaite simplement retirer une règle, on remplace -A par -D. iptables ne sauvegardant pas de manière permanente les règles, elles sont perdues au redémarrage. Il est possible d’installer des applications permettant de faire des backup/restore des règles ou bien d’ajouter les règles au fichier /etc/network/interfaces en utilisant post-up pour les règles à exécuter une fois que l’interface est montée, et post-down pour celles a exécuter quand l’interface est désactivée. Par exemple :

post-up iptables -t nat -A PREROUTING -i vmbr0 -p tcp –dport 80 -j DNAT –to 192.168.2.2:80

post-down iptables -t nat -D PREROUTING -i vmbr0 -p tcp –dport 80 -j DNAT –to 192.168.2.2:80

Pour plus d’informations concernant iptables et ses possibilités, je vous renvoie à ce lien.

Powershell : déploiement d’un script et création de tâche planifiée, sur machine distante

Sous ce titre un peu barbare, je vais partager un script que j’ai brodé aujourd’hui. En local sur ma station, j’utilisais un script Powershell me permettant de fermer des sessions RDP simplement déconnectées (que j’ai d’ailleurs publié dans cet article 😉). Seulement, il a été décidé de le modifier de manière à ce que le script ne demande plus un nom de machine particulière, mais de simplement s’occuper de la machine sur laquelle il est exécuté. En contrepartie, l’idée était de le déployer sur tous les serveurs, afin de pouvoir l’appeler via une tâche planifiée de manière à ce que tous les jours les sessions RDP inactives soient shuntées.

Le script ci-dessous va donc effectuer les tâches suivantes :

  • demander un nom de serveur
  • créer un répertoire sur le disque local C:
  • copier le script dans le répertoire fraîchement créé
  • créer une tâche planifiée, exécutée par l’utilisateur SYSTEM, avec une élévation de droits, qui va donc appeler le script qui a été copié.

Plusieurs choses :

  • Les instructions propres au planificateur de tâches ne fonctionnent qu’à partir de Windows 2012 R2 ou 8.1, indépendamment de la version de PS.
  • J’ai utilisé les partages administratifs au lieu d’un PS-Session pour la copie car un Copy-Item -ToSession nécessite PowerShell V5, or mon environnement est hybride V4 et V5 et je ne peux pas tout déployer directement depuis un hôte en V5.
  • Les commandlets du planificateur de tâches ne fonctionnent qu’en local, il faut donc les appeler sur le système distant via Invoke-Command.
$RemoteServer = Read-Host "Destination Server to install script and create scheduled task ?"
$ScriptDir = "\\"+$RemoteServer+"\c$\"
New-Item -Path $ScriptDir -Name "Scripts" -ItemType "directory"
$FinalDest = "\\"+$RemoteServer+"\c$\Scripts"
Copy-Item "script.ps1" -Destination $FinalDest -Force
if ($?){ Write-Host "Copy successful." }
else { Write-Host "Copy failed." ; break }
Invoke-Command -ComputerName $RemoteServer -ScriptBlock {
$TaskDetails = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$TaskAction = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "C:\Scripts\script.ps1"
$TaskSched = New-ScheduledTaskTrigger -Daily -At 4am
$TaskName = "AutoDisconnectSession"
Register-ScheduledTask -Action $TaskAction -Principal $TaskDetails -Trigger $TaskSched -TaskName $TaskName }
if ($?) { Write-Host "Task successfully created." }
else { "There was an error creating the task." }

Powershell : création d’interfaces graphiques – troisième partie

Suite (et peut-être fin ?) des deux premiers articles concernant la création d’interfaces graphiques en Powershell ; après avoir vu les contrôles de base et les EventHandlers dans la première partie puis l’activation de contrôles ainsi que leur organisation au niveau spatial dans la seconde partie, je vais essayer d’aborder différemment les choses dans ce billet puisque nous allons développer ensemble une petite application récupérant les informations concernant les processeurs d’une station grâce à WMI et placer ces informations récupérées dans des contrôles. Cela est en quelque sorte une sorte de résumé ou de super-tutoriel par rapport à ce qui a été abordé précédemment, avec tout de même du nouveau contenu en termes de contrôles comme le DataGridView ou d’EventHandlers.

Tout d’abord, nous allons définir l’interface : une seule fenêtre, une TextBox pour saisir le nom de la station que nous souhaitons analyser, un Label pour indiquer le statut de la requête, 2 boutons (un pour lancer la requête, un deuxième pour effacer le tableau) et un DataGridView pour y renseigner les informations.

La Form :

$Form = New-Object System.Windows.Forms.Form
$Form.ClientSize = ‘400,450’
$Form.Text = « Mon UI en PS ep. III »
$Form.FormBorderStyle = ‘Fixed3D’
$Form.MaximizeBox = $false

Le Label de statut :

$LabelStatus = New-Object System.Windows.Forms.Label
$LabelStatus.Location = New-Object System.Drawing.Point(20,20)
$LabelStatus.Text = « Saisir un nom de station. »
$Form.controls.Add($LabelStatus)

La TextBox :

$TextBoxStation = New-Object System.Windows.Forms.TextBox
$TextBoxStation.Location = New-Object System.Drawing.Point(20,50)
$TextBoxStation.Width = 100
$TextBoxStation.TabIndex = 1
$Form.controls.Add($TextBoxStation)

Les Button pour déclencher la requête et vider le tableau :

$BoutonGetWMI = New-Object System.Windows.Forms.Button
$BoutonGetWMI.Location = New-Object System.Drawing.Point(150,50)
$BoutonGetWMI.Text = « Afficher les infos »
$BoutonGetWMI.Width = 100
$BoutonGetWMI.TabIndex = 2
$BoutonGetWMI.Enabled = $false
$BoutonClearTab = New-Object System.Windows.Forms.Button
$BoutonClearTab.Location = New-Object System.Drawing.Point(280,50)
$BoutonClearTab.Text = « Effacer le tableau »
$BoutonClearTab.Width = 100
$BoutonClearTab.TabIndex = 3
$BoutonClearTab.Enabled = $false
$Form.controls.AddRange(@($BoutonGetWMI,$BoutonClearTab))

Et enfin, le tableau, qui est représenté par l’objet DataGridView :

$DataGridViewCPU = New-Object System.Windows.Forms.DataGridView
$DataGridViewCPU.Location = New-Object System.Drawing.Point(20,80)
$DataGridViewCPU.Size = ‘360,290’
$DataGridViewCPU.ReadOnly = $true
$DataGridViewCPU.RowHeadersVisible = $false
$DataGridViewCPU.ColumnCount = 3
$DataGridViewCPU.Columns[0].Name = « CPU # »
$DataGridViewCPU.Columns[0].Width = 55
$DataGridViewCPU.Columns[1].Name = « Reference »
$DataGridViewCPU.Columns[1].Width = 140
$DataGridViewCPU.Columns[2].Name = « Freq. en MHz »
$DataGridViewCPU.Columns[2].Width = 140
$Form.controls.Add($DataGridViewCPU)

Les propriétés TabIndex permettent de définir l’ordre dans lequel les contrôles s’activent sur pression de la touche de tabulation. Avec ce code, l’interface est rendue comme telle :

L’interface de base une fois le code de l’UI terminé.
Concernant le DataGridView, plusieurs explications :
  • Cet objet fonctionne comme une collection d’autres objets : DataGridViewRow pour les lignes et DataGridViewColumn pour les colonnes.
  • La propriété ReadOnly permet d’éviter que l’utilisateur saisisse des informations dans les cases.
  • En fonction de ce que l’on souhaite afficher et de la liberté offerte à l’utilisateur, il est possible de bloquer le redimensionnement des colonnes, le tri, etc. Je vous renvoie vers la documentation Microsoft dont les liens sont en fin d’article pour plus d’informations.
Maintenant, il s’agit de coder les EventHandlers ; tout d’abord sur la TextBox, afin d’éviter que l’on lance la requête WMI si la TextBox est vide. Cet EventHandler se déclenche dès que son contenu est modifié : si le texte est vide, le bouton est grisé.

$TextBoxStation.Add_TextChanged({
if ($TextBoxStation.Text -eq «  ») { $BoutonGetWMI.Enabled = $false }
else { $BoutonGetWMI.Enabled = $true }

L’EventHandler pour vider le DataGridView. On efface toutes les lignes de celui-ci.

$BoutonClearTab.Add_Click({
$DataGridViewCPU.Rows.Clear()
$TextBoxStation.Enabled = $true
$TextBoxStation.Text = «  »
   $BoutonClearTab.Enabled = $false })

Et enfin, l’EventHandler sur le bouton de récupération des informations ; c’est ici que l’essentiel du « vrai » code sera placé.

$BoutonGetWMI.Add_Click({
$CPUArray = Get-WmiObject win32_Processor -computername $TextBoxStation.Text
if ($?) {
foreach ($CPU in $CPUArray){
$DataGridViewCPU.Rows.Insert(0,$CPU.DeviceID,$CPU.Name,$CPU.MaxClockSpeed) }
$LabelStatus.Text = « Informations obtenues. »
$BoutonGetWMI.Enabled = $false
$BoutonClearTab.Enabled = $true
$TextBoxStation.Enabled = $false

}
else { $LabelStatus.Text = « Erreur. » }
})

Plusieurs choses concernant ces snippets :
  • En bleu, le code pour désactiver ou activer les boutons en fonction des besoins ; en faisant ceci, je m’assure que l’utilisateur ne peut pas lancer une requête et donc repeupler le tableau d’informations, je l’empêche également de modifier la saisie dans la TextBox. Ces contrôles sont réactivés lorsque le tableau est effacé.
  • En vert, cette condition permet de vérifier si la dernière instruction Powershell a renvoyé une erreur. Cela permet donc de réellement peupler le DataGridView si les objets ont bien été retournés ($? = $true) par la requête WMI ou de modifier le Label en conséquence sinon ($? = $false).
  • En orange, l’instruction permettant de remplir le DataGridView : bien qu’il soit possible d’ajouter les lignes une par une, nous allons dans notre cas les insérer. Afin de pouvoir ajouter un objet DataGridViewRow dans le DataGridView, il faut spécifier l’index ainsi que des valeurs pour les colonnes qui ont été déclarées. Avec cette commande, nous insérons donc tout en haut du tableau (index 0) les valeurs qui nous intéressent dans l’objet retourné par la requête WMI.
La requête n’a pas fonctionné.
La requête a fonctionné.

Par la suite, il est possible d’exploiter le contenu des cellules du DataGridView en appelant simplement les objets dont il est composé. Par exemple, on peut passer une boucle Foreach si l’on souhaite compter le nombre de CPU dont la fréquence est supérieure à 3000 MHz. En reprenant notre DataGridView, la troisième cellule de la ligne est celle de la fréquence, il faut donc analyser son contenu. Attention, les index débutent toujours par 0 et c’est un objet DataGridViewCell que nous allons appeler, ce qui nous intéresse pour la comparaison est sa propriété Value.

$ProcHauteFreq = 0
foreach ($Row in $DataGridViewCPU.Rows) {
if ($Row.Cells[2].Value -gt 3000) { $ProcHauteFreq++ }
}

On peut aussi par exemple exploiter le contenu simple d’une seule ou plusieurs cases. Dans cet exemple, le contenu de la case sélectionnée par l’utilisateur ira remplir le Label. Si plusieurs cellules sont sélectionnées, un tableau de DataGridViewCell sera retourné. Afin d’éviter une multiple sélection par l’utilisateur, on peut affecter la propriété Multiselect à False de cette manière :

$DataGridViewCPU.Multiselect = $False

$LabelStatus.Text = $DataGridViewCPU.SelectedCells.Value

Liens

Développement d’une UI en Powershell : première partiedeuxième partie
Documentation et références des classes : DataGridViewDataGridViewRowDataGridViewColumn – DataGridViewCell