<?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: Laysa Uchoa</title>
    <description>The latest articles on DEV Community by Laysa Uchoa (@laysauchoa).</description>
    <link>https://dev.to/laysauchoa</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%2F823590%2F0083d6c3-9e3e-4848-91d3-e071d404a25a.png</url>
      <title>DEV Community: Laysa Uchoa</title>
      <link>https://dev.to/laysauchoa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/laysauchoa"/>
    <language>en</language>
    <item>
      <title>🔐 Google Cloud Authentication with Workload Identity Federation for GitHub Actions</title>
      <dc:creator>Laysa Uchoa</dc:creator>
      <pubDate>Thu, 21 Dec 2023 21:04:57 +0000</pubDate>
      <link>https://dev.to/laysauchoa/google-cloud-authentication-with-workload-identity-federation-for-github-actions-601</link>
      <guid>https://dev.to/laysauchoa/google-cloud-authentication-with-workload-identity-federation-for-github-actions-601</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the past, when you wanted to allow an application like GitHub to access your Google Cloud resources, you had to follow a few steps. First, you would create a separate IAM service account for each application. Then, you would download a long-lasting JSON service account key and store it in your GitHub repository's secrets. These service account keys could then be used to make GCP API calls from your application.&lt;/p&gt;

&lt;p&gt;Managing these service account keys involved secure storage, regular rotation, and continuous monitoring to protect your Google resources from potential malicious actors. If a malicious actor gained access to your keys, they could maintain prolonged access to your Google Cloud resources. This is why this approach is not recommended.&lt;/p&gt;

&lt;p&gt;So, what's the solution? The answer is Workload Identity Federation (WIF).&lt;/p&gt;

&lt;p&gt;Through the Workload Identity Federation (WIF), you can grant external identities IAM roles, by allowing them to impersonate service accounts that have these roles.&lt;/p&gt;

&lt;p&gt;This approach is superior for the following reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You no longer need to manage key rotation or store service account keys.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The credentials have a short default lifespan and are configurable. This approach simplifies the management and security aspects associated with service account keys.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Steps to Set Up Workload Identity Federation 
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Create a Workload Identity Pool
&lt;/h3&gt;

&lt;p&gt;According to the documentation, a Workload Identity Pool (WIP) is an entity that lets you manage external identities. In general, GCP recommends creating a new pool for each non-Google Cloud environment that needs to access Google Cloud resources, such as development, staging, or production environments. &lt;/p&gt;

&lt;p&gt;In this step, we are defining the locals to be used, and we are using a unique name for the Workload Identity Pool (WIP).&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="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;project_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"PROJECT_ID"&lt;/span&gt;
  &lt;span class="nx"&gt;service_account&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SERVICE_ACCOUNT"&lt;/span&gt;
  &lt;span class="nx"&gt;organization&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"YOUR_ORGANISATION_HERE"&lt;/span&gt;
  &lt;span class="nx"&gt;repository&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"YOUR_REPOSITORY_HERE"&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;"google_iam_workload_identity_pool"&lt;/span&gt; &lt;span class="s2"&gt;"github_pool"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;project&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_id&lt;/span&gt;
  &lt;span class="nx"&gt;workload_identity_pool_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github-pool-oidc"&lt;/span&gt;
  &lt;span class="nx"&gt;display_name&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GitHub pool"&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;"Identity pool for GitHub deployments"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Configure the Workload Identity Pool Provider
&lt;/h3&gt;

&lt;p&gt;In this step, we configure the Workload Identity Pool provider. Your provider should have a unique name. Then, we can set the "Attribute Mapping" to map the GitHub Actions attributes to GCP identity attributes. &lt;/p&gt;

&lt;p&gt;Here, we can also define an "Attribute Condition" which is recommended &lt;strong&gt;to be more secure.&lt;/strong&gt; This condition specifies when the identity provider should be used based on GitHub repository attributes, for example, repository name, repository owner, actor, and others. &lt;/p&gt;

&lt;p&gt;We also set up the OIDC settings by providing the correct issuer URI.&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;"google_iam_workload_identity_pool_provider"&lt;/span&gt; &lt;span class="s2"&gt;"github"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;project&lt;/span&gt;                            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_id&lt;/span&gt;
  &lt;span class="nx"&gt;workload_identity_pool_id&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;google_iam_workload_identity_pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;github_pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workload_identity_pool_id&lt;/span&gt;
  &lt;span class="nx"&gt;workload_identity_pool_provider_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github-provider"&lt;/span&gt;

  &lt;span class="nx"&gt;attribute_mapping&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"attribute.aud"&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assertion.aud"&lt;/span&gt;
    &lt;span class="s2"&gt;"google.subject"&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assertion.sub"&lt;/span&gt;
    &lt;span class="s2"&gt;"attribute.sub"&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assertion.sub"&lt;/span&gt;
    &lt;span class="s2"&gt;"attribute.actor"&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assertion.actor"&lt;/span&gt;
    &lt;span class="s2"&gt;"attribute.repository"&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assertion.repository"&lt;/span&gt;
    &lt;span class="s2"&gt;"attribute.repository_owner"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assertion.repository_owner"&lt;/span&gt;
    &lt;span class="s2"&gt;"attribute.ref"&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assertion.ref"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# If you want to restrict to organisation, use:&lt;/span&gt;
  &lt;span class="c1"&gt;# "assertion.repository_owner==\"${local.organization}\""&lt;/span&gt;

  &lt;span class="c1"&gt;# For more than one repository, use:&lt;/span&gt;
  &lt;span class="c1"&gt;# "assertion.repository==\"ORG/repository-1\" || assertion.repository==\"ORG/repository-2\""&lt;/span&gt;

  &lt;span class="nx"&gt;attribute_condition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assertion.repository==&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;organization&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repository&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;


  &lt;span class="nx"&gt;oidc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;allowed_audiences&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="nx"&gt;issuer_uri&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://token.actions.githubusercontent.com"&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;h3&gt;
  
  
  Step 3: Configure IAM Roles
&lt;/h3&gt;

&lt;p&gt;In this step, we configure the GCP IAM role for our service account. This can be done by setting the roles/iam.workloadIdentityUser to the service account we want to impersonate.&lt;/p&gt;

&lt;p&gt;What this role does is to allow the associated service account to assume identities from a trusted identity provider to access Google Cloud resources which this service account has permission for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Full Terraform code
&lt;/h3&gt;

&lt;p&gt;To sum up, this is the full terraform code we needed for the setup.&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="c1"&gt;# wif.tf&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;project_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"PROJECT_ID"&lt;/span&gt;
  &lt;span class="nx"&gt;service_account&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SERVICE_ACCOUNT"&lt;/span&gt;
  &lt;span class="nx"&gt;project_name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"YOUR_PROJECT_NAME"&lt;/span&gt;
  &lt;span class="nx"&gt;organization&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"YOUR_ORGANISATION_HERE"&lt;/span&gt;
  &lt;span class="nx"&gt;repository&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"YOUR_REPOSITORY_HERE"&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;"google_iam_workload_identity_pool"&lt;/span&gt; &lt;span class="s2"&gt;"github_pool"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;project&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_id&lt;/span&gt;
  &lt;span class="nx"&gt;workload_identity_pool_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github-pool-oidc"&lt;/span&gt;
  &lt;span class="nx"&gt;display_name&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GitHub pool"&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;"Identity pool for GitHub deployments"&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;"google_iam_workload_identity_pool_provider"&lt;/span&gt; &lt;span class="s2"&gt;"github"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;project&lt;/span&gt;                            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_id&lt;/span&gt;
  &lt;span class="nx"&gt;workload_identity_pool_id&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;google_iam_workload_identity_pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;github_pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workload_identity_pool_id&lt;/span&gt;
  &lt;span class="nx"&gt;workload_identity_pool_provider_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github-provider"&lt;/span&gt;

  &lt;span class="nx"&gt;attribute_mapping&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"attribute.aud"&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assertion.aud"&lt;/span&gt;
    &lt;span class="s2"&gt;"google.subject"&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assertion.sub"&lt;/span&gt;
    &lt;span class="s2"&gt;"attribute.sub"&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assertion.sub"&lt;/span&gt;
    &lt;span class="s2"&gt;"attribute.actor"&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assertion.actor"&lt;/span&gt;
    &lt;span class="s2"&gt;"attribute.repository"&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assertion.repository"&lt;/span&gt;
    &lt;span class="s2"&gt;"attribute.repository_owner"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assertion.repository_owner"&lt;/span&gt;
    &lt;span class="s2"&gt;"attribute.ref"&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assertion.ref"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# If you want to restrict the organization, use the:&lt;/span&gt;
  &lt;span class="c1"&gt;# "assertion.repository_owner==\"${local.organization}\""&lt;/span&gt;

  &lt;span class="c1"&gt;# For more than one repository, use:&lt;/span&gt;
  &lt;span class="c1"&gt;# "assertion.repository==\"ORG/repository-1\" || assertion.repository==\"ORG/repository-2\""&lt;/span&gt;

  &lt;span class="c1"&gt;# For one repository:&lt;/span&gt;
  &lt;span class="nx"&gt;attribute_condition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"assertion.repository==&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;organization&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repository&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="nx"&gt;oidc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;allowed_audiences&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="nx"&gt;issuer_uri&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://token.actions.githubusercontent.com"&lt;/span&gt;
  &lt;span class="p"&gt;}&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;"google_service_account_iam_member"&lt;/span&gt; &lt;span class="s2"&gt;"workload_identity_user"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;service_account_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'projects/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;/serviceAccounts/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service_account&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;#Replace with your service account&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"roles/iam.workloadIdentityUser"&lt;/span&gt;
  &lt;span class="nx"&gt;member&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"principalSet://iam.googleapis.com/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;google_iam_workload_identity_pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;github_pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/attribute.repository_owner/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;organization&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Setup GitHub Actions Workflow
