MAIN MENU
Solutions

Paquets d'installation

blue box
Starter pack

Toute la puissance de Devolutions à moitié prix pour les équipes de 5

Preuve de concept : 100+ utilisateurs?

Vue d'ensemble rapide

Conformité

Votre partenaire de confiance en matière de sécurité et de conformité.

Intégrations

Unifiez vos solutions avec les intégrations qu'offre Devolutions

Le blogue Devolutions

Annonces, mises à jour et analyses de Devolutions

PowerShell
Powershell universal démystifié transformez scripts applications web devolutions blogue

PowerShell Universal démystifié : transformez vos scripts en applications web

Cet article vous guide à travers des techniques d'optimisation PowerShell, puis vous montre comment les exploiter dans PowerShell Universal. À la fin, vous disposerez d'un tableau de bord web fonctionnel pour vos scripts, accessible depuis n'importe quel navigateur.

Bienvenue dans notre nouvelle série PowerShell, consacrée aux astuces et techniques PowerShell et à leur intégration dans PowerShell Universal. Voici comment fonctionne cette série :

PowerShell Universal (PSU)

Si vous découvrez PowerShell Universal, c’est le point de départ idéal. PowerShell Universal intègre du code PowerShell dans un serveur web, et en quelques clics, vous pouvez commencer à créer vos propres tableaux de bord avec interface graphique. Il complète parfaitement vos connaissances PowerShell existantes et fonctionne sur toutes les plateformes, aussi bien avec Windows PowerShell que PowerShell 7. Il vous permet de créer des tableaux de bord et des outils sans avoir recours à des techniques complexes comme WPF, WinForms ou autres.

Optimiser les fonctions PowerShell

Commençons par examiner du code PowerShell et appliquons quelques astuces pour l’améliorer.

Avez-vous déjà voulu identifier quel processus héberge un service système particulier, pour réaliser qu’ils apparaissent tous sous le nom « svchost.exe » dans Process Monitor ou Process Explorer?

La solution repose sur deux fonctions PowerShell qui récupèrent les ID de processus de service et les noms de groupes de service, entre autres choses. Elles fonctionnent très bien, mais elles sont lentes à grande échelle. Voyez plutôt :

function Get-ServiceProcessId
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string] $Name
    )

    process
    {
        $svc = Get-CimInstance Win32_Service -Filter "Name='$Name'"
        if ($svc -and ($svc.ProcessId -ne 0))
        {
            $svc.ProcessId
        }
    }
}

Cette excellente fonction prend un nom de processus et retourne son ID (si disponible) :

PS C:\> Get-ServiceProcessId -Name spooler
4784

Puisque cette fonction est compatible avec le pipeline, vous pouvez la faire fonctionner à grande échelle :

PS C:\> Get-Service | Get-ServiceProcessId
5524
1960
(...)

Dans cet appel, Get-Service récupère tous les services, et Get-ServiceProcessId associe automatiquement la propriété « Name » à son paramètre « Name » (ValueFromPipelineByPropertyName). Comme le code de la fonction est intégré dans un bloc process {}, ce code est répété pour chaque élément du pipeline. Écrire des fonctions compatibles avec le pipeline n’est vraiment pas sorcier : désignez un paramètre avec ValueFromPipeline ou ValueFromPipelineByPropertyName et intégrez votre code dans process {} — c’est tout. Votre fonction évolue maintenant automatiquement.

Cependant, l’appel est terriblement lent : les ID de processus s’affichent un par un avec plusieurs secondes de délai. C’est parce que le code de la fonction interroge WMI pour chaque service individuellement, et cette requête WMI prend du temps.

Optimiser les performances

Le problème de performance que vous venez de voir est fréquent dans de nombreux scripts. Chaque fois que vous interrogez des informations — qu’il s’agisse de WMI, d’Active Directory, de bases de données ou autre — le code évolue mal.

Une amélioration facile pour ces scénarios consiste à créer des tables de recherche. PowerShell fournit déjà une applet de commande pour ça. Regardez :

function Get-ServiceProcessId
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string] $Name
    )

    begin
    {
        $hash = Get-CimInstance Win32_Service -ErrorAction Ignore |
            Group-Object -Property Name -AsHashTable -AsString
    }

    process
    {
        $id = $hash.$Name.ProcessId
        if ($id) { $id }
    }
}

Cette fonction est d’une rapidité fulgurante : lorsque vous interrogez plusieurs services, votre requête WMI ne s’exécute qu’une seule fois. Après le délai initial causé par la requête, les PID demandés s’affichent sans délais répétés.

