//JorgenHoc
← Todos os artigos
.NET HostingPor Jorge CalderónAtualizado 12 min read

Azure App Service para .NET — Configuração, Preços e Ajustes

Guia passo a passo para implantar aplicações .NET 10 no Azure App Service: configuração com CLI, níveis de preços, slots de implantação, variáveis de ambiente, escalabilidade e CI/CD com GitHub Actions.

#azure#dotnet#cloud#devops

O Azure App Service é o caminho mais rápido para levar código .NET a uma URL de produção em funcionamento se você já está no ecossistema Microsoft. Ele cuida do patching do sistema operacional, atualizações de runtime, certificados TLS e balanceamento de carga — você só precisa implantar sua aplicação.

Pré-requisitos

# Instalar o Azure CLI
winget install Microsoft.AzureCLI
 
# Fazer login
az login
💡

Versões recentes da Azure CLI mudaram o fluxo de login (encontrei isso na 2.88.0): az login agora lista todas as assinaturas que sua conta pode acessar e pede para você escolher uma ali mesmo, em vez de assumir uma silenciosamente. Então o az account set abaixo não faz mais parte do ritual do primeiro login — você só precisa dele para trocar de assinatura mais tarde.

# Definir sua assinatura (se você tiver várias e quiser trocar depois)
az account set --subscription "My Subscription"

Criando um App Service pela CLI

# 1. Criar um grupo de recursos (contêiner lógico para recursos relacionados)
az group create \
  --name myapp-rg \
  --location eastus
 
# 2. Criar um plano do App Service (define o nível de computação)
az appservice plan create \
  --name myapp-plan \
  --resource-group myapp-rg \
  --sku B1 \
  --is-linux
 
# 3. Criar a aplicação web apontando para o .NET 10
az webapp create \
  --name myapp-12345 \               # Deve ser globalmente único
  --resource-group myapp-rg \
  --plan myapp-plan \
  --runtime "DOTNETCORE:10.0"
 
# Sua aplicação já está disponível em: https://myapp-12345.azurewebsites.net

Uma armadilha que vale conhecer: --runtime usa a forma com dois-pontos (DOTNETCORE:10.0), mas o comando que lista o que sua região oferece imprime a forma com barra vertical — não os compare literalmente em um script:

# Imprime p. ex. DOTNETCORE|10.0 (com suporte até 2028-12-01), DOTNETCORE|8.0, ...
az webapp list-runtimes --os linux
Portal do Azure mostrando o grupo de recursos criado pelos comandos da CLI, com um App Service chamado jorgenhoc-sample-7305 e um plano do App Service chamado jorgenhoc-sample-plan, ambos em East US.
Os três comandos acima, vistos do portal: um grupo de recursos contendo o plano e a aplicação.

Níveis de preços

Os preços abaixo não são de memória — vêm da API pública Retail Prices do Azure (Linux, East US, pagamento por uso, consultados em 2026-08-18; mensal ≈ por hora × 730):

NívelPor hora~MensalRAMvCPUPrincipais recursos
F1 Free$0$01 GBCompartilhado60 CPU-min/dia, sem TLS de domínio personalizado
B1 Basic$0.017~$121,75 GB1Always On, TLS de domínio personalizado
B2 Basic$0.034~$253,5 GB2
B3 Basic$0.067~$497 GB4
S1 Standard$0.095~$691,75 GB1Slots de implantação, autoescala
S2 Standard$0.190~$1393,5 GB2
P0v3 Premium$0.0775~$574 GB1Slots, autoescala, redundância de zona
P1v3 Premium$0.155~$1138 GB2
P2v3 Premium$0.310~$22616 GB4

Reproduza (ou consulte sua própria região) com uma única requisição — sem autenticação:

curl -sG "https://prices.azure.com/api/retail/prices" \
  --data-urlencode "\$filter=serviceName eq 'Azure App Service' and armRegionName eq 'eastus' and priceType eq 'Consumption'"
💡