&lt;/h3&gt;

&lt;p&gt;You need to save as secrets the &lt;code&gt;provider_name&lt;/code&gt; as this is sensitive information. It has the following format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WORKLOAD_IDENTITY_PROVIDER = projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool-oidc/providers/github-provider
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below is an example of a workflow using GCP WIF OpenID Connect (OIDC) in GitHub Actions.&lt;br&gt;
&lt;/p&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;Example GCP WIF with GitHub Actions&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="s"&gt;setup-wif-oidc&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;job_id&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;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;read'&lt;/span&gt;
      &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;write'&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# actions/checkout MUST come before auth&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;actions/checkout@v3'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auth"&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authenticate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Google&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Cloud"&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;google-github-actions/auth@v2"&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;token_format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;access_token"&lt;/span&gt;
          &lt;span class="na"&gt;workload_identity_provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}&lt;/span&gt;
          &lt;span class="na"&gt;service_account&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;YOUR_SERVICE_ACCOUNT&lt;/span&gt;
          &lt;span class="na"&gt;export_environment_variables&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;audience&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GCP_POOL_AUDIENCE }}&lt;/span&gt;
          &lt;span class="na"&gt;create_credentials_file&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;access_token_lifetime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;
      &lt;span class="c1"&gt;# ... further steps are automatically authenticated&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Set&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;up&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Cloud&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;SDK"&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;google-github-actions/setup-gcloud@v1"&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;390.0.0"&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 currently authenticated user&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;gcloud auth list&lt;/span&gt;

      &lt;span class="c1"&gt;# interact with google cloud&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 gcloud&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;gcloud storage buckets list&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The workflow authenticates with Google Cloud, sets up necessary tools, and performs a listing of storage buckets within the Google Cloud environment. &lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: GitHub Action in Action
&lt;/h3&gt;

&lt;p&gt;Find here how the output of the run looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmit5azygi9h47ta0fixm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmit5azygi9h47ta0fixm.png" alt="GitHub Action running" width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  More Ressources
&lt;/h2&gt;

&lt;p&gt;For more information on best practices for using WIF on Google Cloud: &lt;a href="https://cloud.google.com/iam/docs/best-practices-for-using-workload-identity-federation"&gt;https://cloud.google.com/iam/docs/best-practices-for-using-workload-identity-federation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;-&lt;br&gt;
👋 Hello, I'm Laysa, a developer and cloud engineer, a public speaker, and a knowledge sharer as I go along.&lt;/p&gt;

&lt;p&gt;♻️ Share this article, if you like the content.&lt;/p&gt;

&lt;p&gt;💜 &lt;a href="https://linktr.ee/laysauchoa"&gt;Find me everywhere&lt;/a&gt; | &lt;a href="https://twitter.com/laysauchoa"&gt;X&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/laysauchoa/"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>githubactions</category>
      <category>gcp</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>Get OpenSearch mapping with Python</title>
      <dc:creator>Laysa Uchoa</dc:creator>
      <pubDate>Sat, 16 Dec 2023 15:58:11 +0000</pubDate>
      <link>https://dev.to/laysauchoa/get-data-mapping-with-python-4b71</link>
      <guid>https://dev.to/laysauchoa/get-data-mapping-with-python-4b71</guid>
      <description>&lt;p&gt;When no data structure is specified when loading the data, OpenSearch uses dynamic mapping to automatically detect the fields. To check the mapping definition of your data, OpenSearch client provides a function called &lt;code&gt;get_mapping&lt;/code&gt; as shown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pprint&lt;/span&gt;

    &lt;span class="n"&gt;INDEX_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;epicurious-recipes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;mapping_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_mapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INDEX_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Find index doc_type
