<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Felippe Deiró</title>
    <description>The latest articles on DEV Community by Felippe Deiró (@deirofelippe).</description>
    <link>https://dev.to/deirofelippe</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F848681%2F40bb41fc-96c1-4179-a18a-fca293e905c0.jpeg</url>
      <title>DEV Community: Felippe Deiró</title>
      <link>https://dev.to/deirofelippe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/deirofelippe"/>
    <language>en</language>
    <item>
      <title>CI/CD com GitHub Actions e teste local com Act</title>
      <dc:creator>Felippe Deiró</dc:creator>
      <pubDate>Tue, 10 Dec 2024 03:49:58 +0000</pubDate>
      <link>https://dev.to/deirofelippe/cicd-com-github-actions-teste-local-com-act-usando-container-e-alertas-com-telegram-4hn5</link>
      <guid>https://dev.to/deirofelippe/cicd-com-github-actions-teste-local-com-act-usando-container-e-alertas-com-telegram-4hn5</guid>
      <description>&lt;p&gt;O uso de pipeline é útil para automatizar o processo de deploy, executar testes automatizados, agendar tarefas, fazer verificação de segurança e muitos outros. Qualquer coisa que você queira executar de forma automática, consegue com uma pipeline.&lt;/p&gt;

&lt;p&gt;Vamos entender o funcionamento da pipeline do GitHub Actions, usando como base o arquivo do projeto &lt;a href="https://github.com/deirofelippe/agenda-telefonica" rel="noopener noreferrer"&gt;agenda-telefonica&lt;/a&gt;. O que será explicado?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Como usar container&lt;/li&gt;
&lt;li&gt;Criação do job de CI&lt;/li&gt;
&lt;li&gt;Criação do job de build do Docker&lt;/li&gt;
&lt;li&gt;Criação de jobs para aletar via Telegram em caso de sucesso e de falha da pipeline&lt;/li&gt;
&lt;li&gt;Usando Act para simular o GitHub Actions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O arquivo completo da pipeline pode ser vista &lt;a href="https://github.com/deirofelippe/agenda-telefonica/blob/main/.github/workflows/backend.yaml" rel="noopener noreferrer"&gt;aqui&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sumário
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ferramentas&lt;/li&gt;
&lt;li&gt;
Workflow

&lt;ul&gt;
&lt;li&gt;Events, filters e environment&lt;/li&gt;
&lt;li&gt;Job CI: container, testes e vulnerabilidades&lt;/li&gt;
&lt;li&gt;Sevices (containers)&lt;/li&gt;
&lt;li&gt;Steps&lt;/li&gt;
&lt;li&gt;Job Build: push Docker Hub&lt;/li&gt;
&lt;li&gt;Job Notify Success&lt;/li&gt;
&lt;li&gt;Job Notify Failure&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Simulando o GitHub Actions com Act localmente&lt;/li&gt;

&lt;li&gt;Conclusão&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Workflow
&lt;/h2&gt;

&lt;p&gt;Para criar uma pipeline e que ela execute no github, deve seguir esta estrutura:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;.github/
└── workflows/
    └── backend.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Todos os arquivos dentro do diretório &lt;code&gt;workflow/&lt;/code&gt; são pipelines. Todo o código mostrado, ficará dentro de &lt;code&gt;backend.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Para entender as palavras chave da pipeline, pode ser visto &lt;a href="https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions" rel="noopener noreferrer"&gt;aqui&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Events, filters e environment
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pipeline CI-CD do Backend&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;WORKDIR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./backend&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Primeiro definimos o nome do workflow com o &lt;code&gt;name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;O &lt;code&gt;env&lt;/code&gt; são as variáveis que serão usadas nos jobs desse workflow. Foi criado a variável WORKDIR que faz referência ao diretório que alguns comando serão executados.&lt;/p&gt;

&lt;p&gt;Agora temos o &lt;code&gt;on&lt;/code&gt; que define quais serão os eventos e filtros que irão dizer quando o workflow será executado. Esse bloco que dizer que o acionamento desse workflow acontecerá quando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ocorrer o evento &lt;code&gt;push&lt;/code&gt; na branch &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Ocorrer o evento &lt;code&gt;pull_request&lt;/code&gt; na branch &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Ocorrer o evento &lt;code&gt;workflow_dispatch&lt;/code&gt;, ou seja, quando for acionado manualmente como por exemplo pelo GitHub via Browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A chave &lt;code&gt;branches&lt;/code&gt; é um filtro e só permite a execução do workflow se o evento &lt;code&gt;push&lt;/code&gt; ou o &lt;code&gt;pull_request&lt;/code&gt; for direcionado para a branch &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Mais sobre &lt;a href="https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows" rel="noopener noreferrer"&gt;events e filters&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Job CI: container, testes e vulnerabilidades
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ci&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;21.7.3&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

    &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mysql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:8.0&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;3306:3306&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agenda_test&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
          &lt;span class="s"&gt;--health-cmd "mysqladmin ping"&lt;/span&gt;
          &lt;span class="s"&gt;--health-interval 10s&lt;/span&gt;
          &lt;span class="s"&gt;--health-timeout 5s&lt;/span&gt;
          &lt;span class="s"&gt;--health-retries 5&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.version }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.WORKDIR }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run test coverage&lt;/span&gt;
        &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.WORKDIR }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run test:cov:ci&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_REGION }}&lt;/span&gt;
          &lt;span class="na"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_ACCESS_KEY_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_SECRET_ACCESS_KEY }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check dependencies for vulnerabilities&lt;/span&gt;
        &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.WORKDIR }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm audit --audit-level=high&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O &lt;code&gt;jobs&lt;/code&gt; possui outros job, que nesse caso seria o &lt;code&gt;ci&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;notify-success&lt;/code&gt; e &lt;code&gt;notify-failure&lt;/code&gt;. O job ci será executado em uma máquina ubuntu na ultima versão, como diz a chave e o valor &lt;code&gt;runs-on: ubuntu-latest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As chaves &lt;code&gt;strategy.matrix.version&lt;/code&gt; com o valor &lt;code&gt;21.7.3&lt;/code&gt;, significa que esse job será executado 1x, mas pode ser executado mais vezes caso coloque mais versões. A versão é referente a do Nodejs que será configurado nessa máquina.&lt;/p&gt;