C’est parce que nous avons ajouté un bloc begin {} à la fonction. Cette partie s’exécute une seule fois, pour la préparation, avant toute autre chose. Dans ce bloc, nous interrogeons WMI pour tous les services, ce qui ne prend pas vraiment plus de temps que d’en demander un seul.

Pour retrouver ensuite les informations d’un service particulier, les résultats de la requête sont transmis à Group-Object. Cette applet de commande est parfaite pour créer des tables de recherche, car elle prend en charge les paramètres -AsHashtable -AsString qui créent automatiquement une table de hachage indexée par la propriété que vous avez spécifiée. Ça semble complexe? Pas vraiment. Regardez :

PS C:\> $hashtable = Get-CimInstance Win32_Service | Group-Object -Property Name -AsHashTable -AsString
PS C:\> $hashtable.Spooler.processId
4784
PS C:\> $hashtable.WSearch.ProcessId
10152
PS C:\> $hashtable.WSearch
ProcessId Name    StartMode State   Status ExitCode
--------- ----    --------- -----   ------ --------
10152     WSearch Auto      Running OK     0

Avec la table de hachage générée par Group-Object, les noms de clés sont maintenant les noms de services. Lorsque vous spécifiez un nom de service particulier, vous obtenez l’objet service. Lorsque vous ajoutez une propriété spécifique, par exemple .ProcessId, vous obtenez directement son ID de processus.

Voici un autre exemple pour bien ancrer le concept avant de passer à la suite :

PS C:\> $hashtable = Get-Service | Group-Object -Property Status -AsHashTable -AsString
PS C:\> $hashtable.Running.Count
156
PS C:\> $hashtable.Stopped.Count
170
PS C:\> $hashtable.Running
Status  Name             DisplayName
------  ----             -----------
Running AdobeARMservice  Adobe Acrobat Update Service
Running AppIDSvc         Application Identity
Running Appinfo          Application Information
(...)

Cette fois, nous avons groupé par la propriété « Status » et nous pouvons maintenant utiliser n’importe laquelle des constantes de statut pour interroger la table de hachage. Puisque plusieurs services ont le statut « Running » ou « Stopped », chaque clé de la table de hachage retourne ici plus d’un service. Il est maintenant trivial de compter le nombre de services en cours d’exécution ou arrêtés, ou d’afficher tous les services en cours d’exécution. Le concept de regroupement avec des tables de hachage est très flexible et extrêmement puissant.

Cela dit, si vous y réfléchissez un instant, ça soulève un autre défi : maintenant que vous pouvez faire évoluer votre fonction et récupérer rapidement les ID de processus de nombreux services, quelle est l’utilité d’une liste de nombres si le nom du service d’origine auquel appartient un ID de processus est perdu?

Ajouter des informations

Pour ajouter des informations — plutôt que de les remplacer —, PowerShell dispose d’une autre applet de commande exactement à cette fin. Cette version améliorée peut ajouter l’ID de processus grâce à son nouveau paramètre -PassThru :

function Get-ServiceProcessId {

    [CmdletBinding()]

    param

    (

        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName='ServiceName')]

        [string]

        $Name,

        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName='ActualService')]

        [System.ServiceProcess.ServiceController]

        $Service,

        [switch]

        $PassThru

    )

    begin

    {

        $hash = Get-CimInstance Win32_Service -ErrorAction Ignore |

        Group-Object -Property Name -AsHashTable -AsString

    }

    process

    {

        # since the user now has the option to either specify the service name

        # or pipe in an actual service, let's look up the pendant so we always

        # have both and can simplify the remaining code:

        if ($PSCmdlet.ParameterSetName -eq 'ServiceName')

        {

            $Service = Get-Service -Name $Name -ErrorAction Ignore

        }

        else

        {

            $Name = $Service.Name

        }

        # make sure "0" values are replaced by NULL

        $id = $hash.$Name.ProcessId | Where-Object { $_ -gt 0 }

        if ($PassThru)

        {

            # take the service and add the process ID

            $Service |

            Add-Member -MemberType NoteProperty -Name ProcessId -Value $id -PassThru

        }

        else

        {

            $id

        }

    }

}

