web-dev-qa-db-fra.com

Comment importer un module PowerShell personnalisé dans la session distante?

Je développe un module PowerShell personnalisé, que j'aimerais utiliser dans le contexte d'une session distante sur un autre ordinateur. Le code suivant (qui ne fonctionne évidemment pas) explique ce que j'essaie de réaliser:

import-module .\MyCustomModule.psm1
$session = new-pssession -computerName server01
invoke-command -session $session -scriptblock { 
  <# use function defined in MyCustomModule here #> 
}

La première question est de savoir s'il est possible de réaliser ce scénario? Je veux simplement que mon module personnalisé soit physiquement présent sur ma machine, pas sur le serveur distant.

J'ai trouvé ce fil , mais je n'ai pas réussi à le faire fonctionner - il ne permet pas de créer une session de la machine distante vers la locale. J'ai probablement dû faire face aux limitations de configuration mentionnées quelque part dans les commentaires de ce fil ... En outre, l'auteur a mentionné les implications en termes de performances, ce qui est essentiel pour ma solution ...

Si c'est possible, alors comment?

La version de PowerShell n'est actuellement pas une contrainte - si la solution n'est disponible que sur PS 3.0 - je peux vivre avec.

35
Yan Sklyarenko

Il y a eu d'excellents commentaires sur la question, et j'ai passé un certain temps à étudier différentes façons d'aborder le problème.

Pour commencer, ce que j'ai demandé initialement n'est pas possible. Je veux dire, si vous suivez la voie du module, alors le module devrait être physiquement présent sur une machine cible pour pouvoir Import-Module en session distante.

Pour résumer davantage ma question, j'essaie de créer un cadre réutilisable basé sur PowerShell pour les déploiements de produits. Ce sera un déploiement de manière Push, ce qui signifie que nous encourageons les gens à exécuter certains scripts sur une machine locale pour les déployer sur un serveur distant. Dans la mesure où j'ai exploré la région, il y a deux manières possibles qui sont amicales au bon sens.

Approche des modules

Le processus à suivre:

  • placez chaque fonctionnalité logiquement différente dans le module PowerShell (*.psm1)
  • distribuer le module à la machine distante et étendre la variable PSModulePath pour inclure l'emplacement du nouveau module
  • sur une machine cliente, créez une nouvelle session sur le serveur distant et utilisez Invoke-Command -Session $s -ScriptBlock {...}
  • dans le bloc de script, commencez par Import-Module CustomModule - il recherchera le CustomModule sur une machine distante et le trouvera évidemment

Les avantages

Voici les raisons d'aimer cette approche:

  • la conséquence du rôle de module traditionnel - faciliter la création de bibliothèques réutilisables
  • selon le grand livre Windows PowerShell en action , "les modules peuvent être utilisés pour créer des applications spécifiques au domaine". Autant que je sache, cela peut être réalisé en combinant l'imbrication de modules et le mélange de modules script/binaires pour exposer l'interface intuitive spécifique à un certain domaine. Fondamentalement, c'est celui que j'apprécie le plus pour l'objectif du cadre de déploiement basé sur PowerShell

Désavantages

Les éléments suivants sont importants à prendre en considération:

  • Vous devez trouver un moyen de livrer les modules personnalisés à la machine distante. J'ai joué avec NuGet , et je ne suis pas sûr que cela convienne bien à la tâche, mais il existe également d'autres options, par exemple, l'installateur MSI ou plain xcopy du partage dossier. En outre, le mécanisme de livraison devrait prendre en charge les installations de mise à niveau/rétrogradation et (de préférence) multi-instances, mais cela est plus lié à ma tâche qu'au problème en général

Approche des scripts

Le processus à suivre:

  • placez chaque fonctionnalité logiquement différente dans un script PowerShell distinct (* .ps1)
  • sur une machine cliente, créez une nouvelle session sur le serveur distant et utilisez Invoke-Command -Session $s -FilePath .\myscript.ps1 pour charger les fonctions définies dans un script dans la session distante
  • utilisez un autre Invoke-Command -Session $s -ScriptBlock {...} et faites référence à vos fonctions personnalisées - elles seront présentes dans une session

Les avantages

Voici les bons points de cette approche:

  • c'est simple - vous n'avez pas besoin de connaître les particularités des modules. Il suffit d'écrire des scripts PowerShell simples et c'est tout
  • vous n'avez rien à livrer à la machine distante - cela rend la solution encore plus simple et moins sujette aux erreurs de maintenance

Désavantages

