Posted on Leave a comment

Deploy App Service .zip website folder on Azure with cmd

In a recent scenario I had an app service on which I wanted to deploy a .zip package with the appropriate code.

My app service was based on .NET 6 backend and I created an emtpy REST api project to host the app service.

Command to deploy through az cli.

az webapp deploy --resource-group "app-service-rg" --name "geralexgr-webappstatic" --src-path C:\Users\galexiou\Desktop\netwebapp.zip --type zip

Result of deployment through az cli.

On Deployment center logs you can verify your deployment.

Then you can use your website url to get the content of the deployed site. For the .NET rest api default project I should have to get the

url/weatherforecast
Posted on 1 Comment

Deploy windows and linux virtual machines on Azure using terraform

Terraform is one of the best automation providers for DevOps purposes used by hundred of Engineers. It is an open source tool that can be used by anyone for free. In this article I will explain how to deploy windows and linux virtual machines on Azure using a Terraform template.

First things first you will need to have the az cli installed. Then you will have to set your subscription on your current powershell session.

az account set --subscription "12abc123-4567-1234-12345-asdr4334fsd"

Then you will need to create an app role assignment for your subscription. This will be used from terraform for the provision of the resources.

az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/12abc123-4567-1234-12345-asdr4334fsd"

That’s all. You can now deploy your resources through terraform. In the links below I have provided my Github repository along with instructions for the template use.

A tricky part of the deployment is the vm image selection. In order to locate the available azure images names you can use:

az vm image list

Enumeration of available images:

[
  {
    "offer": "CentOS",
    "publisher": "OpenLogic",
    "sku": "7.5",
    "urn": "OpenLogic:CentOS:7.5:latest",
    "urnAlias": "CentOS",
    "version": "latest"
  },
  {
    "offer": "debian-10",
    "publisher": "Debian",
    "sku": "10",
    "urn": "Debian:debian-10:10:latest",
    "urnAlias": "Debian",
    "version": "latest"
  },
  {
    "offer": "flatcar-container-linux-free",
    "publisher": "kinvolk",
    "sku": "stable",
    "urn": "kinvolk:flatcar-container-linux-free:stable:latest",
    "urnAlias": "Flatcar",
    "version": "latest"
  },
  {
    "offer": "openSUSE-Leap",
    "publisher": "SUSE",
    "sku": "42.3",
    "urn": "SUSE:openSUSE-Leap:42.3:latest",
    "urnAlias": "openSUSE-Leap",
    "version": "latest"
  },
  {
    "offer": "RHEL",
    "publisher": "RedHat",
    "sku": "7-LVM",
    "urn": "RedHat:RHEL:7-LVM:latest",
    "urnAlias": "RHEL",
    "version": "latest"
  },
  {
    "offer": "SLES",
    "publisher": "SUSE",
    "sku": "15",
    "urn": "SUSE:SLES:15:latest",
    "urnAlias": "SLES",
    "version": "latest"
  },
  {
    "offer": "UbuntuServer",
    "publisher": "Canonical",
    "sku": "18.04-LTS",
    "urn": "Canonical:UbuntuServer:18.04-LTS:latest",
    "urnAlias": "UbuntuLTS",
    "version": "latest"
  },
  {
    "offer": "WindowsServer",
    "publisher": "MicrosoftWindowsServer",
    "sku": "2019-Datacenter",
    "urn": "MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest",
    "urnAlias": "Win2019Datacenter",
    "version": "latest"
  },
  {
    "offer": "WindowsServer",
    "publisher": "MicrosoftWindowsServer",
    "sku": "2016-Datacenter",
    "urn": "MicrosoftWindowsServer:WindowsServer:2016-Datacenter:latest",
    "urnAlias": "Win2016Datacenter",
    "version": "latest"
  },
  {
    "offer": "WindowsServer",
    "publisher": "MicrosoftWindowsServer",
    "sku": "2012-R2-Datacenter",
    "urn": "MicrosoftWindowsServer:WindowsServer:2012-R2-Datacenter:latest",
    "urnAlias": "Win2012R2Datacenter",
    "version": "latest"
  },
  {
    "offer": "WindowsServer",
    "publisher": "MicrosoftWindowsServer",
    "sku": "2012-Datacenter",
    "urn": "MicrosoftWindowsServer:WindowsServer:2012-Datacenter:latest",
    "urnAlias": "Win2012Datacenter",
    "version": "latest"
  },
  {
    "offer": "WindowsServer",
    "publisher": "MicrosoftWindowsServer",
    "sku": "2008-R2-SP1",
    "urn": "MicrosoftWindowsServer:WindowsServer:2008-R2-SP1:latest",
    "urnAlias": "Win2008R2SP1",
    "version": "latest"
  }
]

In order to narrow down and find Ubuntu available images (use grep instead of Select-string for Unix environments)

az vm image list-offers -p canonical -l eastus | Select-String name

