Recentemente, deparei-me com uma criatura ainda não muito popular no mundo DevOps, os pipelines do Azure DevOps. Senti imediatamente a falta de instruções ou artigos claros sobre o assunto; não sei a que isso se deve, mas a Microsoft claramente tem muito a melhorar na divulgação dessa ferramenta. Hoje, construiremos um pipeline para testes automatizados na nuvem Azure.
Então, a tarefa:
Temos um software que é construído usando o mesmo Azure DevOps, compilado a partir de um projeto no WIX. Se houver interesse, posso falar mais sobre essa ferramenta. De fato, este é um modo mais otimizado de construir instaladores do Windows, substituindo o InstallShield padrão. Portanto, nosso software está sendo construido com sucesso e gera um artefato, um setup.exe, que instala o aplicativo no sistema Windows. Precisamos colocar este aplicativo em uma virtualização semelhante à produção, copiar os testes automatizados preparados pela equipe de testes, executá-los e coletar os resultados para avaliar se a branch está boa ou ruim antes de mesclar. Tudo como no GitLab, só que de uma forma mais complicada.
Como ambiente de virtualização onde iremos executar nossos testes, usaremos, obviamente, o Azure DevTest Labs, uma entidade nas assinaturas do Azure, que foi criada para rodar todo tipo de teste a um custo acessível.
1. Integração do lado da nuvem
Primeiro, precisaremos integrar nosso DevTest Labs com o Azure DevOps, para isso precisaremos de um Service Principal, que é, na essência, uma conta de serviço que permite que os pipelines acessem a nuvem e criem/removam recursos.
Vamos para a assinatura e encontramos o serviço Azure Active Directory.

Localizamos Registros de Aplicativos e clicamos em Nova Registro, isso criará nosso service principal. Não vou entrar em detalhes sobre quais configurações escolher durante a criação, pois isso pode variar entre diferentes assinaturas.

Agora precisamos conceder permissões ao nosso diretor de serviço. Para isso, vamos para as assinaturas, o ícone com a chave. Selecionamos nossa assinatura.

Em seguida, em Controle de Acesso, clicamos em Atribuição de Função e procuramos pelo nome que acabamos de criar essa conta. Damos a função de Contribuidor, isso é suficiente.

Depois, retornamos ao nosso Service Principal no Azure AD e abrimos suas propriedades. Mais tarde, precisaremos de todos os IDs que estão lá, então os salvamos.
Com isso, nossas configurações no portal estão concluídas e vamos para o Azure DevOps.
2. Integração do Azure DevOps
Primeiro, vamos para as configurações do projeto e selecionamos Conexões de Serviço. Criamos um novo elemento do tipo Azure Resource Manager.

Agora precisamos de todos os IDs que anotamos. Clicamos em usar a versão completa do diálogo de conexão de serviço. E inserimos todos os dados que recebemos do Service Principal. Clicamos em verificar e, se tudo estiver ótimo, salvamos a conexão. Agora nossos pipelines podem usá-la para se conectar à nuvem.

3. Criação do pipeline
Agora vamos para a parte mais interessante, a construção do pipeline em si. Abrimos o menu Pipelines-Builds

Nos deparamos com o menu para criar um novo build, que por padrão tentará criar um arquivo YAML com a configuração apropriada. Nós educadamente recusamos isso e escolhemos a opção clássica. É compreensível o desejo da Microsoft de fazer tudo de forma prática e dar a possibilidade de personalizar os pipelines ao máximo através de YAML, mas a documentação escassa e a simples não funcionalidade de muitos módulos nos dizem que ainda é cedo para usar essa funcionalidade.

Dentre a diversidade de templates, precisaremos de um Pipeline Vazio simples. Após sua criação, nos deparamos com uma janela de edição vazia, onde passaremos bastante tempo.

Então, clicamos no + e chegamos a uma espécie de loja de módulos, de onde precisaremos dos seguintes componentes.