&lt;p&gt;Como os testes de integração usa o MySQL, precisamos de subir um container e a chave &lt;code&gt;services&lt;/code&gt; faz isso.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sevices (containers)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;

  &lt;span class="s"&gt;ci&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;

    &lt;span class="s"&gt;services&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mysql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:8.0&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;3306:3306&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
          &lt;span class="na"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agenda_test&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
          &lt;span class="s"&gt;--health-cmd "mysqladmin ping"&lt;/span&gt;
          &lt;span class="s"&gt;--health-interval 10s&lt;/span&gt;
          &lt;span class="s"&gt;--health-timeout 5s&lt;/span&gt;
          &lt;span class="s"&gt;--health-retries 5&lt;/span&gt;

    &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A chave &lt;code&gt;mysql&lt;/code&gt; é só um rótulo que damos para esse container. A imagem que será usada é do MySQL versão 8.0 &lt;code&gt;image: mysql:8.0&lt;/code&gt; e irá ouvir na porta 3306 (&lt;code&gt;ports:&lt;/code&gt; e &lt;code&gt;- 3306:3306&lt;/code&gt;). O &lt;code&gt;env&lt;/code&gt; diz as variáveis de ambiente que será usado e elas precisam ser as mesmas que foi usado no &lt;a href="https://github.com/deirofelippe/agenda-telefonica/blob/main/docker-compose.yaml" rel="noopener noreferrer"&gt;docker-compose.yaml&lt;/a&gt; para ser acessado pela aplicação.&lt;/p&gt;

&lt;p&gt;O &lt;code&gt;options&lt;/code&gt; são as flags ou options usadas no comando &lt;code&gt;docker container create&lt;/code&gt; (&lt;a href="https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idoptions" rel="noopener noreferrer"&gt;veja sobre aqui&lt;/a&gt;). Os options de healthcheck foi usado para dar continuidade na execução do job quando o container estiver pronto para uso. O que cada um significa?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--health-cmd "mysqladmin ping"&lt;/code&gt;: vai executar o comando &lt;code&gt;mysqladmin ping&lt;/code&gt; dentro do container e vai verificar se o status do mysql está como pronto ou não.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--health-interval 10s&lt;/code&gt;: irá executar o teste acima a cada 10s.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--health-timeout 5s&lt;/code&gt;: quando um teste é executado, deve esperar 5s para obter resposta dele.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--health-retries 5&lt;/code&gt;: o teste será executado no máximo 5x em caso de falhas.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Steps
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;

  &lt;span class="s"&gt;ci&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;

    &lt;span class="s"&gt;steps&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.version }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.WORKDIR }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run test coverage&lt;/span&gt;
        &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.WORKDIR }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run test:cov:ci&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_REGION }}&lt;/span&gt;
          &lt;span class="na"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_ACCESS_KEY_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_SECRET_ACCESS_KEY }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check dependencies for vulnerabilities&lt;/span&gt;
        &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.WORKDIR }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm audit --audit-level=high&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Temos finalmente as verificações de qualidade e segurança do código que são os &lt;code&gt;steps&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Primeiro step é o &lt;code&gt;uses&lt;/code&gt; vai executar uma action (um script) que seria o &lt;code&gt;actions/checkout@v4&lt;/code&gt;. Essa action da acesso ao workflow, o código que está no repositorio.&lt;/p&gt;

&lt;p&gt;Segundo step é a configuração do nodejs usando a action &lt;code&gt;actions/setup-node@v4&lt;/code&gt;. Na chave &lt;code&gt;with&lt;/code&gt;, declaramos o &lt;code&gt;node-version&lt;/code&gt; passando como valor o &lt;code&gt;${{ matrix.version }}&lt;/code&gt;, que pega a versão do node que colocamos no &lt;code&gt;strategy.matrix.version&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Terceiro step vai instalar as dependências das exatas versões que estão no package-lock.json. O &lt;code&gt;working-directory&lt;/code&gt; diz em qual diretório que vai ser executado esse step. Na chave &lt;code&gt;run&lt;/code&gt;, nós escrevemos o comando para ser executado, no cado o &lt;code&gt;npm ci&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Quarto step é a execução do teste e da cobertura de código. O comando usado é o &lt;code&gt;npm run test:cov:ci&lt;/code&gt; que vai setar &lt;code&gt;NODE_ENV=ci&lt;/code&gt; para usar as configurações do banco de dados do service, que já foi pré definida no código. O &lt;code&gt;env&lt;/code&gt; possui variáveis de configuração da AWS, porém só a região que precisa ser informada, as outras pode deixar como string vazia. A cobertura de código deve ser superior a 85%, como foi definido no jest.config.js.&lt;/p&gt;

&lt;p&gt;O &lt;code&gt;${{ secrets.VARIAVEL }}&lt;/code&gt; é uma forma de não deixar dados sensíveis à mostra. Mais a diante vou explicar como fazer essa configuração no Act e no GitHub.&lt;/p&gt;

&lt;p&gt;Quinto step é uma checagem de vulnerabilidades. Se tiver alguma vulnerabilidade com level high para cima terá um erro.&lt;/p&gt;