Para aplicações de produção pequenas, o B1 ($12/mês) é excelente. Mas olhe com atenção antes de escolher Standard pelos slots de implantação: **o P0v3 ($57) é mais barato que o S1 (~$69)** com mais RAM, hardware mais novo e tudo o que o Standard oferece. Compare Premium v3 com Standard na sua região antes de assumir que Standard é o nível econômico com slots.

Implantando sua aplicação

Método 1: ZIP Deploy (o mais rápido)

# Compilar e publicar
dotnet publish -c Release -o ./publish
 
# Criar o ZIP
cd ./publish && zip -r ../app.zip . && cd ..
 
# Implantar
az webapp deploy \
  --resource-group myapp-rg \
  --name myapp-12345 \
  --src-path app.zip \
  --type zip

Esse caminho exato — grupo, plano, app, app settings, deploy zip — está automatizado de ponta a ponta em samples/azure-app-service-dotnet (uma API mínima cuja resposta comprova cada afirmação de configuração deste artigo). Duas coisas medidas ao executá-lo, para calibrar expectativas:

  • A implantação completa levou ~2,5 minutos, e quase tudo foi a fase "Starting the site" do Kudu depois do upload — o zip em si foi aceito em cerca de um segundo. Não assuma que a implantação travou ao passar dos 90 segundos.
  • O Git Bash no Windows não inclui zip — e não recorra ao Compress-Archive. O Compress-Archive do PowerShell grava os nomes das entradas com barra invertida, e o rsync do Kudu no Linux os rejeita (Invalid argument (22)) assim que a saída de publish contém uma subpasta; uma saída plana sobrevive por sorte, e é por isso que esse bug se esconde. Use o bsdtar embutido do Windows: C:\Windows\System32\tar.exe -a -cf app.zip -C publish * produz um zip correto com barras normais.

Método 2: GitHub Actions (recomendado para produção)

Primeiro, baixe o perfil de publicação pelo portal ou pela CLI:

az webapp deployment list-publishing-profiles \
  --resource-group myapp-rg \
  --name myapp-12345 \
  --xml > publish-profile.xml

Adicione o conteúdo como um segredo do GitHub com o nome AZURE_WEBAPP_PUBLISH_PROFILE, em seguida:

# .github/workflows/deploy.yml
name: Deploy to Azure App Service
 
on:
  push:
    branches: [main]
 
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
 
    steps:
      - uses: actions/checkout@v4
 
      - name: Setup .NET 10
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '10.0.x'
 
      - name: Restore dependencies
        run: dotnet restore
 
      - name: Build
        run: dotnet build --configuration Release --no-restore
 
      - name: Test
        run: dotnet test --no-build --verbosity normal
 
      - name: Publish
        run: dotnet publish -c Release -o ${{ github.workspace }}/publish
 
      - name: Deploy to Azure
        uses: azure/webapps-deploy@v3
        with:
          app-name: myapp-12345
          publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
          package: ${{ github.workspace }}/publish

Método 3: Azure CLI pelo GitHub Actions (autenticação OIDC — sem segredos)

- name: Azure Login (OIDC)
  uses: azure/login@v2
  with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
 
- name: Deploy
  run: |
    cd ./publish && zip -r ../app.zip . && cd ..
    az webapp deploy \
      --resource-group myapp-rg \
      --name myapp-12345 \
      --src-path ./app.zip \
      --type zip

Variáveis de ambiente e configuração

Definindo as configurações da aplicação

As configurações da aplicação tornam-se variáveis de ambiente dentro do seu app, substituindo o appsettings.json:

# Definir valores individuais
az webapp config appsettings set \
  --resource-group myapp-rg \
  --name myapp-12345 \
  --settings \
    ASPNETCORE_ENVIRONMENT=Production \
    FeatureFlags__NewCheckout=true
 
# Definir a partir de um arquivo JSON
az webapp config appsettings set \
  --resource-group myapp-rg \
  --name myapp-12345 \
  --settings @app-settings.json

O sublinhado duplo (FeatureFlags__NewCheckout) corresponde ao separador de hierarquia : do appsettings.json, e um App Setting vence a mesma chave no appsettings.json. A aplicação de exemplo implantada acima demonstra essa precedência — seu appsettings.json diz "from appsettings.json", mas a resposta implantada mostra o valor definido via CLI:

