Create RDP Menu1

Hello Guys,

Could some one help me with my code. In the end menu I want to combine, $.Basename and $.Rol

Thanks - Andreas :slight_smile:

<# 

Create RDPMenu for Customer based on input server.csv 

Example:
Name         Rol 
--------     ------------------
SVR001,(App1)
SVR002,(DB1)
SVR022,(Datacenter)
SVR008,(Middleware)
SVR012,(MGT)
SVR012,(DomainController1)
SVR013,(Radiusserver1)
...
..
.
#>

Function Create-RDP () {

# PSScriptroot for ISE
$ScriptDir   = Split-Path -Path $psISE.CurrentFile.FullPath 
$Customer    = Read-Host -Prompt "Enter your customer" 
#Write-Host "01. Starting function Create-RDP for customer $Customer servers " -ForegroundColor Cyan; Start-Sleep 1; Write-Host ""

$ServerList  = "$ScriptDir\servers.csv" #Read-Host "Path to servers.csv"
$Servers     =  Import-Csv -Path $ServerList -Delimiter "," -Encoding Default # csv with customer servers (see: Example Server.csv)
$Global:ShortcutDir = "$ScriptDir\$Customer"+"_RDPs" # To Next Scope
New-Item $Global:ShortcutDir -ItemType Directory -Force | Out-Null

# Create RDP files from servers file
#Write-Host "02. Create RDP files from $Customer servers file " -ForegroundColor Cyan ; Start-Sleep 1; Write-Host ""

Foreach ($_ in $servers) {
$wshshell = New-Object -ComObject WScript.Shell
$lnk = $wshshell.CreateShortcut("$Global:ShortcutDir\$($_.Name).lnk")
$lnk.TargetPath = "%windir%\system32\mstsc.exe"
$lnk.Arguments = "/v:$($_.Name)"
$lnk.Description = "$($_.Name) - $($_.Rol)"
$lnk.Save()
 }
}

Create-RDP

Function Show-Menu
{
    param (
        [string]$Title = 'My Menu'
    )
    Clear-Host
    Write-Host "================ $Title ================"

    $Menu = @{}
    $RDP = Get-ChildItem $Global:ShortcutDir   | 
        
        ForEach-Object -Begin {$i = 1} { 
        Write-Host "Press '$i' RDP into : $($_.BaseName)  " ### I want to add also the Server Rol
        $Menu.add("$i",$_.FullName)
        $i++
    }
    Write-Host "Q: Press 'Q' to quit."
    $Selection = Read-Host "Please make a selection"

    if ($Selection -eq 'Q') { Return } 
    Else { Invoke-Item $Menu.$Selection }

    }
$userinput = Show-Menu -Title 'RDP Menu '

You forgot to ask a question. :wink:

As a tip in advance …

Using $_ as your own loop variable is a very bad idea since it is the built in PowerShell pipeline variable. And in general using singular and plural of a certain noun to distinguish between arrays and single elements is a bad idea as well as it’s quite error prone.
A much better approach would be to use for example

