ASP.NET Deploy com GitHub Actions e Microsoft Azure

Fazer o deploy com GitHub Actions de aplicações ASP.NET no Microsoft Azure talvez seja a escolha mais simples e prática para manter a entrega contínua em dia. Neste artigo, você vai configurar todo o processo pela linha de comando, usando o Azure CLI e o GitHub CLI, com uma aplicação ASP.NET Core 3.1.

Também existe um vídeo que mostra essa mesma integração de forma visual, pelo portal. Ele complementa o passo a passo deste texto.

O que é CI/CD

Os termos CI e CD fazem parte da cultura DevOps. O objetivo deles é garantir a entrega e a integridade das nossas aplicações.

CI é a sigla para Continuous Integration, ou integração contínua. Na prática, é no CI que executamos o build e os testes para garantir que nada quebrou.

Continuous delivery e continuous deployment

CD pode significar Continuous Delivery ou Continuous Deployment. A entrega contínua tem um ritmo mais fixo, como releases diárias, semanais ou mensais, de acordo com o negócio.

Já o Continuous Deployment está um passo à frente. Nele, a publicação acontece sempre que as mudanças de código passam pelas etapas de integração.

Neste artigo, vamos cobrir o Continuous Deployment. Ou seja, cada push na branch master gera uma nova publicação automaticamente.

Ferramentas para o deploy com GitHub Actions

Antes de configurar o deploy com GitHub Actions, você precisa do Visual Studio Code e do SDK do .NET Core instalados. Caso ainda não tenha, siga o guia de instalação e primeiros passos com o .NET antes de prosseguir.

Também vale ter o Git configurado na máquina. Se precisar de ajuda, o artigo sobre Git e GitHub cobre a instalação e os primeiros comandos.

Azure CLI

A segunda instalação necessária é o Azure CLI. Ele habilita o comando az no terminal e permite criar e gerenciar recursos do Azure sem abrir o portal.

GitHub CLI

Em seguida, precisamos do GitHub CLI. Ele habilita o comando gh, que cria e clona repositórios direto do terminal.

Windows Terminal

Este item não é obrigatório, mas fica como recomendação. Se você usa Windows, o Windows Terminal torna o trabalho com várias abas e shells bem mais confortável.

Criando o Web App no Microsoft Azure

Tanto faz criar primeiro o Web App ou o repositório no GitHub. Por isso, vamos começar pela aplicação no Azure.

Autenticando no Azure

Depois de instalar o Azure CLI, abra o terminal e faça a autenticação com o comando abaixo.

az login

Em seguida, o navegador abre a tela de login da Microsoft. Assim que você se autenticar, o terminal já estará conectado à sua conta.

Caso tenha mais de uma assinatura ativa, use az account list para listar as assinaturas. Depois, use az account set --subscription NAME para tornar uma delas a padrão.

Criando um resource group

Os resource groups agrupam os recursos de uma solução. Além de ajudar na organização, eles facilitam o gerenciamento de permissões, custos e exclusão em lote.

Para esta demonstração, vamos criar um grupo chamado aspnetghcdeploy.

az group create --location brazilsouth --name aspnetghcdeploy

A flag --location define a região do grupo. Neste caso, usamos o Brazil South.

Criando o plano e o Web App

O próximo passo é criar a aplicação, e para isso precisamos de um App Service Plan. Ele define a faixa de preço e os recursos do servidor que o app utiliza.

Vamos criar um plano usando a SKU gratuita, para não gerar cobranças durante os testes.

az appservice plan create --name aspnetghcdeploy --resource-group aspnetghcdeploy --sku FREE

Os preços variam conforme a região, o sistema operacional e o tier escolhido. Portanto, consulte a página de preços do App Service antes de levar a aplicação para produção.

Com o plano pronto, criamos o Web App com o comando az webapp create.

az webapp create --name aspnetghcdeploy --resource-group aspnetghcdeploy --plan aspnetghcdeploy

Pronto, neste momento já temos uma aplicação rodando em https://aspnetghcdeploy.azurewebsites.net/. O nome do app precisa ser único em todo o Azure, então adapte-o ao seu caso.

Para conferir todos os comandos disponíveis, consulte a referência oficial do Azure CLI.

