Deploying to Windows Azure

Before being able to deploy to Windows Azure a Cloud Service Must exist. In this section it can be seen that a Cloud Service can be created within the context of a deployment or independently.

The following can be used to create a cloud service without a deployment. In this case the cloud service name will be “myelastatestservice” with description “my new service” in North Europe.

1
2
3
4
5
6
7
8
9
10
11
12
 var subscriptionManager = new SubscriptionManager(TestConstants.SubscriptionId);
 var deploymentManager = subscriptionManager.GetDeploymentManager();
 
 deploymentManager
       .AddCertificateFromStore(Constants.Thumbprint)
       .ForNewDeployment(TestConstants.HostedServiceName)
       .SetCspkgEndpoint(@"C:\mypackage")
       .WithNewHostedService("myelastatestservice")
       .WithStorageAccount("account")
       .AddDescription("my new service")
       .AddLocation(LocationConstants.NorthEurope)
       .GoHostedServiceDeployment();

The corresponding activity would be to delete a Cloud Service. This activity will be default delete all production and staging deployments too.

1
2
3
4
5
6
7
8
var subscriptionManager = new SubscriptionManager(TestConstants.SubscriptionId);
 
var deleteManager = subscriptionManager.GetDeploymentManager();
deleteManager
   .AddCertificateFromStore(Constants.Thumbprint)
   .ForNewDeployment("any")
   .SetCspkgEndpoint(@"C:\endpoint")
   .DeleteExistingHostedService(TestConstants.HostedServiceName);

The following can be used to deploy a package (.cspkg) to a Cloud Service which hasn’t been created yet. The usage is for a simple workflow to create a Cloud Service and deploy the package to the service once it has been created. The SetBuildDirectoryPath is used to set the root build directory (solution directory) so that the package can be identified along with the relevant config files (.cscfg and .csdef). The following creates a hosted service called “MyNewHostedService” and so will be addressable as mynewhostedservice.cloudapp.net. We have to tell Fluent Management where to find the connection string in the .cscfg file since the package will need to be uploaded to blob storage (Visual Studio does this during it’s deployment cycle) which is handled by WithStorageConnectionStringName. DeploymentParams.StartImmediately tells Fluent Management to start the deployment immediately. If this is omitted the deployment will remain in the stopped state.

Since the .cscfg is not part of the build itself, i.e. it is sent dynamically as Base64 text as part of the Service Management API for deployments we can vary the number of instances for any and all roles in Fluent Management by adding ForRole and WithInstanceCount.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var subscriptionManager = new SubscriptionManager(TestConstants.SubscriptionId);
var deploymentManager = subscriptionManager.GetDeploymentManager();
 
deploymentManager.AddCertificateFromStore(Constants.Thumbprint)
            .ForNewDeployment(TestConstants.DeploymentName)
            .SetCspkgEndpoint(TestConstants.ProjectBuildRoot)
            .WithNewHostedService("MyNewHostedService")
            .WithStorageConnectionStringName(TestConstants.ConnectionStringName)
            .AddDescription("My new hosted services")
            .AddEnvironment(DeploymentSlot.Production)
            .AddLocation(Constants.LocationNorthEurope)
            .AddParams(DeploymentParams.StartImmediately)
            .ForRole(TestConstants.RoleNameHellocloudWeb)
            .WithInstanceCount(2)
            .ReturnWithoutWaitingForRunningRoles()
            .Go()
            .Commit();

The above usage works well but doesn’t wait for all of the role instances to signal that they are now running. By calling the WaitUntilAllRoleInstancesAreRunning method we will inly be signalled when all role instances are running.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var subscriptionManager = new SubscriptionManager(TestConstants.SubscriptionId);
var deploymentManager = subscriptionManager.GetDeploymentManager();
 