&lt;/span&gt;    &lt;span class="n"&gt;doc_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mapping_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;INDEX_NAME&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mappings&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;())[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mapping_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;INDEX_NAME&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mappings&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;doc_type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;pprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;pprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should be able to see the fields's output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;calories&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;categories&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;desc&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;directions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fat&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ingredients&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;protein&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rating&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sodium&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the mapping with the fields and their respective types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
        {'calories': {'type': 'float'},
         'categories': {'fields': {'keyword': {'ignore_above': 256, 'type': 'keyword'}},
                        'type': 'text'},
         'date': {'type': 'date'},
         'desc': {'fields': {'keyword': {'ignore_above': 256, 'type': 'keyword'}},
                  'type': 'text'},
         'directions': {'fields': {'keyword': {'ignore_above': 256, 'type': 'keyword'}},
                        'type': 'text'},
         'fat': {'type': 'float'},
         'ingredients': {'fields': {'keyword': {'ignore_above': 256,
                                                'type': 'keyword'}},
                         'type': 'text'},
         'protein': {'type': 'float'},
         'rating': {'type': 'float'},
         'sodium': {'type': 'float'},
         'title': {'fields': {'keyword': {'ignore_above': 256, 'type': 'keyword'}},
                   'type': 'text'}}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read more about OpenSearch mapping in the &lt;a href="https://opensearch.org/docs/latest/opensearch/rest-api/index-apis/create-index/#mappings"&gt;official OpenSearch documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opensearch</category>
      <category>elasticsearch</category>
      <category>python</category>
      <category>mapping</category>
    </item>
    <item>
      <title>A Decade of Docker! 🐋</title>
      <dc:creator>Laysa Uchoa</dc:creator>
      <pubDate>Fri, 17 Nov 2023 19:09:24 +0000</pubDate>
      <link>https://dev.to/laysauchoa/a-decade-of-docker-3828</link>
      <guid>https://dev.to/laysauchoa/a-decade-of-docker-3828</guid>
      <description>&lt;p&gt;Can you believe Docker first showed up in a lightning talk that got everyone talking? Today, I'd like to talk about the history of Docker 🐋&lt;/p&gt;

&lt;h2&gt;
  
  
  The Birth of Docker
&lt;/h2&gt;

&lt;p&gt;It all began over a decade ago. Docker, introduced in a lightning talk at PyCon US in 2013, was the brainchild of Solomon Hykes, an entrepreneur and programmer. Hykes, deeply immersed in the world of software development, sought to address a common challenge faced by developers: complexity and inconsistency in deploying applications across various environments.&lt;/p&gt;

&lt;p&gt;So, what was the problem? It was the notorious "it worked on my machine issue." Developers often encountered scenarios where their code worked perfectly on their development environment but failed to function correctly when deployed elsewhere. This inconsistency was a significant pain point in software development.&lt;/p&gt;

&lt;p&gt;Hykes envisioned a platform that could package applications and their dependencies into lightweight, portable containers. These containers, he believed, could be effortlessly deployed and executed consistently on any system, from a developer's laptop to a production server.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Birth of "Docker"
&lt;/h2&gt;

&lt;p&gt;The name "Docker" was chosen to evoke the idea of shipping containers, which are standardized, easily transportable, and capable of holding a variety of goods 📦. Docker containers, in a similar vein, could hold software applications and their dependencies, making them transportable across different computing environments.&lt;/p&gt;

&lt;p&gt;With Docker, developers could now create, share, and run applications in isolated, reproducible containers, eliminating the notorious "it works on my machine" problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker's Journey Unfolds
&lt;/h2&gt;

&lt;p&gt;Docker's journey unfolds as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2013&lt;/strong&gt;: Solomon Hykes introduces Docker in a lightning talk at PyCon US, marking its inception.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2013-2014&lt;/strong&gt;: Docker gains early adopters and community support as developers recognize its potential.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2015&lt;/strong&gt;: Docker, Inc. is formed to provide commercial support and services around Docker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Late 2010s&lt;/strong&gt;: Docker becomes a fundamental tool in the DevOps toolkit, used by organizations worldwide.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2020s&lt;/strong&gt;: Docker continues to evolve and adapt to the changing needs of the software development industry.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Developer's Dream
&lt;/h2&gt;

&lt;p&gt;Docker revolutionized the way developers worked. It allowed developers to concentrate on writing code while abstracting away many of the infrastructure complexities. No longer did they have to worry about the intricate details of setting up development and production environments. Docker containers provide a consistent and efficient way to manage applications and their dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  First World Appearance of Docker
&lt;/h2&gt;

&lt;p&gt;Here you can find the first world appearance of Docker 📺 ⭐ &lt;a href="https://lnkd.in/e2dsHsA3"&gt;Docker Lightning Talk at PyCon US 2013&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Happy learning! 👋
&lt;/h2&gt;

&lt;p&gt;I'm a Cloud Engineer at Nordcloud.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>containers</category>
      <category>programming</category>
      <category>devops</category>
    </item>
    <item>
      <title>Automating Terraform Deployments with GitHub Actions: A Step-by-Step Guide</title>
      <dc:creator>Laysa Uchoa</dc:creator>
      <pubDate>Thu, 02 Nov 2023 21:01:43 +0000</pubDate>
      <link>https://dev.to/laysauchoa/automating-terraform-deployments-with-github-actions-a-step-by-step-guide-122n</link>
      <guid>https://dev.to/laysauchoa/automating-terraform-deployments-with-github-actions-a-step-by-step-guide-122n</guid>
      <description>&lt;p&gt;I'm a Cloud Engineer at Nordcloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;GitHub Actions is a potent automation tool that elevates your software development processes by responding to events and executing predefined workflows. &lt;/p&gt;

&lt;p&gt;In this article, you will learn how to use GitHub Actions to deploy your Terraform code to production. 🔄 &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Scenario&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You've been working on your infrastructure as code (IaC) using Terraform. Now, you're preparing to implement your changes in a production environment. You need these deployments to be error-free, follow best practices, and have appropriate approval procedures. GitHub Actions can be effective for this by using a manual step before deploying to production. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Setup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Setting up your Terraform deployment workflow with GitHub Actions involves several important steps. In this section, we'll walk you through each step in detail.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1) Configure GitHub Actions trigger and permissions&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The first step in automating your Terraform deployments is to configure the GitHub Actions workflow trigger and permissions. This configuration determines when and how the workflow will be initiated.&lt;/p&gt;

&lt;p&gt;Here's the YAML code to configure GitHub Actions:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="c1"&gt;#.github/workflows/deploy-to-prod.yaml&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;Terraform PROD Deployment&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="s"&gt;main&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&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="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
  &lt;span class="na"&gt;issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This configuration accomplishes two key things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trigger Events&lt;/strong&gt;: It specifies that the workflow should be triggered when changes are pushed to the "main" branch and when manually initiated using the "workflow_dispatch" event.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permissions&lt;/strong&gt;: It defines the necessary permissions for the workflow, ensuring that it can read repository contents and write issues.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2) Add initial Setup&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let's examine the workflow steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Checkout Git Repository&lt;/strong&gt;: This step involves checking out your Git repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authenticate with your cloud provider&lt;/strong&gt;: This is crucial if you need to deploy resources, like a Terraform deployment, on your cloud provider. In this example, we are using GCP (Google Cloud Provider) for the deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;jobs&lt;/span&gt;:
  gcp-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Git Repository
        uses: actions/checkout@v3.0.0
        with:
          fetch-depth: 0

      - &lt;span class="nb"&gt;id&lt;/span&gt;: &lt;span class="s2"&gt;"auth"&lt;/span&gt;
        name: &lt;span class="s2"&gt;"Authenticate to Google Cloud"&lt;/span&gt;
        uses: &lt;span class="s2"&gt;"google-github-actions/auth@v1"&lt;/span&gt;
        with:
          credentials_json: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;{ secrets.GOOGLE_CREDENTIALS &lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
          create_credentials_file: &lt;span class="nb"&gt;true
          &lt;/span&gt;export_environment_variables: &lt;span class="nb"&gt;true&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;3) Check the Terraform code&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Ensure Terraform code works in your local setup:&lt;/strong&gt; Do this before using it directly in your workflow  to avoid errors related to your Terraform code itself rather than GitHub Actions errors.&lt;/p&gt;

&lt;p&gt;You can try your terraform commands such as &lt;code&gt;init&lt;/code&gt;, &lt;code&gt;plan&lt;/code&gt; and &lt;code&gt;apply&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

terraform init
terraform plan
terraform apply &lt;span class="c"&gt;# Review your plan before applying&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;4) Add Terraform steps&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In this step, we'll use &lt;code&gt;terraform init&lt;/code&gt; to get your Terraform environment ready by setting up what it needs. Then, we'll run 'terraform plan' to see a preview of the changes that will be made.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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;Setup Terraform&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;hashicorp/setup-terraform@v1&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;terraform_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.5.7&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;Terraform init and validate&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;terraform init&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;Terraform plan&lt;/span&gt;
    &lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;terraform plan&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;5) Create Issue for approval&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You can use GitHub Actions, for example, to deploy your Terraform code, but before applying the changes, you need a mechanism to review your Terraform code. This helps ensure that your infrastructure changes are safe and minimizes the risks of deployment in production&lt;/p&gt;

&lt;p&gt;For the manual approval in the workflow, we will use the &lt;a href="https://github.com/trstringer/manual-approval" rel="noopener noreferrer"&gt;&lt;strong&gt;&lt;code&gt;trstringer/manual-approval&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt; . &lt;/p&gt;

&lt;p&gt;This GitHub Actions workflow enables you to create an approval process within your automation pipeline. It's particularly useful when you want to add a layer of human review and approval, especially for actions such as deploying to production. 👀&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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;trstringer/manual-approval@v1&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;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.TOKEN }}&lt;/span&gt;
    &lt;span class="na"&gt;approvers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;laysauchoa&lt;/span&gt;
    &lt;span class="na"&gt;minimum-approvals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;issue-title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deploying&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;v1.0.0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;prod"&lt;/span&gt;
    &lt;span class="na"&gt;issue-body&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Review&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;terraform&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;plan,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;then&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;approve&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;or&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;deny&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;deployment&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;v1.0.0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;prod."&lt;/span&gt;
    &lt;span class="na"&gt;exclude-workflow-initiator-as-approver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once the Terraform plan step is completed, a corresponding issue is automatically generated for your review. Below, you can see an example of what this issue might look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ftuit3wcu104b43fr70dh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ftuit3wcu104b43fr70dh.png" alt="Issue on GitHub"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this issue, you have the opportunity to thoroughly examine the proposed changes in your Terraform deployment. If you agree with the plan and are ready to proceed with the deployment, you can simply approve the issue. &lt;/p&gt;

&lt;p&gt;Upon approval, the workflow will automatically advance to the next step, applying your Terraform changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fgm3y10p3wqr2lcxer4h2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fgm3y10p3wqr2lcxer4h2.png" alt="Workflow jobs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Deploying changes to production can be risky. Using a manual approval process allows us to review and confirm the deployment plan before proceeding, especially when deploying to production.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;That's it! You've successfully built a CI/CD pipeline using Terraform and GitHub Actions. Just think about how much time and hassle this can save you as your website or system grows. Terraform and GitHub Actions make things easier and faster.&lt;/p&gt;

