At SMEx Digital we have a distributed Scrum development team with resources in Australia and in South Africa. Typically at the start of the day in Australia, the QA resource logs into Octopus deploy and pushes that latest version from Dev to QA to perform integration tests, as there would be new commits coming overnight from the team in South Africa.

Note: We made a conscious decision not to automatically deploy to QA as the testers were being interrupted as when a developer commits code, the code got built and pushed to QA giving them a few minutes of down time.

So to save the tester roughly 10 minutes each morning waiting for environments to be deployed to QA, I decided to automate this process.

Unfortunately, Octopus Deploy doesn't have built in capabilities to schedule a deployment at a given time every day, but thankfully Octopus Deploy has a great API that will let you automate pretty much everything.

This is where the new Azure Functions and the Trigger come in handy.

1. In Microsoft Azure create a new Function App

azure-create-function-app.png

2. Create a New Function

Azure-Functions-New-Function.png

3. Select TimerTrigger - C# and fill in a Cron expression that represents when you want to run the function

AzureFunctions-TimeTrigger.png

4. We want to add the Octopus.Client as a NuGet reference, so select your function | View Files | Add | Add a new project.json file with the following content  

AzureFunctions-AddFiles
{
  "frameworks": {
    "net46":{
      "dependencies": {
        "Octopus.Client": "4.5.0"
      }
    }
   }
}

5. Now select the run.csx and paste in the code that will trigger the deployments

using System;
using Octopus.Client;
using Octopus.Client.Model;


public static async Task Run(TimerInfo myTimer, TraceWriter log)
{
    log.Info($"DeployToQA function executed at: {DateTime.Now}");
    log.Info($"Octopus URL: {GetSetting("OctopusUrl")}");
    var endpoint = new OctopusServerEndpoint(GetSetting("OctopusUrl"), GetSetting("OctopusApiKey"));
    var environmentId = GetSetting("OctopusQAEnvironmentId");

    using (var client = await OctopusAsyncClient.Create(endpoint))
    {
        var projects = await client.Repository.Projects.FindMany(x => !x.IsDisabled);
        foreach (var project in projects)
        {
            log.Info($"Checking project: {project.Name}");
            // Get the latest release
            var releases = await client.Repository.Projects.GetReleases(project);
            var release = releases.Items.FirstOrDefault();
            if (release != null) 
            {
                log.Info($"Latest release is {release.Version} with Id {release.Id}");
                // See if this release is already deployed to QA
                var deploymentQA = await client.Repository.Deployments.FindOne(x => x.ProjectId == project.Id && x.ReleaseId == release.Id && x.EnvironmentId == environmentId);
                if (deploymentQA == null)
                {                    
                    log.Info($"Deploying release {release.Version} to QA");
                    await client.Repository.Deployments.Create(new Octopus.Client.Model.DeploymentResource
                    {
                        EnvironmentId = environmentId,
                        ProjectId = project.Id,
                        ReleaseId = release.Id
                    });
                }
                else {
                    log.Info($"Release {release.Version} is already on QA");

                }
            }
        }
    }

}

public static string GetSetting(string name) =>  System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process);

This code loops through all the active projects, gets the latest release and checks to see if it has been deployed to the QA environment already. If it hasn't then it creates the deployment.

6. Next setup the application variables that the script uses. Click on the Function App in the left nav then select Application Settings

AzureFunctions-ApplicationSettings

7. Add keys for:

  • OctopusUrl - URL to your Octopus Deploy server
  • OctopusApiKey - API key (Generate one from your profile page in Octopus Deploy)
  • OctopusQAEnvironmentId - The ID of the environment (edit the environment, the ID is in the URL, it should be in the form "Environments-<id>"
AzureFunctions-AppSettings

8. Now you can test your function. Go back to the run.csx and click Run

AzureFunctions-Run
AzureFunctions-Log

Now every morning when the tester comes in, there's a fresh deployment for them to start testing on!

1 Comment