&lt;h3&gt;
  
  
  Job Build: push Docker Hub
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;

  &lt;span class="s"&gt;build&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;ci&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up QEMU&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-qemu-action@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Docker Buildx&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login to Docker Hub&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKERHUB_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKERHUB_TOKEN }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and push&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v6&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.WORKDIR }}/&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.WORKDIR }}/Dockerfile&lt;/span&gt;
          &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deirofelippe/agenda-telefonica-backend:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nesse job será feito o build e o push da imagem docker para o docker hub. O &lt;code&gt;build&lt;/code&gt; só será executado após o &lt;code&gt;ci&lt;/code&gt;, essa configuração é feita através do &lt;code&gt;needs&lt;/code&gt;. Por padrão o GitHub Actions executa os jobs em paralelo e caso queira criar dependência entre eles, torna-los sequencial, deve ser informado no needs.&lt;/p&gt;

&lt;p&gt;Primeiro step vai configurar o QEMU que emula plataformas para fazer o build.&lt;/p&gt;

&lt;p&gt;Segundo step configura o BuildX para fazer build da imagem.&lt;/p&gt;

&lt;p&gt;Terceiro step faz login no docker hub, que será armazenado a imagem.&lt;/p&gt;

&lt;p&gt;Quarto step é o que vai realmente fazer o build e o push. Dentro de &lt;code&gt;with&lt;/code&gt;, vamos configurar o &lt;code&gt;context&lt;/code&gt; que será a raiz do código, o &lt;code&gt;file&lt;/code&gt; que será o caminho para o Dockerfile, o &lt;code&gt;push&lt;/code&gt; que é setado como true, a &lt;code&gt;tags&lt;/code&gt; que vamos informar o nome e a tag da imagem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Job Notify Success
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;

  &lt;span class="s"&gt;notify-success&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ success() }}&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Notify Telegram If Success&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;TELEGRAM_MESSAGE='[Agenda Telefônica] Pipeline foi finalizada'&lt;/span&gt;

          &lt;span class="s"&gt;CURL_DATA=$(printf '{"chat_id":"%s","text":"%s"}' "${{ secrets.TELEGRAM_CHAT_ID }}" "$TELEGRAM_MESSAGE")&lt;/span&gt;

          &lt;span class="s"&gt;curl https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage \&lt;/span&gt;
            &lt;span class="s"&gt;--request POST \&lt;/span&gt;
            &lt;span class="s"&gt;--header 'Content-Type: application/json' \&lt;/span&gt;
            &lt;span class="s"&gt;--data "$CURL_DATA"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse job depende do build. A chave e valor &lt;code&gt;if: ${{ success() }}&lt;/code&gt;, permite a execução desse job somente se não houve erro no build. Caso tenha dado erro no ci, o build não será executado e por consequência o notify-success também não, pois o &lt;code&gt;success()&lt;/code&gt; retornará falso.&lt;/p&gt;

&lt;p&gt;Primeiro step vai notificar via Telegram que a pipeline não teve erro. Será feita uma requisição com o curl, informando a url da API do telegram, o token do bot, o id do chat e a mensagem.&lt;/p&gt;

&lt;p&gt;Saiba mais sobre o if &lt;a href="https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsif" rel="noopener noreferrer"&gt;aqui&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Job Notify Failure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;

  &lt;span class="s"&gt;notify-failure&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ failure() }}&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Notify Telegram If Failure&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;TELEGRAM_MESSAGE='[Agenda Telefônica] Falha na pipeline'&lt;/span&gt;

          &lt;span class="s"&gt;CURL_DATA=$(printf '{"chat_id":"%s","text":"%s"}' "${{ secrets.TELEGRAM_CHAT_ID }}" "$TELEGRAM_MESSAGE")&lt;/span&gt;

          &lt;span class="s"&gt;curl https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage \&lt;/span&gt;
            &lt;span class="s"&gt;--request POST \&lt;/span&gt;
            &lt;span class="s"&gt;--header 'Content-Type: application/json' \&lt;/span&gt;
            &lt;span class="s"&gt;--data "$CURL_DATA"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse job depende do build. A chave e valor &lt;code&gt;if: ${{ failure() }}&lt;/code&gt;, permite a execução desse job somente se &lt;strong&gt;HOUVER&lt;/strong&gt; erro no build.&lt;/p&gt;

&lt;p&gt;Primeiro step vai notificar via Telegram que a pipeline teve erro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simulando o GitHub Actions com Act localmente
&lt;/h2&gt;

&lt;p&gt;A instalação do Act pode ser vista &lt;a href="https://nektosact.com/installation/index.html" rel="noopener noreferrer"&gt;aqui&lt;/a&gt;. É preciso ter o Docker instalado.&lt;/p&gt;

&lt;p&gt;Como fiz a instalação usando o GitHub CLI, é só executar o comando &lt;code&gt;gh extension exec act --directory ./ --job ci --secret-file ./.env.act-secrets&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Vamos entender o que cada flag significa:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--directory&lt;/code&gt; ou &lt;code&gt;-C&lt;/code&gt; é o diretório onde está o diretório .github.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--job&lt;/code&gt; ou &lt;code&gt;-j&lt;/code&gt; é o job específico que deseja executar. Sim, se eu quiser, posso executar somente o job build, porem se tiver a keyword &lt;code&gt;needs&lt;/code&gt;, deve ser comentada senão suas dependências serão executadas.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--secret-file&lt;/code&gt; é o arquivo no formato .env que será usado como secrets para ser acessado usando &lt;code&gt;${{ secrets.VARIAVEL }}&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Executar sem a flag &lt;code&gt;--job&lt;/code&gt;, significa que toda a pipeline será executada.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1pm63s498oyr2ulwyk5c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1pm63s498oyr2ulwyk5c.png" alt="Image description" width="608" height="580"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Outras flags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--graph&lt;/code&gt; mostra a ordem de execução dos jobs em fromato de grafo&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--list&lt;/code&gt; ou &lt;code&gt;-l&lt;/code&gt; mostra os jobs, workflows e events associados&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xkmqz83x5d6ljt5scqr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xkmqz83x5d6ljt5scqr.png" alt="Image description" width="800" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flrswnidjx45o3vnkdcgy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flrswnidjx45o3vnkdcgy.png" alt="Image description" width="465" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Obrigado pela leitura :)&lt;/p&gt;