Ubuntu images names for east us region:

 "name": "0001-com-ubuntu-confidential-vm-experimental",
    "name": "0001-com-ubuntu-confidential-vm-focal",
    "name": "0001-com-ubuntu-confidential-vm-test-focal",
    "name": "0001-com-ubuntu-minimal-focal-daily",
    "name": "0001-com-ubuntu-minimal-groovy-daily",
    "name": "0001-com-ubuntu-minimal-hirsute-daily",
    "name": "0001-com-ubuntu-minimal-impish-daily",
    "name": "0001-com-ubuntu-minimal-jammy-daily",
    "name": "0001-com-ubuntu-private-fips-motorola",
    "name": "0001-com-ubuntu-pro-advanced-sla",
    "name": "0001-com-ubuntu-pro-advanced-sla-att",
    "name": "0001-com-ubuntu-pro-advanced-sla-csw",
    "name": "0001-com-ubuntu-pro-advanced-sla-dd",
    "name": "0001-com-ubuntu-pro-advanced-sla-nestle",
    "name": "0001-com-ubuntu-pro-advanced-sla-servicenow",
    "name": "0001-com-ubuntu-pro-advanced-sla-shell",
    "name": "0001-com-ubuntu-pro-advanced-sla-ub01",
    "name": "0001-com-ubuntu-pro-advanced-sla-unp",
    "name": "0001-com-ubuntu-pro-bionic",
    "name": "0001-com-ubuntu-pro-bionic-fips",
    "name": "0001-com-ubuntu-pro-focal",
    "name": "0001-com-ubuntu-pro-focal-fips",
    "name": "0001-com-ubuntu-pro-hidden-msft-fips",
    "name": "0001-com-ubuntu-pro-microsoft",
    "name": "0001-com-ubuntu-pro-trusty",
    "name": "0001-com-ubuntu-pro-xenial",
    "name": "0001-com-ubuntu-pro-xenial-fips",
    "name": "0001-com-ubuntu-server-eoan",
    "name": "0001-com-ubuntu-server-focal",
    "name": "0001-com-ubuntu-server-focal-daily",
    "name": "0001-com-ubuntu-server-groovy",
    "name": "0001-com-ubuntu-server-groovy-daily",
    "name": "0001-com-ubuntu-server-hirsute",
    "name": "0001-com-ubuntu-server-hirsute-daily",
    "name": "0001-com-ubuntu-server-impish",
    "name": "0001-com-ubuntu-server-impish-daily",
    "name": "0001-com-ubuntu-server-jammy-daily",
    "name": "0002-com-ubuntu-minimal-bionic-daily",
    "name": "0002-com-ubuntu-minimal-disco-daily",
    "name": "0002-com-ubuntu-minimal-focal-daily",
    "name": "0002-com-ubuntu-minimal-xenial-daily",
    "name": "0003-com-ubuntu-minimal-eoan-daily",
    "name": "0003-com-ubuntu-server-trusted-vm",
    "name": "test-ubuntu-premium-offer-0002",
    "name": "Ubuntu15.04Snappy",
    "name": "Ubuntu15.04SnappyDocker",
    "name": "UbuntuServer",

Specific information about an image:

az vm image list -p canonical -l eastus --offer 0001-com-ubuntu-pro-bionic --all --sku pro-18_04-lts

Inside linux or windows folder depending on the resource you want to deploy apply your terraform configuration

terraform init
terraform apply

After the successful run of the terraform script.

In order to delete the environment you can run

terraform destroy

By committing destroy your eight resources that deal with your virtual machine will disappear.

Repository for the code:

https://github.com/geralexgr/terraform-az-lin-win

Video tutorial on YouTube:

Posted on Leave a comment

Target multiple environments with Deployment jobs – Azure DevOps

Its a common thing for DevOps engineers to define some agent pools that hold agents and organize their deployments through those pools. I have worked with this approach as well by defining Uat and Prod pools that are used from build and release pipelines.

In this article I will explain how to use the environment resources that are located under Pipelines section.

Environments are a collection of resources on Azure DevOps through which you can define your deployment strategy, approvals and conditions. An environment could be a custom one, or a predefined like a virtual machine or Kubernetes cluster.

It is important to understand the difference between an agent pool and an environment as you have the option to run a deployment job with an environment that has no resources.

This is possible because environments are the scope at which you define pre-deployment checks (approvals, gates, etc). Deploying to resources defined in an environment is an option, not a requirement.

As an example, if you have a serverless Azure web app, you use an environment to define the approvals and gates required to deploy to that environment, but then actual deployment activities occur on an agent from an agent pool because an Azure Web App doesn’t have any physical machines to deploy to.

Lets now examine how you could create your own environments to define your strategies. From environments select add new environment

In my case I will select windows virtual machines as I will use common windows servers as deployment machines.

Then the registration script will be provided to register your machine as an Azure DevOps agent so that jobs can run on those environments. If you use empty environments just for approvals you will not have to run scripts or install an azure devops agent.

Azure DevOps will automatically create a Personal Access Token for the agent registration. You can either create a new one, or use the created one. Keep in mind that the life timespan of this token will be three hours. If you want to have the agent alive, you can extend the expiration time of the token.

You will have to save the script as .ps1 on your target machine and run it as administrator.