Criando o repositório para o deploy com GitHub Actions

Agora precisamos de um repositório no GitHub, que vai hospedar o código e o workflow do deploy com GitHub Actions. O comando gh repo create resolve isso.

gh repo create aspnetghcdeploy

Se preferir trabalhar com um repositório existente, use o comando gh repo clone.

gh repo clone andrebaltieri/aspnetghcdeploy

Na primeira execução do gh, o terminal pede uma autorização adicional. Então, o navegador abre a página do GitHub para concluir a associação da conta.

Notice: additional authorization required
Press Enter to open github.com in your browser...
Authentication complete. Press Enter to continue...

Criando a aplicação ASP.NET Core

Vamos aproveitar para criar a aplicação. Para ter algo mais visual, usamos o template padrão do ASP.NET Core MVC.

cd aspnetghcdeploy
dotnet new mvc
dotnet new gitignore

O comando dotnet new gitignore cria um arquivo .gitignore com tudo o que um projeto .NET precisa. Essa dica chegou pelo Ruan Silva, nos comentários do canal do YouTube.

Enviando o código

Agora enviamos o código local para o GitHub com os comandos abaixo.

git add --all
git commit -m "First version"
git push -u origin master

Note que usamos a branch master no git push. Se preferir, você também pode fazer esse processo de forma visual pelo Visual Studio Code.

Neste momento, o código da aplicação padrão do ASP.NET Core MVC já aparece no seu repositório.

Credenciais para o deploy com GitHub Actions

Para publicar o código no Azure a partir do GitHub, o deploy com GitHub Actions precisa de uma credencial. Aqui, usamos um service principal, que é uma identidade de aplicação com permissão sobre o Web App.

Essa credencial nada mais é do que um JSON com o identificador e o segredo da identidade. Esses dados são sensíveis, então nunca os deixe públicos.

Antes de tudo, precisamos do identificador da assinatura em que o Web App foi criado. Para listar as assinaturas ativas, execute o comando abaixo.

az account list --output table

Com o identificador em mãos, executamos o az ad sp create-for-rbac para gerar a credencial.

az ad sp create-for-rbac --name "aspnetghcdeploy" --role contributor --scopes /subscriptions/<ID>/resourceGroups/aspnetghcdeploy/providers/Microsoft.Web/sites/aspnetghcdeploy --sdk-auth

Repare que o escopo aponta apenas para o Web App, e não para a assinatura inteira. Dessa forma, seguimos o princípio do menor privilégio.

Como resultado, o terminal exibe um JSON parecido com o exemplo abaixo. Copie esse conteúdo, porque vamos usá-lo em breve.

{
  "clientId": "XXXXXXXXXXXXXXXXXXXXXX",
  "clientSecret": "XXXXXXXXXXXXXXXXXXXXXX",
  "subscriptionId": "XXXXXXXXXXXXXXXXXXXXXX",
  "tenantId": "XXXXXXXXXXXXXXXXXXXXXX",
  "activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
  "resourceManagerEndpointUrl": "https://management.azure.com/",
  "activeDirectoryGraphResourceId": "https://graph.windows.net/",
  "sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
  "galleryEndpointUrl": "https://gallery.azure.com/",
  "managementEndpointUrl": "https://management.core.windows.net/"
}

Armazenando as credenciais nos secrets

Como comentamos, essas informações não podem ficar expostas. O melhor lugar para guardá-las é a área de Secrets do repositório no GitHub.

Abra as configurações do repositório, acesse a seção de secrets e crie um novo segredo chamado AZURE_DEPLOY. O valor dessa chave é o JSON obtido no passo anterior.

O nome AZURE_DEPLOY não é reservado. Você pode usar qualquer nome, desde que ele não tenha espaços nem caracteres especiais.

Como funciona o deploy com GitHub Actions

As GitHub Actions são ações organizadas em workflows, ou fluxos, que automatizam o CI/CD da aplicação. Cada deploy com GitHub Actions nasce de um desses fluxos.

Um workflow nada mais é do que um arquivo YAML com os passos que o GitHub deve executar. Além disso, qualquer repositório pode ter um ou mais workflows, disparados conforme as regras de cada arquivo.

Para facilitar, abra o projeto no Visual Studio Code com o comando code . dentro da pasta.