&lt;p&gt;Repositório do projeto: &lt;a href="https://github.com/deirofelippe/agenda-telefonica" rel="noopener noreferrer"&gt;https://github.com/deirofelippe/agenda-telefonica&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>githubactions</category>
      <category>act</category>
      <category>devops</category>
    </item>
    <item>
      <title>Terraform e Magalu Cloud: Provisionando Virtual Machine e Security Group</title>
      <dc:creator>Felippe Deiró</dc:creator>
      <pubDate>Sun, 24 Nov 2024 18:02:58 +0000</pubDate>
      <link>https://dev.to/deirofelippe/terraform-e-deploy-virtual-machine-e-security-group-na-magalu-cloud-kdo</link>
      <guid>https://dev.to/deirofelippe/terraform-e-deploy-virtual-machine-e-security-group-na-magalu-cloud-kdo</guid>
      <description>&lt;p&gt;A ideia desse projeto é provisionar, com Terraform, uma virtual machine e security groups na Magalu Cloud e fazer o deploy do docker compose para executar o backend (feito em Python e Flask) e banco de dados (PostgreSQL).&lt;/p&gt;

&lt;p&gt;O repositório no github para ver o código do &lt;a href="https://github.com/deirofelippe/magalucloud-terraform/tree/main/terraform" rel="noopener noreferrer"&gt;terraform&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sumário
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ferramentas&lt;/li&gt;
&lt;li&gt;
O que será povisionado?

&lt;ul&gt;
&lt;li&gt;Provider&lt;/li&gt;
&lt;li&gt;Virtual Machine&lt;/li&gt;
&lt;li&gt;Security Group&lt;/li&gt;
&lt;li&gt;Output&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Criação da infra e Deploy&lt;/li&gt;

&lt;li&gt;Conclusão&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ferramentas
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ferramenta&lt;/th&gt;
&lt;th&gt;Versão&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Terraform CLI&lt;/td&gt;
&lt;td&gt;1.6.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terraform&lt;/td&gt;
&lt;td&gt;3.6.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MGC&lt;/td&gt;
&lt;td&gt;0.30.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker Engine&lt;/td&gt;
&lt;td&gt;23.3.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker Compose&lt;/td&gt;
&lt;td&gt;2.29.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;3.13.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;17.1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  O que será povisionado?
&lt;/h2&gt;

&lt;p&gt;As referências que cada recurso recebe do outro está resumida no desenho abaixo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbwuuvmrqvnogge3f1o5q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbwuuvmrqvnogge3f1o5q.png" alt="Caixas que representam os recursos e as referência que recebe em forma de seta" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Provider
&lt;/h3&gt;

&lt;p&gt;Primeiro definimos o provider.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;mgc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MagaluCloud/mgc"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.30.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;random&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/random"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"3.6.2"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"mgc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;alias&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sudeste"&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"br-se1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No &lt;code&gt;provider&lt;/code&gt;, as regiões disponíveis são sudeste &lt;code&gt;br-se1&lt;/code&gt; e nordeste &lt;code&gt;br-ne1&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Virtual Machine
&lt;/h3&gt;

&lt;p&gt;A máquina virtual é equivalente ao AWS EC2, onde você tem controle do que quiser instalar para rodar sua solução, seja scripts, servidores web, qualquer linguagem e etc.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Dica:&lt;/strong&gt; faça testes localmente com o &lt;a href="https://developer.hashicorp.com/vagrant/install?product_intent=vagrant" rel="noopener noreferrer"&gt;Vagrant&lt;/a&gt; antes de provisionar algo pago. No futuro farei um post sobre o vagrant, mas por enquanto tenho um &lt;a href="https://github.com/deirofelippe/agenda-telefonica/blob/main/Vagrantfile" rel="noopener noreferrer"&gt;projeto no github&lt;/a&gt; que tem um Vagrantfile para ser usado.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"mgc_virtual_machine_instances"&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mgc_ssh_keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mgc_network_security_groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mgc-vm-instance"&lt;/span&gt;

  &lt;span class="nx"&gt;machine_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"BV1-1-10"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cloud-ubuntu-24.04 LTS"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;associate_public_ip&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;delete_public_ip&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

    &lt;span class="nx"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;security_groups&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mgc_network_security_groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
      &lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;ssh_key_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mgc_ssh_keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O &lt;code&gt;depends_on&lt;/code&gt; cria dependência com o recurso &lt;code&gt;mgc_ssh_keys&lt;/code&gt; e &lt;code&gt;mgc_network_security_groups&lt;/code&gt;. A máquina virtual atual só será criado após a criação de quem ele depende.&lt;/p&gt;

&lt;p&gt;No atributo &lt;code&gt;machine_type&lt;/code&gt;, o valor usado é o &lt;code&gt;BV1-1-10&lt;/code&gt;, que possui 1 vCPU e 1GB de RAM. Essas informações são encontradas ao criar a instância no site.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;image&lt;/code&gt; usada é o &lt;code&gt;cloud-ubuntu-24.04 LTS&lt;/code&gt;. Os valores desse atributo pode ser visto listando os detalhes da instância pelo cli.&lt;/p&gt;