Foreach ($ComputerName in $ComputerNameList){

Hello Olaf, thanks , but the question is in the first sentence , maybe not clear.

The question is also marked in "Write-Host “Press ‘$i’ RDP into : $($.BaseName) " # I want to add also the $($.Rol)” <---- the question.

Regards,
Andreas

You want to combine them into what, a string? “$var1 $var$” it’s no different than the lnk description line in your code just reference the other property

Hello Neemobeer,

I am not sure how to accomplise this.

The vars each come from a different object , so different properties, which i am trying to match.

I need to combine Server.Role , Server.Name and Server.FullPath.

But where do you use at least one of them? Where you want to combine them and whatfor?

In general you can use a [PSCustomObject] to combine whatever you like.

Please read the help completely including the examples to learn how to use it.

In the first function Create-Rdp , I am creating rdp shotcuts based on import-csv server.csv
In the second function Show-Menu i am creating an dynamic menu based on Get-ChildItem from the items in the shortcutDir.
In this Menu if want to see at Write-Host the Name of the server and the Role.
I am not able to populate the Server.Role from the get-childitem , So I want to combine them some how.

Probably its simple but I dont see it now.
Could you run it, and see want your findings are?

Thanks Andreas

the server.csv is like.
Name Rol


SVR001,(App1)
SVR002,(DB1)
SVR022,(Datacenter)
SVR008,(Middleware)
SVR012,(MGT)
SVR012,(DomainController1)
SVR013,(Radiusserver1)

Get-ChildItem returns file system objects. Those objects do not have a property role. You would need to extract the link description to get those information from a *.lnk file.
Why don’t you use the CSV file as source for your menu?

Yess, so I made the csv the source, by exporting the $_.lnk to a csv.
But now the menu item don’t match the servers …


Function Create-Rdp {

# PSScriptroot for ISE
$ScriptDir          = Split-Path -Path $psISE.CurrentFile.FullPath 
$Global:Customer    = Read-Host -Prompt "Enter your Customer" 
$Global:Customer Global:Server " -ForegroundColor Cyan; Start-Sleep 1; Write-Host ""

$Global:Serverlist  = "$ScriptDir\Servers.csv" 
$Global:Shortcutcsv = "$ScriptDir\Shortcuts.csv"
$Global:Servers     =  Import-Csv -Path $Global:Serverlist -Delimiter ";" -Encoding Default 
$Global:ShortcutDir = "$ScriptDir\$Global:Customer"+"_RDPs" 
New-Item $Global:ShortcutDir -ItemType Directory -Force | Out-Null

# Create RDP files from Global:Server file

Foreach ($Global:Server in $Global:Servers) {
$wshshell = New-Object -ComObject WScript.Shell
$lnk = $wshshell.CreateShortcut("$Global:ShortcutDir\$($Global:Server.Name).lnk")
$lnk.TargetPath = "%windir%\system32\mstsc.exe"
$lnk.Arguments = "/v:$($Global:Server.Name)"
$lnk.Description = "$($Global:Server.Name) - $($Global:Server.Role)"
$lnk.Save()
$lnk | Select Description,FullName | Export-Csv $Global:Shortcutcsv -NoTypeInformation -Append
}
 } 
Create-RDP

Function Show-Menu
{
    param (
        [string]$Title = 'My RDP Menu'
    )
    Clear-Host
    Write-Host "================ $Title ================"

    $Menu = @{}
    Import-csv $Global:Shortcutcsv  |      
        ForEach-Object -Begin {$i = 1} { 
        Write-Host "Press '$i' RDP into: $($_.Description)"
        $Menu.add("$i",$_.FullName)
        $i++
    }
    Write-Host "Q: Press 'Q' to quit."
    $Selection = Read-Host "Please make a selection"

    if ($Selection -eq 'Q') { Return } 
    Else { Invoke-Item $Menu.$Selection }

    }
$userinput = Show-Menu -Title 'RDP Menu'

Why? You already have what you want in the input CSV?! :face_with_raised_eyebrow: :thinking: :man_shrugging:t4:

You may make A BIG STEP BACK and start with explaining in simple terms what it is what you’re actually trying to achieve. Don’t explain the way you think you have to go to achieve your goal - explain the bigger picture please.

Okay the big picture is as follow.

I have 10 customers with each a bunch of servers.
For each customer I want a menu with their servers when selecting a specified server it needs to start a RDP session to it.
After accomplising this in a script, I want to convert it to exe.

Why do you need all these *.lnk files then? You could simply start an RDP session with a command line?! :thinking:
Regardless of that … wouldn’t be better to install them an a little more professional RDP solution like the RDP-Manager from CInspiration for example?
https://www.cinspiration.de/uebersicht4.html
Their website seems to be not working at the moment but the tool is actually very nice. I use it every day.

Why that? You know that it will not protect your code from beeing seen, don’t you?

Besides that … you’re overcomplicating this a lot

$InputData = @'
Name,Role
SVR001,App1
SVR002,DB1
SVR022,Datacenter
SVR008,Middleware
SVR012,MGT
SVR012,DomainController1
SVR013,Radiusserver1
'@ |
    ConvertFrom-Csv

$Index = 1
$IndexedInputData =
$InputData | 
    ForEach-Object {
        [PSCustomObject]@{
            Index = $Index++
            Name  = $_.Name
            Role  = $_.Role
        }
    }    

$IndexedInputData

[int]$choice = 0
do {
    try {
        [int]$choice = Read-Host "`nPlease enter index of the desired item (<Enter> = 0 = cancel)" -ErrorAction Stop
    }
    catch {
        Write-Warning "Incorrect input! Please choose a numeric value between '0' and '$($IndexedInputData.Length )'!"
        $choice = -1
    } 
}
until ($choice -ge 0 -and $choice -le $IndexedInputData.Length )
if ($choice -eq 0) {
    $selectedItem = 'none'
}else {
    $selectedItem = $IndexedInputData[$choice - 1].Name
}
$selectedItem

Now you can simply run a command line calling MSTSC.exe with the server name and you have your RDP session. :man_shrugging:t4:

This method should be robust enough to even tolerate false inputs of any kind.

Great Olaf, many thanks :slight_smile: Okay so I added this to a menu with a loop.
This to have them for each customer all together in an general menu.
Sharing this to…

# Main menu all customers
function Show-Menu {
    param (
    [string]$Title1 = "$env:USERNAME Custom script",  
    [string]$Title  = 'Choose Which to Start'

    )
    Clear-Host
    Write-Host "================ $Title1 ================"  -ForegroundColor red -BackgroundColor Blue
    Write-Host "================ $Title  ================"
    Write-Host "1: Press '1' CU1 RDP Menu "
    Write-Host "2: Press '2' CU2 RDP Menu"
    Write-Host "3: Press '3' CU3 RDP Menu"
    Write-Host "4: Press '4' CU4 RDP Menu"
    Write-Host "5: Press '5' CU5 RDP Menu"
    Write-Host "Q: Press 'Q' to quit."
}

if ($MyInvocation.MyCommand.CommandType -eq "ExternalScript")
 { $ScriptPath = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition }
 else
 { $ScriptPath = Split-Path -Parent -Path ([Environment]::GetCommandLineArgs()[0]) 
     if (!$ScriptPath){ $ScriptPath = "." } }

# Functions go here
Function CU1-RDP { & ".\CU1-RDP.ps1" } 
Function CU2-RDP { & ".\CU2-RDP.ps1" } 
Function CU3-RDP { & ".\CU3-RDP.ps1" } 
Function CU4-RDP { & ".\CU4-RDP.ps1" } 
Function CU5-RDP { & ".\CU5-RDP.ps1" } 


      
# Menu loop
do {
    Show-Menu
    $input = Read-Host "Please make a selection"
    Clear-Host
    switch ($input) {
        '1' { CU1-RDP ; break }
        '2' { CU2-RDP ; break }
        '3' { Cu3-RDP; break }
        '4' { CU4-RDP; break }
        '5' { CU5-RDP; break }
        'q' { break } # do nothing
        default {
            Write-Host "You entered '$input'" -ForegroundColor Red
            Write-Host "Please select one of the choices from the menu." -ForegroundColor Red
        }
    }
    Pause
} until ($input -eq 'q')

BR, Andreas

The Main menu is a ps1 script to exe, this is for convenience to simply execute it , not to hide code. And I am aware of RDP managers, but I like to get it done in Powershell .

Why not offering a shortcut file with the proper command line to launch the PowerShell script with the according command line? :man_shrugging:t4:

I’d recommend avoiding the added complexity with a compiled script. I’d prefer to work according to the

I actually don’t get why you’re creating functions you use only once. And the rest of your code is quite repetitive and inefficient and hard to maintain.

Well well its all about the learning curve to me. I`ll just practice and create projects to learn Powershell more advanced. Will be more effective in the end . I try that to with the shortcutfile . About the function, did you meen to keep them all together in one ps1 ?

It does not make that much sense to create functions when you use them only once. :man_shrugging:t4:

Actually you just have to take my code suggestion and add one more line of code to make it work - the command line calling the mstsc.exe with the according computer name. :man_shrugging:t4: Did you actually run my code?

I’d try to avoid repetitive code like this. If there is a target server added you have to change cour code on several places instead of just adding one line to your input CSV.

Do your customers really need to read to press a number that often? IMHO that’s actually not very nice to look at. And you’re clearing the console jsut to display the same menu again. And if one of the options are chosen you show the menu again just to close it? :thinking:

Edit:

Using the variable $input is a bad idea because it is an automatic variable built in PowerShell. Assigning to it might have undesired side effects. :point_up:t3:

To my knowledge a main function is the way to go. I`ll have to figure out how to get all the customers dynamic and seperated inside your code which I am using indeed.
Your code is not showing the servers the first time I run it, and it also exits when using it, gone make it like a loop as the main I showed.

here is a piece of the current.

# Main menu, for user selection:
function Show-Menu {
    param (
    [string]$Title1 = 'Andreas Custom Menu',  
    [string]$Title  = 'Select a menu to start'

    )
    Clear-Host
    Write-Host "================ $Title1 ================"  -ForegroundColor red -BackgroundColor Blue
    Write-Host "================ $Title  ================"
    Write-Host "1: CU1 RDP-Menu "
    Write-Host "2: CU2 RDP-Menu"
        Write-Host "Q: Press 'Q' to quit."
}


# Functions Go here:

function RDP-CU1 {
$InputData = @'
Name,Role
SRV01,(Management server - WinDom)
SRV02,(Store & Forward server)
SRV03,(MCS Database server1)
SRV04,(MCS Database server2)


'@ |
    ConvertFrom-Csv

$Index = 1
$IndexedInputData =
$InputData | 
    ForEach-Object {
        [PSCustomObject]@{
            Index = $Index++
            Name  = $_.Name
            Role  = $_.Role
        }
    }    

$IndexedInputData

[int]$choice = 0
do {
    try {
        [int]$choice = Read-Host "`nPlease enter index of the desired item (<Enter> = 0 = cancel)" -ErrorAction Stop
    }
    catch {
        Write-Warning "Incorrect input! Please choose a numeric value between '0' and '$($IndexedInputData.Length )'!"
        $choice = -11
    } 
}
until ($choice -ge 0 -and $choice -le $IndexedInputData.Length )
if ($choice -eq 0) {
    $selectedItem = 'none'
}else {
    $selectedItem = $IndexedInputData[$choice - 1].Name
}

mstsc.exe /v:$selectedItem
}

       
# Main menu loop:
do {
    Show-Menu
    $input = Read-Host "Please make a selection"
        switch ($input) {
        '1' { CU1-RDP ; break }
        '2' { CU2-RDP ; break }
                'q' { break } # do nothing
        default {
            Write-Host "You entered '$input'" -ForegroundColor Red
            Write-Host "Please select one of the choices from the menu." -ForegroundColor Red
        }
    }
    Pause
} until ($input -eq 'q')

That’s actually not common for PowerShell. Are you coming from another programming language?

What does that mean? :thinking: Do you use the same script for all customers? So all customers can see the server names from all other customers? Why don’t you use CSV files as input? I just added it inside my code suggestion for demo purposes here in the forum. Usually it’s good to separate data (input data) from logic (code). :wink:

So you changed my code suggestion?

That’s by design. It offers a choice and when one of the options is chosen it exits. :man_shrugging:t4: Do you want to open more than one RDP session at a time?

And again:

Using the variable name input is a bad idea because it’s a built in variable of PowerShell. Assing to it may cause undesired side effects.

I’d recommend using VSCode as your IDE and add besides the PowerShell extension one that’s called Error Lens. It helps avoiding common errors in advance.

Hey Olaf,

I have no programming background, I`m an application engineer Linux -Windows started to use taskautomation /scripting with powershell since a year or two.

This script is not ment for others , only for own use , might share it with someone. Indeed working in several customer environments.This RDP menu is ment to easily switch and access other enviroments. And in the first place fun learning gathering knowledge about PS.

All your recommentations I investigate :slight_smile:

Thanks , now F1