&lt;p&gt;See you next time! 👋&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="c1"&gt;#.github/workflows/deploy-to-prod.yaml&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;Terraform PROD Deployment&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="s"&gt;develop&lt;/span&gt;
    &lt;span class="na"&gt;workflow_dispatch&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="s"&gt;develop&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
    &lt;span class="na"&gt;issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;gcp-deploy&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout Git Repository&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@v3.0.0&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;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auth"&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authenticate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Google&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Cloud"&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;google-github-actions/auth@v1"&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;credentials_json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;secrets.GOOGLE_CREDENTIALS&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;create_credentials_file&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;export_environment_variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;Setup Terraform&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;hashicorp/setup-terraform@v1&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;terraform_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.5.7&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;Terraform init and validate&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;terraform init&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;Terraform plan&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;terraform plan&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;trstringer/manual-approval@v1&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;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;approvers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;laysauchoa&lt;/span&gt;
          &lt;span class="na"&gt;minimum-approvals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
          &lt;span class="na"&gt;issue-title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deploying&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;v1.0.0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;prod"&lt;/span&gt;
          &lt;span class="na"&gt;issue-body&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Review&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;terraform&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;plan,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;then&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;approve&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;or&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;deny&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;deployment&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;v1.0.0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;prod."&lt;/span&gt;
          &lt;span class="na"&gt;exclude-workflow-initiator-as-approver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;Terraform apply&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;terraform apply&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>githubactions</category>
      <category>terraform</category>
      <category>github</category>
      <category>automation</category>
    </item>
    <item>
      <title>Automate GitHub Pull Requests with GitHub Actions</title>
      <dc:creator>Laysa Uchoa</dc:creator>
      <pubDate>Sat, 22 Oct 2022 20:37:37 +0000</pubDate>
      <link>https://dev.to/laysauchoa/automatically-open-pull-requests-with-github-actions-3ipd</link>
      <guid>https://dev.to/laysauchoa/automatically-open-pull-requests-with-github-actions-3ipd</guid>
      <description>&lt;p&gt;Whenever you catch yourself in a repetitive task, try this: step back and think about ways you might automate it. One tool CI/CD folks love to use for automation is GitHub Actions. &lt;/p&gt;

&lt;p&gt;GitHub Actions are widely used to run tests and perform checks on our code. But what happens when your automation changes the code, and you want to commit those changes? If you are thinking about running the script manually and opening a pull request, this is an option. But here's an even better option: automate the process by using GitHub Actions to open pull requests for you. This article shows you how, step by step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario
&lt;/h2&gt;

&lt;p&gt;At Aiven, we've got an interesting problem: we keep expanding our list of cloud providers. This is nice because it gives you the flexibility to choose whatever provider works for you. For us, maintainers of dev portal documentation, it's a challenge to ensure that the most up-to-date information about our cloud providers is available for our readers.&lt;/p&gt;

&lt;p&gt;You can find a list of Aiven's cloud providers on [Developer.aiven.io], which is also the portal where we host the developer documentation. To quickly retrieve a list of currently-available cloud providers, my colleague wrote a Python script that pulls our cloud listing from the &lt;a href="https://api.aiven.io/doc/#operation/ListClouds"&gt;Aiven API&lt;/a&gt; and &lt;a href="https://github.com/aiven/devportal/pull/675/files"&gt;generates documentation&lt;/a&gt;, which is really cool!&lt;/p&gt;

&lt;p&gt;Having the script was great. Now, by running the script, we could quickly generate fresh documentation out of the Aiven API data. This didn't automate the process entirely, though. Getting the changes merged still required a number of manual actions on a regular basis: run the script, check if there are new changes to commit, create pull requests, etc.&lt;/p&gt;

&lt;p&gt;This is a good example where you can configure a GitHub Actions workflow to run the script periodically and open a pull request when changes are detected. So if you have a similar challenge, below you'll find how to add an automation flow step by step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure GitHub Actions workflow
&lt;/h2&gt;

&lt;p&gt;Time for some action - or better, GitHub Actions! You can follow these steps to configure your GitHub Actions workflow:&lt;/p&gt;

&lt;h3&gt;
  
  
  1) Check the script
&lt;/h3&gt;

&lt;p&gt;Let's ensure that the script works correctly before using it for automation. So, here you can run your script a couple of times and check that it is working as it is supposed to.&lt;/p&gt;

&lt;p&gt;To make the process easier, you can add the commands to run the script in a Makefile. Makefiles are a helpful way to organize code compilation, and it helps to run long commands in a shorter version using the &lt;code&gt;make&lt;/code&gt; command. Following our example, here is what we add to the &lt;a href="https://github.com/aiven/devportal/blob/main/Makefile#L96-L97"&gt;Makefile&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```
SOURCEDIR     = .
# (Re)Generate cloud listing
cloud-list:
    python "$(SOURCEDIR)/scripts/aiven/clouds.py" "includes/clouds-list.rst"
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can simply run the script with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```
make cloud-list
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This step ensures that the script you use to change the code produces the changes you want to commit in a pull request. Mistakes happen. If your script isn't working as expected, you may need to debug your code before proceeding. If the script is working, then you can go ahead and create the GitHub workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Create a GitHub workflow
&lt;/h3&gt;

&lt;p&gt;In this step, you need to add a YAML file to define the workflow configuration. First, make sure you have created a &lt;code&gt;.github/workflows/&lt;/code&gt; folder; if not, you can go ahead and create the directory. Then, create a YAML file to define the workflow configuration, for example, &lt;code&gt;.github/workflows/cloud-list.yaml&lt;/code&gt;. In this file, you can define the name of the workflow. In our example, we start by giving a name to our workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```
name: Cloud - Create PR to update available list
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pick a name for a file and for the workflow that is related to the changes you are bringing to your code. This can help your code and to identify your workflows. Check out this quick guide from GitHub in &lt;a href="https://docs.github.com/en/actions/quickstart"&gt;how to create your first workflow&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  3) Choose a trigger
&lt;/h3&gt;

&lt;p&gt;GitHub Actions enable you to trigger your workflow to run based on a certain event or events. In the example, the job needs to run periodically. For that, configure it to run &lt;code&gt;on schedule&lt;/code&gt; according to a &lt;a href="https://ostechnix.com/a-beginners-guide-to-cron-jobs/"&gt;cron job&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The cron syntax can be confusing sometimes. To double-check your cron syntax, you can use an online and free tool called &lt;a href="https://crontab.guru/"&gt;crontab guru&lt;/a&gt;. Besides this, the page contains many &lt;a href="https://crontab.guru/examples.html"&gt;examples&lt;/a&gt; that can help understand the syntax better.&lt;/p&gt;

&lt;p&gt;For example, this workflow is configured to run every Tuesday at 6 AM UTC (8 AM CEST) time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```
on:
schedule:
    - cron: "0 6 * * 2"
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On GitHub Actions, the time is based on UTC, so you may want to consider this during the setup.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt;  On GitHub Actions, the shortest interval you can run scheduled workflows is once every 5 minutes.&lt;br&gt;
If you want to run your job in every pull request or have a different use case, you can check out all the available triggers to run your workflow on the &lt;a href="https://docs.github.com/en/actions/using-workflows/triggering-a-workflow"&gt;GitHub documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3) Install dependencies and run the script
&lt;/h3&gt;

&lt;p&gt;A GitHub workflow is composed of one or more jobs. A job is a set of steps executed in a certain environment defined on the &lt;code&gt;runs-on&lt;/code&gt;. In the example below, the &lt;code&gt;CloudList&lt;/code&gt; job runs on the latest ubuntu image. After defining the environment, you need to &lt;code&gt;checkout&lt;/code&gt; from a branch, for example the main branch, and decide on the Python version. Finally, you need to add a step to install the dependencies and run your script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```
jobs:
CloudList:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout the repo
        uses: actions/checkout@v2
    - name: Set up Python 3.8
        uses: actions/setup-python@v2
        with:
        python-version: "3.8"
    - name: Install dependencies
        run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Update available cloud list
        run: make cloud-list
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this, the job already runs periodically and generates the file. However, the final goal is to open a pull request so maintainers can check the changes and merge the code. So let's see how you can add the pull request step to your workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  4) Create Pull request
&lt;/h3&gt;

&lt;p&gt;You can create a pull request by using the &lt;a href="https://github.com/peter-evans/create-pull-request"&gt;Create Pull request&lt;/a&gt; action in our workflow. A cool feature here is that you can add some customization related to your pull request that can help in the pull request review process, including labels and a defined branch name, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```
- name: Create Pull Request
uses: peter-evans/create-pull-request@v4
with:
    commit-message: Cloud - Update available list
    title: Cloud - Update available list
    body: Cloud - Update available list
    base: main
    labels: automated-pr, Clouds &amp;amp; Regions
    branch: cloud-update-advanced-params
    delete-branch: true
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The workflow runs the specified jobs according to the scheduled time even if the action is not merged.&lt;/p&gt;

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

&lt;p&gt;Besides periodically running a workflow, you can combine it with the option to manually trigger your workflow, which can help you to test it. You can check out this &lt;a href="https://github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-workflow_dispatch/"&gt;GitHub Actions manual&lt;/a&gt; for that.&lt;/p&gt;