&lt;p&gt;No atributo &lt;code&gt;network&lt;/code&gt;, temos o campo &lt;code&gt;associate_public_ip&lt;/code&gt; que indica que ao criar a instância, deverá ser cria um IP público e o campo &lt;code&gt;delete_public_ip&lt;/code&gt; indica que ao destruir a instância, o IP gerado também deverá ser deletado. Importante saber que o IP público tem custo.&lt;/p&gt;

&lt;p&gt;O atributo &lt;code&gt;interface&lt;/code&gt; possui o atributo &lt;code&gt;security_groups&lt;/code&gt; onde deverá ser atribuido o id do security group criado, que será explicado mais adiante.&lt;/p&gt;

&lt;p&gt;Por fim temos o campo &lt;code&gt;ssh_key_name&lt;/code&gt; que seu valor deve ser atribuido ao campo &lt;code&gt;name&lt;/code&gt; recurso &lt;code&gt;mgc_ssh_keys&lt;/code&gt;, não o seu id.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"random_string"&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;length&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
  &lt;span class="nx"&gt;special&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="nx"&gt;lower&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;min_numeric&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nx"&gt;min_lower&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"mgc_ssh_keys"&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mgc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sudeste&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mgc_ssh_key_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;random_string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;key&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"../mgc_ssh_key.pub"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No recurso &lt;code&gt;mgc_ssh_keys&lt;/code&gt; indicamos o &lt;code&gt;provider&lt;/code&gt; que ele pertence, o &lt;code&gt;name&lt;/code&gt; que será referenciado no recurso &lt;code&gt;mgc_virtual_machine_instances&lt;/code&gt; e que foi concatenado com uma string aleatória gerado pelo recurso &lt;code&gt;random_string&lt;/code&gt;. O conteúdo da chave pública deverá ser colocada no campo &lt;code&gt;key&lt;/code&gt;, a função file pega o conteúdo de um arquivo e atribui no campo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A geração da chave SSH está nessa seção.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Security Group
&lt;/h3&gt;

