Se connecter en remote shell sur Windows Serveur avec WinRM et .NET

Windows Remote Management permet d’ouvrir à distance un shell sur Windows Server 2008 (WinRM existe depuis Windows Vista et Windows Server 2003 R2). Concrétement, il est possible d’exécuter des lignes de commandes sur un serveur à distance.

Pour des raisons évidentes de sécurité, certains pré-requis sont nécessaires. Si l’ordinateur client n’est pas sur le même domaine que le serveur, ou si l’authentification ne se fait pas par Kerberos alors les transmissions doivent être chiffrées. Concernant la ligne de commande, les droits sont respéctés. Un utilisateur qui ne présente pas les droits suffisants pour exécuter une commande sur le serveur se verra refuser l’exécution de la dite commande à distance.

Petite limitation, un utilisateur ne peut pas ouvrir plus de 5 “runspaces” à la fois.

Par défaut, sur Windows Serveur 2008 R2, WinRM n’est pas activée. Pour l’activer, ouvrez une invite de commande en tant qu’administrateur
et tapez la ligne suivante :

winrm quickconfig

C:\Windows\system32>winrm quickconfig
WinRM already is set up to receive requests on this machine.
WinRM already is set up for remote management on this machine.

Le serveur est maintenant prêt pour recevoir les requêtes des clients. Dans cet article, pour simplifier le processus, l’ordinateur client fait partie du même domaine que le serveur afin d'éviter le cryptage des transmissions.

Pour tester le code ci-dessous, les pré-requis sont :

  • un windows serveur 2008 avec un domaine Active Directory
  • un client sous windows
  • un compte administrateur sur le domaine

Comme énoncé précédemment, pour éxécuter certaines commandes il faut être connecté avec un utilisateur ayant des droits spéciaux. Si vous êtes connecté à votre ordinteur client avec ce type d’utilisateur, il ne sera pas necessaire de passer à la connexion des informations d’authentification (login/mot de passe). En revanche, si vous êtes connecté avec un utilisateur basique, il faudra les spécifier au constructeur de la connexion.

L’utilisation de Windows Remote Shell en .NET se repose sur l’espace de noms System.Management.Automation.Runspaces. Pour ajouter ce namespace, ouvrez le fichier .csproj de votre projet avec un éditeur de texte (par exemple Notepad++) et cherchez le bloc de lignes contenant les références.

Par exemple :

<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />

Ajoutez en dessous la ligne :

<Reference Include="System.Management.Automation" />

Il est maintenant possible d’importer le namespace dans le fichier .cs :

using System.Management.Automation.Runspaces;

Tout est maintenant prêt pour commencer à développer.

La 1re étape consiste à construire un objet PSCredential pour nous identifier sur le serveur. Si cette étape est omise, le compte utilisateur qui exécute l’application sera utilisé. Le constructeur de PSCredential prend en paramètres une chaine de caractères représentant le nom d’utilisateur et un objet de type System.Security.SecureString qui stocke le mot de passe :

SecureString pwd = new SecureString();

foreach (char c in "P@ssw0rd".ToCharArray())
{
    pwd.AppendChar(c);
}

PSCredential cred = new PSCredential("Aymeric", pwd);

La 2ème étape établit la connexion vers le serveur. WSMANConnectionInfo prend en paramètre un booléen pour l’utilisation ou non du SSL, le nom du serveur, le port (5985 par défaut ou 5986 pour le SSL), le nom de l’application, un schéma et enfin les paramètres d’identification créés auparavant (null si l’on souhaite utiliser le compte actuellement connecté au poste client).

WSManConnectionInfo rri = 
    new WSManConnectionInfo(false,
        "serveur.aymeric.lan",
        5985,
        "/wsman",
        "http://schemas.microsoft.com/powershell/Microsoft.PowerShell",
        cred);

Une fois la connexion créée, on peut construire un RunSpace qui nous permettra d’exécuter les commandes à distance :

Runspace myRunSpace = RunspaceFactory.CreateRunspace(rri);
myRunSpace.Open();

Pour terminer, la classe Pipeline permet d’envoyer des commandes au serveur. Dans l’exemple ci-dessous, la commande ipconfig est envoyée au serveur. Le résultat de la commande est affiché dans la console du client :

Pipeline p = myRunSpace.CreatePipeline("ipconfig");

foreach (PSObject i in p.Invoke())
{
    Console.WriteLine(i.ToString());
}

Remoteshell


Voir également