Resposta JSON da aplicação de exemplo implantada: environment Production, message 'set from App Settings via az cli' substituindo o valor do appsettings.json, o nome do site do App Service e um WEBSITE_INSTANCE_ID.
A resposta do exemplo implantado: ASPNETCORE_ENVIRONMENT chega como Production sem que o app o defina, e o App Setting substitui o valor incluído no appsettings.json.

Strings de conexão

As strings de conexão recebem tratamento especial — são acessíveis via ConnectionStrings:Name na configuração do .NET:

az webapp config connection-string set \
  --resource-group myapp-rg \
  --name myapp-12345 \
  --connection-string-type SQLAzure \
  --settings DefaultConnection="Server=myserver.database.windows.net;..."
// Na sua aplicação, lê primeiro do App Settings do Azure, depois do appsettings.json
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");

Usando o Key Vault para segredos

Referencie segredos do Key Vault sem armazená-los no App Settings:

# Criar o Key Vault
az keyvault create --name myapp-kv --resource-group myapp-rg --location eastus
 
# Armazenar um segredo
az keyvault secret set --vault-name myapp-kv --name DbPassword --value "s3cr3t!"
 
# Habilitar identidade gerenciada atribuída pelo sistema na aplicação web
az webapp identity assign --resource-group myapp-rg --name myapp-12345
 
# Obter o ID do principal
principalId=$(az webapp identity show --resource-group myapp-rg --name myapp-12345 --query principalId -o tsv)
 
# Conceder à aplicação web acesso de leitura aos segredos do Key Vault
az keyvault set-policy --name myapp-kv --object-id $principalId --secret-permissions get list

Referencie os segredos no App Settings usando referências do Key Vault:

az webapp config appsettings set \
  --resource-group myapp-rg \
  --name myapp-12345 \
  --settings "DbPassword=@Microsoft.KeyVault(VaultName=myapp-kv;SecretName=DbPassword)"

Slots de implantação

Os slots de implantação fornecem ambientes de staging e trocas sem tempo de inatividade. Disponíveis nos níveis S1 e superiores.

# Criar um slot de staging
az webapp deployment slot create \
  --name myapp-12345 \
  --resource-group myapp-rg \
  --slot staging
 
# Implantar no staging (não em produção)
az webapp deploy \
  --resource-group myapp-rg \
  --name myapp-12345 \
  --slot staging \
  --src-path app.zip \
  --type zip
 
# Testar o staging em: https://myapp-12345-staging.azurewebsites.net
 
# Trocar staging com produção (quase sem tempo de inatividade)
az webapp deployment slot swap \
  --resource-group myapp-rg \
  --name myapp-12345 \
  --slot staging \
  --target-slot production
 
# Reverter (trocar novamente)
az webapp deployment slot swap \
  --resource-group myapp-rg \
  --name myapp-12345 \
  --slot staging \
  --target-slot production
💡

Configure definições específicas do slot (como strings de conexão) com o sinalizador "slot setting". Essas definições NÃO são trocadas junto com o slot — o slot de staging mantém sua conexão com o banco de dados de staging após a troca.

# Marcar uma configuração como específica do slot (não será trocada)
az webapp config appsettings set \
  --resource-group myapp-rg \
  --name myapp-12345 \
  --slot staging \
  --slot-settings ConnectionStrings__DefaultConnection="staging-db-connection"

Escalabilidade

Escalonamento manual (nível Basic)

# Escalar verticalmente (alterar o tamanho da VM)
az appservice plan update \
  --name myapp-plan \
  --resource-group myapp-rg \
  --sku S2
 
# Escalar horizontalmente (adicionar instâncias) — o nível Basic suporta apenas escalonamento horizontal manual
az appservice plan update \
  --name myapp-plan \
  --resource-group myapp-rg \
  --number-of-workers 3

Autoescala (nível Standard e superiores)

# Habilitar autoescala para o plano do App Service
az monitor autoscale create \
  --resource-group myapp-rg \
  --resource myapp-plan \
  --resource-type Microsoft.Web/serverFarms \
  --name myapp-autoscale \
  --min-count 1 \
  --max-count 5 \
  --count 1
 