cd aspnetghcdeploy
code .

Por padrão, os workflows ficam na pasta .github/workflows, na raiz do repositório. Então, crie o arquivo abaixo.

.github/workflows/azure-deploy.yml

Durante cada push, o GitHub procura arquivos .yml nessa pasta e executa os que se aplicam ao evento.

Gatilho do deploy com GitHub Actions

Um workflow tem basicamente duas partes: on e jobs. A primeira define quando o fluxo roda, e a segunda define o que ele executa.

name: Deploy to Azure

on:

jobs:

A propriedade name dá nome ao workflow. Vale lembrar que, no YAML, a indentação importa, então mantenha exatamente o mesmo espaçamento.

Nesta aplicação, o deploy com GitHub Actions deve acontecer somente quando o código mudar na branch master. Mudanças em desenvolvimento, homologação ou outras branches não geram publicação.

name: Deploy to Azure

on:
  push:
    branches:
      - master

Como podemos notar, o disparo fica bem intuitivo. A cada push na branch master, o fluxo segue adiante.

Definindo a máquina do job

O primeiro passo é informar o sistema operacional da máquina que executa o job. Para isso, usamos o atributo runs-on.

Neste exemplo, teremos apenas um job, chamado build-and-deploy, que compila e publica o código.

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

Checkout e instalação do .NET

A máquina Ubuntu nasce apenas com o básico do sistema operacional. Portanto, precisamos baixar o código e instalar o .NET.

Aqui entra a grande vantagem das actions. Milhares de pessoas fazem deploy de aplicações ASP.NET Core no Azure todos os dias, e os passos repetitivos já existem prontos para reuso.

Com a palavra uses, reaproveitamos actions públicas no workflow. Para baixar o código, usamos a action checkout, mantida pelo próprio GitHub.

Em seguida, instalamos o .NET com a action setup-dotnet. O bloco with informa a versão do SDK.

    steps:
      - uses: actions/checkout@master

      - name: Set up .NET Core
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: "3.1.102"

Build e publish

Para compilar a aplicação, não precisamos de nenhuma action adicional. Afinal, o .NET já está instalado, então basta executar o dotnet build.

      - name: Build with dotnet
        run: dotnet build --configuration Release

Note a flag --configuration Release. Sem ela, a aplicação compilaria em modo Debug.

Da mesma forma, o dotnet publish gera os arquivos finais em uma pasta de saída, sem exigir outra action.

      - name: dotnet publish
        run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/myapp

Login e deploy com GitHub Actions no Azure

Com o pacote pronto, precisamos autenticar no Azure. Nesse passo, a action azure/login usa a credencial guardada nos secrets do repositório.

      - name: Azure Login
        uses: azure/login@v1.1
        with:
          creds: ${{ secrets.AZURE_DEPLOY }}

Qualquer segredo do repositório fica criptografado. No YAML, você o acessa pela sintaxe ${{ secrets.KEY }}.

O último passo é o deploy com GitHub Actions propriamente dito, feito pela action azure/webapps-deploy. Veja o workflow completo.

name: Deploy to Azure

on:
  push:
    branches:
      - master

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@master

      - name: Set up .NET Core
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: "3.1.102"

      - name: Build with dotnet
        run: dotnet build --configuration Release

      - name: dotnet publish
        run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/myapp

      - name: Azure Login
        uses: azure/login@v1.1
        with:
          creds: ${{ secrets.AZURE_DEPLOY }}

      - name: Deploy to Azure Web App
        uses: azure/webapps-deploy@v1
        with:
          app-name: "aspnetghcdeploy"
          slot-name: "production"
          package: ${{env.DOTNET_ROOT}}/myapp

O parâmetro app-name indica o Web App de destino. Enquanto isso, o package aponta para a pasta gerada pelo dotnet publish.

Enviando o workflow e integrando o Azure

Até aqui, o workflow existe apenas na sua máquina. Vamos enviá-lo para a branch master com os comandos abaixo, ou pelo painel de controle de versão do Visual Studio Code.

git add --all
git commit -m "First deploy test"
git push -u origin master

Por fim, informamos ao Azure que o deploy desta aplicação vem do GitHub. Isso acontece pela configuração de deployment source, com o comando abaixo.