&lt;p&gt;When the action takes place, this is what the shiny new pull request looks like ✨:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ed5_YcpW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qzi036kv4xkiqjtqh497.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ed5_YcpW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qzi036kv4xkiqjtqh497.png" alt="Automated open pull request" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So every week, the workflow runs and keeps our Aiven &lt;a href="https://docs.aiven.io/docs/platform/reference/list_of_clouds.html"&gt;cloud list&lt;/a&gt; always updated for our beloved users. 🧡&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As devportal maintainers, we are happy to use GitHub Action in our CI/CD process to keep our Aiven &lt;a href="https://docs.aiven.io/docs/platform/reference/list_of_clouds.html"&gt;cloud list&lt;/a&gt; always up-to-date. &lt;/p&gt;

&lt;p&gt;In this article, we reviewed the workflow code one segment at a time. The complete script looks like this:&lt;/p&gt;

&lt;p&gt;File: &lt;code&gt;.github/workflows/cloud-list.yaml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Cloud - Create PR to update available list
on:
  schedule:
    - cron: "0 6 * * 2"
jobs:
  CloudList:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout the repo
        uses: actions/checkout@v2
      - name: Set up Python 3.8
        uses: actions/setup-python@v2
        with:
          python-version: "3.8"
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
      - name: Update available cloud list
        run: make cloud-list
      - name: Create Pull Request
        uses: peter-evans/create-pull-request@v4
        with:
          commit-message: Cloud - Update available list
          title: Cloud - Update available list
          body: Cloud - Update available list
          base: main
          labels: automated-pr, Clouds &amp;amp; Regions
          branch: cloud-update-advanced-params
          delete-branch: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Read more
&lt;/h2&gt;

&lt;p&gt;Besides periodically running a workflow, you can also add the option to trigger your workflow manually. You can check out this &lt;a href="https://github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-workflow_dispatch/"&gt;GitHub Actions manual&lt;/a&gt; with all the information.&lt;/p&gt;

&lt;p&gt;If what you have in mind for your pull request looks different, you can check out more &lt;a href="https://github.com/peter-evans/create-pull-request/blob/main/docs/examples.md"&gt;examples&lt;/a&gt; on how to customize the &lt;code&gt;create-pull-request&lt;/code&gt; actions to fit your use case.&lt;/p&gt;

&lt;p&gt;Here you can find more &lt;a href="https://github.com/aiven/devportal/pull/945/files"&gt;examples&lt;/a&gt; to learn how to use GitHub Actions to open pull requests for you. Have fun automating your pull requests ⚙️!&lt;/p&gt;




&lt;p&gt;I'm a Cloud Engineer at Nordcloud. &lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>pullrequest</category>
      <category>automation</category>
      <category>github</category>
    </item>
    <item>
      <title>Visiting Google’s Office</title>
      <dc:creator>Laysa Uchoa</dc:creator>
      <pubDate>Fri, 19 Aug 2022 07:34:25 +0000</pubDate>
      <link>https://dev.to/laysauchoa/the-googles-munich-office-4ojd</link>
      <guid>https://dev.to/laysauchoa/the-googles-munich-office-4ojd</guid>
      <description>&lt;h2&gt;
  
  
  The Google’s Munich Office
&lt;/h2&gt;

&lt;p&gt;Google is the house of innovation and a dream company of many folks, especially in TECH. I heard a lot about Google's office and how nice they are. In this post, I will share with you my impressions of Google's Munich office.&lt;/p&gt;

&lt;h3&gt;
  
  
  The visit
&lt;/h3&gt;

&lt;p&gt;To visit Google's office, you need to be invited by someone who works at the company or attend an event happening in the company. You will receive an identification ID to walk into the building. I was invited by a software engineer from Google who was responsible for me and accompanied me during the whole visit. This was great because I would get lost in this huge building without guidance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fcadr7axaqy8n9y1y4ga1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fcadr7axaqy8n9y1y4ga1.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Stats
&lt;/h3&gt;

&lt;p&gt;I heard that this office has approximately 500-700 employees. Also, 80% of employees work in software development. Those are the following areas which those teams are working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chrome tool&lt;/li&gt;
&lt;li&gt;Security and Privacy &lt;/li&gt;
&lt;li&gt;Engineering and productivity tools&lt;/li&gt;
&lt;li&gt;Internal tools&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The building
&lt;/h3&gt;

&lt;p&gt;The building is H U G E. The engineer who was accompanying me told me that took him three months to stop getting lost in the building. I was lucky to have someone with me to show me the way.&lt;/p&gt;

&lt;p&gt;If you are wondering where is the office, Google's Munich office is located at Erika-Mann-Straße 33. If you are driving by public transportation, the nearest metro station is the München Donnersbergerbrücke. It's a very central location! Now, let's talk about the office's delicious food...&lt;/p&gt;

&lt;h3&gt;
  
  
  The food
&lt;/h3&gt;

&lt;p&gt;There are two restaurants in the building. The food is of high quality. In the menu, we had fried chicken, fish, soya balls, and salads. Also, you could get your own wok plate. Everything was very fresh and prepared in front of us. &lt;/p&gt;

&lt;p&gt;And because it is summer, we even had the chance to have ice cream 🍦&lt;/p&gt;

&lt;p&gt;Besides the restaurants, Google's Munich office has its own rooftop Cafe. Not only the coffee was delicious, but we also had &lt;a href="https://en.wikipedia.org/wiki/Kaiserschmarrn" rel="noopener noreferrer"&gt;Käseschmarrn&lt;/a&gt; and some Tiramisu there. Also, you have access to nice drinks like tea, juices, beer, and a variety of snacks too. &lt;/p&gt;

&lt;p&gt;You know what they say &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Out of sight, out of mind &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So imagine you can only see the healthy options, would it make it easy for you to make better decisions?! Google's office design takes this to another level. The refrigerators are designed to show healthy choices and hide the unhealthy ones. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fohuu2ul102cnvt6vmzqd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fohuu2ul102cnvt6vmzqd.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Rooms and Office Decoration
&lt;/h3&gt;

&lt;p&gt;The building has many rooms for all tastes. Some rooms are inspired by the local metro stations like the Candidplatz room, for example, inspired in the &lt;a href="https://de.wikipedia.org/wiki/U-Bahnhof_Candidplatz" rel="noopener noreferrer"&gt;U-Bahnhof Candidplatz&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fi663sx0lf0x9a267j07e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fi663sx0lf0x9a267j07e.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the Munich office, you can also find &lt;a href="https://en.wikipedia.org/wiki/Obatzda" rel="noopener noreferrer"&gt;Obatzda&lt;/a&gt; room named after a Bavarian delicacy, &lt;a href="https://en.wikipedia.org/wiki/Marienplatz" rel="noopener noreferrer"&gt;Marienplatz&lt;/a&gt; named after the central square of Munich and they even have their own Google Straße which means Google Street in German. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F6j5aj4hmqkh9a3n8eld0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F6j5aj4hmqkh9a3n8eld0.png" alt="Google street"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What do you think? I love the idea!!! ❤️&lt;/p&gt;

&lt;p&gt;Also, the building has room for almost everything one can wish for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sleep and nap 😴&lt;/li&gt;
&lt;li&gt;Massage and relax 🧘🏻‍♀️&lt;/li&gt;
&lt;li&gt;Breastfeed 🤱 &lt;/li&gt;
&lt;li&gt;Biophilia room with lots of plants 🌱&lt;/li&gt;
&lt;li&gt;Video games and boarding games 🕹&lt;/li&gt;
&lt;li&gt;Library room 📚&lt;/li&gt;
&lt;li&gt;Cooking room 🥨&lt;/li&gt;
&lt;li&gt;Music room to learn piano, guitar, etc 🎸&lt;/li&gt;
&lt;li&gt;Fitness studio 🏃🏻‍♀️&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It REALLY feels like you are in some sort of paradise! 🤩&lt;/p&gt;

&lt;h3&gt;
  
  
  Sustainability
&lt;/h3&gt;

&lt;p&gt;The building recycles 2.409 million litres of rainwater per year. Also, each open office and pretty much everywhere has trash cans with a separation system. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fb9wnkms5cwqofto2n6nn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fb9wnkms5cwqofto2n6nn.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seems simple but I've never been in an office where also the open office had such a thing. 🤔&lt;/p&gt;

&lt;p&gt;One interesting thing is that if you need anything hardware, you can buy it directly from a machine located in Google's office, so no need to ask for someone to buy it for you and wait many days until it arrives.&lt;/p&gt;

&lt;p&gt;There is also a place with free things that the employees leave there for other employees to take. Who does not love free stuff?! I even got a nice keyboard. I find very sustainable and maybe an idea that other offices can use ;)&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Visiting such an office was a joy! I hope you enjoyed the text and if you have any questions about Google's office, feel free to drop here!&lt;/p&gt;

&lt;p&gt;Hey, let's connect! ♡&lt;br&gt;
You can follow me on &lt;a href="https://twitter.com/laysauchoa" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or add me on &lt;a href="https://www.linkedin.com/in/laysauchoa/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. &lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__823590"&gt;
    &lt;a href="/laysauchoa" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F823590%2F0083d6c3-9e3e-4848-91d3-e071d404a25a.png" alt="laysauchoa image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/laysauchoa"&gt;Laysa Uchoa&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/laysauchoa"&gt;grateful for helpful compiler messages. Working as Cloud Engineer at Nordcloud.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>google</category>
      <category>munich</category>
      <category>devcommunity</category>
      <category>developers</category>
    </item>
    <item>
      <title>Write search queries with Python and OpenSearch®</title>
      <dc:creator>Laysa Uchoa</dc:creator>
      <pubDate>Mon, 01 Aug 2022 10:44:27 +0000</pubDate>
      <link>https://dev.to/laysauchoa/write-search-queries-with-python-and-opensearchr-4ccb</link>
      <guid>https://dev.to/laysauchoa/write-search-queries-with-python-and-opensearchr-4ccb</guid>
      <description>&lt;h2&gt;
  
  
  A Python-perfect dinner party with OpenSearch®
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;When I plan a dinner party, I want my guests to have a great experience, and definitely, I do not want anyone hungry. I need to check the ingredients, my guests' diet restrictions, and preferences. If you also feel that planning that special dinner can be challenging, you are in for a treat. &lt;/p&gt;

&lt;p&gt;In this blog, I'll show how to find delicious recipes in the pythonic way. We just need data, Python and the powers of OpenSearch® to plan a perfect dinner party!&lt;/p&gt;

&lt;p&gt;Our support material in this learning journey will be a useful &lt;a href="https://github.com/aiven/demo-opensearch-python"&gt;CLI application&lt;/a&gt; that lets you explore common types of OpenSearch query, and even run them yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting started
&lt;/h3&gt;

&lt;p&gt;Some may agree that our search results can be only as good as our dataset. But do not worry, we will be using a high-quality dataset from &lt;a href="https://www.kaggle.com/datasets/hugodarwood/epirecipes?select=full_format_recipes.json"&gt;Epicurious&lt;/a&gt; which contains over 20.000 full recipes, ratings, and nutrition information for us.&lt;/p&gt;

&lt;p&gt;I'll be using Aiven's fully managed OpenSearch service to get our cluster up and running. I've prepared a &lt;a href="https://github.com/aiven/demo-opensearch-python"&gt;demo&lt;/a&gt; that contains all the code to connect, send data and perform the search queries. &lt;/p&gt;

&lt;p&gt;Everything is explained in the project &lt;a href="https://github.com/aiven/demo-opensearch-python/blob/main/README.rst"&gt;README.rst&lt;/a&gt;, so you can just focus on understanding the queries. &lt;/p&gt;

&lt;h3&gt;
  
  
  Ingest data to the OpenSearch cluster
&lt;/h3&gt;

&lt;p&gt;The first step is to load the data into our OpenSearch cluster, so we can start to query. Check out how to load the data to your cluster &lt;a href="https://developer.aiven.io/docs/products/opensearch/howto/sample-dataset.html#load-the-data-with-python"&gt;using the Python client&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Now, we can start to play with data!&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding recipes
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Range query
&lt;/h4&gt;

&lt;p&gt;Rumors are that my grandfather is coming to the dinner, so I know I should have at least one recipe that is low in sodium. Low sodium meals are recommended to reduce blood pressure, but in general, this is a good healthy option for everyone.&lt;/p&gt;

&lt;p&gt;We can use the &lt;code&gt;range&lt;/code&gt; function to help us to find documents where the field value (in this case, &lt;code&gt;sodium&lt;/code&gt;) is within a certain range.&lt;/p&gt;

&lt;p&gt;Recipes under 140 ms of sodium per serving are considered low sodium meals. So let’s look for recipes around 100 - 140 mg, and build this query as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
      &lt;span class="s"&gt;"range"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
         &lt;span class="s"&gt;"sodium"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
            &lt;span class="s"&gt;"gte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"lte"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;140&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can use the demo program to see the &lt;code&gt;range&lt;/code&gt; query in action by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python search.py "sodium" 100 140
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm curious to see what kind of recipes, we get under this condition, and here we go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;['Salsa Verde ',
 'Green Bean and Red Onion Salad with Warm Cider Vinaigrette ',
 'Toasted-Pecan Pie ',
 'Provençal Chicken and Tomato Roast ',
 'Sauteed Cod Provençale ',
 'Roasted Potatoes and Asparagus with Parmesan ',
 'Sweet-and-Sour Baby Carrots ',
 'Ricotta Puddings with Glazed Rhubarb ',
 'Butternut Squash and White Bean Soup ',
 'Turkish Zucchini Pancakes ']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;'Turkish Zucchini Pancakes' seems like a delicious recipe, so this would be my choice.&lt;/p&gt;

&lt;h4&gt;
  
  
  Match-phrase query
&lt;/h4&gt;

&lt;p&gt;Also, I need to find a delicious salad recipe for the occasion 🥗. It's radish season and this vegetable goes really well in summer salads, so let's use &lt;code&gt;match_phrase&lt;/code&gt; to look for a "title"  containing "Salad with Radish".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
      &lt;span class="s"&gt;"match_phrase"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
         &lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
            &lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;"Salad with radish"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can run this query using the demo program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python search.py match-phrase "title" "Salad with Radish"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and here is our result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;['Green Bean and Red Onion Salad with Radish Dressing ']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We only got one match and it seems like radish is only part of the dressing. The reason is that the order of words is important when you use &lt;code&gt;match_phrase&lt;/code&gt;. In this case, the phrase &lt;code&gt;Salad with Radish&lt;/code&gt; only appeared once, hence our single result.&lt;/p&gt;

&lt;p&gt;We can fix that by adding some flexibility to our search. There is a powerful feature on &lt;code&gt;match_phrase&lt;/code&gt; that allows us to define the distance that the search words can be from each other. This parameter is called &lt;code&gt;slop&lt;/code&gt; (default=0). So let's try again with the &lt;code&gt;slop&lt;/code&gt; parameter set to 3.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
      &lt;span class="s"&gt;"match_phrase"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
         &lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
            &lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;"Salad with radish"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"slop"&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="p"&gt;}&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;We can run this query using the demo program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python search.py match-phrase "title" "Salad with Radish" --slop 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not surprisingly, we got more results this time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;['Green Bean and Red Onion Salad with Radish Dressing ',
 'Winter Salad with Black Radish, Apple, and Escarole ',
 'Avocado Radish Salad with Lime Dressing ',
 'Chickpea Salad Sandwich With Creamy Carrot-Radish Slaw ',
 'Mâche, Frisée, and Radish Salad with Mustard Vinaigrette ',
 'Frisée and Radish Salad with Goat Cheese Croutons ',
 'Endive, Mâche, and Radish Salad with Champagne Vinaigrette ',
 'Butter Lettuce and Radish Salad with Fresh Spring Herbs ',
 'Butter Lettuce and Radish Salad with Lemon-Garlic Vinaigrette ',
 'Shaved Carrot and Radish Salad With Herbs and Pumpkin Seeds ']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, our results match with "Radish Salad with", "Salad with &lt;code&gt;&amp;lt;something else&amp;gt;&lt;/code&gt; Radish" and so on.&lt;br&gt;
We can pick one and move forward to find a desert.&lt;/p&gt;
&lt;h4&gt;
  
  
  Match query
&lt;/h4&gt;

&lt;p&gt;Let's explore how the &lt;code&gt;match&lt;/code&gt; function works, building a query to find "Chocolate Carrot Cake" in our "title".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
      &lt;span class="s"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
         &lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
            &lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;"Chocolate Carrot Cake"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"operator"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"and"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;match&lt;/code&gt; parameter will report results in a sorted order of how closely they relate to "Chocolate Carrot Cake" 🥕. By default &lt;code&gt;match&lt;/code&gt; uses the "OR" operator, giving results for "Chocolate" or "Carrot" or "Cake". However, I want to have all these terms included in the "title" when we search. We can use the "AND" operator for that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python search.py match "title" "Chocolate Carrot Cake" --operator "and"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are our cake results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;['Chocolate-Orange Carrot Cake ',
 'Milk Chocolate Semifreddo with Star Anise Carrot Cake ']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything seems delicious and we are ready for the party 🥳! &lt;/p&gt;

&lt;p&gt;And what's for your dinner? You can play around writing your own search queries and find your own perfect dinner. &lt;/p&gt;

&lt;p&gt;I'll be happy to hear from you or answer any questions you may have at &lt;a href="//mailto:laysa.uchoa@gmail.com"&gt;laysa.uchoa@gmail.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy meal, everyone!&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples and other resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/aiven/demo-opensearch-python"&gt;Find the code sample on the GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.aiven.io/docs/products/opensearch/howto/opensearch-search-and-python.html"&gt;Continue the tutorial to learn how to query data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.aiven.io/docs/products/opensearch/index.html"&gt;Find more documentation resources for Aiven for OpenSearch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensearch</category>
      <category>opensource</category>
      <category>python</category>
      <category>elasticsearch</category>
    </item>
    <item>
      <title>PyCamp 2022 - Going camping without bugs</title>
      <dc:creator>Laysa Uchoa</dc:creator>
      <pubDate>Thu, 05 May 2022 07:02:20 +0000</pubDate>
      <link>https://dev.to/laysauchoa/pycamp-2022-going-camping-without-bugs-1ih1</link>
      <guid>https://dev.to/laysauchoa/pycamp-2022-going-camping-without-bugs-1ih1</guid>
      <description>&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What it is&lt;/li&gt;
&lt;li&gt;Where it is&lt;/li&gt;
&lt;li&gt;PyCamp activities&lt;/li&gt;
&lt;li&gt;Who can join&lt;/li&gt;
&lt;li&gt;Final thoughts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is the PyCamp &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;🌱 Nature + 🤗 OSS + 🐍 Python friends = ⛺ PyCamp&lt;/p&gt;

&lt;p&gt;After many years of PyCamp events in Argentina, the event had its first edition in Europe this Spring in Barcelona, Spain. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Our plan is to not have a fixed plan.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I heard this from the organizer Manuel, @reydelhumo. And you all must be thinking...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/Pla6TIXl1TmCJpLRsp/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/Pla6TIXl1TmCJpLRsp/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes! So how a non business event was so organized and successful? The answer is community.&lt;br&gt;
&lt;a href="https://i.giphy.com/media/m6BXPlP86xeSs/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/m6BXPlP86xeSs/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Where to camp &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The location chosen was the beautiful region of Girona near Barcelona, Spain. Surrounded by nature, the pythonistas could choose any project or workshop to participate in. What was the view from there? Breath and take a look!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fhc7wl86pfxj774iq7xz4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fhc7wl86pfxj774iq7xz4.png" alt="Event view"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any similarity with Window desktop image is just pure coincidence... &lt;/p&gt;

&lt;p&gt;We did not sleep in tends in the event. We lived in a very well equipped house with shared rooms and bathrooms. Also, with enough sockets to charger our laptops.&lt;/p&gt;

&lt;h3&gt;
  
  
  PyCamp Activities &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The event counted with 25 people spending 4 days together doing what they love the most - collaborating with others through code. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F617jwk0ct0pyugsgrraj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F617jwk0ct0pyugsgrraj.png" alt="Projects"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From translating Japanese mangas to building bots and making music with Python, the projects were interesting and fun. Some of themes were: Python packaging, Twitter API, Telegram bots and more.&lt;/p&gt;

&lt;p&gt;I worked in one of the projects named as Pylículas. The goal was to make a Telegram bot that could give movie recommendations to the user. Fun!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/W4zOVGmI9pYbswnQdw/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/W4zOVGmI9pYbswnQdw/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, if you did not find any projects that you like, you could always propose one yourself even if you do not know how to do. People are there are very collaborative and helpful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who can join &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;If you like Python and want to contribute to OSS projects, this is the right event for you. Amazing event, right? But you are not a Pythonista yourself? No problem at all.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/KczqttEJqm55hE1ccU/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/KczqttEJqm55hE1ccU/giphy.gif"&gt;&lt;/a&gt;&lt;br&gt;
In this edition, we had people talking about DevOps tools and JavaScript frameworks, so feel free to bring different topics to the event. Python beginners, we hear you! There are a bunch of mentors to help you during the projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final thoughts &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;So my final words about this event is that living and collaborating with others makes without saying a true community event. So see you next Spring at PyCamp Spain? ⛺&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fs08kahaleujikqla8ji2.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fs08kahaleujikqla8ji2.jpeg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>community</category>
      <category>devjournal</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Copy OpenSearch index data to S3</title>
      <dc:creator>Laysa Uchoa</dc:creator>
      <pubDate>Sun, 20 Mar 2022 13:16:00 +0000</pubDate>
      <link>https://dev.to/laysauchoa/copy-opensearch-index-data-to-s3-27bb</link>
      <guid>https://dev.to/laysauchoa/copy-opensearch-index-data-to-s3-27bb</guid>
      <description>&lt;p&gt;It is a good practice to perform backups of your OpenSearch (OS) data to another storage service. This way you can access your data and restore it if something unexpected happens to it.&lt;/p&gt;

&lt;p&gt;In this article, you can find out how to dump your OpenSearch data to&lt;br&gt;
an:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS S3 bucket&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To copy the index data, we will be using &lt;a&gt;&lt;code&gt;elasticsearch-dump&lt;/code&gt;&lt;br&gt;
tool&lt;/a&gt;. You can read the &lt;a href="https://github.com/elasticsearch-dump/elasticsearch%20dump/blob/master/README.md" rel="noopener noreferrer"&gt;instructions on GitHub&lt;/a&gt; on how to install it. From this library, we will use &lt;code&gt;elasticdump&lt;/code&gt; command to copy the input index data to a specific output.&lt;/p&gt;

&lt;p&gt;Make sure to have &lt;code&gt;elasticsearch-dump&lt;/code&gt; &lt;a&gt;tool&lt;/a&gt; installed for the next steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Copying data from OpenSearch to AWS S3
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  OpenSearch cluster as the &lt;code&gt;input&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  AWS S3 bucket as the &lt;code&gt;output&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Information about your OS cluster and AWS service:&lt;/p&gt;

&lt;p&gt;OpenSearch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;SERVICE_URI&lt;/code&gt;: OpenSearch service URI.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;INPUT_INDEX_NAME&lt;/code&gt;: the index that you aim to copy from your input source.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;S3 bucket:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  AWS credentials (&lt;code&gt;ACCESS_KEY_ID&lt;/code&gt; and &lt;code&gt;SECRET_ACCESS_KEY&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  S3 file path. for e.g. &lt;code&gt;s3://${BUCKET_NAME}/${FILE_NAME}.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find more information about AWS credentials in the &lt;a href="https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html" rel="noopener noreferrer"&gt;AWS docs&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Export OpenSearch index data to S3
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;elasticsearch-dump&lt;/code&gt; command to copy the data from your &lt;strong&gt;OpenSearch cluster&lt;/strong&gt; to your &lt;strong&gt;AWS S3 bucket&lt;/strong&gt;. Use your&lt;br&gt;
OpenSearch &lt;code&gt;SERVICE_URI&lt;/code&gt; for the &lt;code&gt;input&lt;/code&gt;. For the &lt;code&gt;output&lt;/code&gt;, choose an AWS S3 file path including the file name that you want for your document.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 {.shell}
elasticdump \
--s3AccessKeyId "${ACCESS_KEY_ID}" \
--s3SecretAccessKey "${SECRET_ACCESS_KEY}" \
--input=SERVICE_URI/INPUT_INDEX_NAME --output "s3://${BUCKET_NAME}/${FILE_NAME}.json"  


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That is how you can copy your OpenSearch data to a S3 bucket. 🙋🏻‍♀️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fkh4ebq0jitmk9yyquv2t.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fkh4ebq0jitmk9yyquv2t.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensearch</category>
      <category>s3</category>
      <category>elasticsearch</category>
      <category>elasticsearchdump</category>
    </item>
    <item>
      <title>print("Hello, devs!")</title>
      <dc:creator>Laysa Uchoa</dc:creator>
      <pubDate>Sun, 20 Mar 2022 12:54:57 +0000</pubDate>
      <link>https://dev.to/laysauchoa/printhello-devs-4nce</link>
      <guid>https://dev.to/laysauchoa/printhello-devs-4nce</guid>
      <description>&lt;p&gt;Hello, peeps!&lt;/p&gt;

&lt;p&gt;My name is Laysa Uchoa and I am brazilian living in Munich, Germany. I am a software developer curious about structured and unstructured data with a mission to share helpful things I find while working in tech. Let's &lt;a href="https://twitter.com/laysauchoa"&gt;connect&lt;/a&gt;! 🐦&lt;/p&gt;

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

&lt;p&gt;I love Python and Python communities. So few years ago, I started organizing &lt;a href="https://www.meetup.com/PyLadiesMunich/"&gt;Pyladies Munich&lt;/a&gt; to offer this space for coding ladies to meet and connect with each other.&lt;/p&gt;

&lt;p&gt;I really believe that: If you can dream, you can make it happen in Python 🐍&lt;/p&gt;

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

&lt;p&gt;Besides coding and running tech conferences, you can find me learning new languages. I love human languages (Portuguese 🇧🇷, Spanish 🇪🇸, German 🇩🇪 and English 🇺🇸) besides programming languages. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your turn:&lt;/strong&gt; drop "Hi" in your programming or human language, and I will try to guess 🔎&lt;/p&gt;

</description>
      <category>introduction</category>
      <category>pyladies</category>
      <category>munich</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>How to migrate your Elasticsearch client to OpenSearch</title>
      <dc:creator>Laysa Uchoa</dc:creator>
      <pubDate>Sun, 20 Mar 2022 12:03:04 +0000</pubDate>
      <link>https://dev.to/laysauchoa/how-to-migrate-your-elasticsearch-client-to-using-opensearch-502p</link>
      <guid>https://dev.to/laysauchoa/how-to-migrate-your-elasticsearch-client-to-using-opensearch-502p</guid>
      <description>&lt;p&gt;In this article, I will show you how to switch from Elasticsearch to an OpenSearch client.&lt;/p&gt;

&lt;p&gt;When we think about database migration, we normally think about migrating the data itself. However, you have to consider migrating your client, and especially which version is being used. &lt;/p&gt;

&lt;p&gt;In this article, we will briefly explain the database migration process, and how to perform your client migration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client migration from Elasticsearch to OpenSearch
&lt;/h3&gt;

&lt;p&gt;Migrating a native Elasticsearch client to OpenSearch requires changes in the client code so that you can continue to interact with your cluster. Are you curious how this can be done? Here we will go over client migration in three languages: Python, Java, and JavaScript (Node.js).&lt;/p&gt;

&lt;p&gt;Before diving into the code, there is one thing you should keep in mind. According to &lt;a href="https://opensearch.org/docs/latest/clients/index/#legacy-clients"&gt;OpenSearch docs&lt;/a&gt;, Elasticsearch clients for v7.10.2 should work in terms of compatibility with OpenSearch client v1. However, the latest version of Elasticsearch clients may contain checks that can break compatibility. Here are the recommendations regarding the Elasticsearch client version that you should have to migrate to OpenSearch v1.0.0.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Client&lt;/th&gt;
&lt;th&gt;Recommended version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://search.maven.org/artifact/org.elasticsearch.client/elasticsearch-rest-client/7.13.4/jar"&gt;Java low-level REST client&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;7.13.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://search.maven.org/artifact/org.elasticsearch.client/elasticsearch-rest-high-level-client/7.13.4/jar"&gt;Java high-level REST client&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;7.13.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://pypi.org/project/elasticsearch/7.13.4/"&gt;Python&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;7.13.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@elastic/elasticsearch/v/7.13.0"&gt;NodeJS&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;7.13.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Check the full table on the &lt;a href="https://opensearch.org/docs/latest/clients/index/#legacy-clients"&gt;OpenSearch page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As you can see, it is recommended that first, you upgrade or downgrade your Elasticsearch to match v7.13.4 or v7.13.0, check if is still running correctly, then migrate to the compatible OpenSearch version v1.0.0. Finally, you can upgrade to the latest OpenSearch version that contains additional features and bug fixes. Doing this will help you correct API incompatibilities that may appear during your client migration process.&lt;/p&gt;

&lt;p&gt;So let's check how those changes are done code-wise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python
&lt;/h3&gt;

&lt;p&gt;For Pythonists, the changes needed in their Python client are regarding the library in use, and how their Python client objects are called. Here we are considering the official Python client libraries for &lt;a href="https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/index.html"&gt;Elasticsearch&lt;/a&gt; and &lt;a href="https://opensearch.org/docs/latest/clients/python/"&gt;OpenSearch&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With a few steps, you can replace your Elasticsearch client with the OpenSearch one.&lt;/p&gt;

&lt;p&gt;In the dependencies, change libraries and versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- elasticsearch==7.10.2
&lt;/span&gt;&lt;span class="gi"&gt;+ opensearch-py==1.0.0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the source code, change the imports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- from elasticsearch import Elasticsearch
&lt;/span&gt;&lt;span class="gi"&gt;+ from opensearchpy import OpenSearch
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- client_against_opensearch = Elasticsearch(ES_SERVICE_URI, use_ssl=True)
&lt;/span&gt;&lt;span class="gi"&gt;+ client_against_opensearch = OpenSearch(OS_SERVICE_URI, use_ssl=True)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The good news is that you can reuse the same APIs as the Elasticsearch ones in your OpenSearch client. Take a look at the full example on our &lt;a href="https://github.com/aiven/opensearch-migration-examples/tree/main/python-client-migration"&gt;OpenSearch migration repository&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you want to know more about the Python OpenSearch client and its compatibility, feel free to explore those resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://opensearch.org/docs/latest/clients/index/"&gt;Compatibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opensearch.org/docs/latest/clients/python"&gt;Python Client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aiven/opensearch-migration-examples/tree/main/python-client-migration"&gt;Python, OpenSearch migration examples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Java
&lt;/h3&gt;

&lt;p&gt;Good news for Java client users, minimal changes are needed when you are migrating to OpenSearch. You only need to install the new dependencies and change your imports. Here you can find the changes related to imports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- implementation 'org.elasticsearch.client:elasticsearch-rest-client:7.10.2'
- implementation 'org.elasticsearch.client:elasticsearch-rest-high-level-client:7.10.2'
&lt;/span&gt;&lt;span class="gi"&gt;+ implementation 'org.opensearch.client:opensearch-rest-client:1.1.0'
+ implementation 'org.opensearch.client:opensearch-rest-high-level-client:1.1.0'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your Java client, the changes look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- private static final String CLIENT_LIBRARY = "org.elasticsearch.client:elasticsearch-rest-client:7.10.2";
&lt;/span&gt;&lt;span class="gi"&gt;+ private static final String CLIENT_LIBRARY = "org.opensearch.client:opensearch-rest-client:1.1.0";
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By changing the imports, you should be able to use your OpenSearch client with the same APIs as the Elasticsearch ones. &lt;/p&gt;

&lt;p&gt;Find out more about OpenSearch Java client, including a full running example of OpenSearch client migration on the links below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://opensearch.org/docs/latest/clients/index/"&gt;Compatibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opensearch.org/docs/latest/clients/java-rest-high-level/"&gt;Java High Level Client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aiven/opensearch-migration-examples/tree/main/java-client-migration"&gt;Java, OpenSearch migration examples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  JavaScript (Node.js/NodeJS)
&lt;/h3&gt;

&lt;p&gt;Client migration in JavaScript is pretty straightforward, you only need to install the new dependency and change the &lt;code&gt;require&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;The dependencies can be installed with npm as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm install --save @opensearch-project/opensearch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find in this sample the client change for the NodeJS client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- const { Client } = require('@elastic/elasticsearch');
&lt;/span&gt;&lt;span class="gi"&gt;+ const { Client } = require('@opensearch-project/opensearch');
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should be able to reuse the APIs once the imports are set correctly.&lt;/p&gt;

&lt;p&gt;Check more resources for OpenSearch migration with NodeJS client:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://opensearch.org/docs/latest/clients/index/"&gt;Compatibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opensearch.org/docs/latest/clients/javascript/"&gt;NodeJS Client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aiven/opensearch-migration-examples/tree/main/node-client-migration"&gt;NodeJS, OpenSearch migration examples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;The examples in this article are part of the OSS project for &lt;a href="https://github.com/aiven/opensearch-migration-examples"&gt;OpenSearch migration examples&lt;/a&gt;, feel free to make a fork and add more examples in your favorite languages. It is a nice way to share your knowledge with the OpenSearch and Elasticsearch communities.&lt;/p&gt;

</description>
      <category>elasticsearch</category>
      <category>opensearch</category>
      <category>python</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