&lt;p&gt;O security group são regras de tráfego, por padrão na Magalu Cloud, todo o &lt;a href="https://docs.magalu.cloud/docs/network/overview" rel="noopener noreferrer"&gt;tráfego é bloqueado&lt;/a&gt; e deve ser configurado as permissões. Um resumo do que será permitido nesse arquivo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tráfego de entrada pela porta 22 (SSH) para o meu IP (&lt;code&gt;x.x.x.x/32&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Tráfego de entrada pela porta 3000 para a comunicação de qualquer IP com a aplicação (&lt;code&gt;0.0.0.0/0&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Tráfego de saída pela porta 80 (HTTP) para todos os hosts locais (&lt;code&gt;0.0.0.0/0&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Tráfego de entrada pela porta 80 (HTTP) para todos os IPs (&lt;code&gt;0.0.0.0/0&lt;/code&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"mgc_network_security_groups"&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mgc-network-security-group"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MGC Network Security Group"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sem mistério, essa é a criação do security group, as regras de permissão serão atribuidas a ela.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"http"&lt;/span&gt; &lt;span class="s2"&gt;"my_ip"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://ipv4.icanhazip.com"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"mgc_network_security_groups_rules"&lt;/span&gt; &lt;span class="s2"&gt;"allow_ingress_ssh"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;depends_on&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mgc_network_security_groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Permite tráfego SSH de entrada somente do meu IP"&lt;/span&gt;
  &lt;span class="nx"&gt;direction&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ingress"&lt;/span&gt;
  &lt;span class="nx"&gt;ethertype&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"IPv4"&lt;/span&gt;
  &lt;span class="nx"&gt;port_range_min&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
  &lt;span class="nx"&gt;port_range_max&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
  &lt;span class="nx"&gt;protocol&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
  &lt;span class="nx"&gt;remote_ip_prefix&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;trimspace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my_ip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response_body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/32"&lt;/span&gt;
  &lt;span class="nx"&gt;security_group_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mgc_network_security_groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O nosso primeiro recurso &lt;code&gt;mgc_network_security_groups_rules&lt;/code&gt; vai depender o recurso &lt;code&gt;mgc_network_security_groups&lt;/code&gt; referenciado no campo &lt;code&gt;depends_on&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A regra de entrada de tráfego será informada no campo &lt;code&gt;direction&lt;/code&gt; com o valor &lt;code&gt;ingress&lt;/code&gt;, pela porta &lt;code&gt;22&lt;/code&gt; definida no &lt;code&gt;port_range_min&lt;/code&gt; e &lt;code&gt;port_range_max&lt;/code&gt;, usando o protocolo &lt;code&gt;tcp&lt;/code&gt; e será usado também o protocolo &lt;code&gt;IPv4&lt;/code&gt; definido em &lt;code&gt;ethertype&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;O IP permitido, definido no &lt;code&gt;remote_ip_prefix&lt;/code&gt;, é o público de sua rede que será buscado dinâmicamente via requisição http para a url &lt;code&gt;http://ipv4.icanhazip.com&lt;/code&gt;. O &lt;code&gt;http&lt;/code&gt; é o data source que fará a requisição e retornará o IP público. A função &lt;code&gt;trimspace()&lt;/code&gt; irá tirar os espaços em branco da string e o /32 no final do IP vai dizer que é exatamente esse IP que será permitido, pois a máscara /32 permite somente um IP.&lt;/p&gt;

&lt;p&gt;Por fim, essa regra será aplicada no security group referenciado no campo &lt;code&gt;security_group_id&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"mgc_network_security_groups_rules"&lt;/span&gt; &lt;span class="s2"&gt;"allow_ingress_http"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;depends_on&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mgc_network_security_groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Permite tráfego HTTP de entrada para todos os IPs"&lt;/span&gt;
  &lt;span class="nx"&gt;direction&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ingress"&lt;/span&gt;
  &lt;span class="nx"&gt;ethertype&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"IPv4"&lt;/span&gt;
  &lt;span class="nx"&gt;port_range_min&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
  &lt;span class="nx"&gt;port_range_max&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
  &lt;span class="nx"&gt;protocol&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
  &lt;span class="nx"&gt;remote_ip_prefix&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;
  &lt;span class="nx"&gt;security_group_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mgc_network_security_groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nossa segunda regra permite o tráfego de entrada (&lt;code&gt;ingress&lt;/code&gt; no &lt;code&gt;direction&lt;/code&gt;), na porta &lt;code&gt;80&lt;/code&gt; (&lt;code&gt;port_range_min&lt;/code&gt; e &lt;code&gt;port_range_max&lt;/code&gt;) pelo protocolo &lt;code&gt;tcp&lt;/code&gt; (no &lt;code&gt;protocol&lt;/code&gt;). O IP seguirá o protocolo &lt;code&gt;IPv4&lt;/code&gt; (no &lt;code&gt;ethertype&lt;/code&gt;) e o IP em si será qualquer um &lt;code&gt;0.0.0.0/0&lt;/code&gt; (no &lt;code&gt;remote_ip_prefix&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"mgc_network_security_groups_rules"&lt;/span&gt; &lt;span class="s2"&gt;"allow_egress_http"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;depends_on&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mgc_network_security_groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Permite tráfego HTTP de saída"&lt;/span&gt;
  &lt;span class="nx"&gt;direction&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"egress"&lt;/span&gt;
  &lt;span class="nx"&gt;ethertype&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"IPv4"&lt;/span&gt;
  &lt;span class="nx"&gt;port_range_min&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
  &lt;span class="nx"&gt;port_range_max&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
  &lt;span class="nx"&gt;protocol&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
  &lt;span class="nx"&gt;remote_ip_prefix&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;
  &lt;span class="nx"&gt;security_group_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mgc_network_security_groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nossa terceira regra, dessa vez permite o tráfego de saída &lt;code&gt;egress&lt;/code&gt; (no &lt;code&gt;direction&lt;/code&gt;), ou seja, o processo que escutar na porta &lt;code&gt;80&lt;/code&gt; (&lt;code&gt;port_range_min&lt;/code&gt; e &lt;code&gt;port_range_max&lt;/code&gt;) poderá fazer requisição a qualquer recurso externo usando p protocolo &lt;code&gt;tcp&lt;/code&gt; (no &lt;code&gt;protocol&lt;/code&gt;). O IP seguirá o protocolo &lt;code&gt;IPv4&lt;/code&gt; (no &lt;code&gt;ethertype&lt;/code&gt;) e o IP será qualquer um &lt;code&gt;0.0.0.0/0&lt;/code&gt; (no &lt;code&gt;remote_ip_prefix&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"mgc_network_security_groups_rules"&lt;/span&gt; &lt;span class="s2"&gt;"allow_ingress_app"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;depends_on&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mgc_network_security_groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Permite tráfego do App de entrada"&lt;/span&gt;
  &lt;span class="nx"&gt;direction&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ingress"&lt;/span&gt;
  &lt;span class="nx"&gt;ethertype&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"IPv4"&lt;/span&gt;
  &lt;span class="nx"&gt;port_range_min&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;
  &lt;span class="nx"&gt;port_range_max&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;
  &lt;span class="nx"&gt;protocol&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
  &lt;span class="nx"&gt;remote_ip_prefix&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;
  &lt;span class="nx"&gt;security_group_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mgc_network_security_groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nossa quarta e última regra permite o tráfego de entrada (&lt;code&gt;ingress&lt;/code&gt; no &lt;code&gt;direction&lt;/code&gt;), na porta &lt;code&gt;3000&lt;/code&gt; (&lt;code&gt;port_range_min&lt;/code&gt; e &lt;code&gt;port_range_max&lt;/code&gt;), que será a porta de nossa aplicação, e usará protocolo &lt;code&gt;tcp&lt;/code&gt; (no &lt;code&gt;protocol&lt;/code&gt;). O IP seguirá o protocolo &lt;code&gt;IPv4&lt;/code&gt; (no &lt;code&gt;ethertype&lt;/code&gt;) e o IP será qualquer um &lt;code&gt;0.0.0.0/0&lt;/code&gt; (no &lt;code&gt;remote_ip_prefix&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Output
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"public_ip"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"IP Público"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mgc_virtual_machine_instances&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_address&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No fim do provisionamento da infraestrutura, será exibido pelo &lt;code&gt;output&lt;/code&gt; o IP público, através do campo &lt;code&gt;public_address&lt;/code&gt;, gerado para o acesso à máquina virtual.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criação da infra e Deploy
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Baixe o código &lt;code&gt;git clone https://github.com/deirofelippe/magalucloud-terraform.git&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;cd ./magalucloud-terraform&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Para gerar essa chave, use o comando &lt;code&gt;ssh-keygen -t rsa -b 2048 -f ./mgc_ssh_key -N ""&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Irá criar um par de chave pública e privada do tipo rsa &lt;code&gt;-t rsa&lt;/code&gt;, com 2048 bits &lt;code&gt;-b 2048&lt;/code&gt;, o nome do arquivo será mgc_ssh_key e será criado na pasta atual &lt;code&gt;-f ./mgc_ssh_key&lt;/code&gt;, por ultimo será gerado uma nova passphrase com valor vazio &lt;code&gt;-N ""&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;O comando irá gerar a chave privada &lt;code&gt;mgc_ssh_key&lt;/code&gt; e a chave pública &lt;code&gt;mgc_ssh_key.pub&lt;/code&gt;. A chave privada sempre ficará com você e a chave pública ficará no servidor desejado. O terraform vai fazer o upload e a configuração da chave pública no servidor automaticamente, não necessitando do uso do comando &lt;code&gt;ssh-copy-id&lt;/code&gt; para fazer o ssh remoto conceder o acesso sem o uso de senha.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Gere os arquivos de configuração: &lt;code&gt;terraform -chdir=./terraform init&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Veja o plano de execução: &lt;code&gt;terraform -chdir=./terraform plan&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Criar a infra no provedor: &lt;code&gt;terraform -chdir=./terraform apply -auto-approve&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copie o IP público gerado e cole nos comandos abaixo, trocando o &lt;code&gt;&amp;lt;IP-PÚBLICO&amp;gt;&lt;/code&gt; e execute-os.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxq9oywhvbwk36q47nusg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxq9oywhvbwk36q47nusg.png" alt="IP público gerado e mostrando onde fica no terminal" width="549" height="281"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Teste o acesso com o comando &lt;code&gt;ssh -i ./mgc_ssh_key -p 22 ubuntu@&amp;lt;IP-PÚBLICO&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;rsync -vahz -e 'ssh -i ./mgc_ssh_key -p 22' ./scripts ubuntu@&amp;lt;IP-PÚBLICO&amp;gt;:/home/ubuntu&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;executa por baixo dos panos o ssh com as options informado pelo &lt;code&gt;-e&lt;/code&gt;, vai transferir o diretório ./script para o host @:.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;rsync -vahz -e 'ssh -i ./mgc_ssh_key -p 22' ./.env ./docker-compose.prod.yaml ubuntu@&amp;lt;IP-PÚBLICO&amp;gt;:/home/ubuntu/scripts&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Execute o arquivo remotamente que irá instalar o docker e executar o docker compose: &lt;code&gt;ssh -i ./mgc_ssh_key -p 22 ubuntu@&amp;lt;IP-PÚBLICO&amp;gt; "bash -c './scripts/config-virtual-machine.sh'"&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;No arquivo &lt;a href="https://github.com/deirofelippe/magalucloud-terraform/blob/main/requests.http" rel="noopener noreferrer"&gt;./requests.http&lt;/a&gt; do repositório, possui as requests que podem ser feitas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5c40o2uf8k55yzi8hu4v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5c40o2uf8k55yzi8hu4v.png" alt="Fazendo requisição com Rest Client para criação de lançamento" width="800" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frsgs1zgfuf9jp6jgmf6o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frsgs1zgfuf9jp6jgmf6o.png" alt="Fazendo requisição com Rest Client para busca de lançamentos" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ao final destrua os recursos criados &lt;code&gt;terraform -chdir=./terraform destroy -auto-approve&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Obrigado pela leitura :)&lt;/p&gt;

&lt;p&gt;Repositório do projeto: &lt;a href="https://github.com/deirofelippe/magalucloud-terraform" rel="noopener noreferrer"&gt;https://github.com/deirofelippe/magalucloud-terraform&lt;/a&gt;&lt;/p&gt;

</description>
      <category>magalucloud</category>
      <category>terraform</category>
      <category>devops</category>
      <category>docker</category>
    </item>
    <item>
      <title>Usando a API da Zoho CRM</title>
      <dc:creator>Felippe Deiró</dc:creator>
      <pubDate>Sat, 14 Oct 2023 23:28:38 +0000</pubDate>
      <link>https://dev.to/deirofelippe/usando-a-api-da-zoho-crm-3n34</link>
      <guid>https://dev.to/deirofelippe/usando-a-api-da-zoho-crm-3n34</guid>
      <description>&lt;h2&gt;
  
  
  Gerando os Tokens
&lt;/h2&gt;

&lt;p&gt;Código implementado em nodejs e insomnia está no &lt;a href="https://github.com/deirofelippe/post-zoho-api"&gt;repositório&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.zoho.com/crm/developer/docs/api/v4/oauth-overview.html"&gt;Documentação&lt;/a&gt; para saber mais detalhes da geração dos tokens.&lt;/p&gt;

&lt;p&gt;Para gerar tokens é preciso de:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Client ID: id da aplicação que fará a requisição&lt;/li&gt;
&lt;li&gt;Client Secret: secret da aplicação que fará a requisição&lt;/li&gt;
&lt;li&gt;Authentication code: código temporário (3 a 10 minutos) para gerar o access token e refresh token&lt;/li&gt;
&lt;li&gt;Access Token: da acesso temporário à API (1 hora)&lt;/li&gt;
&lt;li&gt;Refresh Token: atualiza o access token quando expirar&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Obs: Client pode ser qualquer aplicação baseado na web (postman, insomnia, servidor, curl e etc)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Antes de gerar o Client ID e Secret, precisa registrar o client &lt;a href="https://api-console.zoho.com/"&gt;aqui&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Crie um Self Client para teste.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gxpA2PxX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cbl4uyphxgelxd4xmgl0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gxpA2PxX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cbl4uyphxgelxd4xmgl0.png" alt="Image description" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pQHOmJSi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gqa7rnv44495cy2r01bq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pQHOmJSi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gqa7rnv44495cy2r01bq.png" alt="Image description" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ap8IRmu4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ions7mpf845z77k1dy9a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ap8IRmu4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ions7mpf845z77k1dy9a.png" alt="Image description" width="800" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AbJKZ4g0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kmwwui2297ru3f3noicc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AbJKZ4g0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kmwwui2297ru3f3noicc.png" alt="Image description" width="800" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Já conseguimos o Client ID e o Client Secret na aba 'Client Secret'.&lt;/p&gt;

&lt;p&gt;Na aba 'Generate Code', coloque o escopo que será usado. Scope são permissões do que o client poderá fazer na API. Ele pode somente ler dados? inserir? deletar? pode ler somente dados de um módulo específico como Leads, Products ou Contracts?&lt;/p&gt;

&lt;p&gt;Mais informações sobre &lt;a href="https://www.zoho.com/crm/developer/docs/api/v4/scopes.html"&gt;módulos aqui&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vou dar permissão somente para ler os módulos e configurações: &lt;code&gt;ZohoCRM.modules.READ,ZohoCRM.settings.ALL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UsWBXGvt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tsikbamwx7cxaybnr7ek.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UsWBXGvt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tsikbamwx7cxaybnr7ek.png" alt="Image description" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O time duration é o tempo que o authentication code vai durar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LhKr2YBg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/12sukdkzj5b7i4ugjf6c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LhKr2YBg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/12sukdkzj5b7i4ugjf6c.png" alt="Image description" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Selecione CRM e depois production.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Uwpo0d5B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bmhm0bfeie11nab0fafe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Uwpo0d5B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bmhm0bfeie11nab0fafe.png" alt="Image description" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Com as chaves, agora precisamos gerar o access token e refresh token. Para isso vamos para o insomnia.&lt;/p&gt;

&lt;p&gt;Como está na &lt;a href="https://www.zoho.com/crm/developer/docs/api/v4/access-refresh.html"&gt;documentação&lt;/a&gt;, precisamos da url a seguir para gerar os tokens:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://accounts.zoho.com/oauth/v2/token"&gt;https://accounts.zoho.com/oauth/v2/token&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Precisamos dos seguintes parametros e o tipo de body deve ser &lt;strong&gt;form-data&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q6gLoxmj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b6o9cnoi99lgzouavwpj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q6gLoxmj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b6o9cnoi99lgzouavwpj.png" alt="Image description" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--38v5wNE0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6v5l9ckpiqefddxfw0zm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--38v5wNE0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6v5l9ckpiqefddxfw0zm.png" alt="Image description" width="800" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pronto! Agora temos os tokens. Não se esqueça que o body deve ser do tipo &lt;em&gt;form-data&lt;/em&gt; e não &lt;em&gt;json&lt;/em&gt; ou qualquer outro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Endpoints
&lt;/h2&gt;

&lt;p&gt;Cada requisição ao endpoint a partir de agora, vamos precisar usar o access token. Que seguirá o padrão: &lt;code&gt;Authorization: Zoho-oauthtoken 1000.xxx...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZeKM-N0q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9dfdc1epgxp4e42gxqa9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZeKM-N0q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9dfdc1epgxp4e42gxqa9.png" alt="Image description" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  GET Modules
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.zoho.com/crm/developer/docs/api/v4/modules-api.html"&gt;https://www.zoho.com/crm/developer/docs/api/v4/modules-api.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Buscando os módulos, temos vários objetos e atributos. Os mais importantes por agora é o id e o api_name, que é por onde vamos buscar os registros em um módulo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3NTQteQK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gyv18li8taajatk5s0p6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3NTQteQK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gyv18li8taajatk5s0p6.png" alt="Image description" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  GET Module Records
&lt;/h3&gt;

&lt;p&gt;A url dos registros buscados, segue o seguinte padrão (&lt;a href="https://www.zoho.com/crm/developer/docs/api/v4/modules-api.html"&gt;documentação!&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.zohoapis.com/crm/v4/Leads"&gt;https://www.zohoapis.com/crm/v4/Leads&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para buscar campos especificos é através do parametro fields (&lt;a href="https://www.zoho.com/crm/developer/docs/api/v4/get-records.html"&gt;documentation?&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;?fields=id&amp;amp;converted=true&amp;amp;per_page=3&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Busquei por id, pois todo registro de todo módulo deve ter id.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--94YvfXR_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nc9jdnpuwvgx6l7a28rs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--94YvfXR_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nc9jdnpuwvgx6l7a28rs.png" alt="Image description" width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para saber com mais detalhes quais são os campos que um módulo tem, busque o registro do registro do módulo pelo id, como será mostrado a seguir.&lt;/p&gt;

&lt;h3&gt;
  
  
  GET Module Records por ID
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.zoho.com/crm/developer/docs/api/v4/get-records.html"&gt;Documentação :)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F5borX3w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kma29tm09az59f6mmm66.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F5borX3w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kma29tm09az59f6mmm66.png" alt="Image description" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Caso queira buscar campos especificos, pode usar o &lt;code&gt;fields&lt;/code&gt; no final da url:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.zohoapis.com/crm/v4/Leads/4361639000...44014?fields=State,First_Name"&gt;https://www.zohoapis.com/crm/v4/Leads/4361639000...44014?fields=State,First_Name&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Um outro endpoint muito útil é o &lt;a href="https://www.zoho.com/crm/developer/docs/api/v4/search-records.html"&gt;Search Records&lt;/a&gt;, onde pode fazer um consulta registros quando um campo for igual a algo.&lt;/p&gt;

&lt;p&gt;Chegamos ao fim. Espero ter ajudado.&lt;/p&gt;

</description>
      <category>zoho</category>
      <category>zohocrm</category>
      <category>api</category>
    </item>
  </channel>
</rss>