Vous trouverez le paramètre -PassThru sur plusieurs applets de commande PowerShell, et lorsqu’il est présent, vous savez que vous avez le choix entre retourner une valeur nouvellement calculée ou ajouter cette valeur à l’objet d’entrée d’origine. C’est plus clair lorsqu’on regarde les résultats. Par défaut, rien ne change et vous continuez à obtenir les ID de processus des services :

PS C:\> Get-Service | Get-ServiceProcessId
5524
1960
(...)

Si vous spécifiez -PassThru, vous obtenez les objets d’entrée d’origine (les services que vous avez passés dans le pipeline) :

PS C:\> Get-Service | Get-ServiceProcessId -PassThru

Status  Name              DisplayName
------  ----              -----------
Stopped AarSvc_78962bb    Agent Activation Runtime_78962bb
Running AdobeARMservice   Adobe Acrobat Update Service
Stopped ADPSvc            Aggregated Data Platform Service
(...)

Ces objets ont maintenant une nouvelle propriété nommée ProcessId que vous pouvez utiliser pour le tri. Pour la rendre visible, utilisez Select-Object :

PS C:\> Get-Service | Get-ServiceProcessId -PassThru | Sort-Object ProcessId -Descending | Select-Object -Property DisplayName, ProcessId

DisplayName                                         ProcessId
-----------                                         ---------
Web Threat Defense Service                          19084
Diagnostic System Host                              18580
Benutzerdienst für die Plattform für verbundene Ge... 17608
WebClient                                           16616
Quality Windows Audio Video Experience              15328
(...)

Voici comment ça fonctionne :

Pour qu’une fonction puisse transmettre des informations, elle a besoin d’accéder à l’objet d’entrée d’origine. C’est pourquoi la nouvelle fonction dispose maintenant de deux paramètres mutuellement exclusifs : -Name (spécifier un nom de service comme « Spooler ») et -Service (fournir une instance réelle d’un service). En interne, la fonction détecte quelle information l’utilisateur a fournie et calcule ce qui manque.

Lorsque l’utilisateur spécifie -PassThru, l’objet service est transmis à Add-Member. Add-Member peut ajouter dynamiquement de nouvelles propriétés à n’importe quel objet. Dans cet exemple, il ajoute une nouvelle « NoteProperty » (valeur statique) nommée « ProcessId » et stocke l’ID de processus nouvellement récupéré dans cette propriété. C’est tout.

Une fois que vous maîtrisez cette technique, vous pouvez concevoir votre code de manière beaucoup plus modulaire, comme nous allons le découvrir.

Un système modulaire comme des Lego

Pour s’exercer, ajoutons deux autres fonctions qui trouvent le nom de processus pour un ID de processus donné, et le nom du groupe de service. L’approche est la même, avec les mêmes ingrédients que nous venons de voir.

Ajouter le nom de processus

Voici une fonction PowerShell modulaire qui récupère le nom de processus en clair à partir de n’importe quel objet ayant une propriété nommée ProcessId ou Id de type entier :

function Get-ProcessNameById {

    [CmdletBinding()]

    param

    (

        [Parameter(Mandatory, ValueFromPipeline)]

        [Object]

        $InputObject,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]

        [int]

        [Alias('Id')]

        $ProcessId,

        [switch]

        $PassThru

    )

    process

    {

        $ProcessName = if ($ProcessId)

        {

            (Get-Process -Id $ProcessId).Name

        }

        if ($PassThru)

        {

            $InputObject |

            Add-Member -MemberType NoteProperty -Name ProcessName -Value $ProcessName -PassThru

        }

        else

        {

            $ProcessName

        }

    }

}

Voici un survol rapide des astuces de codage PowerShell utilisées ici :

PS C:\> Get-Process -id $pid | Get-ProcessNameById
powershell_ise

Ou, appliquez-la à nos services et obtenez une liste de services, leurs processus sous-jacents et leurs ID de processus :

PS C:\> Get-Service | Get-ServiceProcessId -PassThru | Get-ProcessNameById -PassThru | Where-Object ProcessId | Select-Object -Property Name, ProcessName, ProcessId | select -First 5

Name             ProcessName ProcessId
----             ----------- ---------
AdobeARMservice  armsvc      5524
AppIDSvc         svchost     1960
Appinfo          svchost     13852
AudioEndpointBuilder svchost 4324
Audiosrv         svchost     4456
(...)

Groupes de services

Comme vous l’avez peut-être remarqué, de nombreux services partagent le même processus, et il existe de nombreux processus avec des noms identiques (« svchost »). C’est intentionnel : sous Windows, de nombreux services peuvent fonctionner dans la même DLL.