# Adicionar regra de escala para fora (adicionar instância quando CPU > 70%)
az monitor autoscale rule create \
  --resource-group myapp-rg \
  --autoscale-name myapp-autoscale \
  --condition "CpuPercentage > 70 avg 5m" \
  --scale out 1
 
# Adicionar regra de escala para dentro (remover instância quando CPU < 30%)
az monitor autoscale rule create \
  --resource-group myapp-rg \
  --autoscale-name myapp-autoscale \
  --condition "CpuPercentage < 30 avg 10m" \
  --scale in 1

Configurando o Always-On

Por padrão, as aplicações no nível Basic e superiores são encerradas após 20 minutos de inatividade. Habilite o Always On:

az webapp config set \
  --resource-group myapp-rg \
  --name myapp-12345 \
  --always-on true
⚠️

O Always On não está disponível no nível F1 Free. Se você estiver no nível Free e enfrentar cold starts, faça upgrade para pelo menos o nível B1.

Domínio personalizado e TLS

# Adicionar um domínio personalizado
az webapp config hostname add \
  --resource-group myapp-rg \
  --webapp-name myapp-12345 \
  --hostname www.yourdomain.com
 
# Criar um certificado TLS gerenciado (gratuito)
az webapp config ssl create \
  --resource-group myapp-rg \
  --name myapp-12345 \
  --hostname www.yourdomain.com
 
# Vincular o certificado
az webapp config ssl bind \
  --resource-group myapp-rg \
  --name myapp-12345 \
  --certificate-thumbprint <thumbprint-from-previous-command> \
  --ssl-type SNI

Monitoramento com Application Insights

# Criar o Application Insights
az monitor app-insights component create \
  --app myapp-insights \
  --resource-group myapp-rg \
  --location eastus
 
# Obter a chave de instrumentação
instrumentationKey=$(az monitor app-insights component show \
  --app myapp-insights \
  --resource-group myapp-rg \
  --query instrumentationKey -o tsv)
 
# Definir na aplicação web
az webapp config appsettings set \
  --resource-group myapp-rg \
  --name myapp-12345 \
  --settings APPLICATIONINSIGHTS_CONNECTION_STRING="InstrumentationKey=$instrumentationKey"

Adicione o pacote NuGet à sua aplicação:

dotnet add package Microsoft.ApplicationInsights.AspNetCore
// Program.cs
builder.Services.AddApplicationInsightsTelemetry();

Referência útil da CLI

# Visualizar os logs da aplicação em tempo real
az webapp log tail --resource-group myapp-rg --name myapp-12345
 
# Exibir as configurações atuais da aplicação
az webapp config appsettings list --resource-group myapp-rg --name myapp-12345
 
# Reiniciar a aplicação
az webapp restart --resource-group myapp-rg --name myapp-12345
 
# Obter a URL da aplicação
az webapp show --resource-group myapp-rg --name myapp-12345 --query defaultHostName -o tsv
 
# Listar todas as aplicações em execução em um grupo de recursos
az webapp list --resource-group myapp-rg --query "[].{Name:name, State:state, URL:defaultHostName}" -o table

Para Onde Ir a Partir Daqui

O App Service é o caminho de menor resistência para uma aplicação .NET que já compila e roda, porque a plataforma cuida do runtime para você. Se preferir controlar o runtime — para fixar uma versão específica do .NET, adicionar dependências nativas ou manter o deploy portátil entre provedores — empacote a aplicação você mesmo com um Dockerfile para .NET.

Se ainda não escolheu um provedor, a comparação de hosting .NET coloca o App Service ao lado de Railway, Fly.io e Render em níveis de plano e trade-offs.

Leituras adicionais

Sobre o autor

Jorge Calderón

Engenheiro de software com mais de uma década construindo e operando aplicações .NET em produção — camadas de dados com EF Core, serviços intensivos em async e implantações em Azure e contêineres. Todos os benchmarks e projetos de exemplo destes guias estão publicados em um repositório público no GitHub para que você possa reproduzi-los.

Perfil no GitHubLinkedIn ↗Benchmarks e código de exemplo

Artigos relacionados