deploymentManager.AddCertificateFromStore(Constants.Thumbprint)
            .ForNewDeployment(TestConstants.DeploymentName)
            .SetCspkgEndpoint(TestConstants.ProjectBuildRoot)
            .WithNewHostedService("MyNewHostedService")
            .WithStorageConnectionStringName(TestConstants.ConnectionStringName)
            .AddDescription("My new hosted services")
            .AddEnvironment(DeploymentSlot.Production)
            .AddLocation(Constants.LocationNorthEurope)
            .AddParams(DeploymentParams.StartImmediately)
            .ForRole(TestConstants.RoleNameHellocloudWeb)
            .WithInstanceCount(2)
            .WaitUntilAllRoleInstancesAreRunning()
            .Go()
            .Commit();

Similarly there are many occasions when a Cloud Service already exists and you’ll want to deploy to that. The following can be used in this case with the WithExistingHostedService method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var subscriptionManager = new SubscriptionManager(TestConstants.SubscriptionId);
var deploymentManager = subscriptionManager.GetDeploymentManager();
 
deploymentManager.AddCertificateFromStore(Constants.Thumbprint)
            .ForNewDeployment(TestConstants.DeploymentName)
            .SetCspkgEndpoint(TestConstants.ProjectBuildRoot)
            .WithExistingHostedService("MyNewHostedService")
            .WithStorageConnectionStringName(TestConstants.ConnectionStringName)
            .AddDescription("My new hosted services")
            .AddEnvironment(DeploymentSlot.Production)
            .AddLocation(Constants.LocationNorthEurope)
            .AddParams(DeploymentParams.StartImmediately)
            .ForRole(TestConstants.RoleNameHellocloudWeb)
            .WithInstanceCount(2)
            .ReturnWithoutWaitingForRunningRoles()
            .Go()
            .Commit();

There are also occasions when we would want to replace the entire .cscfg file for the deployment to override the stored file. In Fluent Management the .cscfg file can be resaved anytime but sometimes it may be easier to just replace the configuration on the fly.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var subscriptionManager = new SubscriptionManager(TestConstants.SubscriptionId);
var deploymentManager = subscriptionManager.GetDeploymentManager();
 
deploymentManager.AddCertificateFromStore(Constants.Thumbprint)
            .ForNewDeployment(TestConstants.DeploymentName)
            .SetCspkgEndpoint(TestConstants.ProjectBuildRoot)
            .WithExistingHostedService("MyNewHostedService")
            .WithStorageConnectionStringName(TestConstants.ConnectionStringName)
            .AddDescription("My new hosted services")
            .AddEnvironment(DeploymentSlot.Production)
            .AddLocation(Constants.LocationNorthEurope)
            .AddParams(DeploymentParams.StartImmediately)
            .ForRole(TestConstants.RoleNameHellocloudWeb)
            .WithInstanceCount(2)
            .ReplaceConfiguration(Constants.NewCscfgFile)
            .Go()
            .Commit();

Fluent Management seeks to enable the use of both SSL and Remote Desktop. The problem with providing this support is being able to modify the .csdef file. Any addition or change to RD or SSL means that a rebuild needs to occur. Fluent Management uses msbuild to do this rather than use “cspack” directly. It creates a workflow which will rebuild the project from the command line after creating a new “Service Certificate” – in the case of the example below a certificate with a Common Name (CN) of “helloelastacloud.cloudapp.net”. SSL will be enabled for any and all roles in the package that are specified via the EnableSslForRole method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var subscriptionManager = new SubscriptionManager(TestConstants.SubscriptionId);
var deploymentManager = subscriptionManager.GetDeploymentManager();
 
deploymentManager.AddCertificateFromStore(Constants.Thumbprint)
            .ForNewDeployment(TestConstants.DeploymentName)
            .SetBuildDirectoryRoot(TestConstants.ProjectBuildRoot)
            .EnableSslForRole(TestConstants.RoleNameHellocloudWeb)
            .GenerateAndAddServiceCertificate("elastacloud")
            .WithExistingHostedService("MyNewHostedService")
            .WithStorageConnectionStringName(TestConstants.ConnectionStringName)
            .AddDescription("My new hosted services")
            .AddEnvironment(DeploymentSlot.Production)
            .AddLocation(Constants.LocationNorthEurope)
            .AddParams(DeploymentParams.StartImmediately)
            .ForRole(TestConstants.RoleNameHellocloudWeb)
            .WithInstanceCount(2)
            .ReplaceConfiguration(Constants.NewCscfgFile)
            .Go()
            .Commit();