Pour récupérer leur groupe de service, nous pouvons réutiliser Get-ServiceProcessId. Cette fois, nous ne lisons pas l’ID de processus depuis les objets WMI, mais plutôt PathName :

function Get-ServiceGroupName

{

    [CmdletBinding()]

    param

    (

        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName='ServiceName')]

        [string]

        $Name,

        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName='ActualService')]

        [System.ServiceProcess.ServiceController]

        $Service,

        [switch]

        $PassThru

    )

    begin

    {

        $hash = Get-CimInstance Win32_Service |

        Group-Object -Property Name -AsHashTable -AsString

    }

    process

    {

        # since the user now has the option to either specify the service name

        # or pipe in an actual service, let's look up the pendant so we always

        # have both and can simplify the remaining code

        if ($PSCmdlet.ParameterSetName -eq 'ServiceName')

        {

            $Service = Get-Service -Name $Name -ErrorAction Ignore

        }

        else

        {

            $Name = $Service.Name

        }

        # get the launch command for this service

        $pathName = $hash.$Name.PathName

        # look if the parameter "-k" was specified, followed by the service group name

        # we are after (uses RegEx)

        $groupName = if($pathName -match '-k\s+(\w+)')

        {

            $matches[1]

        }

        if ($PassThru)

        {

            # take the service and add the process ID

            $Service |

            Add-Member -MemberType NoteProperty -Name GroupName -Value $groupName -PassThru

        }

        else

        {

            $groupName

        }

    }

}

Et puisque nous avons utilisé des scripts modulaires compatibles avec le pipeline, nous obtenons une autre pièce de code Lego que nous pouvons combiner avec les autres au besoin :

PS C:\> Get-Service | Get-ServiceProcessId -PassThru | Get-ProcessNameById -PassThru | Get-ServiceGroupName -PassThru | Where-Object ProcessId | Select-Object -Property Name, ProcessName, GroupName, ProcessId

Name             ProcessName GroupName                      ProcessId
----             ----------- ---------                      ---------
AdobeARMservice  armsvc                                     5524
AppIDSvc         svchost     LocalServiceNetworkRestricted  1960
Appinfo          svchost     netsvcs                        13852
AudioEndpointBuilder svchost LocalSystemNetworkRestricted   4324
Audiosrv         svchost     LocalServiceNetworkRestricted  4456
(...)

PowerShell Universal à la rescousse

Vous avez peut-être investi quelques heures de travail et créé d’excellents scripts PowerShell qui vous fournissent les informations dont vous avez besoin. Mais vous en souviendrez-vous la semaine prochaine? Le mois prochain? Et si vous avez besoin de ces informations en production? Ou si vous voulez qu’un collègue du service d’assistance puisse les consulter sans avoir à lui expliquer les consoles et les scripts PowerShell?

C’est là qu’intervient PowerShell Universal. Il est entièrement gratuit pour un usage personnel, et si vous souhaitez l’utiliser à des fins commerciales, la licence est économique par serveur, non par utilisateur.

En résumé, PowerShell Universal intègre un hôte PowerShell dans un serveur web. Ça vous permet de créer en toute sécurité des tableaux de bord riches en interface graphique, de transformer des scripts en services et en services web, et bien plus encore.

Pour l’instant, nous voulons utiliser la version personnelle gratuite pour créer un tableau de bord riche en interface graphique où nous pouvons consulter les informations de service que nous venons de récupérer. Gardez à l’esprit que c’était juste un exemple — vous pouvez bien sûr remplacer notre code par ce qui vous importe davantage, et afficher des serveurs VMWare, des collections de photos ou des comptes utilisateurs de la même manière.

Configurer PSU

Voici les quelques étapes pour configurer PSU sur votre propre machine. Aucune licence, aucun coût, aucun privilège administrateur requis. Vous pouvez utiliser le module PowerShell Universal pour installer le serveur Universal. Pour installer le module, utilisez Install-Module ou Install-PSResource.

Install-Module Devolutions.PowerShellUniversal

Pour installer le serveur Universal, utilisez Install-PSUServer.

Install-PSUServer -LatestVersion

L’exécution de cette commande a des effets différents selon votre système d’exploitation :

Créer une application PSU

Pour ajouter une interface graphique à notre script, accédez à http://localhost:5000 pour ouvrir votre console de gestion PSU. Vous devez vous connecter avec le compte Devolutions que vous avez créé précédemment.