Antes de começarmos a configuração das tarefas do pipeline, precisamos criar e colocar no projeto alguns arquivos. Esses serão o Template ARM da nossa máquina virtual, que geraremos no Azure DevTest Labs, um script para obter o IP da máquina após a criação e, se desejado, scripts dos nossos testes ou do que queremos executar no host.
4. Geração do Template ARM
Para criar a máquina virtual, precisamos inicialmente gerar um template, um arquivo JSON, que vamos colocar no código do projeto, para que o pipeline possa lê-lo.
Vamos ao nosso laboratório e encontramos o menu Fórmulas (bases reutilizáveis), clicamos em criar novo.

Nos deparamos com uma longa lista de imagens como base, a escolha do tamanho da máquina, tudo igual ao que fazemos ao criar uma máquina virtual. Neste estágio, não vamos nos deter, vamos diretamente para o último item das propriedades da máquina, que são os artefatos. Você pode usar qualquer configuração necessária para o seu ambiente. Por exemplo, eu adiciono a máquina em o domínio e adiciono uma conta de serviço como administrador para que o pipeline possa acessar esta máquina com essa conta. Isso pode variar, mas para testes de código bem-sucedidos precisamos de um artefato do qual falaremos mais detalhadamente. Para que a versão mais recente do software que estamos testando seja instalada na nossa máquina, usaremos o artefato "Download Azure Pipelines Artifact and Run Script". Lembre-se no início eu mencionei que em algum lugar um build é criado com o instalador do aplicativo? Agora precisamos informar à máquina virtual, ou melhor, ao template, que ela deve ir e buscar esse artefato. E não apenas buscá-lo, mas também instalá-lo, para isso precisamos preencher campos específicos com as informações do projeto, o nome do build e a chave secreta. A chave secreta, como em todos os sistemas desse tipo, é gerada na conta, neste caso no Azure DevOps, e é salva nos Secrets do seu laboratório. Aqui há uma pequena ressalva, nos Secrets nós a salvaremos, mas o template não fica com isso, pois ele será executado por outro usuário dentro do pipeline, portanto precisamos inserir a chave secreta manualmente novamente no template.
Outro artefato que precisamos incluir é o "Configure WinRM", que será necessário para o acesso à máquina. Há apenas um parâmetro, hostname. Como não o conhecemos antecipadamente, utilizaremos a variável %COMPUTERNAME%.

Então adicionamos todos os artefatos necessários e agora precisamos entender por que estamos aqui. Vamos obter o ARM Template gerado na aba Advanced da mesma janela de criação da fórmula.

Copiamos o conteúdo da página para o arquivo VMtemplate.json e o colocamos na raiz do projeto. Não precisamos mais da nuvem, vamos voltar ao pipeline.
5. Configuração do pipeline
Vamos começar pelo mais importante e interessante, a criação da máquina virtual. É para isso que fizemos todas essas integrações e templates. No item de Subscription do Azure RM, selecionamos nossa conexão de serviço que configuramos no item 2. Em seguida, deve surgir o ambiente de laboratório disponível para nós. Depois, escolhemos o JSON que geramos e definimos algumas variáveis obrigatórias. O login e a senha da máquina podem ser definidos diretamente ou através de variáveis, mas eu realmente não tenho certeza se isso funciona; o que eu colocar lá, não consegui acessar a máquina depois com essas credenciais. O principal é definir o nome da máquina, que deve ser sempre único, se possível. Para isso, uso uma variável de ambiente de construção.