Instead of creating a new Service Certificate for the deployment an existing one can be used. simply replace the call to GenerateAndAddServiceCertificate with UploadExistingServiceCertificate. The certificate should exist in your personal store and the thumbprint and private key password should be passed to the method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
deploymentManager.AddCertificateFromStore(Constants.Thumbprint)
            .ForNewDeployment(TestConstants.DeploymentName)
            .SetBuildDirectoryRoot(TestConstants.ProjectBuildRoot)
            .EnableSslForRole(TestConstants.RoleNameHellocloudWeb)
            .UploadExistingServiceCertificate(Constants.ServiceThumbprint, "Password1")
            .WithExistingHostedService("MyNewHostedService")
            .WithStorageConnectionStringName(TestConstants.ConnectionStringName)
            .AddDescription("My new hosted services")
            .AddEnvironment(DeploymentSlot.Production)
            .AddLocation(Constants.LocationNorthEurope)
            .AddParams(DeploymentParams.StartImmediately)
            .ForRole(TestConstants.RoleNameHellocloudWeb)
            .WithInstanceCount(2)
            .ReplaceConfiguration(Constants.NewCscfgFile)
            .Go()
            .Commit();

Aside from uploading a service certificate you can reuse one that has been uploaded to a particular Cloud Service using UsePreviouslyUploadedServiceCertificate.

In order to enable Remote Desktop “on the fly” for a particular role the EnableRemoteDesktopForRole can be used with the WithUsernameAndPassword to personalise the remote desktop experience for a user. In a similar way to other operations in Fluent Management this lends itself well to “reactive deployments” which allow the user to create a generic package and then customise for a particular user based on some sets of dynamic conditions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
deploymentManager.AddCertificateFromStore(Constants.Thumbprint)
            .ForNewDeployment(TestConstants.DeploymentName)
            .SetBuildDirectoryRoot(TestConstants.ProjectBuildRoot)
            .EnableRemoteDesktopForRole(TestConstants.RoleNameHellocloudWeb)
            .WithUsernameAndPassword("username", "Password123")
            .UsePreviouslyUploadedServiceCertificate("testname", Constants.ServiceThumbprint)
            .WithExistingHostedService("MyNewHostedService")
            .WithStorageConnectionStringName(TestConstants.ConnectionStringName)
            .AddDescription("My new hosted services")
            .AddEnvironment(DeploymentSlot.Production)
            .AddLocation(Constants.LocationNorthEurope)
            .AddParams(DeploymentParams.StartImmediately)
            .ForRole(TestConstants.RoleNameHellocloudWeb)
            .WithInstanceCount(2)
            .ReplaceConfiguration(Constants.NewCscfgFile)
            .Go()
            .Commit();

It’s also possible to combine the addition of SSL and Remote Desktop in one call like so. Note the use of EnableRemoteDesktopAndSslForRole.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
deploymentManager.AddCertificateFromStore(Constants.Thumbprint)
            .ForNewDeployment(TestConstants.DeploymentName)
            .SetBuildDirectoryRoot(TestConstants.ProjectBuildRoot)
            .EnableRemoteDesktopAndSslForRole(TestConstants.RoleNameHellocloudWeb)
            .WithUsernameAndPassword("username", "Password123")
            .UsePreviouslyUploadedServiceCertificate("testname", Constants.ServiceThumbprint)
            .WithExistingHostedService("MyNewHostedService")
            .WithStorageConnectionStringName(TestConstants.ConnectionStringName)
            .AddDescription("My new hosted services")
            .AddEnvironment(DeploymentSlot.Production)
            .AddLocation(Constants.LocationNorthEurope)
            .AddParams(DeploymentParams.StartImmediately)
            .ForRole(TestConstants.RoleNameHellocloudWeb)
            .WithInstanceCount(2)
            .ReplaceConfiguration(Constants.NewCscfgFile)
            .Go()
            .Commit();
Posted in