In part 2 I discussed how to create a startup task to run tests during application startup. But what if something goes wrong and your test output contains a bunch of failures or the output isn’t generated? If the fix is obvious, making the required changes and redeploying is the obvious path, but if the fix isn’t obvious then you should consider using remote desktop to connect to the deployed application and troubleshoot it.
Remote desktop on Windows Azure allows you to connect to a specific instance of your application and access the Windows desktop for the VM hosting the application instance. Unfortunately this only works from Windows clients currently. Even though Microsoft provides a remote desktop application for OS X, it can’t successfully connect using the .rdp file generated by Windows Azure.
Configuring for Remote Desktop
Remote desktop isn’t enabled by default, so we have to go through a few steps to enable it. Sepecifically generating a certificate, installing it into Windows, encrypting the login password for the remote desktop session, and adding some XML to the ServiceDefition and ServiceConfiguration files.
Create a Certificate
I used OpenSSL to create the certificate, but you can use the MakeCert utility also. For OpenSSL the commands I used were:
openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout RDPcert.pem -out RDPcert.pem openssl pkcs12 -export -out RDPcert.pfx -in RDPcert.pem -name "My Certificate"
The first command will create a .pem file and the second creates a .pfx file based on the .pem file.
Install the Certificate
Next, we need to install the .pfx file on the Windows client. Fortunately this is as easy as finding the file and double clicking on it. This will launch the certificate installation wizard, and you can just click next all the way through, accepting the defaults. That will load it into the personal store for your current user account, which is where we want it.
To get the file into Windows Azure, we need to create a hosted service but not deploy anything to it. I used the Windows Azure management portal for this:
- Login to the management portal using your Windows Azure subscription.
Select Hosted Services, Storage Accounts & CDN, and then select Hosted Services.
- Either select an existing hosted service, or create a new one. If you are creating a new one, be sure to select Do not deploy. This will create the hosted service without requiring you to deploy an application to it.
Under the hosted service, select the Certificates folder, and then click the Add Certificate icon on the ribbon. This will prompt you to upload the .pfx file we created earlier.
That’s it. Anything deployed to this hosted service can use this certificate for a remote desktop session.
Finally, we need to update the configuration files in our project to enable remote desktop for the application. The changes to the ServiceDefinition.csdef are pretty straight forward, it’s simply adding an section inside the element. It should look something like this:
<WorkerRole name="WorkerRole"> <Imports> <Import moduleName="RemoteForwarder" /> <Import moduleName="RemoteAccess" /> </Imports>
The ServiceConfiguration.cscfg is a little more complex, and we have to use a tool from the [Windows Azure SDK] to encrypt the password for the remote desktop session. Here’s a ServiceConfiguration.cscfg file with all the entries needed for remote desktop:
<?xml version="1.0"?> <ServiceConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" serviceName="blahblahsporf" osFamily="2" osVersion="*" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration"> <Role name="WorkerRole"> <ConfigurationSettings> <Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.Enabled" value="true" /> <Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountUsername" value="username" /> <Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountEncryptedPassword" value="encrypted password" /> <Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountExpiration" value="2013-02-22T13:34:31.8080460-05:00" /> <Setting name="Microsoft.WindowsAzure.Plugins.RemoteForwarder.Enabled" value="true" /> </ConfigurationSettings> <Instances count="2" /> <Certificates> <Certificate name="Microsoft.WindowsAzure.Plugins.RemoteAccess.PasswordEncryption" thumbprint="cert thumbprint" thumbprintAlgorithm="sha1" /> </Certificates> </Role> </ServiceConfiguration>
A few things to note here:
The username that will be used to login to the remote desktop session is specified by the Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountUsername entry.
Remote desktop access will expire on the date specified by Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountExpiration entry. It’s generally a good idea to set the expiration date to sometime soon to prevent unauthorized access.
The Certificates section entry for Microsoft.WindowsAzure.Plugins.RemoteAccess.PasswordEncryption specifies the thumbprint of the certificate we uploaded to Windows Azure and used to encrypt the password.
Now we need to fill in a few things, starting with the encrypted password. The csencrypt command can be used to generate an encrypted password, but in order to run this we need to know the thumbprint for the certificate. Luckily, this is visible in the Windows Azure management portal by selecting the certificate we uploaded earlier.
Go ahead and enter the thumbprint into the Microsoft.WindowsAzure.Plugins.RemoteAccess.PasswordEncryption setting now also, then we'll use csencrypt to encrypt the password.
Csencrypt is installed as part of the Windows Azure SDK and can usually be found at C:\Program Files\Windows Azure SDK\v1.6\bin. Use the following parameters when running it:
csencrypt encrypt-password -Output password.txt -Thumbprint <certificate thumbprint>
This will prompt you to enter and confirm the password to use for the remote desktop connection, and will save it to the password.txt file. There’s also a parameter you can use to copy it the clipboard, but if you’re like me you’re constantly copying things to the clipboard and overwriting stuff you meant to keep.
Open up the password.txt file in notepad, copy and paste the contents into the value attribute of the Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountUsername element.
That’s it for configuration. All that’s left now is to pack up the file using pack.cmd and then deploy it to Windows Azure
After the instances have started, go to the management portal, select an instance, and then click Connect from the ribbon. This will download the .rdp file used to connect to this session. Opening this file will launch the remote desktop client.
The first thing you will see is that the publisher can’t be identified. Just click connect here, and when prompted enter the password you used with the csencrypt command earlier. Next it will say that the identity of the remote computer cannot be identified, just select Yes here.
That’s it, your connected! So now what?
Navigating the Remote Session
In order to work with the Windows Azure remote desktop session you need to understand where things are located. Here’s the general layout and important things to know for Ruby applications:
- The application lives at e:\approot. This is where the ‘WorkerRole’ directory from the deployment is placed, complete with the ‘app’ folder containing your application.
- Ruby is installed into a directory under c:\resources\directory\GUID.WorkerRole.ruby. The GUID part of the directory name is going to be some random value, so just look it up when you visit the desktop.
- Ruby is not in the path for your desktop session. So look up the path above and add it’s bin directory to your PATH environment variable as one of the first steps visiting the desktop.
- Also add the \lib\ruby\gems\1.9.1\bin directory to the PATH, as some of the executable gems get installed there on Windows Azure.
Once you have the path set up, you can just open a command prompt, make changes to your code, and rerun your tests until everything is working correctly. Then, fix it locally on your source, repackage it, and redeploy it.
Why fix it locally and redeploy it if you’ve fixed it in the cloud via remote desktop? Because the things you do via remote desktop aren’t persisted between restarts. Windows Azure uses the package you uploaded each time it starts a new instance; that is the master image as far as it’s concerned. So you need to fix that master image and re-upload it in order to permanently apply whatever changes you made.
So it’s not exactly SSH, but using remote desktop to connect to your hosted application allows you to make changes and run tests live without having to build and redeploy between changes. The big thing to remember though is that any changes you make aren’t persisted, so you will need to implement them locally, rebuild the package, and redeploy to make them permanent.