Introdução
Neste laboratório, você aprenderá a controlar o fluxo de execução de playbooks do Ansible em um sistema Red Hat Enterprise Linux (RHEL). Você começará escrevendo um playbook que utiliza estruturas de controle fundamentais, incluindo loops para repetir tarefas de forma eficiente e condicionais para executar tarefas apenas quando critérios específicos são atendidos. Você também implementará handlers para acionar ações, como reinícios de serviços, apenas quando uma alteração ocorre, tornando sua automação mais inteligente e eficiente.
Com base nessas habilidades fundamentais, você explorará técnicas mais avançadas para gerenciar a execução de playbooks. Isso inclui o uso de instruções block e rescue para lidar com falhas de tarefas de forma graciosa e o emprego de changed_when e failed_when para obter controle granular sobre o status da tarefa. Para concluir o laboratório, você aplicará todos esses conceitos em um exercício prático para implantar um servidor web seguro, solidificando sua capacidade de criar automação Ansible robusta e confiável.
Escrever um Playbook com Loops e Condicionais
Nesta etapa, você aprenderá dois conceitos fundamentais no Ansible para controlar a execução de tarefas: loops e condicionais. Loops permitem que você repita uma tarefa várias vezes com valores diferentes, o que é altamente eficiente para tarefas como instalar vários pacotes ou criar vários usuários. Condicionais, usando a palavra-chave when, permitem que você execute uma tarefa apenas quando critérios específicos são atendidos, como o sistema operacional ser de uma versão específica ou um arquivo já existir.
Primeiro, vamos garantir que o Ansible esteja instalado em sua VM LabEx. Usaremos o gerenciador de pacotes DNF para isso.
sudo dnf install -y ansible-core
Você deverá ver uma saída indicando que ansible-core e suas dependências estão sendo instalados.
...
Installed:
ansible-core-2.x.x-1.el9.x86_64
...
Complete!
Agora, vamos configurar nosso diretório de projeto. Todo o nosso trabalho para este laboratório ficará dentro de um diretório dedicado para manter as coisas organizadas.
cd ~/project
mkdir control-flow-lab
cd control-flow-lab
Um projeto Ansible precisa de um arquivo de inventário, que define os hosts que você deseja gerenciar. Para este laboratório, gerenciaremos a máquina local, localhost.
Crie um arquivo de inventário chamado inventory usando o editor nano:
nano inventory
Adicione a seguinte linha ao arquivo. Isso diz ao Ansible para executar o playbook em localhost e se conectar a ele diretamente em vez de usar SSH.
localhost ansible_connection=local
Salve o arquivo e saia do nano pressionando Ctrl+X, depois Y e Enter.
Em seguida, criaremos nosso primeiro playbook, playbook.yml, para demonstrar um loop. Este playbook instalará uma lista de ferramentas úteis de linha de comando.
nano playbook.yml
Insira o seguinte conteúdo YAML no editor. Este playbook define uma tarefa que usa o módulo ansible.builtin.dnf para instalar pacotes. A diretiva become: yes diz ao Ansible para executar tarefas com privilégios de sudo, o que é necessário para instalar pacotes. A palavra-chave loop fornece uma lista de nomes de pacotes. O Ansible executará esta tarefa uma vez para cada item na lista, substituindo o placeholder {{ item }} pelo nome do pacote atual.
---
- name: Install common tools
hosts: localhost
become: yes
tasks:
- name: Install specified packages
ansible.builtin.dnf:
name: "{{ item }}"
state: present
loop:
- git
- tree
- wget
Salve e saia do editor. Agora, execute o playbook usando o comando ansible-playbook, especificando seu arquivo de inventário com o sinalizador -i.
ansible-playbook -i inventory playbook.yml
A saída mostrará a execução do playbook. O Ansible verificará cada pacote e o instalará se ele ainda não estiver presente. O PLAY RECAP no final resume os resultados.
PLAY [Install tools and run conditional tasks] *********************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [Install specified packages] **********************************************
changed: [localhost] => (item=git)
changed: [localhost] => (item=tree)
changed: [localhost] => (item=wget)
PLAY RECAP *********************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Agora, vamos modificar o playbook para incluir uma tarefa condicional. Adicionaremos uma tarefa que exibe uma mensagem, mas apenas se o sistema operacional for Red Hat Enterprise Linux. Este é um caso de uso comum para adaptar a automação a ambientes específicos.
Abra o arquivo playbook.yml novamente:
nano playbook.yml
Adicione as seguintes tarefas ao final do arquivo. A palavra-chave when avalia a expressão fornecida. ansible_facts['distribution'] é uma variável que o Ansible descobre automaticamente sobre o host gerenciado. A primeira tarefa será executada porque nosso ambiente é RHEL, e a segunda tarefa será pulada.
---
- name: Install tools and run conditional tasks
hosts: localhost
become: yes
tasks:
- name: Install specified packages
ansible.builtin.dnf:
name: "{{ item }}"
state: present
loop:
- git
- tree
- wget
- name: Show message on Red Hat systems
ansible.builtin.debug:
msg: "This system is a Red Hat family distribution."
when: ansible_facts['distribution'] == "RedHat"
- name: Show message on other systems
ansible.builtin.debug:
msg: "This system is NOT a Red Hat family distribution."
when: ansible_facts['distribution'] != "RedHat"
Salve e saia do editor. Execute o playbook atualizado:
ansible-playbook -i inventory playbook.yml
Observe a saída com atenção. A tarefa de instalação de pacotes provavelmente relatará ok para todos os itens, pois eles já estão instalados. Mais importante, você verá a primeira mensagem de depuração impressa, enquanto a segunda é marcada como skipping.
PLAY [Install tools and run conditional tasks] *********************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [Install specified packages] **********************************************
ok: [localhost] => (item=git)
ok: [localhost] => (item=tree)
ok: [localhost] => (item=wget)
TASK [Show message on Red Hat systems] *****************************************
ok: [localhost] => {
"msg": "This system is a Red Hat family distribution."
}
TASK [Show message on other systems] *******************************************
skipping: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
Você escreveu e executou com sucesso um playbook Ansible que usa loops para realizar ações repetitivas e condicionais para controlar a execução de tarefas com base nos fatos do sistema.
Implementar Handlers para Disparar Reinícios de Serviço
Nesta etapa, você aprenderá sobre os handlers do Ansible. Handlers são tarefas especiais que só são executadas quando "notificadas" por outra tarefa. Eles são tipicamente usados para ações que só devem ocorrer quando uma alteração foi feita, como reiniciar um serviço após seu arquivo de configuração ter sido atualizado. Essa abordagem é mais eficiente do que reiniciar um serviço a cada execução do playbook, pois garante que a ação seja tomada apenas quando necessário.
Criaremos um playbook que instala o servidor web Nginx, implanta uma página inicial personalizada e usa um handler para recarregar o Nginx apenas quando o conteúdo da página inicial muda.
Primeiro, vamos criar um novo diretório para este exercício para manter nosso projeto organizado.
cd ~/project
mkdir control-handlers-lab
cd control-handlers-lab
Como antes, precisamos de um arquivo de inventário para dizer ao Ansible onde executar o playbook.
nano inventory
Adicione a seguinte linha para especificar a máquina local.
localhost ansible_connection=local
Salve e saia do editor (Ctrl+X, Y, Enter).
Em seguida, precisamos de um arquivo para servir como a página inicial do nosso servidor web. Criaremos um diretório files para armazená-lo.
mkdir files
Agora, crie um arquivo simples index.html dentro do diretório files.
nano files/index.html
Adicione o seguinte conteúdo HTML:
<h1>Welcome to the Ansible Handler Lab!</h1>
Salve e saia do editor.
Agora, você criará o playbook deploy_nginx.yml. Este playbook realizará três ações principais: instalar o Nginx, copiar o arquivo index.html e definir um handler para recarregar o Nginx.
nano deploy_nginx.yml
Insira o seguinte conteúdo. Preste muita atenção à palavra-chave notify na tarefa "Copy homepage" e à seção handlers correspondente no final. A diretiva become: yes diz ao Ansible para executar tarefas com privilégios de sudo, o que é necessário para instalar pacotes e gerenciar serviços.
---
- name: Deploy Nginx with a handler
hosts: localhost
become: yes
tasks:
- name: Ensure Nginx is installed
ansible.builtin.dnf:
name: nginx
state: present
- name: Start and enable Nginx service
ansible.builtin.systemd:
name: nginx
state: started
enabled: yes
- name: Copy homepage
ansible.builtin.copy:
src: files/index.html
dest: /usr/share/nginx/html/index.html
notify: reload nginx
handlers:
- name: reload nginx
ansible.builtin.systemd:
name: nginx
state: reloaded
Salve e saia do editor.
Agora, execute o playbook pela primeira vez.
ansible-playbook -i inventory deploy_nginx.yml
Você verá a saída mostrando que o Nginx foi instalado (ou já estava presente), o serviço Nginx foi iniciado e habilitado, o arquivo index.html foi copiado (status changed) e, importante, o handler foi notificado e executado ao final do play.
...
TASK [Copy homepage] ***********************************************************
changed: [localhost]
RUNNING HANDLER [reload nginx] *************************************************
changed: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Você pode verificar que o servidor web está em execução e servindo sua página personalizada usando curl.
curl http://localhost
A saída deve ser o conteúdo do seu arquivo index.html.
<h1>Welcome to the Ansible Handler Lab!</h1>
Agora, execute exatamente o mesmo playbook novamente sem fazer nenhuma alteração.
ansible-playbook -i inventory deploy_nginx.yml
Desta vez, observe a saída. A tarefa "Copy homepage" relatará ok em vez de changed, pois o arquivo no destino já corresponde à origem. A tarefa "Start and enable Nginx service" também relatará ok, pois o serviço já está em execução e habilitado. Como nenhuma tarefa notificou o handler, o handler não foi executado.
...
TASK [Copy homepage] ***********************************************************
ok: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Para ver o handler em ação novamente, vamos modificar o arquivo de origem index.html.
nano files/index.html
Altere o conteúdo para o seguinte:
<h1>The Handler Ran Again!</h1>
Salve e saia. Agora, execute o playbook mais uma vez.
ansible-playbook -i inventory deploy_nginx.yml
Como o arquivo de origem mudou, a tarefa "Copy homepage" relatará changed novamente, o que, por sua vez, notificará e executará o handler reload nginx.
...
TASK [Copy homepage] ***********************************************************
changed: [localhost]
RUNNING HANDLER [reload nginx] *************************************************
changed: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Verifique a alteração com curl mais uma vez.
curl http://localhost
Você deverá ver a mensagem atualizada.
<h1>The Handler Ran Again!</h1>
Este exercício demonstra o poder e a eficiência dos handlers para gerenciar o estado do serviço em resposta a alterações de configuração.
Gerenciar Falhas de Tarefas com Block e Rescue
Nesta etapa, você aprenderá como lidar com erros de forma elegante em seus playbooks Ansible. Por padrão, se qualquer tarefa falhar, o Ansible para de executar todo o playbook naquele host. Embora este seja um padrão seguro, às vezes você precisa de mais controle. Você explorará dois métodos para tratamento de erros: a diretiva simples ignore_errors e a estrutura mais poderosa block, rescue e always, que fornece uma maneira de tentar tarefas e definir ações de recuperação se elas falharem.
Primeiro, vamos criar um novo diretório para este exercício.
cd ~/project
mkdir control-errors-lab
cd control-errors-lab
Crie o arquivo inventory padrão para localhost.
nano inventory
Adicione o seguinte conteúdo:
localhost ansible_connection=local
Salve e saia do editor (Ctrl+X, Y, Enter).
Agora, vamos criar um playbook chamado playbook.yml que foi projetado para falhar. A primeira tarefa tentará instalar um pacote que não existe.
nano playbook.yml
Insira o seguinte conteúdo. Este playbook tenta instalar um pacote falso httpd-fake e, em seguida, um pacote real, mariadb-server.
---
- name: Demonstrate Task Failure
hosts: localhost
become: yes
tasks:
- name: Attempt to install a non-existent package
ansible.builtin.dnf:
name: httpd-fake
state: present
- name: Install MariaDB server
ansible.builtin.dnf:
name: mariadb-server
state: present
Salve e saia do editor. Agora, execute o playbook.
ansible-playbook -i inventory playbook.yml
Você verá a primeira tarefa falhar com uma mensagem de erro porque o pacote httpd-fake não pode ser encontrado. Crucialmente, o Ansible parará e a segunda tarefa, "Install MariaDB server", não será executada.
...
TASK [Attempt to install a non-existent package] *******************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "No match for argument: httpd-fake", "rc": 1, "results": []}
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
Agora, vamos usar block e rescue para lidar com essa falha de forma mais elegante. A palavra-chave block agrupa um conjunto de tarefas. Se qualquer tarefa dentro do block falhar, o Ansible pula o restante das tarefas no block e executa as tarefas na seção rescue. A seção always será executada independentemente de as seções block ou rescue terem sido bem-sucedidas ou falhado.
Modifique playbook.yml para usar essa estrutura.
nano playbook.yml
Substitua todo o conteúdo pelo seguinte. Aqui, tentamos instalar o pacote falso no block. Quando ele falha, a seção rescue será executada, instalando mariadb-server como uma etapa de recuperação. A seção always imprimirá uma mensagem no final.
---
- name: Handle Task Failure with Block and Rescue
hosts: localhost
become: yes
tasks:
- name: Attempt primary task, with recovery
block:
- name: Attempt to install a non-existent package
ansible.builtin.dnf:
name: httpd-fake
state: present
- name: This task will be skipped
ansible.builtin.debug:
msg: "This message will not appear because the previous task fails."
rescue:
- name: Install MariaDB server on failure
ansible.builtin.dnf:
name: mariadb-server
state: present
always:
- name: This always runs
ansible.builtin.debug:
msg: "The block has completed, either by success or rescue."
Salve e saia. Execute o playbook novamente.
ansible-playbook -i inventory playbook.yml
Observe a saída. A primeira tarefa no block falha como esperado. A segunda tarefa no block é pulada. O Ansible então passa para a seção rescue e instala com sucesso mariadb-server. Finalmente, a seção always é executada.
...
TASK [Attempt to install a non-existent package] *******************************
fatal: [localhost]: FAILED! => ...
TASK [This task will be skipped] ***********************************************
skipping: [localhost]
RESCUE START *******************************************************************
TASK [Install MariaDB server on failure] ***************************************
changed: [localhost]
ALWAYS START *******************************************************************
TASK [This always runs] ********************************************************
ok: [localhost] => {
"msg": "The block has completed, either by success or rescue."
}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=1 unreachable=0 failed=0 skipped=1 rescued=1 ignored=0
Agora, vamos ver o que acontece quando o block é bem-sucedido. Edite o playbook e corrija o nome do pacote.
nano playbook.yml
Altere httpd-fake para um pacote real, httpd.
## ... (rest of the playbook)
block:
- name: Attempt to install a valid package
ansible.builtin.dnf:
name: httpd ## Corrected from httpd-fake
state: present
- name: This task will now run
ansible.builtin.debug:
msg: "This message will now appear because the previous task succeeds."
## ... (rest of the playbook)
Salve e saia. Execute o playbook mais uma vez.
ansible-playbook -i inventory playbook.yml
Desta vez, ambas as tarefas no block são bem-sucedidas. Como o block foi concluído sem erros, a seção rescue é completamente pulada. A seção always ainda é executada, como o nome sugere.
...
TASK [Attempt to install a valid package] **************************************
changed: [localhost]
TASK [This task will now run] **************************************************
ok: [localhost] => {
"msg": "This message will now appear because the previous task succeeds."
}
RESCUE START *******************************************************************
skipping rescue
ALWAYS START *******************************************************************
TASK [This always runs] ********************************************************
ok: [localhost] => {
"msg": "The block has completed, either by success or rescue."
}
PLAY RECAP *********************************************************************
localhost : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Agora você usou com sucesso a estrutura block/rescue/always para criar um playbook robusto que pode lidar com falhas e executar ações de recuperação.
Controlar o Estado da Tarefa com changed_when e failed_when
Nesta etapa, você obterá um controle mais refinado sobre como o Ansible interpreta o resultado de suas tarefas. Você aprenderá sobre duas diretivas poderosas: changed_when e failed_when.
changed_when: Por padrão, módulos comoansible.builtin.commandouansible.builtin.shellquase sempre relatam um estado "changed" (alterado), mesmo que o comando que executaram não tenha modificado o sistema.changed_whenpermite definir uma condição personalizada que determina se uma tarefa deve ser relatada como "changed". Isso é crucial para escrever playbooks idempotentes e para acionar handlers com precisão.failed_when: Às vezes, um comando pode sair com um código de status diferente de zero (que o Ansible considera uma falha), mesmo quando o resultado é aceitável.failed_whenpermite substituir as condições de falha padrão, permitindo que seu playbook continue com base em critérios mais inteligentes, como a saída do comando ou um código de saída específico.
Vamos começar configurando um novo diretório de projeto.
cd ~/project
mkdir control-state-lab
cd control-state-lab
Crie o arquivo inventory padrão para localhost.
nano inventory
Adicione o seguinte conteúdo:
localhost ansible_connection=local
Salve e saia do editor (Ctrl+X, Y, Enter).
Usando changed_when
Primeiro, vamos ver como uma tarefa de comando se comporta por padrão. Criaremos um playbook que executa o comando date. Este comando simplesmente imprime a data e não altera o sistema, mas o módulo command o relatará como uma alteração.
Crie um novo playbook chamado playbook.yml.
nano playbook.yml
Insira o seguinte conteúdo:
---
- name: Control Task State
hosts: localhost
tasks:
- name: Check local time (default behavior)
ansible.builtin.command: date
Salve e saia. Agora, execute o playbook.
ansible-playbook -i inventory playbook.yml
Observe na saída que a tarefa é relatada como changed=1, mesmo que nada no sistema tenha sido modificado.
...
TASK [Check local time (default behavior)] *************************************
changed: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0 ...
Agora, vamos usar changed_when para dizer ao Ansible que este comando nunca altera o sistema. Modifique playbook.yml.
nano playbook.yml
Adicione changed_when: false à tarefa.
---
- name: Control Task State
hosts: localhost
tasks:
- name: Check local time (with changed_when)
ansible.builtin.command: date
changed_when: false
Salve e saia. Execute o playbook novamente.
ansible-playbook -i inventory playbook.yml
Desta vez, a tarefa relata ok e o resumo final mostra changed=0. Você substituiu com sucesso o comportamento padrão.
...
TASK [Check local time (with changed_when)] ************************************
ok: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 ...
Usando failed_when
Em seguida, vamos explorar failed_when. Criaremos uma tarefa que verifica a existência de um arquivo que não está lá. O comando "falhará" por padrão.
Primeiro, crie um arquivo dummy para pesquisar.
echo "System is running" > status.txt
Agora, modifique playbook.yml para procurar a palavra "ERROR" neste arquivo. O comando grep sairá com um código de status 1 porque a palavra não é encontrada, o que o Ansible interpreta como uma falha.
nano playbook.yml
Substitua o conteúdo pelo seguinte:
---
- name: Control Task State
hosts: localhost
tasks:
- name: Check for ERROR in status file (will fail)
ansible.builtin.command: grep ERROR status.txt
Salve e saia. Execute o playbook.
ansible-playbook -i inventory playbook.yml
Como esperado, a execução do playbook para com uma mensagem FAILED!.
...
TASK [Check for ERROR in status file (will fail)] ******************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": ["grep", "ERROR", "status.txt"], "delta": "...", "end": "...", "msg": "non-zero return code", "rc": 1, ...}
...
Isso não é o que queremos. A ausência de "ERROR" é uma condição de sucesso para nós. Podemos usar failed_when para redefinir o que constitui uma falha. Diremos ao Ansible para falhar apenas se o código de retorno do comando for maior que 1. Um código de retorno de 1 (padrão não encontrado) agora será considerado um sucesso. Também precisamos registrar o resultado da tarefa para inspecionar seu código de retorno (rc).
Modifique playbook.yml mais uma vez.
nano playbook.yml
Atualize o playbook com register e failed_when.
---
- name: Control Task State
hosts: localhost
tasks:
- name: Check for ERROR in status file (with failed_when)
ansible.builtin.command: grep ERROR status.txt
register: grep_result
failed_when: grep_result.rc > 1
changed_when: false
Também adicionamos changed_when: false porque grep é uma operação somente leitura e não altera o sistema.
Salve e saia. Execute o playbook final.
ansible-playbook -i inventory playbook.yml
Sucesso! A tarefa agora relata ok porque seu código de retorno foi 1, o que não atende à nossa nova condição de falha (rc > 1). O playbook é concluído com sucesso.
...
TASK [Check for ERROR in status file (with failed_when)] ***********************
ok: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 ...
Agora você aprendeu como usar changed_when e failed_when para definir precisamente os estados de sucesso, alterado e falha de suas tarefas, levando a uma automação mais robusta e inteligente.
Implantar um Servidor Web Seguro Usando Controle de Tarefas
Nesta etapa final, você combinará todos os conceitos que aprendeu — loops, condicionais, handlers e tratamento de erros — para construir um playbook único e robusto. O objetivo é implantar o servidor web Apache (httpd), protegê-lo com mod_ssl, gerar um certificado SSL autoassinado e implantar uma página inicial personalizada. Este exercício prático espelha uma tarefa de automação do mundo real.
Primeiro, vamos configurar o diretório do projeto para este exercício final.
cd ~/project
mkdir control-review-lab
cd control-review-lab
Como sempre, crie um arquivo inventory para definir seu host de destino.
nano inventory
Adicione a entrada localhost:
localhost ansible_connection=local
Salve e saia do editor (Ctrl+X, Y, Enter).
Em seguida, precisamos de um diretório para armazenar os arquivos que nosso playbook implantará.
mkdir files
Agora, crie uma página inicial personalizada, index.html, dentro do diretório files.
nano files/index.html
Adicione o seguinte conteúdo HTML. Esta será a página servida pelo nosso servidor web seguro.
<h1>Secure Web Server Deployed by Ansible!</h1>
<p>This page is served over HTTPS.</p>
Salve e saia do editor.
Agora é hora de construir o playbook principal, deploy_secure_web.yml. Este playbook será mais complexo do que os anteriores, integrando múltiplos conceitos.
nano deploy_secure_web.yml
Insira o seguinte playbook completo. Leia os comentários dentro do código para entender como cada parte contribui para o objetivo geral.
---
- name: Deploy a Secure Apache Web Server
hosts: localhost
become: yes
vars:
packages_to_install:
- httpd
- mod_ssl
ssl_cert_path: /etc/pki/tls/certs/localhost.crt
ssl_key_path: /etc/pki/tls/private/localhost.key
tasks:
- name: Stop nginx to free port 80
ansible.builtin.systemd:
name: nginx
state: stopped
ignore_errors: yes
- name: Install httpd and mod_ssl packages
ansible.builtin.dnf:
name: "{{ packages_to_install }}"
state: present
- name: Generate self-signed SSL certificate if it does not exist
ansible.builtin.command: >
openssl req -new -nodes -x509
-subj "/C=US/ST=None/L=None/O=LabEx/CN=localhost"
-keyout {{ ssl_key_path }}
-out {{ ssl_cert_path }}
args:
creates: "{{ ssl_cert_path }}"
- name: Deploy custom index.html
ansible.builtin.copy:
src: files/index.html
dest: /var/www/html/index.html
notify: restart httpd
- name: Start and enable httpd service
ansible.builtin.systemd:
name: httpd
state: started
enabled: yes
handlers:
- name: restart httpd
ansible.builtin.systemd:
name: httpd
state: restarted
Vamos detalhar o que este playbook faz:
vars: Define variáveis para os pacotes a serem instalados e os caminhos para o certificado e chave SSL, tornando o playbook mais fácil de ler e manter.- Tarefa Stop Nginx: Para o serviço nginx da etapa anterior do laboratório para liberar a porta 80 para o Apache. Usa
ignore_errors: yescaso o nginx não esteja em execução. - Tarefa de Instalação: Usa a variável
packages_to_installpara instalar tantohttpdquantomod_ssl. - Tarefa de Geração de Certificado: Esta é uma tarefa chave. Ela usa o comando
opensslpara criar um certificado autoassinado. A diretivaargs: { creates: ... }torna esta tarefa idempotente. O comando só será executado se o arquivo de certificado (/etc/pki/tls/certs/localhost.crt) ainda não existir. - Tarefa de Implantação da Página Inicial: Copia seu
index.htmlpersonalizado. Crucialmente, ele usanotify: restart httpdpara acionar o handler se o arquivo for alterado. - Tarefa de Início do Serviço: Usa o módulo systemd para iniciar e habilitar o serviço httpd após toda a configuração estar no lugar, garantindo que ele inicie na inicialização.
- Handler: O handler
restart httpdexecuta uma reinicialização do Apache usando systemd, que é acionada apenas quando um arquivo de configuração ou conteúdo é alterado.
Salve e saia do editor. Agora, execute seu playbook abrangente.
ansible-playbook -i inventory deploy_secure_web.yml
Na primeira execução, você deverá ver várias tarefas relatando changed, incluindo a parada do nginx, instalação de pacotes, geração de certificado, cópia de arquivo e início do serviço.
...
TASK [Start and enable httpd service] ******************************************
changed: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=6 changed=5 unreachable=0 failed=0 ...
Finalmente, verifique se seu servidor web seguro está funcionando. Primeiro teste a versão HTTP, depois a versão HTTPS com o sinalizador -k para ignorar avisos sobre o certificado autoassinado.
curl http://localhost
Você deverá ver o conteúdo da sua página inicial personalizada.
<h1>Secure Web Server Deployed by Ansible!</h1>
<p>This page is served over HTTPS.</p>
Você também pode testar a versão HTTPS:
curl -k https://localhost
Se você executar o playbook novamente, verá que nenhuma tarefa relata changed, e o handler não é executado, provando que seu playbook é idempotente.
Parabéns! Você construiu com sucesso um playbook Ansible prático e robusto que combina loops, variáveis, execução de comandos idempotentes e handlers para implantar um aplicativo seguro.
Resumo
Neste laboratório, você aprendeu a controlar a execução de playbooks Ansible em um sistema RHEL. Você começou configurando um ambiente de projeto básico, incluindo a instalação do Ansible e a criação de um arquivo de inventário. Em seguida, explorou estruturas fundamentais de fluxo de controle, usando loops para repetir eficientemente tarefas com diferentes entradas e condicionais com a instrução when para executar tarefas apenas sob circunstâncias específicas. Com base nisso, você implementou handlers para criar automações responsivas, como acionar uma reinicialização de serviço apenas quando seu arquivo de configuração foi modificado.
O laboratório também cobriu técnicas avançadas para gerenciar a execução de playbooks. Você aprendeu como construir playbooks mais robustos usando cláusulas block e rescue para lidar com falhas de tarefas de forma graciosa. Além disso, você obteve controle granular sobre os resultados das tarefas usando changed_when e failed_when para definir condições personalizadas de sucesso e falha. Finalmente, você consolidou todas essas habilidades aplicando-as a um cenário prático: a implantação de um servidor web seguro, demonstrando como combinar efetivamente loops, condicionais, handlers e tratamento de erros em um fluxo de trabalho de automação do mundo real.



