Olá pessoal, sejam muito bem-vindos a mais uma aula. Nesta aula nós falaremos sobre um recurso que o Terraform nos oferece que deve ser utilizado com muita atenção em casos muito específicos, que são os provisioners. Um provisioner permite que nós executemos, que a gente execute um script localmente ou mesmo remotamente no momento em que um recurso é criado ou destruído. Além disso, nós podemos utilizar provisioners também para copiar arquivos da nossa máquina local para uma máquina remota, para uma instância, por exemplo, que nós acabamos de criar lá na AWS EC2. Bom, vamos para um exemplo para ficar mais claro como nós podemos utilizar um Provisioner. Primeiro eu vou comentar aqui esses módulos que nós adicionamos no nosso projeto. Não fará muita diferença aqui no meu projeto, porque eu não fiz o deploy desses recursos. Na verdade, depois da última vez que nós fizemos o deploy, eu acabei excluindo os recursos. Então, para nós exemplificarmos, o que eu farei aqui será o seguinte. Eu vou criar um arquivo local, tá? Um arquivo local. Então nós teremos aqui um local file e o nome do arquivo pode ser file. Beleza. Nós vamos especificar um nome para este arquivo, um nome qualquer, então eu terei aqui filename, opa, não é file permission, é filename, beleza, que será test.txt. Nós teremos aqui um conteúdo qualquer, eu vou colocar hello world. Beleza? E agora nós falaremos de fato sobre provisioner. Nós podemos declarar um provisioner no próprio recurso. Então dentro do próprio bloco do recurso, nós podemos adicionar um bloco alinhado, que é esse bloco provisioner. E nós temos três diferentes tipos de provisioner, que é o file, que como eu já adiantei anteriormente, nos permite copiar um arquivo local para o nosso recurso remoto, que nós estamos criando, por exemplo, uma máquina virtual, não é o nosso caso, então nós não vamos utilizar file aqui agora. Nós temos o local exec, que nos permite executar um comando local, e o nosso caso, então nós não vamos utilizar o file aqui agora nós temos o local exec que nos permite executar um comando local e o remote exec que nos permite executar um comando remotamente, então vamos utilizar o local exec como exemplo e nós temos aqui o comando então nós podemos executar por exemplo um echo e nós podemos printar aqui o nome do arquivo aqui o nome do arquivo o nome do arquivo que nós estamos especificando. Para isso nós temos uma forma de referenciar um recurso que é um pouquinho diferente. Nós utilizamos o self. Então quando nós queremos referenciar um atributo do próprio recurso onde nós estamos criando aquele provisioner, nós utilizamos a palavra chave self. E aqui nós já podemos ver todos os atributos que nós temos disponíveis. Nós podemos, então, colocar aqui, por exemplo, filename. E eu vou colocar aqui também, na verdade, o nome do arquivo. E eu vou colocar created. E aqui eu vou dar o append em um determinado arquivo, que será o arquivo log.txt, para nós termos como exemplo. Por default, ele vai utilizar aqui o interpretador padrão, o shell padrão que nós temos configurado na nossa máquina local, mas nós poderíamos também alterar o nosso intérprete. Beleza? Por enquanto nós teremos somente isso. E basicamente é isso. Nós temos o nosso primeiro provisioner. Nós podemos aqui rodar o Terraform. Eu vou dar aqui Terraform Apply. E dar um auto approve, tá? Para nós verificarmos o que acontece. Vamos esperar que seja executado. Beleza, nós precisamos aqui, na verdade, de rodar o Terraform Init, tá? Então, eu vou rodar aqui o Terraform init, traço o upgrade, porque nós estamos utilizando o outro provider, ele baixou aqui o nosso novo provider, e agora nós podemos rodar, de fato, o apply e colocar aqui um alt approve para nós vermos se isso vai funcionar ou não. Perfeito. O arquivo teste foi criado com o nosso hello world, e aqui nós temos também o arquivo log.txt, dizendo que este arquivo foi criado, assim como nós colocamos aqui no nosso comando. Atenção, quando nós declaramos um provision dessa forma aqui, nós iremos ter a garantia de que este comando vai rodar no momento em que o recurso está sendo criado. Caso este recurso seja alterado, caso haja um update do recurso, este comando não será executado. Então, este provisioner deve ser utilizado somente quando nós queremos executar um comando no momento da criação e não da atualização. Aqui nós não vamos conseguir fazer um exemplo com o local file, porque qualquer alteração que nós fizermos fará com que haja um replace do recurso, ou seja, o recurso vai ser excluído, então vai ser criado, e dessa forma o nosso provisioner será executado. Porém, nós temos uma outra possibilidade também, que é declararmos o provisioner para ser rodado quando o nosso recurso for excluído. Então nós temos aqui o when e nós podemos especificar create ou destroy. Nós vamos colocar destroy porque por padrão esse when é o create. Então nós podemos colocar aqui que este cara será agora um destroyed, na verdade deleted. Vamos colocar aqui deleted. Agora nós teremos uma outra mensagem de erro. A questão é a seguinte, para que este cara aqui seja executado, nós não podemos remover a declaração deste recurso. Aí você pode perguntar, mas como nós vamos excluir um recurso sem remover a declaração do recurso aqui do nosso arquivo de configuração. Isso é bem estranho. Bom, nós podemos fazer o seguinte, tem aqui uma forma de nós garantirmos que isso seja executado, mas nós temos que fazer em dois passos. Nós primeiro precisamos colocar um count aqui igual a zero, para garantir que o recurso permaneça definido aqui, permaneça declarado no nosso arquivo, mas que ele seja excluído na prática. E aí depois que nós fizermos essa atualização, darmos um terraform apply, aí sim nós podemos remover a declaração do arquivo aqui do nosso arquivo de configuração. Ou a declaração do recurso do nosso arquivo de configuração. Beleza? Então, vamos testar. Eu coloquei aqui count igual a 0 e agora vou rodar o terraform apply com alta approve desta forma de novo para nós entendermos o que vai acontecer. Bom, nós podemos verificar que o arquivo teste.txt foi excluído e agora o nosso arquivo log.txt tem aqui o teste deleted. Beleza? Então agora nós já entendemos como executar esses comandos de acordo com o ciclo de vida dos nossos recursos nós temos também a possibilidade de especificar aqui um onFailure que possui essas duas propriedades esses dois valores possíveis que é o continue ou seja, quando o comando do nosso provisioner falhar quando houver qualquer falha nós vamos continuar com a execução do Terraform e nada vai acontecer, tá? Só haverá uma falha na execução do comando. Porém, se nós colocarmos aqui fail, que é a opção padrão, nós vamos ter um erro, tá? Então, o Terraform vai ficar ali num estado inválido, que nós chamamos de Tempted. Esse recurso estará com o estado de Tempted, tá? Ele não terá Um status de sucesso Então nós podemos declarar este onFailure Aqui como por exemplo, sendo contínuo Dessa forma, caso haja qualquer erro Beleza, para o Terraform não faz diferença Ele continua gerenciando Os recursos da forma como ele sempre fez Só que você não tem a garantia De que este comando será executado Com sucesso Beleza? Então, basicamente o nosso provisioner do tipo local exec é isso aqui que nós deveríamos saber. Nós temos essas possibilidades, porém é muito comum que a gente queira executar um comando ou um script na nossa VM, na nossa máquina remota que nós estamos acabando de provisionar para fazer uma configuração. Isso não é recomendado, não é recomendado que a gente utilize um provisioner para isso porque geralmente nós temos outras opções bem melhores, como por exemplo nós vemos o UserData. Se eu tenho o UserData que nós podemos utilizar de uma forma muito simples, sem precisar lidar com as credenciais de acesso à máquina. Por que não usá-lo? Por que utilizar um provisioner, sendo que eu tenho outras opções? E é isso que a documentação do Terraform, inclusive, sugere. Que o provisioner seja utilizado simplesmente no último cenário, no último caso. Mas caso você precise, caso você já tenha analisado todas as opções, e no seu cenário você precisa de executar um comando nessa máquina remota, você pode, então, utilizar o Remote Exec, tá? É o tipo de provisioner que te permitirá fazer isso. E aqui nós temos a sintaxe, nós temos a forma como nós podemos utilizar. Então, ele coloca uma nota aqui, um warning, dizendo para nós utilizarmos os provisioners simplesmente no último caso. Existem outras opções melhores, e ele sempre coloca esses warnings aqui por toda a documentação que fala sobre provisioners. Então, evite utilizar provisioners, utilize em último caso. Porque aqui nós já temos um problema. Para nós executarmos um script remoto, nós precisamos especificar a conexão, precisamos especificar credenciais. Aqui neste exemplo, por exemplo, ficou bom. Mas aqui, neste caso de uso, nós temos ele passando host, que vem do próprio self, ou seja, vem do próprio recurso aqui ele pega o public IP, porém, aqui ele está especificando a senha através de uma variável então essa senha tem que estar declarada em algum lugar aqui já entra a parte de gerenciamento de credenciais ah, mas existem outras formas eu não preciso passar a senha, eu posso utilizar também eu posso utilizar SSH, eu posso utilizar essas chaves, esses certificados, mas de toda forma você tem que gerenciar isso e criar ali uma instância com SSH aqui é bem complexo, você precisa gerenciar isso dentro dos seus próprios recursos, dentro dos seus arquivos de configuração e pode tornar a sua infraestrutura, além de mais vulnerável, pode tornar também mais complexa. Então, realmente, em muitos cenários não compensa utilizar esse tipo de provisioner ou esse recurso para nós executarmos, para nós configurarmos as nossas máquinas virtuais. Mas se você precisar, você precisa especificar um outro bloco, que é o bloco de connection. Quando nós especificamos esse bloco de connection, você pode ter vários provisioners aqui, e todos esses provisioners utilizarão essa conexão, ou você pode também especificar o connection dentro do próprio provisioner. Aqui nós temos alguns outros argumentos, nós temos o inline para permitir que a gente passe todos os nossos comandos como sendo uma lista de strings. Então, cada comando será um item dentro dessa lista. Nós temos script, onde nós passamos um script local, ou scripts, onde nós passar ali vários scripts, vários arquivos. Beleza, temos aqui alguns outros exemplos e aqui nós já vemos inclusive o outro tipo de provisioner, que é o provisioner file, que é bem simples, ele simplesmente copia um arquivo ou uma pasta local para a sua VM remota. Então, nós temos inclusive a documentação específica, novamente aqui o warning, e ele mostra vários exemplos, copiando o arquivo, especificando um conteúdo, ao invés de especificar o source, o arquivo local, ele especifica um content para ser jogado em determinado arquivo remoto. Nós temos também a cópia de pastas, e ele dá aqui alguns outros exemplos. Isso daqui pode ser bem útil, mas, novamente, como nós estamos falando de acesso remoto, nós precisamos gerenciar as conexões. Beleza? Então, não vou passar aqui muitos exemplos, até porque isso daqui não é algo para nós utilizarmos todos os dias, mas agora você já conhece os provisioners e você já sabe como utilizá-los. Espero que você tenha gostado. Beijo você na nossa próxima aula.