Create a Remote Desktop Conn. Mgr. file for Azure

Every now and then, I will spin up a test environment in azure with a couple of Virtual Machines. And as much as enjoy the User interface of the Portal, there have been times that I longed for the ease of use of the Remote Desktop Connection Manager.

One of the frustrations is that the *.rdp files downloaded from Azure run at full screen, which I find awkward, so the first thing I do is bring down the resolution, which is a pain, when I have several machines.

So I decided to create a tool to auto-generate a Remote Desktop Configuration Manager configuration file.

This also gave me the opportunity to play around with the way *.rdg stores passwords.

RDG security

First a word about security.

The Remote Desktop Connection Manager uses the “Data Protection API” to “Encrypt” passwords stored within the *.rdg file.

cred.PNG

The great thing about this API, is that if another user were to open this file on another machine, it can’t be read. Only the user running on the same machine can extract this password.

Note that any program or script running under your user’s context can read this password as plaintext, works great for this script, but my one design change to the “Remote Desktop Connection Manager” would be to add a “PIN” or other layer of security abstraction to prevent other “rouge” processes or users from gaining access to the passwords stored locally on my machine.

Azure

This script will log into your Azure Account, enumerate all the service groups, and get a list of all Virtual Machines within these groups, Creating entries within the “Remote Desktop Connection Manager” for each Azure Virtual Machine.

Script

<#
 .SYNOPSIS
Auto Generate a RDG file for Azure.
.DESCRIPTION
Will create a Microsoft Remote Desktop Connection Manager *.RDG file
from the Virtual Machines within your Azure Tenant.
.PARAMETER Path
Location of the target *.RDG file.
The default is "My Azure Machines.rdg" placed on the desktop
.PARAMETER Force
Will create the RDG file *even* if the file already exists (force it).
.PARAMETER Credential
An array of [PSCredential] objects to be placed in the RDG file.
.PARAMETER AzureCred
Credentials for logging into Azure
.EXAMPLE
C:\PS> .\RDGGen.ps1
Generate the RDG file with no built in credentials.
.EXAMPLE
C:\PS> $cred = Get-Credential
C:\PS> .\RDGGen.ps1 -Credential $Cred
Generate an RDG file with credentials from the prompt.
.NOTES
Please be aware that although credentials are stored within the *.RDG file
"encrypted", any program running within the user's context can extract the
password as plain text. YMMV.
Copyright Keith Garner, All rights reserved.
Apache License
#>
[cmdletbinding()]
param(
[pscredential[]] $Credential,
[string] $path = ([Environment]::GetFolderPath("Desktop") + "\My Azure Machines.rdg" ),
[switch] $force,
[pscredential] $AzureCred
)
#region Support Routines
function Get-CredentialBlob {
param( [pscredential[]] $Credential )
process {
foreach ( $cred in $Credential ) {
$PasswordBytes = [System.Text.Encoding]::Unicode.GetBytes($cred.GetNetworkCredential().password)
$SecurePassword = [Security.Cryptography.ProtectedData]::Protect($PasswordBytes, $null, [Security.Cryptography.DataProtectionScope]::LocalMachine)
$Base64Password = [System.Convert]::ToBase64String($SecurePassword)
@"
<credentialsProfiles>
<credentialsProfile inherit="None">
<profileName scope="Local">$($cred.UserName)</profileName>
<userName>$($cred.UserName)</userName>
<password>$($Base64Password)</password>
<domain>.</domain>
</credentialsProfile>
</credentialsProfiles>
"@
}
}
}
function Get-MyAzureServices {
param ( $Services )
foreach ( $Service in $Services ) {
@"
<group>
<properties>
<expanded>True</expanded>
<name>$($Service.label)</name>
</properties>
"@
foreach ( $VM in Get-AzureVM ServiceName $service.label ) {
$Port = $VM | Get-AzureEndpoint | ? Name -eq RemoteDesktop | % Port
@"
<server>
<properties>
<displayName>$($VM.HostName)</displayName>
<name>$($VM.ServiceName).cloudapp.net:$Port</name>
</properties>
</server>
"@
}
@"
</group>
"@
}
}
#endregion
# Connect to Azure and get the server list..
Import-module azure Force ErrorAction SilentlyContinue
$Services = get-azureservice ErrorAction SilentlyContinue
if ( -not $Services ) {
if ( $AzureCred ) {
Add-AzureAccount Credential $AzureCred
}
else {
Add-AzureAccount
}
$Services = get-azureservice ErrorAction SilentlyContinue
}
@"
<?xml version="1.0" encoding="utf-8"?>
<RDCMan programVersion="2.7" schemaVersion="3">
<file>
$( get-CredentialBlob $Credential )
$(
if ( $Credential ) {
@"
<logonCredentials inherit="None">
<profileName scope="File">$($Credential | Select-object first 1 | % UserName )</profileName>
</logonCredentials>
"@
}
)
<remoteDesktop inherit="None">
<sameSizeAsClientArea>True</sameSizeAsClientArea>
<fullScreen>False</fullScreen>
<colorDepth>24</colorDepth>
</remoteDesktop>
<properties>
<expanded>True</expanded>
<name>Azure</name>
</properties>
$( Get-MyAzureServices $Services )
</file>
<connected />
<favorites />
<recentlyUsed />
</RDCMan>
"@ | out-file filepath $path Encoding utf8 force:$Force
if ( test-path $path ) {
& 'C:\Program Files (x86)\Microsoft\Remote Desktop Connection Manager\RDCMan.exe' $path
}

view raw
RDGGen.ps1
hosted with ❤ by GitHub

-k

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s