Dans la barre latérale gauche, cliquez sur « Apps », puis sur « App ». À droite, vous voyez la liste des applications que vous avez créées précédemment. Au départ, cette liste est vide.

Cliquez sur « Create App ». Donnez-lui un nom convivial, par exemple « Service Dashboard ». Dans le champ ci-dessous, entrez l’« URL » de votre application. Celle-ci doit être un nom relatif, préfixé d’une barre oblique « / », par exemple « /list-services ». Vous pouvez laisser « Description » vide. Assurez-vous que le curseur « Definition » affiche « Code » et non « Command ». Cliquez ensuite sur OK. La nouvelle application apparaît dans votre liste d’applications.

Créer application psu devolutions blogue

Cliquez ensuite sur l’icône en forme de crayon. Cela ouvre l’éditeur PowerShell intégré. Le code par défaut est le suivant :

New-UDApp -Content { 'Hello, World!' }

Pour tester cette application, cliquez sur le bouton « View App » en haut. Votre script PowerShell web s’exécute et vous voyez un message convivial dans votre navigateur. Vous voyez également comment accéder à cette application : son URL est http://localhost:5000/list-services/Home.

Points importants

Avant de continuer, voici deux points importants à retenir :

Afficher un tableau

Remplacez le code par défaut à l’intérieur des accolades par ce code :

New-UDApp -Content {

    # 1.) prepare the data to show

    # get all services...
    $data = Get-Service -ErrorAction Ignore |
    Sort-Object -Property DisplayName |
    # ...select the properties to display in the table
    Select-Object -Property DisplayName, Name, StartMode, StartType

    # 2.) output it using one of the many available UI components, i.e. as a table
    New-UDTable -Data $data

}

Afficher un tableau devolutions blogue psu

Le code récupère des informations et les stocke dans $data. Ensuite, il appelle l’une des nombreuses applets de commande UD… disponibles pour afficher les données dans votre application web. New-UDTable, par exemple, crée automatiquement un tableau pour vous.

Une fois que vous cliquez sur « View App », vous pouvez afficher le tableau dans votre navigateur.

Voir l’application devolutions blogue psu

Vous avez peut-être remarqué que nous avons ajouté -ErrorAction Ignore à notre code. C’est parce que par défaut, PSU exécute votre code dans PowerShell 7. PowerShell 7 se comporte légèrement différemment de Windows PowerShell, et Get-Service peut émettre des exceptions sur les systèmes non anglophones. Chaque fois que des exceptions sont levées dans votre code, l’application affiche ces messages d’erreur. Pour éviter ça, assurez-vous toujours que votre code ne génère pas d’exceptions non gérées.

Visionneuse de services

Maintenant que vous avez vu à quel point il est simple d’ajouter une interface utilisateur graphique basée sur un navigateur à votre code, rassemblons tout et créons notre visionneuse de processus de service étendue. Remplacez le code par celui que nous avons créé précédemment :

# first, define the functions we created
# these functions could also be stored in external modules so they are
# out of the way, but for now we keep everything as a single script


# submit name of service or pipe in services
# adds the process ID of the process that executes a service
function Get-ServiceProcessId {

    [CmdletBinding()]

    param

    (

        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName='ServiceName')]

        [string]

        $Name,

        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName='ActualService')]

        [System.ServiceProcess.ServiceController]

        $Service,

        [switch]

        $PassThru

    )

    begin

    {

        $hash = Get-CimInstance Win32_Service -ErrorAction Ignore |

        Group-Object -Property Name -AsHashTable -AsString

    }

    process

    {

        # since the user now has the option to either specify the service name

        # or pipe in an actual service, let's look up the pendant so we always

        # have both and can simplify the remaining code

        if ($PSCmdlet.ParameterSetName -eq 'ServiceName')

        {

            $Service = Get-Service -Name $Name -ErrorAction Ignore

        }

        else

        {

            $Name = $Service.Name

        }

        # make sure "0" values are replaced by NULL

        $id = $hash.$Name.ProcessId | Where-Object { $_ -gt 0 }

        if ($PassThru)

        {

            # take the service and add the process ID

            $Service |

            Add-Member -MemberType NoteProperty -Name ProcessId -Value $id -PassThru

        }

        else

        {

            $id

        }

    }

}