Em seguida, configuramos outro ponto importante. Depois que a máquina for iniciada, precisamos saber suas configurações, ou melhor, o pipeline precisa. Para isso, criamos um script, por exemplo, GetLabVMParams.ps1 e o colocamos no mesmo projeto. O texto do script eu peguei do site da Microsoft, mas fiz alguns ajustes para o meu ambiente, pois ele pegava o PublicIP e o FQDN da máquina. Não tenho nenhum desses, mas há um PrivateIP que não é tão fácil de obter, por isso adicionei um pedaço.
Param( [string] $labVmId)
$labVmComputeId = (Get-AzureRmResource -Id $labVmId).Properties.ComputeId
# Obter o nome do grupo de recursos da VM do laboratório
$labVmRgName = (Get-AzureRmResource -Id $labVmComputeId).ResourceGroupName
# Obter o nome da VM do laboratório
$labVmName = (Get-AzureRmResource -Id $labVmId).Name
# Obter o endereço IP público da VM do laboratório
# $labVMIpAddress = (Get-AzureRmPublicIpAddress -ResourceGroupName $labVmRgName -Name $labVmName).IpAddress
# Obter o FQDN da VM do laboratório
# $labVMFqdn = (Get-AzureRmPublicIpAddress -ResourceGroupName $labVmRgName -Name $labVmName).DnsSettings.Fqdn
# Obter o endereço IP privado da VM do laboratório
$VmNetworkdetails= (((Get-AzureRmVM -ResourceGroupName $labVmRgName -Name $labVmName).NetworkProfile).NetworkInterfaces).Id
$nicname = $VmNetworkdetails.substring($VmNetworkdetails.LastIndexOf("/")+1)
$labVMnetwork = (Get-AzureRmNetworkInterface -Name $nicname -ResourceGroupName $labVmRgName)|Select-Object -ExpandProperty IPConfigurations
$labVMIpAddress = $labVMnetwork.PrivateIpAddress
# Definir uma variável labVmRgName para armazenar o nome do grupo de recursos da VM do laboratório
Write-Host "##vso[task.setvariable variable=labVmRgName;]$labVmRgName"
# Definir uma variável labVMIpAddress para armazenar o endereço IP da VM do laboratório
Write-Host "##vso[task.setvariable variable=labVMIpAddress;]$labVMIpAddress"
# Definir uma variável labVMFqdn para armazenar o nome FQDN da VM do laboratório
Write-Host "##vso[task.setvariable variable=labVMFqdn;]$labVMFqdn"
Write-Output $labVMIpAddress
Set-Item wsman:localhostclienttrustedhosts * -ForceDe tudo que o script lê, precisamos apenas da variável labVMIpAddress. Bem, isso é para mim; talvez você precise de mais alguma coisa, por isso não excluí nada, apenas comentei o que é desnecessário.
Também explico a última linha do script, ela permite que nossa máquina de build tenha acesso a qualquer host via WinRM.
Na próxima etapa, vamos executar nosso incrível script. Ele precisará da mesma conexão com a nuvem, e a variável de entrada com o ID da máquina já será conhecida a partir da etapa anterior. Como? Aqui é importante mencionar algo maravilhoso chamado Variáveis de Saída. Cada etapa pode ter uma lista de variáveis que são passadas adiante para as próximas etapas do pipeline. Portanto, para o nosso super script, essa variável será labVMIpAddress, não se esqueça de mencioná-la.

Em seguida, faço coisas bastante simples, que podem variar de caso para caso. Executo um script remoto para criar um compartilhamento, onde depois carregarei meus scripts.
New-Item “C:test" –type directory
New-SMBShare –Name “test” –Path “C:test” –FullAccess everyonePelo título das tarefas, é claro que agora copiamos um script de exemplo para a máquina e em uma etapa adicional o executamos. Para o endereço da máquina remota, utilizaremos nossa variável $(labVMIpAddress). Em seguida, usamos a tarefa 'pegar artefato do compartilhamento' e copiamos os resultados da execução do script para o nosso ambiente de build, depois, com a mesma tarefa padrão, salvamos esses arquivos como artefato do build. Após não precisarmos mais da máquina, no último passo, a destruímos. A principal dificuldade, como pode ser vista pelo volume do artigo, é se integrar com a nuvem e estabelecer contato com a máquina virtual que você criou; depois disso, você pode se divertir o quanto quiser.
Este é meu primeiro artigo, então não sejam duros, críticas são bem-vindas.
Fonte: habr.com