Bien sûr, ce n'est pas idéal:

  • il y a moins de contrôle sur la solution: par exemple, si vous "importez" un ensemble de fonctions dans la session, toutes sont "importées" et visibles par l'utilisateur, donc pas "d'encapsulation", etc. Je suis sûr que de nombreuses solutions peuvent vivre avec cela, alors ne décidez pas uniquement sur ce point
  • la fonctionnalité de chaque fichier doit être autonome - toute importation de points ou de modules à partir de là recherchera la machine distante, pas la machine locale

Enfin, je dois dire que la machine distante doit encore être préparée pour la télécommande. Voici ce que je veux dire:

  • la politique d'exécution doit être remplacée par quelque chose, car elle est limitée par défaut: Set-ExecutionPolicy Unrestricted
  • La communication à distance PowerShell doit être activée: Enable-PSRemoting
  • le compte exécuté par le script doit être ajouté aux administrateurs locaux du serveur distant
  • si vous prévoyez d'accéder aux partages de fichiers dans la session distante, assurez-vous que vous êtes au courant authentification multi-sauts et prenez les mesures appropriées
  • assurez-vous que votre antivirus est votre ami et ne vous envoie pas vers le enfer PowerShell
43
Yan Sklyarenko

Voici une autre approche: recréez le module dans une session distante, sans copier aucun fichier.

Je n'ai fait aucune tentative pour gérer les dépendances entre les modules, mais cela semble fonctionner correctement pour les modules autonomes simples. Il repose sur la disponibilité du module dans la session locale, car cela facilite la détermination des exportations, mais avec un peu de travail supplémentaire, il fonctionnerait également avec un fichier de module.

function Import-ModuleRemotely([string] $moduleName,[System.Management.Automation.Runspaces.PSSession] $session)
{
    $localModule = get-module $moduleName;
    if (! $localModule) 
    { 
        write-warning "No local module by that name exists"; 
        return; 
    }
    function Exports([string] $paramName, $dictionary) 
    { 
        if ($dictionary.Keys.Count -gt 0)
        {
            $keys = $dictionary.Keys -join ",";
            return " -$paramName $keys"
        }
    }
    $fns = Exports "Function" $localModule.ExportedFunctions;
    $aliases = Exports "Alias" $localModule.ExportedAliases;
    $cmdlets = Exports "Cmdlet" $localModule.ExportedCmdlets;
    $vars = Exports "Variable" $localModule.ExportedVariables;
    $exports = "Export-ModuleMember $fns $aliases $cmdlets $vars;";

    $moduleString= @"
if (get-module $moduleName)
{
    remove-module $moduleName;
}
New-Module -name $moduleName {
$($localModule.Definition)
$exports;
}  | import-module
"@
    $script = [ScriptBlock]::Create($moduleString);
    invoke-command -session $session -scriptblock $script;
}
7
Rob

Je ne crois pas que cela soit pris en charge à droite de la boîte sans aucun "piratage". La décision intelligente serait probablement de placer le module sur un emplacement public comme un serveur de fichiers et de l'importer sur le serveur lorsque vous en avez besoin. Ex:

$session = new-pssession -computerName server01
invoke-command -session $session -scriptblock {
    #Set executionpolicy to bypass warnings IN THIS SESSION ONLY
    Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
    #Import module from public location
    Import-Module \\fileserver\folders\modulelocation...


    <# use function defined in MyCustomModule here #> 
}
3
Frode F.

Merci pour ce fil, c'était utile….

Mais j'ai en fait réécrit la fonction.

Sachez que la fonction d'origine dans cet article ou cette fonction réécrite comprend des données de manifeste de module. Vous ne pouvez donc pas vous fier aux vérifications de version du module.

function Import-ModuleRemotely {
    Param (
        [string] $moduleName,
        [System.Management.Automation.Runspaces.PSSession] $session
    )

    Import-Module $moduleName

    $Script = @"
    if (get-module $moduleName)
    {
        remove-module $moduleName;
    }

    New-Module -Name $moduleName { $($(Get-Module $moduleName).Definition) } | Import-Module
"@

    Invoke-Command -Session $Session -ScriptBlock {
        Param($Script)
        . ([ScriptBlock]::Create($Script))
        Get-Module 
    } -ArgumentList $Script
}
0

Qu'en est-il de créer un bloc de script à partir de votre fonction personnalisée et de l'envoyer aux serveurs cibles à l'aide de Invoke-command

Import-module YourModule
$s = [scriptblock]::Create($(get-item Function:\Your-ModuleFunction).Definition)

Invoke-Command -ScriptBlock $s -Computername s1,s2,sn
0
Jiří Soyka Domin