# pipe in any object with a property "ProcessId" or "Id", and add a property with
# the actual process name
function Get-ProcessNameById {

    [CmdletBinding()]

    param

    (

        [Parameter(Mandatory, ValueFromPipeline)]

        [Object]

        $InputObject,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]

        [int]

        [Alias('Id')]

        $ProcessId,

        [switch]

        $PassThru

    )

    process

    {

        $ProcessName = if ($ProcessId)

        {

            (Get-Process -Id $ProcessId).Name

        }

        if ($PassThru)

        {

            $InputObject |

            Add-Member -MemberType NoteProperty -Name ProcessName -Value $ProcessName -PassThru

        }

        else

        {

            $ProcessName

        }

    }

}


# submit the name of a service, or pipe in services
# adds the property "GroupName", exposing the name of the service group for
# the given service
function Get-ServiceGroupName

{

    [CmdletBinding()]

    param

    (

        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName='ServiceName')]

        [string]

        $Name,

        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName='ActualService')]

        [System.ServiceProcess.ServiceController]

        $Service,

        [switch]

        $PassThru

    )

    begin

    {

        $hash = Get-CimInstance Win32_Service |

        Group-Object -Property Name -AsHashTable -AsString

    }

    process

    {

        # since the user now has the option to either specify the service name

        # or pipe in an actual service, let's look up the pendant so we always

        # have both and can simplify the remaining code

        if ($PSCmdlet.ParameterSetName -eq 'ServiceName')

        {

            $Service = Get-Service -Name $Name -ErrorAction Ignore

        }

        else

        {

            $Name = $Service.Name

        }

        # get the launch command for this service

        $pathName = $hash.$Name.PathName

        # look if the parameter "-k" was specified, followed by the service group name

        # we are after (uses RegEx)

        $groupName = if($pathName -match '-k\s+(\w+)')

        {

            $matches[1]

        }

        if ($PassThru)

        {

            # take the service and add the process ID

            $Service |

            Add-Member -MemberType NoteProperty -Name GroupName -Value $groupName -PassThru

        }

        else

        {

            $groupName

        }

    }

}


# NOW we are defining our web app
# thanks to our modular functions, this part is short and simple to
# understand and modify
New-UDApp -Content {

    # 1.) prepare the data to show

    # retrieve all services (make sure to catch any error)
    $data = Get-Service -ErrorAction Ignore |
    # ...add the service process ID...
    Get-ServiceProcessId -PassThru |
    # ...take only services that have a process ID...
    Where-Object ProcessId |
    # ...add the process name for the process ID...
    Get-ProcessNameById -PassThru |
    # ...add the service group name so we can differentiate svchost...
    Get-ServiceGroupName -PassThru |
    # ...sort by service displayname...
    Sort-Object -Property DisplayName |
    # select the properties to display in the table
    Select-Object -Property DisplayName, Name, ProcessName, GroupName, ProcessId

    # 2.) output it using one of the many available UI components, i.e. as a table
    New-UDTable -Data $data

}

Au début, vous trouvez les trois fonctions que nous avons créées précédemment. Dans une prochaine partie de cette série, nous déplacerons ces fonctions dans des modules externes, mais pour l’instant, tout est défini dans le code pour éviter toute dépendance.

Ensuite, les données récupérées sont affichées par la même New-UDTable que vous avez utilisée précédemment. Lorsque vous cliquez sur « View App », votre code s’exécute, les informations de service sont récupérées et un tableau vous présente les données.

Visionneuse de services devolutions blogue psu

Tout ce dont vous avez besoin, c’est l’URL de cette application web : http://localhost:5000/list-services/Home. Vous pouvez la sauvegarder comme lien sur votre bureau ou la mettre en favoris dans votre navigateur. Chaque fois que vous avez besoin d’informations sur les services, vous n’avez plus à chercher des scripts et à les exécuter dans une console.

Et quand vous y réfléchissez bien, vous voyez les formidables possibilités : vous pouvez créer vos tableaux de bord personnels. Mais avec une licence corporative et en configurant PSU avec des privilèges administrateur, vous pourriez aussi héberger vos scripts en tant que service, offrant un accès par navigateur avec un contrôle de sécurité granulaire à n’importe qui dans votre organisation.

Nous y arriverons tout au long de cette série. Pour l’instant, vous avez vu comment configurer PSU et créer votre première application web PSU. Dans la prochaine partie, nous repartirons d’un code PowerShell et étendrons le tableau de bord avec de nouveaux contrôles et des fonctionnalités supplémentaires. Nous expliquerons également pourquoi le tableau de service que vous venez de créer affiche les colonnes dans un ordre quelque peu inattendu, et comment le corriger.