$ErrorActionPreference="Stop";If(-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole( [Security.Principal.WindowsBuiltInRole] "Administrator")){ throw "Run command in an administrator PowerShell prompt"};If($PSVersionTable.PSVersion -lt (New-Object System.Version("3.0"))){ throw "The minimum version of Windows PowerShell that is required by the script (3.0) does not match the currently running version of Windows PowerShell." };If(-NOT (Test-Path $env:SystemDrive\'azagent')){mkdir $env:SystemDrive\'azagent'}; cd $env:SystemDrive\'azagent'; for($i=1; $i -lt 100; $i++).....

During the configuration I proceeded with the default values. (just press enter)

When you finish with the configuration of your agents you will have the recources listed under your environment. In my case those are two virtual machines, test-qa and test-dev

Lets now define a deployment strategy. You can choose between the rolling, runOnce, canary deployments. Each of them are explained on the documentation article that I have included on the bottom of the page.

  • runOnce is the simplest deployment strategy and most steps are executed once. (preDeploy, deploy, routeTraffic)
  • A rolling deployment replaces instances of the previous version of an application with instances of the new version of the application on a fixed set of virtual machines (rolling set) in each iteration
  • Canary deployment strategy is an advanced deployment strategy that helps mitigate the risk involved in rolling out new versions of applications. By using this strategy, you can roll out the changes to a small subset of servers first. As you gain more confidence in the new version, you can release it to more servers in your infrastructure and route more traffic to it.

In the below pipeline I added a rolling strategy. The max parallel execution percentage is 50% this means that the deployment will be executed first on my dev server and then on my qa server after all tasks of the dev are finished. The deployment test task will create a file and paste some text on it.

trigger:
- none

pool:
  vmImage: ubuntu-latest

stages:
- stage: deployment
  displayName: Deploy on Environment
  jobs:
  - deployment: VMDeploy
    displayName: Deploy to VM
    environment: 
      name: azure-test-dev
      resourceType: VirtualMachine
    strategy:
      rolling:
        maxParallel: 50%
        preDeploy:
          steps:
          - script: echo running pre deployment steps
        deploy:
          steps:
            - script: echo running rolling strategy on windows servers > C:\output

Strategy sequence:

  • Dev server -> predeploy step
  • Dev server -> deploy step

When the deployment for the dev server is completed then the qa server deployment will start.

  • QA server -> predeploy step
  • QA server -> deploy step

You can also define approvals and checks for your stages on the deployments. For example you could define a stage to be deployed only during working hours or from a specific branch.

Lastly you should give access to the environment from the pipeline that you created.

File created from the pipeline

https://docs.microsoft.com/en-us/azure/devops/pipelines/process/deployment-jobs?view=azure-devops

https://stackoverflow.com/questions/68912591/trying-to-understand-deployment-jobs-in-azure-pipelines

Posted on Leave a comment

Run Unit tests for .NET applications – Azure DevOps

In this article I will demonstrate how one can write and integrate Unit tests on .NET solutions and automate the testing procedure through Azure DevOps.

For my scenario I have a simple Console App .NET application inside a solution with the name ConsoleApp1. This solution includes also another project, the TestProject1 which is of type MSTest project.

My ConcoleApp only contains a simple function that adds two numbers. The code is shown below as well as the solution structure.

The unit test methods are also simple and will verify whether the add function works properly. The TestMethod1 will verify if the int input is correct, and the second one will fail as I try to evaluate a string with an int value.

In order to build the tests you should right click on TestProject1 and press run tests.

By checking the output we can verify that the UnitTest1 was successful in comparison with UnitTest2 which failed.

Lets now examine how to automate the procedure of the build and test using Azure DevOps. The first step is to upload the solution on a source control site and integrate Azure DevOps with it. I have already documented the procedure on a previous article build-pipeline-for-net-solutions-using-cake2-azure-devops

My pipeline contains two stages, one for the build of the project and one for the run of unit tests. The code is available below:

pool:
  vmImage: ubuntu-latest

trigger: none
pr: none 

stages:

  - stage:
    displayName: Build .NET application
    jobs:
      - job:
        displayName: build job
        steps:
          - task: UseDotNet@2
            displayName: Use .NET6 to build
            inputs:
              version: '6.0.x'
              includePreviewVersions: true
          - task: DotNetCoreCLI@2
            displayName: Building Visual studio solution
            inputs:
              command: 'build'
              projects: '**/*.csproj'
              arguments: '--configuration debug'

  - stage: 
    displayName: Run unit tests
    jobs:
      - job:
        displayName: unit tests job
        steps:
          - task: DotNetCoreCLI@2
            displayName: Run Visual studio tests
            inputs:
              command: test
              projects: '**/*Test*/*.csproj'
              arguments: '--configuration debug'

As you can verify the tests stage failed because of the unit test that was not successful.

Azure DevOps also can give you an overview of how many tests succeeded. In my case I had 50% success rate because 1 out of 2 tests failed.

By pressing the 50% tests button you can also get a detailed review of the tests.

In more detail the method that fails is TestMethod2 and the exact message points to the comparison between the int and string values.

Video tutorial on YouTube: