none
Envoyer un message à des utilisateurs RDS RRS feed

  • Question

  • Bonjour,

    Je suis relativement débutant en powershell, ce qui fait que je coince sur un élément, qui, je pense, n'est pas vraiment problématique.

    Pour commencer un peu de contexte:

    Nous avons une application nommée "GH" (GH.exe) installée sur des serveurs RDS.

    Cette dernière est "faite maison" par nos équipes de développement, et, faute de système de MàJ automatique, nous devons remplacer les fichiers exécutables (copier/coller) à chaque nouvelle version. Jusqu'à maintenant nous appelions les utilisateurs un par un pour leur demander de fermer l'application, sauf que le nombre d'utilisateurs a grossi et que nous ne pouvons plus nous permettre d'en appeler 20 pour cela.

    Nous souhaitons donc utiliser la fonction d'envoi de messages "Send-RDUserMessage", pour leur signaler cela.

    Sachant qu'il n'y a pas que l'application GH sur ces serveurs (mais aussi un paquet d'autres), nous avons besoin d'envoyer un message uniquement aux utilisateurs qui ont cette application lancée.

    J'ai donc commencé à créer un bout de code pour cela, voir ci-dessous :

    # Déclaration des fonctions
    function grep {
      $input | out-string -stream | select-string $args
    }
    # Déclaration des variables
    $appname = "GH"
    $broker = "rdbroker.domain"
    $hostservers = "RDS1.domain", "RDS2.domain", "RDS3.domain"
    $title = "Mise à jour $appname"
    $body = @"
    Bonjour,
    Nous allons procéder à une mise à jour sur $appname.
    Merci d'enregistrer votre travail puis de fermer l'application.
    "@
    # Début
    Clear-Host
    Write-Host -ForegroundColor Yellow "Le message suivant sera envoyés aux utilisateurs actuels de $appname :"
    Write-Host -ForegroundColor Cyan "$body"
    # Insérer ici bout de code demandant de continuer
    # Vérifie les process contenant "GH" sur tous les serveurs hôte"
    ForEach ($i in $hostservers)
    {
        Invoke-Command -ComputerName $i {Get-Process -Includeusername | format-table -Property Username, Path} | grep $appname | Select -Unique
    }
    # Liste l'ensemble des sessions RDS sur la ferme et récupère leur ID
    Get-RDUserSession -ConnectionBroker "$broker" | Format-Table -Property ServerName, DomainName, UserName, SessionID -HideTableHeaders

    J'ai donc un affichage de:

    1. La liste des utilisateurs ayant GH de lancé

    2. La liste des session RDS avec leur UnifiedSessionID

    Je souhaite donc faire la corrélation entre les deux pour garder dans ma deuxième table uniquement les ID de session des utilisateurs qui ont lancé GH et leur serveur RDS hôte, puis faire une boucle qui enverra le message avec quelque chose du genre

    "Send-RDUserMessage -hostserver $i -UnifiedSessionID $j -MessageTitle "$title" -MessageBody "$body"

    Nous envisagions également de faire cette fermeture en 3 étapes (1: message, 2: second message, 3: fermeture forcée de l'application)

    Je suis évidemment enclin à revoir ma logique si elle vous paraît faussée.

    Merci !

    Eliott

    PS : Serveurs RDS 2012R2, broker en 2016.

    mardi 31 octobre 2017 09:11

Réponses

  • Ha ok donc une commande powershell de ce type devrait fonctionner

     (gwmi win32_process |where { $_.name -eq "GH.exe"}).SessionId | ForEach-Object {msg  $_  MESSAGE}

    cdlt


    Ludovik DOPIERALA sur Facebook

    • Proposé comme réponse Ludovik DOPIERALA vendredi 3 novembre 2017 14:26
    • Marqué comme réponse Eliott T mardi 7 novembre 2017 10:27
    vendredi 3 novembre 2017 14:26
  • Bonjour,

    Merci Ludovik, grâce à vous j'ai pu créer mon script final, que je met ci-dessous pour que les futures recherches sur internet puissent le retrouver (et éventuellement l'améliorer, puisque je ne suis pas du tout développeur)

    # Déclaration des variables
    $delay1 = 120
    $delay2 = 30
    $appname = "GH"
    $broker = "broker.fqdn"
    $hostservers = "RDS1.fqdn", "RDS2.fqdn", "RDS3.fqdn"
    $title = "Maintenance $appname"
    $body1 = @"
    Bonjour,
    Nous allons procéder à une mise à jour sur $appname.
    Merci d'enregistrer votre travail puis de fermer l'application.
    "@
    $body2 = @"
    En l'absence de réponse de votre part, nous allons fermer $appname dans $delay2 secondes.
    "@
    
    # Début
    Clear-Host
    Write-Host -ForegroundColor Yellow "Liste des Sessions"
    Get-RDUserSession -ConnectionBroker "$broker" | Sort-Object SessionID | Format-Table -Property ServerName, DomainName, UserName, SessionID
    Write-Host -ForegroundColor Yellow "Le message suivant sera envoyés aux utilisateurs actuels de $appname :"
    Write-Host -ForegroundColor Cyan "$body1"
    Pause
    # Première étape : Envoi du premier message
    ForEach ($i in $hostservers){
        Invoke-Command $i -ScriptBlock {
            (gwmi win32_process |where{
                $_.name -eq "$using:appname.exe"}).SessionId |
                    ForEach-Object {
                            if ($_) 
                            {
                                Send-RDUserMessage -Hostserver localhost -UnifiedSessionID "$_" -MessageTitle "$using:title" -MessageBody "$using:body1"
                                echo "Message envoyé à la session $_ "
                            }
                        }
        }
    }
    # Seconde étape : Envoi du second message
    Start-Sleep -Seconds $delay1
    Write-Host -ForegroundColor Yellow "Le message suivant sera envoyés aux utilisateurs qui n'ont pas encore fermé $appname :"
    Write-Host -ForegroundColor Cyan "$body2"
    ForEach ($i in $hostservers){
        Invoke-Command $i -ScriptBlock {
            (gwmi win32_process |where{
                $_.name -eq "$using:appname.exe"}).SessionId |
                    ForEach-Object {
                            if ($_) 
                            {
                                Send-RDUserMessage -Hostserver localhost -UnifiedSessionID "$_" -MessageTitle "$using:title" -MessageBody "$using:body2"
                                echo "Message envoyé à la session $_ "
                            }
                        }
        }
    }
    # Troisième étape : Fermeture du processus
    Start-Sleep -Seconds $delay2
    ForEach ($i in $hostservers)
    {
        Invoke-Command $i {
            Get-Process "*$using:appname*" | Stop-Process -Force
            }
    }

    • Marqué comme réponse Eliott T mardi 7 novembre 2017 10:27
    mardi 7 novembre 2017 10:26

Toutes les réponses

  • Utiliser New-Object et un tableau me semble tout indiqué (le tableau contenant les PSObject) :

    <#Exemple de code utilisant un PSObject #>
    $myTable = @()
    $Servers = @("srv-01","srv-02","srv-03")
    
    foreach ($srv in $servers)
    {
      $process = invoke-command -computername $srv -scriptblock { Get-Process }
      foreach ($proc in $process) 
      {
        $Obj2Add = new-Object -Type PSObject -Property @{
                                 server = $srv
                                 procname = $proc.name
                                 procid = $proc.id 
                                 }
        $myTable += $obj2add
        $obj2add = $null
      }
    }
    
    $myTable | FT

    mardi 31 octobre 2017 11:43
  • Merci Loïc,

    J'ai testé le bout de code fourni et il semblerais que cela me remonte bien les process avec l'ID de process sur chaque serveur, sauf que ce qui m'intéresse c'est l'UnifiedSessionId (remonté avec "Get-RDUserSession") de chaque Process "GH.exe". Cela peut se faire je pense par le facteur commun qu'est le nom d'utilisateur (ou le combiné domaine/username), à la manière d'un JOIN sous SQL ?

    Ci dessous une capture de mon script initial avec un "schéma paint"

    https://i.imgur.com/VO34zgm.png

    PS : Désolé pour le délai de réponse, j'ai demandé à être notifié lors d'une réponse, mais cela n'as pas été le cas

    vendredi 3 novembre 2017 10:52
  • L'application démarre en RemoteApp ou les utilisateurs sont en bureau à distance ?
    Avez-vous plusieurs serveur RDS ? Si oui n'est-il pas possible de mettre en maintenance le serveur le temps de la MAJ ?
    vendredi 3 novembre 2017 12:12
  • Merci pour la réponse,

    Comme expliqué dans le premier message, nous avons plusieurs serveur RDS en loadbalancing derrière un broker. Ces serveurs font tourner plusieurs applications dont notre fameux "GH.exe".

    Je ne pense pas utile de mettre un serveur complet en maintenance sachant que seul 1/4 de nos users utilisent cette application et que mettre les serveurs en maintenance implique d'en gêner 100%. C'est d'ailleurs pour cela que je souhaite faire un script powershell pour les avertir gentiment.

    J'estime que bloquer les nouvelles connexions sur un serveur est nécessaire uniquement si jamais, on a besoin de reboot.

    La réponse dans ma question tiens selon moi uniquement dans le tri et filtrage des résultats des commandes Get-Process et Get-RDUserSession.


    vendredi 3 novembre 2017 12:24
  • Bonjour

    MSG * /Server:<Votre serveur> Déconnectez vous svp 

    ou pour le serveur sur lequel vous exécutez la commande

    MSG *  Déconnectez vous svp 


    cdlt


    Ludovik DOPIERALA sur Facebook


    • Modifié Ludovik DOPIERALA vendredi 3 novembre 2017 12:39 signature
    • Proposé comme réponse Ludovik DOPIERALA vendredi 3 novembre 2017 12:43
    • Non proposé comme réponse Eliott T vendredi 3 novembre 2017 13:05
    vendredi 3 novembre 2017 12:38
  • Merci mais cela ne réponds pas au problème, je souhaite envoyer le message uniquement aux utilisateurs qui ont l'application "GH.exe"de lancée, par à tous (quel est l’intérêt de gêner les utilisateurs qui n'utilisent pas cette application, et représentent 3/4 de nos users ?)

    cdlt

    vendredi 3 novembre 2017 13:05
  • Ok essayes un truc du gnere

    $Sessions = Get-RDUserSession -ConnectionBroker "$broker"
    Foreach ($Session in $Sessions){
        $HG = Get-WmiObject -Class Win32_Process -Filter "Name= '$myAppName'" -ComputerName $Session.ServerName | ? { $_.GetOwner().User -eq $Session.UserName }
        if ($HG -ne $null) {
            write-host "présent"
        }
    }

    • Proposé comme réponse Ydhem vendredi 3 novembre 2017 13:25
    • Non proposé comme réponse Eliott T mardi 7 novembre 2017 14:55
    vendredi 3 novembre 2017 13:20
  • Merci Loïc,

    J'ai testé le bout de code fourni et il semblerais que cela me remonte bien les process avec l'ID de process sur chaque serveur, sauf que ce qui m'intéresse c'est l'UnifiedSessionId (remonté avec "Get-RDUserSession") de chaque Process "GH.exe". Cela peut se faire je pense par le facteur commun qu'est le nom d'utilisateur (ou le combiné domaine/username), à la manière d'un JOIN sous SQL ?

    Ci dessous une capture de mon script initial avec un "schéma paint"

    https://i.imgur.com/VO34zgm.png

    PS : Désolé pour le délai de réponse, j'ai demandé à être notifié lors d'une réponse, mais cela n'as pas été le cas

    L'exemple montre comment imbriquer plusieurs résultats dans un objet unique pour ensuite ne retourner qu'une collection. Ce n'est pas un "join" au sens T-SQL mais le résultat revient effectivement au même (comme de créer une vue calculée sous sql).  Du coup, dans la boucle "serveurs" vous incluez le code de votre choix dans la section -ScriptBlock {} et le tour est joué. 

    ScriptBlock peut inclure plusieurs commandes (à séparer avec ";") et utiliser des variables.


    vendredi 3 novembre 2017 13:20
  • Ha ok donc une commande powershell de ce type devrait fonctionner

     (gwmi win32_process |where { $_.name -eq "GH.exe"}).SessionId | ForEach-Object {msg  $_  MESSAGE}

    cdlt


    Ludovik DOPIERALA sur Facebook

    • Proposé comme réponse Ludovik DOPIERALA vendredi 3 novembre 2017 14:26
    • Marqué comme réponse Eliott T mardi 7 novembre 2017 10:27
    vendredi 3 novembre 2017 14:26
  • yes.
    vendredi 3 novembre 2017 16:01
  • Bonjour,

    Merci Ludovik, grâce à vous j'ai pu créer mon script final, que je met ci-dessous pour que les futures recherches sur internet puissent le retrouver (et éventuellement l'améliorer, puisque je ne suis pas du tout développeur)

    # Déclaration des variables
    $delay1 = 120
    $delay2 = 30
    $appname = "GH"
    $broker = "broker.fqdn"
    $hostservers = "RDS1.fqdn", "RDS2.fqdn", "RDS3.fqdn"
    $title = "Maintenance $appname"
    $body1 = @"
    Bonjour,
    Nous allons procéder à une mise à jour sur $appname.
    Merci d'enregistrer votre travail puis de fermer l'application.
    "@
    $body2 = @"
    En l'absence de réponse de votre part, nous allons fermer $appname dans $delay2 secondes.
    "@
    
    # Début
    Clear-Host
    Write-Host -ForegroundColor Yellow "Liste des Sessions"
    Get-RDUserSession -ConnectionBroker "$broker" | Sort-Object SessionID | Format-Table -Property ServerName, DomainName, UserName, SessionID
    Write-Host -ForegroundColor Yellow "Le message suivant sera envoyés aux utilisateurs actuels de $appname :"
    Write-Host -ForegroundColor Cyan "$body1"
    Pause
    # Première étape : Envoi du premier message
    ForEach ($i in $hostservers){
        Invoke-Command $i -ScriptBlock {
            (gwmi win32_process |where{
                $_.name -eq "$using:appname.exe"}).SessionId |
                    ForEach-Object {
                            if ($_) 
                            {
                                Send-RDUserMessage -Hostserver localhost -UnifiedSessionID "$_" -MessageTitle "$using:title" -MessageBody "$using:body1"
                                echo "Message envoyé à la session $_ "
                            }
                        }
        }
    }
    # Seconde étape : Envoi du second message
    Start-Sleep -Seconds $delay1
    Write-Host -ForegroundColor Yellow "Le message suivant sera envoyés aux utilisateurs qui n'ont pas encore fermé $appname :"
    Write-Host -ForegroundColor Cyan "$body2"
    ForEach ($i in $hostservers){
        Invoke-Command $i -ScriptBlock {
            (gwmi win32_process |where{
                $_.name -eq "$using:appname.exe"}).SessionId |
                    ForEach-Object {
                            if ($_) 
                            {
                                Send-RDUserMessage -Hostserver localhost -UnifiedSessionID "$_" -MessageTitle "$using:title" -MessageBody "$using:body2"
                                echo "Message envoyé à la session $_ "
                            }
                        }
        }
    }
    # Troisième étape : Fermeture du processus
    Start-Sleep -Seconds $delay2
    ForEach ($i in $hostservers)
    {
        Invoke-Command $i {
            Get-Process "*$using:appname*" | Stop-Process -Force
            }
    }

    • Marqué comme réponse Eliott T mardi 7 novembre 2017 10:27
    mardi 7 novembre 2017 10:26