Opening Ports on Cloud Services
There are scenarios that warrants us to open ports of the Windows Firewall. Imagine an application that coordinates work across many compute nodes. The workload coordinator needs to know and manage each compute node.
The diagram above, depicts a Virtual Machine that has direct access to instances of a Cloud Service without going through a Load Balancer. Both the Virtual Machine and Cloud Service are deployed to a Virtual Network on Microsoft Azure. Using Internal Endpoints in this scenario, would not yield the desired configuration. Endpoints are defined at the Cloud Service boundary and are IP addresses belong to the Data Center’s Network Address Space. Therefore, In order to allow the Virtual Machine to communicate over specific ports to individual Cloud Service instances, we need to use PowerShell and a Startup Task to configure the Windows Firewall.
Using PowerShell to Open a Port
$port = New-Object -ComObject HNetCfg.FWOpenPort $port.Port = 8080 $port.Name = 'Compute Node Inbound Port' $port.Enabled = $true $fwMgr = New-Object -ComObject HNetCfg.FwMgr $profile = $fwMgr.LocalPolicy.CurrentProfile $profile.GloballyOpenPorts.Add($port)
Using a Command Line to Open a Port
REM Add a firewall rule in a startup task. REM Add an inbound rule requiring security and encryption for TCP port 8080 traffic. netsh advfirewall firewall add rule name="Require Encryption for Inbound TCP/8080" protocol=TCP dir=in localport=8080 security=authdynenc action=allow >> "%TEMP%\StartupLog.txt" 2>&1 REM If an error occurred, return the errorlevel. EXIT /B %errorlevel%
Configuring a Cloud Service Startup Task
Startup tasks are actions that are taken before your roles begin. Startup tasks are defined in the ServiceDefinition.csdef file by using the Task element within the Startup element. Frequently startup tasks are batch files, but they can also be console applications, or batch files that start PowerShell scripts.
We have two options to open Ports on the Windows Firewall. For this post, I will use PowerShell because it has more steps. This example can be used as a starting point to run other PowerShell scripts from Startup Tasks.
Cloud Service – ServiceDefinition.csdef
The Startup Task is defined on a per role basis. In our case, we must elevate the execution context so that we may interact with the Windows Firewall.
<?xml version="1.0" encoding="utf-16"?> <ServiceDefinition xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="ComputeService" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition"> <WorkerRole name="Compute" vmsize="Standard_D1"> <Startup> <Task commandLine=".\startup.cmd > startup_log.txt" executionContext="elevated" /> </Startup> <Runtime executionContext="elevated"> <EntryPoint> <ProgramEntryPoint commandLine="worker.cmd" setReadyOnProcessStart="true" /> </EntryPoint> </Runtime> </WorkerRole> </ServiceDefinition>
Command Line – startup.cmd
This command file elevates the execution policy for PowerShell, then it executes the startup.ps1 file. It’s important to note that every time the command line is executed, it produces a StartupLog file, which helps to confirm that everything ran as expected. If an error occurs, this log file can save you countless hours of debugging by providing insights about what may have gone wrong.
REM Attempt to set the execution policy by using PowerShell version 2.0 syntax. PowerShell -Version 2.0 -ExecutionPolicy Unrestricted .\startup.ps1 >> "%TEMP%\StartupLog.txt" 2>&1 IF %ERRORLEVEL% EQU -393216 ( REM PowerShell version 2.0 isn't available. Set the execution policy by using the PowerShell version 1.0 calling method. PowerShell -Command "Set-ExecutionPolicy Unrestricted" >> "%TEMP%\StartupLog.txt" 2>&1 PowerShell .\startup.ps1 >> "%TEMP%\StartupLog.txt" 2>&1 ) REM If an error occurred, return the errorlevel. EXIT /B %errorlevel% echo SUCCESS exit /b 0
PowerShell – startup.ps1
This file contains the PowerShell script listed above. This is also the place where we can configure other aspects of out Cloud Service via PowerShell.
$port = New-Object -ComObject HNetCfg.FWOpenPort $port.Port = 8080 $port.Name = 'Compute Node Inbound Port' $port.Enabled = $true $fwMgr = New-Object -ComObject HNetCfg.FwMgr $profile = $fwMgr.LocalPolicy.CurrentProfile $profile.GloballyOpenPorts.Add($port)
Packaging
For this to work, we need to place the Command Line and PowerShell files at the root of the Cloud Service Role. If you are using Visual Studio to create your package, be sure to set both files to Copy Always otherwise they will be missing from the resulting package. If you are using PowerShell to create the package, the CmdLet will take everything from the role’s directory.
Deploying the Cloud Service
There is nothing different about deploying a Cloud Service that has Startup Tasks. Using your tool of preference, create a new Cloud Service package and deploy it to Microsoft Azure.
Using PowerShell to package the Cloud Service requires you to navigate to the Cloud Service directory and execute the Save-AzureServiceProjectPackage command. This will produce a cloud_package.cspkg package file.
To deploy Azure Cloud Services, I usually use PowerShell. This is an example of what you may write yourself.
New-AzureDeployment ` -Package 'C:\\cloud_package.cspkg' ` -Configuration 'C:\\ServiceConfiguration.Cloud.cscfg' ` -Slot Production ` -Label 'Compute-2015-02-17' ` -ServiceName 'DemoCloudService' ` -Name 'DemoCloudService' ` -Verbose
Resources
- Run Startup Tasks in Azure
- Strategies for Writing Startup Tasks that can run Multiple Times
- Best Practices for Startup Tasks
- Define Startup Tasks for a Role
- Define Environment Variables Before a Role Starts
- Use Local Storage to Store Files During Startup
- Make a Startup Task Perform Different Actions on the Compute Emulator and the Cloud
- Use AppCmd.exe to Configure IIS at Startup
- Add Firewall Rules By Using a Startup Task
- WebRole Schema
- WorkerRole Schema