E aí pessoal, tudo bem? Agora que nós já conhecemos o conceito de infraestrutura como código, que nós já listamos alguns dos seus benefícios e também falamos de alguns tipos diferentes de IAC, nós falaremos sobre a diferença entre código declarativo e código imperativo. Começando aqui pelo paradigma, são paradigmas diferentes. Com o código declarativo nós estamos falando de uma linguagem ou de uma notação que descreve o resultado esperado, que descreve o estado final da sua infraestrutura. Você não precisa necessariamente saber quais são os passos, quais são os comandos, quais são as sequências de ações que serão executadas para que aquele estado final seja alcançado. Para isso, nós vamos utilizar linguagens como, por exemplo, JSON, como YAML, ou algumas variações dessas linguagens ou notações. Por outro lado, quando nós falamos em código imperativo, nós estamos falando de um código, de uma linguagem que expressa exatamente a sequência de ações que será executada. E essa sequência é muito importante. Então, às vezes, você muda a ordem daquelas ações, a ordem que está declarada aquele código, aqueles comandos, com certeza você vai ter um resultado diferente. Você pode ter um erro, você pode ter algo diferente daquilo que você estava planejando. Então, em código imperativo, você descreve exatamente a sequência de ações para atingir o seu resultado esperado. E para isso, nós vamos utilizar linguagens bem comuns que nós temos, como por exemplo Python, JavaScript, TypeScript, JavaScript, algumas linguagens proprietárias dependendo da ferramenta, mas nós temos essa flexibilidade maior, nós vamos trabalhar com linguagens que são muito conhecidas. Por outro lado, nós temos o fator de legibilidade. Então, nós temos o código declarativo, que utiliza essas linguagens ou notações, como eu já citei, que são muito fáceis de serem lidas, de serem entendidas, uma vez que você não precisa de ter o conhecimento prévio de uma linguagem de programação. Se você abrir um arquivo .im ou um arquivo .json, você vai entender o que está ali. Você vai entender quais são os parâmetros, quais são as configurações que estão sendo definidas ali, porque está tudo muito explícito, mas não tem ali uma sequência de ações ou passos que devem ser executados. Por outro lado, em código imperativo, como você tem essa maior flexibilidade, como você pode utilizar todos os benefícios, todas as vantagens que uma linguagem de código imperativo te entrega, você pode utilizar IF, você pode utilizar loops, você pode utilizar classes e tudo mais que você consegue utilizar com aquela linguagem, nós podemos ter um código um pouco menos legível. Então, às vezes é necessário que você tenha um conhecimento prévio e que você saiba um pouquinho daquela linguagem para que você entenda o que está acontecendo naquele código. Nós temos também o fator de gerenciamento de estado. Quando nós falamos em ferramentas que utilizam o código declarativo como base, geralmente nós temos ali um estado mantido, persistido em algum lugar que descreve o estado atual do ambiente. Por quê? Bom, se eu faço uma alteração naquele código declarativo E eu desejo executar aquela ferramenta para executar aquele código declarativo Nós precisamos saber o que foi alterado em relação ao estado atual Para que aquela ferramenta saiba exatamente quais são as ações executadas Para que aquele novo estado seja atingido Então é preciso conhecer o estado atual para saber o que alterar e chegar naquele estado desejado. Já em código imperativo nós temos cenários em que nós mantemos o estado atual e temos cenários em que esse estado atual não é mantido. Então muitas vezes você precisa verificar naquela sequência de ações qual é o estado atual e dependendo de uma condição ou outra, você vai executar uma ação diferente, uma determinada ação, enquanto para um outro estado, para uma outra condição, você vai executar uma ação diferente. Então temos essa diferença entre gerenciamento de estado, mas nós veremos também que existem ferramentas em que esse estado é mantido. Então é bem interessante, inclusive nós veremos isso um pouco mais adiante. Temos aqui uma outra característica, que é a característica de previsibilidade. Com o código declarativo, como você está descrevendo exatamente o estado que você deseja, é previsível que após executar o comando para rodar o código declarativo, claro que você vai ter aquele estado que você especificou ali, tá? Então é mais previsível. Novamente, entra a questão de flexibilidade do código imperativo, que pode, quando executado, não gerar ali o estado esperado, tá? Novamente, nós estamos falando aí de linguagens de programação muito dinâmicas, muito flexíveis e que nós vamos ter o mesmo problema, a mesma dificuldade de previsibilidade que nós temos quando nós falamos do desenvolvimento de qualquer aplicação. Você está tratando ali com código procedural, imperativo, com um código orientado a objetos e assim por diante. Então temos essa diferença de previsibilidade também. Agora vamos falar de exemplos. Para código declarativo nós temos o Terraform, temos o CloudFormation, o Puppet e o Ansible. E para imperativo alguns exemplos são a WSCLI, a WSCDK, o Chef, temos também o Azure Command Line, então todas essas ferramentas de Command Line geralmente são ferramentas de código imperativo. Mas eu queria deixar claro também que algumas opções aqui, por exemplo, de ferramentas que utilizam código declarativo, podem também oferecer extensões onde você pode especificar um código imperativo. E a mesma coisa também de código imperativo, que permite que você especifique alguma coisa em código declarativo. Então, eu coloquei aqui a principal característica da linguagem, mas ela pode ter também um overlap entre código declarativo e código imperativo. Beleza? Mas pode ser que tenha ficado bastante abstrato todos esses conceitos, que você não tenha conseguido visualizar essas características muito bem, e é por isso que eu trouxe aqui alguns exemplos, para que você entenda um pouco do que eu estou falando. Bom, aqui eu tenho quatro exemplos, eu gostaria aqui de começar primeiro do lado direito, na parte superior. Nós temos um código IEMEO, e esse daqui é um código que nós utilizaríamos, por exemplo, na ferramenta AWS CloudFormation, tá? Então, quando nós estamos trabalhando com CloudFormation, nós temos a opção de especificar um código em YAML ou então um código JSON. E aqui nós podemos observar que é bastante simples, tá? código JSON. E aqui nós podemos observar que é bastante simples. Nós especificamos aqui resources e depois nós dizemos que nós queremos uma máquina EC2. Essa chave aqui é uma chave arbitrária, que você poderia colocar qualquer coisa. É simplesmente o identificador interno desse recurso que você está criando. Aqui nós especificamos o tipo, que é AWS EC2 Instance, e nós descrevemos as propriedades dessa instância. No caso, nós estamos escrevendo simplesmente o tipo dessa instância e qual que é a imagem. Então, você pode observar aqui que nós estamos simplesmente descrevendo o que nós queremos. Nós não sabemos exatamente quais são os comandos que devem ser utilizados, nós não sabemos se uma API REST será chamada, ou se será utilizado ali, por exemplo, um command line para que essa instância seja criada. Então, é bem direto, nós descrevemos o estado. Temos a mesma coisa também aqui para Terraform, onde nós utilizamos essa linguagem HCL, que nós falaremos mais sobre ela adiante. Que funciona exatamente igual, é bem parecido, inclusive tem um pouco de JSON aqui nessa linguagem, é uma variação de JSON, onde nós especificamos aqui o nosso provider AWS, dizemos a região, e nós também especificamos aqui um recurso, uma AWS Instance, e damos o nome de exemplo aqui, poderia ser qualquer outro nome, qualquer outra chave, especificando também uma AMI, que seria a nossa imagem, e qual que é o tipo da instância. Então, é a variação, a mesma variação aqui deste exemplo que nós criamos com CloudFormation. Beleza, esses são dois exemplos de linguagens declarativas, onde nós especificamos o estado da nossa infraestrutura sem entender exatamente quais são os passos a serem executados. Então, se você roda esse comando, vai ser criado essa instância. Se você rodar novamente, a própria ferramenta é responsável por identificar o estado atual e verificar se foi alterado alguma coisa. Se não foi alterado nada, nenhuma ação será executada. Agora nós temos por outro lado, aqui do lado esquerdo, um código em TypeScript, onde a ideia é fazermos exatamente o que nós já fizemos aqui nesses dois exemplos anteriores. é fazermos exatamente o que nós já fizemos aqui nesses dois exemplos anteriores. Nós temos aqui, porém, uma abordagem imperativa. Nós estamos falando de um código em TypeScript, então nós temos todo o poder que o TypeScript nos oferece. Aqui primeiro nós obtemos a nossa VPC default, utilizando o próprio package da AWS. Em seguida nós criamos uma nova instância com o tipo T2 e nós especificamos qual é a imagem que nós queremos e também a VPC. A diferença aqui é que nós estamos obtendo primeiro a nossa VPC default e nós estamos setando essa VPC aqui diretamente na nossa instância, coisa que nós não fizemos para o código declarativo, simplesmente para simplificar aqui um pouquinho. Então nós temos essa abordagem diferente, mas que também vai atingir o mesmo resultado. Inclusive nós temos aqui a mesma chave, myec2instance, que nós temos aqui também no nosso arquivo YAML. Isso aqui não foi por acaso, tá? Tem um motivo, por quê? Quando nós falamos em CloudFormation, que é essa ferramenta específica da AWS, quando nós executamos esse código, nós executamos um código do CDK, que pode ser em várias linguagens diferentes, tá? Pode ser em JavaScript, pode ser em Python, pode ser em Java, em C Sharp, então você simplesmente escolhe a sua linguagem e escreve o seu código. E quando nós executamos, na verdade nós estamos sintetizando um código CloudFormation, então o que ele vai fazer aqui é nos dar esse output, ele vai criar um arquivo em email ou JSON, exatamente da forma como nós escrevemos aqui, e esse resultado vai ser algo parecido com o resultado que nós colocamos aqui em YAML. Então, se houver alguma diferença, se nós alterarmos qualquer coisa, ele vai criar um outro arquivo YAML, e quando nós fizermos o deploy, vai haver a comparação do estado atual com esse novo estado desejado que nós temos nesse arquivo YAML gerado. Então, é bem importante nós entendermos esse conceito para ficar claro o que está acontecendo. Agora nós temos o último exemplo também de código imperativo, porém agora utilizando Bash Script, que é esse último exemplo aqui, onde nós estamos executando a própria ferramenta, o próprio command line da AWS, dizendo AWS EC2 run instances, passando o nosso image ID, o próprio command line da AWS, dizendo AWS EC2 run instances passando o nosso image ID o nosso instance type e aqui um outro parâmetro que é a nossa key para nós acessarmos através de SSH essa instância também é uma abordagem imperativa nós poderíamos dizer também de infraestrutura como código mas nós temos uma diferença aqui muito grande. Entre esse último exemplo e todos os três exemplos anteriores que nós citamos. Esse código aqui, esse último código, ele não é idempotente. Por quê? O que seria isso? Se nós rodarmos várias vezes esse comando AWS C2 run instances, se nós rodarmos, por exemplo, 10 vezes, após essas 10 vezes nós teremos 10 instâncias rodando. Por outro lado, se nós rodarmos 10 vezes qualquer um desses outros códigos aqui, nós teremos uma única instância. Porque, novamente, nós temos essa comparação do estado atual com o novo estado que nós gostaríamos de atingir. É claro que nós poderíamos ter uma verificação aqui, tentar garantir essa idepotência através desse código imperativo, através do BASH, mas com certeza isso seria muito difícil e não valeria a pena. Você teria, por exemplo, que dar um nome único para essa instância e quando rodar novamente teria que verificar se já existe uma instância com esse nome, verificar ali quais são as propriedades que você precisa alterar, se você precisa derrubar aquela instância, destruir aquela instância e criar uma nova. Então, a complexidade para atingir isso, para fazer com que o seu código se torne idempotente, é muito grande. Então, idempotência é esse conceito que eu espero que tenha ficado claro já, que garante que múltiplas execuções de um mesmo conjunto de instruções, que a execução daquele código produza sempre o mesmo resultado final. Então, se você pega aquele seu script e executa 10 vezes, no final você deve ter exatamente os recursos que foram especificados ali, sem se preocupar com duplicidades. Se você alterou um recurso específico, você espera que somente aquele recurso específico seja alterado e somente aquela configuração específica seja alterada. E você espera também que aquela ferramenta possa entender que para uma determinada configuração é preciso que o recurso seja excluído e depois seja criado novamente. Então, idpotência na verdade é uma das características mais importantes de infraestrutura como código, tá? Sem ela seria muito difícil realmente nós implementarmos IAC nas nossas operações. Beleza? Então, por hoje é isso. Espero que tenha ficado claro todos esses conceitos. Eu espero que você tenha gostado. Vejo você na nossa próxima aula.