az webapp deployment source config --name aspnetghcdeploy --resource-group aspnetghcdeploy --repo-url https://github.com/andrebaltieri/aspnetghcdeploy --branch master

A partir de agora, qualquer alteração enviada para a branch master dispara uma nova publicação no Azure.

Acompanhando o deploy com GitHub Actions

Você pode acompanhar ao vivo cada deploy com GitHub Actions na aba Actions do repositório. A URL segue o formato https://github.com/USER/REPOSITORY/actions.

Nessa tela, cada execução mostra os passos do job, o tempo de cada um e os logs completos. Assim, fica fácil descobrir em qual etapa uma falha aconteceu.

Quando o job termina com sucesso, basta abrir o endereço do Web App. Em poucos minutos, a nova versão já está no ar.

Boas práticas atuais para deploy com GitHub Actions

Este tutorial usa o .NET Core 3.1 e as versões das actions daquela época. No entanto, vale conhecer o que a documentação recomenda hoje para o deploy com GitHub Actions.

Primeiro, a Microsoft recomenda autenticar com OpenID Connect, que usa tokens de curta duração. Dessa forma, o repositório não precisa guardar um segredo de longa duração.

Além disso, as versões atuais do Azure CLI usam a flag --json-auth no lugar da antiga --sdk-auth. A documentação também sugere o papel Website Contributor, mais restrito que o Contributor.

As actions também evoluíram. Hoje, a documentação de deploy no App Service usa azure/login@v2 e azure/webapps-deploy@v3.

Outra mudança é o nome da branch padrão. Atualmente, o GitHub cria repositórios com a branch main, então ajuste o gatilho do workflow conforme o seu projeto.

Se quiser outra abordagem para o mesmo cenário, o artigo sobre deploy no Azure via GitHub Actions também cria a infraestrutura pela linha de comando.

Conclusão sobre o deploy com GitHub Actions

Publicar uma aplicação ASP.NET Core no Azure sem abrir o portal é totalmente possível. Com o Azure CLI e o GitHub CLI, criamos o resource group, o plano, o Web App, o repositório e a credencial de publicação.

Em seguida, o workflow em YAML fez o restante: checkout, instalação do .NET, build, publish, login e deploy. Portanto, o deploy com GitHub Actions transforma cada push na master em uma nova versão no ar.

Não esqueça de conferir o vídeo no começo do artigo. Nele, você vê o mesmo processo pelo portal e aprende mais sobre GitHub, ASP.NET, Azure e variáveis de ambiente.

FAQ

Preciso pagar para fazer deploy com GitHub Actions no Azure?

Não necessariamente. O plano Free do App Service atende testes, e o GitHub oferece minutos gratuitos de Actions conforme o tipo de conta.

Qual a diferença entre continuous delivery e continuous deployment?

No continuous delivery, a publicação segue um ritmo definido pelo negócio. Já no continuous deployment, toda mudança aprovada nas etapas de integração vai direto para produção.

Onde devo guardar as credenciais do Azure?

Nos secrets do repositório no GitHub. Assim, o valor fica criptografado e o workflow o acessa por ${{ secrets.NAME }}.

Posso usar o publish profile em vez do service principal?

Sim. O publish profile é uma credencial no nível do app, mas exige a autenticação básica habilitada no App Service.

O workflow funciona com a branch main?

Sim. Basta trocar master por main na seção on.push.branches do arquivo YAML.

Glossário

Termo Significado
CI Integração contínua, etapa que compila e testa o código a cada mudança.
CD Entrega ou implantação contínua, etapa que publica a aplicação.
GitHub Actions Plataforma de automação do GitHub baseada em workflows YAML.
Workflow Arquivo YAML que define gatilhos, jobs e passos de uma automação.
Job Conjunto de passos executados na mesma máquina dentro de um workflow.
Resource group Contêiner lógico que agrupa recursos relacionados no Azure.
App Service Plan Definição de tier, região e recursos de computação de um App Service.
Service principal Identidade de aplicação usada para acessar recursos do Azure.
Secret Valor criptografado armazenado no repositório e acessado pelos workflows.
OpenID Connect Protocolo de autenticação que permite login com tokens de curta duração.

Fontes