To improve security with our solutions, we use VNets on all our Azure subscriptions and do not expose remote desktop port (3389) publicly. Since internal endpoints are only reachable within a Cloud Service, this reduce the possible surface of attack.

Connect to Cloud Services instances / Virtual machines without having to go in the Azure portal

We needed a way to export all the virtual machines and cloud services role instances for an Azure subscription without having to go in the Azure portal to get the RDP files. For us, it would not have worked anyway since the rdp file that the portal generate point to the cloudapp.net address.

For our need, we use the private IP address of the instances but you can easily change the script to use the public DNS name instead.

How to do that

First, you select your subscription

Select-AzureSubscription -SubscriptionName $SubscriptionName

The script

#requires -Version 3.0 -Module Azure
[CmdletBinding(DefaultParametersetName='NoExport')]
param(
    [Parameter(Position=0, Mandatory=$true, ParameterSetName='NoExport')]
    [Parameter(Position=0, Mandatory=$true, ParameterSetName='ExportRdp')]
    [string]
    $SubscriptionName,

    [Parameter(Position=1, ParameterSetName='NoExport')]
    [Parameter(Position=1, ParameterSetName='ExportRdp')]
    [string]
    $ServiceName,

    [Parameter(Position=2, ParameterSetName='ExportRdp')]
    [Switch]
    $ExportRdp,

    [Parameter(Position=3, Mandatory=$true, ParameterSetName='ExportRdp')]
    [System.IO.DirectoryInfo]
    $RdpOutputDirectory
)
$ErrorActionPreference = 'Stop'

Import-Module Azure

Write-Verbose "Using subscription $SubscriptionName"
Select-AzureSubscription -SubscriptionName $SubscriptionName

if($ServiceName) {
    Write-Verbose "Querying cloud service $ServiceName"
    $services = Get-AzureService -ServiceName $ServiceName
}
else {
    Write-Verbose "Querying all cloud services."
    $services = Get-AzureService
}

Write-Verbose "Getting deployments informations."
$result = $services | Get-AzureDeployment -ErrorAction SilentlyContinue | % {
    $svcName = $_.ServiceName
    $_.RoleInstanceList | % {
        @{ ServiceName=$svcName; RoleName=$_.RoleName; InstanceName=$_.InstanceName; IpAddress=$_.IPAddress }
    }
}

if($ExportRdp) {
    $RdpOutputDirectory.Create()
    $dir = $RdpOutputDirectory.CreateSubdirectory("RDP-$SubscriptionName")
    Write-Verbose "Exporting rdp files to directory '$($dir.FullName)'."
   
    $result | % {
        $outputFilename = '{0}\{1}.{2}.{3}.rdp' -f $dir.FullName, $_.ServiceName, $_.RoleName, $_.InstanceName
        "full address:s:$($_.IpAddress)`r`nLoadBalanceInfo:s:Cookie: mstshash=$($_.RoleName)#$($_.InstanceName)" | Out-File $outputFilename
    }
}

$result

What it does is really simple, it loop through all deployments of all cloud services inside your subscription and construct an array of objects containing the service name, role name, instance name and ip address.

If you use the ExportRdp switch you need to provide a mandatory RdpOutputDirectory parameter in which rdp files will be exported.

Rdp files will have the following naming pattern: {ServiceName}.{RoleName}.{InstanceName}.rdp

You can easily change this script to modify the generated rdp to include a default username by adding the following line

username:s:{RdpUsername}

where {RdpUsername} is the username

What you'll need

  • An Azure subscription with at least one cloud services (can be a role or virtual machine)
  • Azure PowerShell CmdLet

Let me know what you think!