<?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: Aditya Gupta</title>
    <description>The latest articles on DEV Community by Aditya Gupta (@itsadityagupta).</description>
    <link>https://dev.to/itsadityagupta</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F532114%2Fc6d8575c-22c7-4462-86b4-112537d3df06.jpeg</url>
      <title>DEV Community: Aditya Gupta</title>
      <link>https://dev.to/itsadityagupta</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/itsadityagupta"/>
    <language>en</language>
    <item>
      <title>How to set up my Yelposhere data engineering project on GCP?</title>
      <dc:creator>Aditya Gupta</dc:creator>
      <pubDate>Mon, 17 Apr 2023 07:14:09 +0000</pubDate>
      <link>https://dev.to/itsadityagupta/how-to-set-up-my-yelposhere-data-engineering-project-on-gcp-16bi</link>
      <guid>https://dev.to/itsadityagupta/how-to-set-up-my-yelposhere-data-engineering-project-on-gcp-16bi</guid>
      <description>&lt;h1 id="heading-what-is-yelposphere"&gt;What is YelpoSphere?&lt;/h1&gt;

&lt;p&gt;YelpoSphere is a data pipeline that &lt;em&gt;extracts, transforms, and loads&lt;/em&gt; data from Yelp's public dataset into a GCP environment and performs various analyses on the data. The pipeline combines &lt;strong&gt;GCS, BigQuery, Dataproc, Cloud Composer, Cloud Run and Cloud Build&lt;/strong&gt; for data processing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;YelpoSphere helps users provide insights into the restaurant business.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It performs the following tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Loads the Yelp data into the &lt;strong&gt;GCS&lt;/strong&gt; bucket.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clean, preprocess and ingest data from GCS to &lt;strong&gt;BigQuery&lt;/strong&gt; using &lt;strong&gt;Dataproc&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Uses &lt;strong&gt;DBT&lt;/strong&gt; to normalize the data and performs exploratory data analysis (EDA) to identify trends and insights.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Visualizes the results using &lt;strong&gt;Google Data Studio&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Orchestrates different tasks using &lt;strong&gt;Cloud Composer&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is also my capstone project for the &lt;a href="https://github.com/DataTalksClub/data-engineering-zoomcamp"&gt;Data Engineering Zoomcamp 2023&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Aditya-Gupta1/yelposphere"&gt;&lt;/a&gt;&lt;a href="https://github.com/Aditya-Gupta1/yelposphere"&gt;https://github.com/Aditya-Gupta1/yelposphere&lt;/a&gt;&lt;/p&gt;



&lt;h1 id="heading-wheres-the-data-coming-from"&gt;Where's the data coming from?&lt;/h1&gt;

&lt;p&gt;The dataset is taken from the &lt;a href="https://www.yelp.com/dataset"&gt;Official Yelp Open Dataset&lt;/a&gt; website. It is a subset of Yelp's businesses, reviews, and user data for personal, educational, and academic purposes. It is available in JSON files.&lt;/p&gt;

&lt;p&gt;Here's a list of files available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;business.json&lt;/code&gt;: Contains business data including location data, attributes, and categories.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;reviews.json&lt;/code&gt;: Contains full review text data including the user_id that wrote the review and the business_id the review is written for.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;user.json&lt;/code&gt;: User data including the user's friend mapping and all the metadata associated with the user.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;checkin.json&lt;/code&gt;: Checkins on a business.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;tip.json&lt;/code&gt;: Tips are written by a user of a business. Tips are shorter than reviews and tend to convey quick suggestions.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a detailed description of the schema and examples, please visit the &lt;a href="https://www.yelp.com/dataset/documentation/main"&gt;official documentation&lt;/a&gt; website.&lt;/p&gt;

&lt;h1 id="heading-what-is-the-tech-stack"&gt;What is the tech stack?&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Data Storage:&lt;/strong&gt; &lt;a href="https://cloud.google.com/storage"&gt;Google Cloud Storage (GCS)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Data Processing:&lt;/strong&gt; &lt;a href="https://cloud.google.com/dataproc"&gt;Dataproc&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Data Transformations:&lt;/strong&gt; &lt;a href="https://www.getdbt.com/"&gt;Data Build Tool (DBT)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Workflow Orchestration:&lt;/strong&gt; &lt;a href="https://cloud.google.com/composer"&gt;Cloud Composer&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Infrastructure:&lt;/strong&gt; &lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Programming Language:&lt;/strong&gt; &lt;a href="https://www.python.org/"&gt;Python&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Data Visualization:&lt;/strong&gt; &lt;a href="https://datastudio.withgoogle.com/"&gt;Google Data Studio&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Extras:&lt;/strong&gt; &lt;a href="https://cloud.google.com/build"&gt;Cloud Build&lt;/a&gt;, &lt;a href="https://cloud.google.com/run"&gt;Cloud Run&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id="heading-what-does-the-pipeline-look-like"&gt;What does the pipeline look like?&lt;/h1&gt;

&lt;p&gt;Here's a complete overview of the pipeline:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ePnt0WOV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680953641131/634d3679-d647-40e7-a29f-bec5c03110fe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ePnt0WOV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680953641131/634d3679-d647-40e7-a29f-bec5c03110fe.png" alt="" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When an airflow DAG starts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;It copies the data from a public GCS bucket into the project's data lake.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It will wait for files to come in the GCS bucket's &lt;code&gt;data/raw&lt;/code&gt; folder.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once the files are received, it submits Dataproc jobs to the cluster to ingest data into BigQuery raw tables.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once the data is ingested into raw tables, it will send a GET request to Cloud Run which will execute DBT jobs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;DBT will perform transformations and load the data into a final dataset, making it ready for visualization.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This final dataset acts as a source for Google Data Studio which creates a dashboard using it. On the other hand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;When commits are pushed to the main branch of the repo, a new Cloud Build is triggered.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This creates a new docker image that gets deployed on Cloud Run.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures that DBT commands perform up-to-date transformations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Cloud Build and Cloud Run are used to dockerize the DBT project and run DBT commands. This is required because Airflow does not have any convenient operators to run DBT core commands. And scheduling DBT cloud jobs is not supported in the FREE plan of DBT.&lt;/p&gt;
&lt;p&gt;In short, it is a workaround to orchestrate DBT with Cloud Composer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id="heading-what-do-i-need-to-run-this-pipeline"&gt;What do I need to run this pipeline?&lt;/h1&gt;

&lt;h2 id="heading-operating-system"&gt;Operating System&lt;/h2&gt;

&lt;p&gt;Prefer &lt;strong&gt;Linux&lt;/strong&gt; for this data pipeline. This is because Terraform throws EOF errors on Windows unexpectedly, causing inconvenience.&lt;/p&gt;

&lt;p&gt;You can use Google VM in case you're having a Windows machine.&lt;/p&gt;

&lt;h2 id="heading-tools"&gt;Tools&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Python&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Terraform&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Git&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Refer to the following blog for installation instructions on Python (Anaconda) and Terraform:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://itsadityagupta.hashnode.dev/setting-up-the-development-environment-on-google-virtual-machine"&gt;&lt;/a&gt;&lt;a href="https://itsadityagupta.hashnode.dev/setting-up-the-development-environment-on-google-virtual-machine"&gt;https://itsadityagupta.hashnode.dev/setting-up-the-development-environment-on-google-virtual-machine&lt;/a&gt;&lt;/p&gt;



&lt;h2 id="heading-gcp-resources"&gt;GCP Resources&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A GCP Project&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A service account&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;GCloud CLI&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="heading-setting-up-the-service-account"&gt;Setting up the service account&lt;/h3&gt;

&lt;p&gt;You can refer to the following blog for general instructions on creating a service account and managing keys:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://itsadityagupta.hashnode.dev/setting-up-a-gcp-service-account"&gt;&lt;/a&gt;&lt;a href="https://itsadityagupta.hashnode.dev/setting-up-a-gcp-service-account"&gt;https://itsadityagupta.hashnode.dev/setting-up-a-gcp-service-account&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;But, here are the roles that are required for this pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Cloud Run&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Cloud Run Service Agent&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Composer Worker&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Compute Admin&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dataproc Administrator&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Environment and Storage Object Administrator&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Secret Manager Secret Accessor&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Storage Admin&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Storage Object Admin&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Storage Transfer Admin&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, you'll need to give one more access to this service account:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;In your GCP console, go to IAM &amp;gt; Service Accounts and select your service account.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go to Permissions &amp;gt; Grant Access&lt;/p&gt;
&lt;p&gt;  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DKkZJ3Ko--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680951837379/97f2ebdf-2d5f-461c-bd95-5bb5de0bafef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DKkZJ3Ko--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680951837379/97f2ebdf-2d5f-461c-bd95-5bb5de0bafef.png" alt="" width="565" height="463"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You need to add your default compute service account as a new principal. It follows the format &lt;code&gt;{&lt;/code&gt;&lt;a href="mailto:your-project-number%7D-compute@developer.gserviceaccount.com"&gt;&lt;code&gt;your-project-number}-compute@developer.gserviceaccount.com&lt;/code&gt;&lt;/a&gt; and you can locate your project number in the project settings. Once you have identified your project number, simply add the default compute service account as a new principal and grant it the &lt;code&gt;Cloud Composer v2 API Service Agent Extension&lt;/code&gt; role.&lt;/p&gt;
&lt;p&gt;  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MjFLpx_0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680952080592/915eaeae-c844-4ba0-8831-40d5c090bb9f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MjFLpx_0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680952080592/915eaeae-c844-4ba0-8831-40d5c090bb9f.png" alt="" width="552" height="887"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;  `&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;

&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You should have an environment variable named &lt;code&gt;GOOGLE_APPLICATION_CREDENTIALS&lt;/code&gt; which points to the location of the key file of the service account. This is necessary for Terraform to manage resources on your behalf.&lt;/p&gt;
&lt;p&gt;Command: &lt;code&gt;export GOOGLE_APPLICATION_CREDENTIALS=/path/to/keyfile.json&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id="heading-setting-up-the-gcloud-cli"&gt;Setting up the GCloud CLI&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Download the installer from &lt;a href="https://cloud.google.com/sdk/docs/downloads-interactive#windows"&gt;here&lt;/a&gt; and install the CLI app.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command (assuming that you have the &lt;code&gt;GOOGLE_APPLICATION_CREDENTIALS&lt;/code&gt; environment variable pointing to your service account key file):&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code&gt;gcloud auth activate-service-account --key-file &lt;span class="hljs-variable"&gt;$GOOGLE_APPLICATION_CREDENTIALS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you're on Windows, you can provide the path to the key file instead of the environment variable.&lt;/p&gt;

&lt;h1 id="heading-setup-your-pipeline"&gt;Setup your pipeline&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: Throughout the process, enable any Google APIs that you're prompted to.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id="heading-step-0-get-the-code"&gt;Step 0: Get the code&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Fork the &lt;a href="https://github.com/Aditya-Gupta1/yelposphere"&gt;repository&lt;/a&gt; (This is important!)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clone the forked repo.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code&gt;git &lt;span class="hljs-built_in"&gt;clone&lt;/span&gt; &amp;lt;forked-repo-git-url&amp;gt;
&lt;span class="hljs-built_in"&gt;cd&lt;/span&gt; yelp-gcp-infrastructure/terraform
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="heading-step-1-modify-the-dbt-env-variable"&gt;Step 1: Modify the DBT env variable&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;dbt/Dockerfile&lt;/code&gt;, modify the value of the &lt;code&gt;GCP_PROJECT&lt;/code&gt; environment variable with the value of your project id. (Specifically, &lt;a href="https://github.com/Aditya-Gupta1/yelposphere/blob/7d56be4d5d2df90909f9bb5389aa9872b3c3a9b4/dbt/Dockerfile#L6"&gt;this&lt;/a&gt; line)&lt;/p&gt;

&lt;p&gt;Commit and push the changes to your repository.&lt;/p&gt;

&lt;h2 id="heading-step-2-save-the-sa-key-file-in-secret-manager"&gt;Step 2: Save the SA key file in Secret Manager&lt;/h2&gt;

&lt;p&gt;DBT will require the service account key file to connect to BigQuery and execute transformations. This key file can be stored as a secret in the Secret Manager and accessed inside DBT's &lt;code&gt;profile.yml&lt;/code&gt; file from Cloud Run.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Go to Secret Manager &amp;gt; Create Secret. (You may need to enable the Secret Manager API)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Give &lt;code&gt;dbt-service-keyfile&lt;/code&gt; as the name of the secret. (Name should exactly match this!)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on Browse and upload the service account key file as the Secret Value.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Create.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the secret overview&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ePqTY2KJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680960607367/42c804a6-5457-4c41-8a44-23750e4f2994.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ePqTY2KJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680960607367/42c804a6-5457-4c41-8a44-23750e4f2994.png" alt="" width="448" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;h2 id="heading-step-3-setup-cloud-run-and-cloud-build-for-dbt"&gt;Step 3: Setup Cloud Run and Cloud Build for DBT&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Go to Cloud Run &amp;gt; Create a Service.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select &lt;code&gt;Continuously deploy new revisions from a source repository&lt;/code&gt; &amp;gt; Setup with Cloud Build.&lt;/p&gt;
&lt;p&gt;  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c7W0tPAn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680957392471/e33ddaab-36bb-4cb6-bdc9-65c1c79dc644.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c7W0tPAn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680957392471/e33ddaab-36bb-4cb6-bdc9-65c1c79dc644.png" alt="" width="543" height="216"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on "Manage connected repositories", sign in to your GitHub account and install the Build Agent on the forked repo only. Then click on Next.&lt;/p&gt;
&lt;p&gt;  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NCcpz5At--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/v2/resize:fit:575/1%2AVXl-9zNHlnsYopajDBf8jA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NCcpz5At--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/v2/resize:fit:575/1%2AVXl-9zNHlnsYopajDBf8jA.png" alt="" width="575" height="770"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select "Dockerfile" as the build type and provide the file path as &lt;code&gt;/dbt/Dockerfile&lt;/code&gt;. Click Save.&lt;/p&gt;
&lt;p&gt;  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MUCRWxDl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680957717426/e886ec94-252c-4f3b-8fec-9c22259acafd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MUCRWxDl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680957717426/e886ec94-252c-4f3b-8fec-9c22259acafd.png" alt="" width="556" height="477"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Give your Cloud Run service a name. Pick a region. This region should be the same as that of your BigQuery dataset (to be configured in the further steps). And then select "Require Authentication".&lt;/p&gt;
&lt;p&gt;  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q0z9BnMj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680958579346/e828e2f9-a425-401a-8062-c25c735a551f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q0z9BnMj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680958579346/e828e2f9-a425-401a-8062-c25c735a551f.png" alt="" width="477" height="623"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Expand the "&lt;strong&gt;Container, Networking, Security&lt;/strong&gt;" section. Inside the &lt;strong&gt;Container&lt;/strong&gt; section, choose Maximum requests per container to be 1.&lt;/p&gt;
&lt;p&gt;  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eKTFyhvr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680960990542/50355e42-c825-4edb-8726-4da0182cf2f3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eKTFyhvr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680960990542/50355e42-c825-4edb-8726-4da0182cf2f3.png" alt="" width="486" height="282"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Under the Secrets section, select "Reference a Secret". Select the secret you created in the previous step. Reference method as "mounted as volume", and mount path as &lt;code&gt;secrets&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X6UldFjv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680961171244/9c8a1b84-8085-49bb-aa2e-d8acbe2631a6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X6UldFjv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680961171244/9c8a1b84-8085-49bb-aa2e-d8acbe2631a6.png" alt="" width="458" height="403"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now, under the &lt;strong&gt;Security&lt;/strong&gt; section, select your service account and click Create.&lt;/p&gt;
&lt;p&gt;  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_IMOecJ_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680961263407/692bc44a-0096-45b5-b52d-cec3f7c67eeb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_IMOecJ_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680961263407/692bc44a-0096-45b5-b52d-cec3f7c67eeb.png" alt="" width="344" height="390"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your Cloud Run service is ready. Go inside your service and you'll find an endpoint for this service. Copy this as you'll need it in future steps.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AHX1kfmv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680961483349/db81d52d-3a88-4853-9928-0283a8f858f5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AHX1kfmv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1680961483349/db81d52d-3a88-4853-9928-0283a8f858f5.png" alt="" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you have set up your DBT code to run.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A drawback of this workaround is that you can't view the data lineage or documentation. If you want to view that, you can always set this up using the DBT cloud as described in the course video:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=rjf6yZNGX8I&amp;amp;list=PL3MmuxUbc_hJed7dXYoJw8DoCuVHhGEQb&amp;amp;index=37"&gt;&lt;/a&gt;&lt;a href="https://www.youtube.com/watch?v=rjf6yZNGX8I&amp;amp;list=PL3MmuxUbc_hJed7dXYoJw8DoCuVHhGEQb&amp;amp;index=37"&gt;https://www.youtube.com/watch?v=rjf6yZNGX8I&amp;amp;amp;list=PL3MmuxUbc_hJed7dXYoJw8DoCuVHhGEQb&amp;amp;amp;index=37&lt;/a&gt;&lt;/p&gt;



&lt;h3 id="heading-optional-understanding-dbt-transformations"&gt;(Optional) Understanding DBT Transformations&lt;/h3&gt;

&lt;p&gt;Let me walk you through the use of DBT in this pipeline. It is responsible for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Partitioning &lt;code&gt;stg_businesses&lt;/code&gt; table on the &lt;code&gt;is_open&lt;/code&gt; column. This will help others in querying this data on the basis of open or closed businesses, as per the requirement.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Creating different data models to answer business questions that will be used to create the final dashboard.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can go through the data model &lt;a href="https://github.com/Aditya-Gupta1/yelposphere/tree/main/dbt/models"&gt;code&lt;/a&gt; to understand how exactly they look.&lt;/p&gt;

&lt;h2 id="heading-step-4-build-your-infrastructure-using-terraform"&gt;Step 4: Build your infrastructure using Terraform&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href="https://github.com/Aditya-Gupta1/yelposphere/blob/main/terraform/terraform.tfvars"&gt;&lt;code&gt;terraform/terraform.tfvars&lt;/code&gt;&lt;/a&gt; file and modify the fields marked with &lt;strong&gt;"TODO"&lt;/strong&gt;, with appropriate values.&lt;/p&gt;

&lt;p&gt;As of now, all the resources are created in the &lt;code&gt;asia-south1&lt;/code&gt; region except for Cloud Composer which is created in &lt;code&gt;us-central1&lt;/code&gt; region (I suggest not changing the region for Cloud Composer). &lt;em&gt;For the rest of the resources, assign the&lt;/em&gt; &lt;strong&gt;&lt;em&gt;same&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;region if you want to change it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The GCS buckets should have a globally unique name which is why you &lt;strong&gt;may&lt;/strong&gt; have to rename them in the file.&lt;/p&gt;

&lt;p&gt;The terraform variable &lt;a href="https://github.com/Aditya-Gupta1/yelposphere/blob/a79bc8d2713bd6a0374c9393ceedf20a34fa5d5b/terraform/terraform.tfvars#L9"&gt;"datalake_name"&lt;/a&gt; will be the name of the GCS bucket for your pipeline which will contain all your data and spark jobs. You'll need it in the next step.&lt;/p&gt;

&lt;p&gt;Also, make sure to change the service account for cloud composer (in that same file) to your service account's email.&lt;/p&gt;

&lt;p&gt;After modifying the necessary fields, run the terraform command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Enter "yes" when prompted and wait for completion.&lt;/p&gt;

&lt;p&gt;Here's what Terraform is doing behind the scenes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Enables the required GCP APIs to work with different resources.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Creates the project's data lake with Spark ingestion scripts inside the &lt;code&gt;scripts&lt;/code&gt; folder.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Creates BigQuery dataset and tables with defined schema.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Creates a Dataproc cluster.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Creates a Cloud Composer environment and uploads the Airflow DAG into it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once the resources are created, it generates output with a list of resources created and some metadata that is required in the later steps. (All the output values are defined in &lt;a href="https://github.com/Aditya-Gupta1/yelposphere/blob/main/terraform/outputs.tf"&gt;this&lt;/a&gt; file.)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="heading-a-note-on-cloud-composer"&gt;A note on Cloud Composer&lt;/h3&gt;

&lt;p&gt;Cloud Composer generally takes around 20-30 mins for successful creation. But during the development, I observed that it might fail randomly due to timeout issues at the backend and throw errors stating permission issues. When it fails, it takes around 45-50 mins to throw the error. 😤&lt;/p&gt;

&lt;p&gt;Now, if you have correctly set up your service account as mentioned earlier, you have no issues with permissions. In such cases, you can try running Terraform once again by changing the name and region of the composer environment. I've found &lt;code&gt;us-central1&lt;/code&gt; to be the most reliable till now.&lt;/p&gt;

&lt;p&gt;Or you can manually create Composer (Composer 2) in the console and give the name, region, and service account and click Create. Once the environment is created, go inside it and click on "Open DAGs Folder".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ODTPh7pR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681046614113/dc57eb4e-75ec-425d-b055-ad4d6f93ddd3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ODTPh7pR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681046614113/dc57eb4e-75ec-425d-b055-ad4d6f93ddd3.png" alt="" width="800" height="37"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will open a GCS bucket where the composer stores all the Airflow DAGs. Click on "Upload files" and upload the &lt;a href="https://github.com/Aditya-Gupta1/yelposphere/blob/main/cloud_composer/ingest_data_dags.py"&gt;&lt;code&gt;cloud_composer/ingest_data_dags.py&lt;/code&gt;&lt;/a&gt; file. This contains the DAG for this project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O6Wk_LJu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681046765632/941f8371-774c-4b08-a6e2-39c8854bfb9e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O6Wk_LJu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681046765632/941f8371-774c-4b08-a6e2-39c8854bfb9e.png" alt="" width="769" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the DAG is uploaded, you can move on to the next steps.&lt;/p&gt;

&lt;h2 id="heading-step-6-upload-airflow-variables-using-airflow-ui"&gt;Step 6: Upload Airflow variables using Airflow UI&lt;/h2&gt;

&lt;p&gt;I've used Airflow variables to pass dynamic values to the DAG. These values are related to infrastructure like the BigQuery table names, Dataproc cluster names, the path of the spark jobs etc.&lt;/p&gt;

&lt;p&gt;You can get these values from the terraform output. Run the following commands (&lt;strong&gt;&lt;em&gt;inside the terraform folder&lt;/em&gt;&lt;/strong&gt;) to get these variables in a JSON file to easily upload them to Airflow.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;terraform output -json &amp;gt; ../tf_output.json
&lt;span class="hljs-built_in"&gt;cd&lt;/span&gt; ..
python utils/get_variables.py --input tf_output.json --output  airflow_variables.json
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You will get &lt;code&gt;airflow_variables.json&lt;/code&gt; file that you can upload to the Airflow UI using the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;In the composer environment, click on "&lt;strong&gt;Open Airflow UI&lt;/strong&gt;". You will be redirected to an Airflow dashboard.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go to Admin &amp;gt; Variables.&lt;/p&gt;
&lt;p&gt;  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WLMty788--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681047323685/e0dbdd5d-9450-4896-be7b-282bcb359bbf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WLMty788--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681047323685/e0dbdd5d-9450-4896-be7b-282bcb359bbf.png" alt="" width="734" height="253"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on "Choose File". Select the airflow_variables.json file and click on "Import Variables".&lt;/p&gt;
&lt;p&gt;  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nAhTbQ4k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681047432557/286a0f0b-ef7f-477e-a7cf-f96e930d198f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nAhTbQ4k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681047432557/286a0f0b-ef7f-477e-a7cf-f96e930d198f.png" alt="" width="560" height="362"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This will upload 16 variables to Airflow.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Similarly, select the &lt;a href="https://github.com/Aditya-Gupta1/yelposphere/blob/main/data_paths_airflow_variables.json"&gt;&lt;code&gt;data_paths_airflow_variables.json&lt;/code&gt;&lt;/a&gt; file already present in the repo and import variables from this file as well.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This uploads 5 more variables to Airflow.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="heading-step-7-create-a-cloud-run-connection-in-airflow"&gt;Step 7: Create a Cloud Run connection in Airflow&lt;/h2&gt;

&lt;p&gt;As I mentioned in the overview section, Airflow needs to send HTTP requests to Cloud Run. So you need to create an Airflow HTTP connection for it to connect to Cloud Run's endpoint. Follow the steps to do so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;In the Airflow UI, under the Admin tab, click on Connections.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the &lt;strong&gt;+&lt;/strong&gt; Icon.&lt;/p&gt;
&lt;p&gt;  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6XVHWo_H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681047964319/79dc2ef6-7ae3-4b5f-b04e-636c79f25494.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6XVHWo_H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681047964319/79dc2ef6-7ae3-4b5f-b04e-636c79f25494.png" alt="" width="656" height="254"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select connection_id as "cloud_run", connection type as "HTTP" and host as your Cloud Run service endpoint. Click on Save.&lt;/p&gt;
&lt;p&gt;  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ru0f61Ck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681048095167/39de3ecd-a220-493e-9e64-a9329bc71048.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ru0f61Ck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681048095167/39de3ecd-a220-493e-9e64-a9329bc71048.png" alt="" width="800" height="713"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, you can refresh your Airflow UI until there are no import errors shown at the top of Airflow UI. (It may take a few seconds for errors to resolve)&lt;/p&gt;

&lt;p&gt;Before:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--piFkzsXa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681048205256/1e01c5f6-d567-4550-8bc4-600444115a01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--piFkzsXa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681048205256/1e01c5f6-d567-4550-8bc4-600444115a01.png" alt="" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w_y6tler--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681048238294/fae15a68-b641-40eb-befa-95b6452592e9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w_y6tler--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681048238294/fae15a68-b641-40eb-befa-95b6452592e9.png" alt="" width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means that your DAG is imported successfully to Airflow and now we just need to run it.&lt;/p&gt;

&lt;h2 id="heading-step-8-run-the-airflow-dag"&gt;Step 8: Run the Airflow DAG&lt;/h2&gt;

&lt;p&gt;Finally, you get to run your DAG and visualize the progress. Click on the &lt;code&gt;process_yelp_data&lt;/code&gt; DAG followed by the play button and then "&lt;strong&gt;Trigger DAG&lt;/strong&gt;".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pxbceyC1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681048751531/4f39bbfe-5ed8-4327-be3d-bd94c61f327f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pxbceyC1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681048751531/4f39bbfe-5ed8-4327-be3d-bd94c61f327f.png" alt="" width="800" height="106"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will start the DAG as you can see on the UI:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--296sStC8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681048848423/700b01f8-891b-4b5f-96dc-4e387c4b87be.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--296sStC8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681048848423/700b01f8-891b-4b5f-96dc-4e387c4b87be.png" alt="" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also see the DAG under the "Graph" tab:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Pwj37Zm4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681561446729/62da7cdd-a03d-4c12-acd6-f3a51040bd37.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Pwj37Zm4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681561446729/62da7cdd-a03d-4c12-acd6-f3a51040bd37.png" alt="" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's what's happening:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;It gets the data from a public bucket into the data lake.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It checks if the files are present in the location given by airflow variables, which is the data lake URL.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If yes, it will submit a spark job to the Dataproc cluster to load that particular file into BigQuery.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once the spark job succeeds, the JSON files are deleted.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;And finally, a GET request is sent to the Cloud Run endpoint to execute DBT transformations and tests.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see all the DBT models created under the &lt;code&gt;yelp_dbt&lt;/code&gt; dataset in BigQuery.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that a task may fail, but that is probably because of the less memory in Airflow. In this case, you just have to wait until it retries itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And here it gets completed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bn3sYjcq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681049357832/47a981c9-74e1-4a05-b3d8-b0dd2283411f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bn3sYjcq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681049357832/47a981c9-74e1-4a05-b3d8-b0dd2283411f.png" alt="" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GBrFYPlt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media.tenor.com/eIV3XiQyw4AAAAAC/you-did-it-willy-wonka-and-the-chocolate-factory.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GBrFYPlt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media.tenor.com/eIV3XiQyw4AAAAAC/you-did-it-willy-wonka-and-the-chocolate-factory.gif" alt="You Did It GIFs | Tenor" width="498" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="heading-step-9-optional-create-a-dashboard-with-google-data-studio"&gt;Step 9 (Optional): Create a dashboard with Google Data Studio&lt;/h2&gt;

&lt;p&gt;I have used Google Data Studio to create a dashboard on top of the DBT models. You can either create your dashboard or simply view my dashboard here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://lookerstudio.google.com/reporting/03290c4d-e20a-4e61-9978-f4e0ae893ff5"&gt;&lt;/a&gt;&lt;a href="https://lookerstudio.google.com/reporting/03290c4d-e20a-4e61-9978-f4e0ae893ff5"&gt;https://lookerstudio.google.com/reporting/03290c4d-e20a-4e61-9978-f4e0ae893ff5&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Here's a screenshot of my dashboard in case you're having any trouble viewing it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--plSlWNQw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681049724843/e06b9ea4-6c70-450d-ada6-e8680a03df1e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--plSlWNQw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681049724843/e06b9ea4-6c70-450d-ada6-e8680a03df1e.png" alt="" width="800" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty cool, isn't it? 😉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3ZvpEXm0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media2.giphy.com/media/IbUUbU4xUDJWcgGMGP/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3ZvpEXm0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media2.giphy.com/media/IbUUbU4xUDJWcgGMGP/giphy.gif" alt="Yeah-i-know GIFs - Get the best GIF on GIPHY" width="478" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PS: I'll leave out the steps to create a dashboard on google data studio as I believe it is very convenient and self-explanatory since it is a drag-and-drop tool.&lt;/p&gt;

&lt;h1 id="heading-clean-up-your-gcp-resources"&gt;Clean up your GCP Resources!&lt;/h1&gt;

&lt;p&gt;Well, this is important because:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1ELXQYzs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://y.yarn.co/5dd375ae-7f47-4555-a9ef-a0a7305cdc18_text.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1ELXQYzs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://y.yarn.co/5dd375ae-7f47-4555-a9ef-a0a7305cdc18_text.gif" alt="YARN | It costs money. | Moonstruck (1987) | Video gifs by quotes |  5dd375ae | 紗" width="400" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you don't clean it, then ...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qXgL48Kw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media0.giphy.com/media/xAe3BXJVr9tJscgei5/200w.gif%3Fcid%3D6c09b952gubsg4c6fxuqqengghnw9arr5mmkd163us6ncnc3%26rid%3D200w.gif%26ct%3Dg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qXgL48Kw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media0.giphy.com/media/xAe3BXJVr9tJscgei5/200w.gif%3Fcid%3D6c09b952gubsg4c6fxuqqengghnw9arr5mmkd163us6ncnc3%26rid%3D200w.gif%26ct%3Dg" alt="Hey-that-costs-money GIFs - Get the best GIF on GIPHY" width="200" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So better clean it up as soon as you're done checking out the project. Here are the steps to do it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Inside the &lt;code&gt;terraform&lt;/code&gt; folder, run the &lt;code&gt;terraform destroy&lt;/code&gt; command and enter yes when prompted. It'll take a few minutes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once done, check your GCS buckets and delete any remaining buckets.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Generally, Composer's GCS bucket doesn't get deleted with Terraform destroy and has to be manually deleted.)&lt;/p&gt;

&lt;p&gt;Then go to &lt;strong&gt;Cloud Build, Cloud Run, Artifact Registry and Secret Manager&lt;/strong&gt; to delete everything present there.&lt;/p&gt;

&lt;p&gt;That's it.&lt;/p&gt;

&lt;h1 id="heading-now-what"&gt;Now What?&lt;/h1&gt;

&lt;p&gt;Since now you got to build the data pipeline (and destroy it 😂), here are a few things that I think can be improved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Better code structure for Terraform.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;More data transformations on DBT.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Adding more data quality tests on DBT models.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Research about any easier way to orchestrate DBT with Airflow (although I did a LOT of research and asked a lot of different communities about this)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CI/CD Pipeline&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id="heading-conclusion"&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;This was my capstone project for the Data Engineering Zoomcamp.&lt;/p&gt;

&lt;p&gt;I hope you like it. If you have any questions about the project, face any problems while following along or have a suggestion for me, feel free to comment or drop me a DM on &lt;a href="https://twitter.com/itsadityagupta"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/itsadityagupta/"&gt;Linkedin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LTGfpHxp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://news-legacy-vendors.abc-prod.net.au/scribble/2971157/5356cc15-7d8f-4309-8330-89dd39bca58f.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LTGfpHxp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://news-legacy-vendors.abc-prod.net.au/scribble/2971157/5356cc15-7d8f-4309-8330-89dd39bca58f.gif" alt="COVID updates: Victoria to reach 70 per cent first doses target today,  restrictions ease in 12 NSW regions - ABC News" width="480" height="309"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;This article is published w/ &lt;a href="https://scattr.io?ref=dev"&gt;Scattr  ↗️&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Storage Transfer Service in GCP</title>
      <dc:creator>Aditya Gupta</dc:creator>
      <pubDate>Thu, 30 Mar 2023 06:34:19 +0000</pubDate>
      <link>https://dev.to/itsadityagupta/storage-transfer-service-in-gcp-47i5</link>
      <guid>https://dev.to/itsadityagupta/storage-transfer-service-in-gcp-47i5</guid>
      <description>&lt;h2 id="heading-introduction"&gt;Introduction&lt;/h2&gt;

&lt;p&gt;The Storage Transfer Service is a &lt;strong&gt;data migration service&lt;/strong&gt; provided by Google Cloud Platform (GCP) that allows users to move large amounts of data between on-premises data centres, cloud storage solutions, and other cloud providers.&lt;/p&gt;

&lt;p&gt;The Storage Transfer Service can transfer data from sources such as Amazon S3, Microsoft Azure, and other third-party storage providers, as well as from file servers and other on-premises storage systems. Users can set up transfers that are one-time or recurring, with customizable scheduling and filtering options.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The service supports various transfer options, including parallel transfer and resumable transfer, which helps to ensure data integrity and efficient data transfer. Additionally, it provides features like data validation, notification, and logging that enable users to monitor the transfer process and detect issues.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The Storage Transfer Service can be managed using the GCP Console or through APIs, and it is charged based on the amount of data transferred and the number of operations performed.&lt;/p&gt;




&lt;p&gt;In this blog post, I'll guide you through the process of creating a storage transfer service using Terraform to transfer data from one Google Cloud Storage (GCS) bucket to another. To follow along with this tutorial, you'll need to have the following prerequisites in place:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Terraform installed on your local machine&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A Google Cloud Platform (GCP) project created in the Google Cloud Console&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A GCP service account with a JSON private key file and the necessary permissions&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you haven't created a service account yet, check out my previous blog post on how to create one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://itsadityagupta.hashnode.dev/setting-up-a-gcp-service-account"&gt;&lt;/a&gt;&lt;a href="https://itsadityagupta.hashnode.dev/setting-up-a-gcp-service-account"&gt;https://itsadityagupta.hashnode.dev/setting-up-a-gcp-service-account&lt;/a&gt;&lt;/p&gt;



&lt;h2 id="heading-assumptions"&gt;Assumptions&lt;/h2&gt;

&lt;p&gt;I have a Google Cloud Storage (GCS) bucket named "&lt;strong&gt;gcs_capstone_dezoomcamp&lt;/strong&gt;" that contains approximately &lt;strong&gt;9 gigabytes&lt;/strong&gt; of data. My goal is to transfer all the files from this bucket to the "data" folder within a &lt;strong&gt;new&lt;/strong&gt; bucket called "&lt;strong&gt;destination&lt;/strong&gt;".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The source bucket is already created whereas the destination bucket has to be created.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id="heading-initializing-a-terraform-project"&gt;Initializing a Terraform Project&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a new directory for your Terraform project and create a new file named "&lt;strong&gt;main.tf&lt;/strong&gt;". In the "&lt;strong&gt;main.tf&lt;/strong&gt;" file, define the provider by specifying "google" as the provider and your project credentials.&lt;/li&gt;
&lt;/ol&gt;


&lt;a href="https://gist.github.com/Aditya-Gupta1/4f30855c9a70477d662fe5a4325e81db"&gt;&lt;/a&gt;&lt;a href="https://gist.github.com/Aditya-Gupta1/4f30855c9a70477d662fe5a4325e81db"&gt;https://gist.github.com/Aditya-Gupta1/4f30855c9a70477d662fe5a4325e81db&lt;/a&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: If the environment variable &lt;code&gt;GOOGLE_APPLICATION_CREDENTIALS&lt;/code&gt; is set to the path of the service account key file, then the &lt;code&gt;credentials&lt;/code&gt; fields is not necessary.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Run the &lt;code&gt;terraform init&lt;/code&gt; command to initialize the project.&lt;/p&gt;

&lt;h2 id="heading-create-the-destination-gcs-bucket"&gt;Create the destination GCS bucket&lt;/h2&gt;

&lt;p&gt;Use the "&lt;strong&gt;google_storage_bucket&lt;/strong&gt;" resource to define the destination bucket. Specify the "name" parameter to set the name of the bucket (globally unique), and the "location" parameter to set the location of the bucket.&lt;/p&gt;


&lt;a href="https://gist.github.com/Aditya-Gupta1/8a785b0c503bf07f4aab7a1b6e888ae1"&gt;&lt;/a&gt;&lt;a href="https://gist.github.com/Aditya-Gupta1/8a785b0c503bf07f4aab7a1b6e888ae1"&gt;https://gist.github.com/Aditya-Gupta1/8a785b0c503bf07f4aab7a1b6e888ae1&lt;/a&gt;

&lt;h2 id="heading-grant-permissions-to-gcs-buckets"&gt;Grant permissions to GCS buckets&lt;/h2&gt;

&lt;p&gt;Storage Transfer Service uses a &lt;em&gt;default service account&lt;/em&gt; for data transfer which doesn't have required accesses by default. Hence, we have to configure access to the source GCS bucket and the destination GCS bucket before creating a data transfer job.&lt;/p&gt;

&lt;h3 id="heading-roles-for-source-gcs-bucket"&gt;Roles for source GCS bucket&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;One of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Storage Object Viewer&lt;/strong&gt; (&lt;code&gt;roles/storage.objectViewer&lt;/code&gt;) if the transfer is to another Cloud Storage bucket.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Storage Object Creator&lt;/strong&gt; (&lt;code&gt;roles/storage.objectCreator&lt;/code&gt;) if the transfer is to a file system.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Plus one of the:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Storage Legacy Bucket Writer&lt;/strong&gt; (&lt;code&gt;roles/storage.legacyBucketWriter&lt;/code&gt;) if the object deletes permission is required.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Storage Legacy Bucket Reader&lt;/strong&gt; (&lt;code&gt;roles/storage.legacyBucketReader&lt;/code&gt;) if the object deletes permission is not required.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id="heading-assigning-roles-to-the-source-gcs-bucket"&gt;Assigning roles to the source GCS bucket&lt;/h3&gt;

&lt;p&gt;To assign roles and permissions to the storage transfer service, it's important to first identify the default service account being used. This can be achieved by utilizing the "&lt;strong&gt;google_storage_transfer_project_service_account&lt;/strong&gt;" data block.&lt;/p&gt;

&lt;p&gt;Once the service account has been identified, it can be granted the necessary roles and added as a member to the relevant Google Cloud Storage (GCS) bucket. Let's look at the terraform code to assign roles to the source GCS bucket:&lt;/p&gt;


&lt;a href="https://gist.github.com/Aditya-Gupta1/a49dd76be109f5c242b285fc3641c792"&gt;&lt;/a&gt;&lt;a href="https://gist.github.com/Aditya-Gupta1/a49dd76be109f5c242b285fc3641c792"&gt;https://gist.github.com/Aditya-Gupta1/a49dd76be109f5c242b285fc3641c792&lt;/a&gt;

&lt;p&gt;&lt;em&gt;Note that the&lt;/em&gt; &lt;code&gt;legacyBucketWriter&lt;/code&gt; &lt;em&gt;role is not assigned to the source bucket because, in the data transfer job, I'll not delete the source files.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="heading-roles-for-destination-gcs-bucket"&gt;Roles for destination GCS bucket&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Storage Legacy Bucket Writer&lt;/strong&gt; (&lt;code&gt;roles/storage.legacyBucketWriter&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="heading-assigning-roles-to-the-destination-gcs-bucket"&gt;Assigning roles to the destination GCS bucket&lt;/h3&gt;


&lt;a href="https://gist.github.com/Aditya-Gupta1/8b6fc66fd3ff91f76befa86e254fcf93"&gt;&lt;/a&gt;&lt;a href="https://gist.github.com/Aditya-Gupta1/8b6fc66fd3ff91f76befa86e254fcf93"&gt;https://gist.github.com/Aditya-Gupta1/8b6fc66fd3ff91f76befa86e254fcf93&lt;/a&gt;

&lt;h2 id="heading-define-the-data-transfer-job"&gt;Define the data transfer job&lt;/h2&gt;

&lt;p&gt;Use the "&lt;strong&gt;google_storage_transfer_job&lt;/strong&gt;" resource to define the data transfer job. Specify the source and destination bucket names as well as the desired transfer options.&lt;/p&gt;


&lt;a href="https://gist.github.com/Aditya-Gupta1/536facac85cf99eea1398f3ecb90ca64"&gt;&lt;/a&gt;&lt;a href="https://gist.github.com/Aditya-Gupta1/536facac85cf99eea1398f3ecb90ca64"&gt;https://gist.github.com/Aditya-Gupta1/536facac85cf99eea1398f3ecb90ca64&lt;/a&gt;

&lt;h2 id="heading-overview"&gt;Overview&lt;/h2&gt;

&lt;p&gt;Here's a complete &lt;strong&gt;"main.tf"&lt;/strong&gt; file:&lt;/p&gt;


&lt;a href="https://gist.github.com/Aditya-Gupta1/3db7dedf4e85dd9bcc1d44632676441f"&gt;&lt;/a&gt;&lt;a href="https://gist.github.com/Aditya-Gupta1/3db7dedf4e85dd9bcc1d44632676441f"&gt;https://gist.github.com/Aditya-Gupta1/3db7dedf4e85dd9bcc1d44632676441f&lt;/a&gt;

&lt;p&gt;Now, &lt;strong&gt;run the command&lt;/strong&gt; &lt;code&gt;terraform apply&lt;/code&gt; and enter "yes" when prompted. This will perform the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Create a destination bucket.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Assign roles to the default service account for the source and the destination bucket.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a data transfer job &lt;strong&gt;&lt;em&gt;with a schedule as defined&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Make sure to run &lt;code&gt;terraform destroy&lt;/code&gt; to destroy all the resources.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id="heading-conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;By following the steps outlined in this blog post, you can easily define a storage transfer job and assign the necessary permissions to the source and destination buckets. This allows you to transfer data between buckets or even between different cloud storage providers.&lt;/p&gt;

&lt;p&gt;More information about this can be found in the following blogs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://cloud.google.com/storage-transfer-service"&gt;https://cloud.google.com/storage-transfer-service&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_transfer_job"&gt;https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_transfer_job&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://cloud.google.com/storage-transfer/docs/sources-and-sinks"&gt;https://cloud.google.com/storage-transfer/docs/sources-and-sinks&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found this blog post helpful and informative. I'd love to hear your thoughts on the topic or any issues you've had following along. Did I miss anything important?&lt;/p&gt;

&lt;p&gt;Please leave a comment below and let me know what you think. Your feedback is valuable and can help me improve my writing and provide additional insights for other readers. Thank you for taking the time to read this post!&lt;/p&gt;


&lt;p&gt;This article is published w/ &lt;a href="https://scattr.io?ref=dev"&gt;Scattr  ↗️&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Setting up a GCP Service account</title>
      <dc:creator>Aditya Gupta</dc:creator>
      <pubDate>Thu, 30 Mar 2023 06:33:50 +0000</pubDate>
      <link>https://dev.to/itsadityagupta/setting-up-a-gcp-service-account-4213</link>
      <guid>https://dev.to/itsadityagupta/setting-up-a-gcp-service-account-4213</guid>
      <description>&lt;h2 id="heading-introduction"&gt;Introduction&lt;/h2&gt;

&lt;p&gt;A service account in Google Cloud Platform (GCP) is a special type of account that allows applications running on GCP to interact with other GCP services, such as Google Cloud Storage, Google Cloud SQL, and Google Compute Engine.&lt;/p&gt;

&lt;p&gt;Service accounts are typically used by applications that run on GCP, such as virtual machines, containers, and applications running on Google Kubernetes Engine. Using service accounts, applications can securely access GCP resources without requiring users to authenticate individually.&lt;/p&gt;

&lt;p&gt;Each service account is associated with a set of credentials, which typically include a private key that can be used to authenticate the service account with other GCP services. Service accounts can also be granted specific permissions to access particular resources, making it easy to control access to GCP resources on a per-service basis.&lt;/p&gt;

&lt;p&gt;In this blog post, I will guide you through the step-by-step process of creating a service account on the Google Cloud Platform (GCP). This account will be specifically created to be used for my future blog posts on GCP. By following this tutorial, you will be able to create a service account and obtain the necessary credentials to interact with other GCP services.&lt;/p&gt;

&lt;p&gt;The instructions provided in this post will be concise and easy to follow, ensuring that you can complete the process quickly and efficiently. In case you need to create a service account in the future, you can refer back to this post for guidance. Let's get started!&lt;/p&gt;

&lt;h2 id="heading-creating-a-service-account"&gt;Creating a Service Account&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Sign in to your google cloud console at &lt;a href="https://console.cloud.google.com"&gt;&lt;strong&gt;https://console.cloud.google.com&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;From the dropdown at the top, select the project you want your service account to be associated with.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;From the navigation menu on the left-hand side, select "&lt;strong&gt;IAM &amp;amp; Admin&lt;/strong&gt;" and then select "&lt;strong&gt;Service accounts&lt;/strong&gt;".&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the "&lt;strong&gt;Create service account&lt;/strong&gt;" button at the top of the page.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enter a name for your service account and provide an optional description. Click "&lt;strong&gt;Create and Continue&lt;/strong&gt;".&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Under the "&lt;strong&gt;Grant this service account access to project&lt;/strong&gt;", select the following roles to be assigned to the service account and click "&lt;strong&gt;Continue&lt;/strong&gt;" (you can modify the roles as per your requirements):&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DReg_raZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1678346726486/c723ec2f-f813-48bd-b54b-3154e4d14e24.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DReg_raZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1678346726486/c723ec2f-f813-48bd-b54b-3154e4d14e24.png" alt="" width="294" height="399"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt; Click "&lt;strong&gt;Done&lt;/strong&gt;".&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id="heading-creating-a-service-account-key"&gt;Creating a Service Account Key&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;From the navigation menu on the left-hand side, select "&lt;strong&gt;IAM &amp;amp; Admin&lt;/strong&gt;" and then select "&lt;strong&gt;Service accounts&lt;/strong&gt;".&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Locate the service account that you created and click on its name.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the "&lt;strong&gt;Keys&lt;/strong&gt;" tab and then click "&lt;strong&gt;Add Key&lt;/strong&gt;".&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the "&lt;strong&gt;Create new key&lt;/strong&gt;" button, select "&lt;strong&gt;JSON&lt;/strong&gt;" as the Key Type and click "&lt;strong&gt;Create&lt;/strong&gt;".&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This will download the JSON key file to your local machine.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id="heading-setting-up-the-environment-variable"&gt;Setting up the environment variable&lt;/h2&gt;

&lt;p&gt;One last thing is to create an environment variable &lt;code&gt;GOOGLE_APPLICATION_CREDENTIALS&lt;/code&gt; and point it to the path of the downloaded key file.&lt;/p&gt;

&lt;h2 id="heading-conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Now, you have a service account that can be used to interact with other GCP services and infrastructure management (through IaaC tools like Terraform etc.) as well.&lt;/p&gt;

&lt;p&gt;To learn more about service accounts, you can go over the official documentation here: &lt;a href="https://cloud.google.com/iam/docs/service-account-overview"&gt;https://cloud.google.com/iam/docs/service-account-overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope you found this blog post helpful and informative. I'd love to hear your thoughts on the topic or any issues you've had following along. Did I miss anything important? Please leave a comment below and let me know what you think. Your feedback is valuable and can help me improve my writing and provide additional insights for other readers. Thank you for taking the time to read this post!&lt;/p&gt;


&lt;p&gt;This article is published w/ &lt;a href="https://scattr.io?ref=dev"&gt;Scattr  ↗️&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Setting up the development environment on Google Virtual Machine</title>
      <dc:creator>Aditya Gupta</dc:creator>
      <pubDate>Thu, 30 Mar 2023 06:33:48 +0000</pubDate>
      <link>https://dev.to/itsadityagupta/setting-up-the-development-environment-on-google-virtual-machine-1hm3</link>
      <guid>https://dev.to/itsadityagupta/setting-up-the-development-environment-on-google-virtual-machine-1hm3</guid>
      <description>&lt;p&gt;I'm participating in this year's cohort of the &lt;a href="https://github.com/DataTalksClub/data-engineering-zoomcamp"&gt;Data Engineering Zoomcamp 2023.&lt;/a&gt; This is a community-led, free data engineering course of about 8 weeks. In this blog, I'll summarise the steps to configure a Google Virtual Machine to make it ready for the rest of the course.&lt;/p&gt;

&lt;h2 id="heading-configure-ssh-keys"&gt;Configure SSH Keys&lt;/h2&gt;

&lt;p&gt;Generate a new SSH key with the following commands:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-built_in"&gt;cd&lt;/span&gt; ~/.ssh
ssh-keygen -t rsa -f &amp;lt;key-file-name&amp;gt; -C &amp;lt;username&amp;gt; -b 2048
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It'll raise a prompt to enter a passphrase. You can leave it and press &lt;code&gt;enter&lt;/code&gt;. If it asks for confirmation, press &lt;code&gt;enter&lt;/code&gt; again. Here's an example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mWWdZTLy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676808135879/b1991e1e-17f7-48ad-9969-42b0bfa139e5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mWWdZTLy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676808135879/b1991e1e-17f7-48ad-9969-42b0bfa139e5.png" alt="" width="416" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This generates 2 files in the .ssh folder, one for the public (&lt;code&gt;gcp-blog.pub&lt;/code&gt;) and one for the private key (&lt;code&gt;gcp-blog&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--09LpvOYx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676808454293/65b3151b-8e81-44d4-bad4-d773350341f5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--09LpvOYx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676808454293/65b3151b-8e81-44d4-bad4-d773350341f5.png" alt="" width="434" height="50"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, upload the public key to GCP with the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the &lt;code&gt;gcp-blog.pub&lt;/code&gt; file and copy its contents. Or you can use the &lt;code&gt;cat&lt;/code&gt; command to display the contents in the terminal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RcM18dI6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676959147606/be6e4c24-d37d-4648-8db1-25e3a5af226d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RcM18dI6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676959147606/be6e4c24-d37d-4648-8db1-25e3a5af226d.png" alt="" width="800" height="55"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the Google Cloud console &amp;gt; Compute Engine &amp;gt; Settings &amp;gt; Metadata.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HkM4UIY6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676958736525/5de62633-306d-41a4-85d4-e26848decba8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HkM4UIY6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676958736525/5de62633-306d-41a4-85d4-e26848decba8.png" alt="" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on SSH Keys &amp;gt; Add SSH Keys&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qX51meTS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676958954494/e1839785-46aa-47d7-9521-c53beafff57a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qX51meTS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676958954494/e1839785-46aa-47d7-9521-c53beafff57a.png" alt="" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Paste the contents of the public key that you copied previously on the text box and click Save.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3nElkj_h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676959617038/92a7383a-6381-4664-9fc1-2d1973961641.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3nElkj_h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676959617038/92a7383a-6381-4664-9fc1-2d1973961641.png" alt="" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, you can connect to your Google VMs using the following command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ssh -i &amp;lt;PATH_TO_PRIVATE_KEY&amp;gt; &amp;lt;USERNAME&amp;gt;@&amp;lt;EXTERNAL_IP&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="heading-create-a-virtual-machine"&gt;Create a Virtual Machine&lt;/h2&gt;

&lt;p&gt;To set up a Virtual Machine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to Compute Engine &amp;gt; VM Instances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bgJeMJFG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676960349912/be0d9107-6b5c-4cb1-b42c-267f86b23ac2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bgJeMJFG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676960349912/be0d9107-6b5c-4cb1-b42c-267f86b23ac2.png" alt="" width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on Create Instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GkAR5Ejq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676960443427/43d7004b-5764-4695-b70d-e5b8aa2b85cd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GkAR5Ejq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676960443427/43d7004b-5764-4695-b70d-e5b8aa2b85cd.png" alt="" width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Populate the configurations for the VM with the following details (&lt;em&gt;Name and Region can be as per your preference&lt;/em&gt;):&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O-WGDUo3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676960829175/4d599ca8-df67-4c2b-b8eb-09c9a1c393bc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O-WGDUo3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676960829175/4d599ca8-df67-4c2b-b8eb-09c9a1c393bc.png" alt="" width="800" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next, change the boot disk with the following configurations:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gYbk3hOL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676964375425/41f1d4b8-dbc0-4151-ae37-5dff3699d368.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gYbk3hOL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676964375425/41f1d4b8-dbc0-4151-ae37-5dff3699d368.png" alt="" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leave the rest of the configurations to default values and click Create.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This will spin up a virtual machine instance for you. In order to ssh into this instance, run the following command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ssh -i &amp;lt;PATH_TO_PRIVATE_KEY&amp;gt; &amp;lt;USERNAME&amp;gt;@&amp;lt;EXTERNAL_IP&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here's an example on my system:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8adYqv36--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676964776339/6ae41758-2a8b-41b4-bbc0-a1341b49718c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8adYqv36--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676964776339/6ae41758-2a8b-41b4-bbc0-a1341b49718c.png" alt="" width="592" height="572"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also configure an ssh alias, which is a convenient way to store the ssh details in a config file. You can follow my blog on this and set up your alias to easily connect with a VM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://itsadityagupta.hashnode.dev/ssh-simplified-aliasing-credentials-with-config-files"&gt;&lt;/a&gt;&lt;a href="https://itsadityagupta.hashnode.dev/ssh-simplified-aliasing-credentials-with-config-files"&gt;https://itsadityagupta.hashnode.dev/ssh-simplified-aliasing-credentials-with-config-files&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;I have created an alias for the VM by the name &lt;code&gt;dezoomcamp&lt;/code&gt;. Here's the new command to ssh:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ssh dezoomcamp
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w6nrAOVM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676965031665/26148bb9-5801-49be-bb46-672cc72f79ba.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w6nrAOVM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676965031665/26148bb9-5801-49be-bb46-672cc72f79ba.png" alt="" width="514" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to connect to a VM using any other options, please go through the official documentation on &lt;a href="https://cloud.google.com/compute/docs/connect/standard-ssh#connect_to_vms"&gt;&lt;strong&gt;Connecting to VMs&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id="heading-configure-the-virtual-machine"&gt;Configure the Virtual Machine&lt;/h2&gt;

&lt;p&gt;Now that you have a Virtual Machine running and a way to ssh into it and run Linux commands, let's start with installing the requirements of the course to make it ready for development.&lt;/p&gt;

&lt;h4 id="heading-installing-anaconda"&gt;Installing Anaconda&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Visit &lt;a href="https://www.anaconda.com/products/distribution"&gt;Anaconda's website&lt;/a&gt; and copy the link to Linux 64-Bit Installer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0kdaSF3e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676965555810/fde6028a-9b3f-49c1-a9a2-6bba3b37fd21.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0kdaSF3e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676965555810/fde6028a-9b3f-49c1-a9a2-6bba3b37fd21.png" alt="" width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Download the file in the VM:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;      wget https://repo.anaconda.com/archive/Anaconda3-2022.10-Linux-x86_64.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the downloaded file:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;      bash Anaconda3-2022.10-Linux-x86_64.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Keep pressing Enter to scroll down and enter &lt;code&gt;yes&lt;/code&gt; to accept the license terms.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Press Enter to confirm the default location.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enter &lt;code&gt;yes&lt;/code&gt; to run the &lt;code&gt;conda init&lt;/code&gt; when asked.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After the installation is complete, run the command &lt;code&gt;source .bashrc&lt;/code&gt; to apply the changes to the &lt;code&gt;.bashrc&lt;/code&gt; file. Alternatively, you can log out of the session using the &lt;code&gt;logout&lt;/code&gt; command and then ssh back in for the changes to take effect.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lLNEcIPw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676966299230/ef36655c-fcfe-4096-bee4-acb02e48a2f2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lLNEcIPw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676966299230/ef36655c-fcfe-4096-bee4-acb02e48a2f2.png" alt="" width="295" height="67"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll notice the anaconda environment name in the shell prompt once the changes are applied. Also, from the above image it is confirmed that python is also installed.&lt;/p&gt;

&lt;h4 id="heading-installing-docker"&gt;Installing Docker&lt;/h4&gt;

&lt;p&gt;Run the following commands:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo apt-get update
sudo apt-get upgrade
sudo apt-get install docker.io
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will install docker but you'll not be able to run it without &lt;code&gt;sudo&lt;/code&gt;. It'll throw a permission denied error:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M92nXBP---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676966712431/e49b7047-f3e2-4a51-9b95-25a9b2cb099b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M92nXBP---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676966712431/e49b7047-f3e2-4a51-9b95-25a9b2cb099b.png" alt="" width="626" height="89"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To run docker without &lt;code&gt;sudo&lt;/code&gt;, run the following commands:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo groupadd docker
sudo gpasswd -a &lt;span class="hljs-variable"&gt;$USER&lt;/span&gt; docker
sudo service docker restart
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(Refer to &lt;a href="https://github.com/sindresorhus/guides/blob/main/docker-without-sudo.md"&gt;this link&lt;/a&gt; for an explanation of the above commands.)&lt;/p&gt;

&lt;p&gt;Log out of the ssh session and log back in to re-evaluate the group memberships and try running the &lt;code&gt;docker run hello-world&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0HAdxyPg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676967224382/7bd906e1-1790-47b1-bda1-8558bc33c11e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0HAdxyPg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676967224382/7bd906e1-1790-47b1-bda1-8558bc33c11e.png" alt="" width="564" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This time it works!&lt;/p&gt;

&lt;h4 id="heading-installing-docker-compose"&gt;Installing docker-compose&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Go to the docker-compose &lt;a href="https://github.com/docker/compose"&gt;GitHub repo&lt;/a&gt; and under the latest release, find the "&lt;code&gt;docker-compose-linux-x86_64&lt;/code&gt;" asset and copy its link.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new &lt;code&gt;bin&lt;/code&gt; folder in VM and download the asset into it:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;      mkdir bin
      &lt;span class="hljs-built_in"&gt;cd&lt;/span&gt; bin
      wget https://github.com/docker/compose/releases/download/v2.16.0/docker-compose-linux-x86_64 -O docker-compose
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This will create a new file &lt;code&gt;docker-compose&lt;/code&gt; in the &lt;code&gt;bin&lt;/code&gt; folder.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make this file executable:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;      chmod +x docker-compose
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now, add the bin folder to the path:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Go to the home directory: &lt;code&gt;cd&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the .bashrc file: &lt;code&gt;nano .bashrc&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Paste the following to the end of the &lt;code&gt;.bashrc&lt;/code&gt; file:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;      &lt;span class="hljs-built_in"&gt;export&lt;/span&gt; PATH=&lt;span class="hljs-string"&gt;"&lt;span&gt;${HOME}&lt;/span&gt;/bin:&lt;span&gt;${PATH}&lt;/span&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Press &lt;code&gt;Ctrl+O&lt;/code&gt; &amp;gt; &lt;code&gt;enter&lt;/code&gt; &amp;gt; &lt;code&gt;Ctrl+X&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run &lt;code&gt;source .bashrc&lt;/code&gt; for the changes to take effect.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Now, you have docker-compose installed as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f42zfkFZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676968180159/71ca4022-fbb0-486b-baff-c8a063b71159.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f42zfkFZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676968180159/71ca4022-fbb0-486b-baff-c8a063b71159.png" alt="" width="374" height="93"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4 id="heading-installing-pgcli"&gt;Installing Pgcli&lt;/h4&gt;

&lt;p&gt;Pgcli is used to connect to the Postgres database and execute queries. It can be installed using the following command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;pip install pgcli
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once the installation is complete, you can connect to a Postgres database using the command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;pgcli -h &amp;lt;hostname&amp;gt; -u &amp;lt;username&amp;gt; -d &amp;lt;database-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It will prompt for the password afterwards.&lt;/p&gt;

&lt;h4 id="heading-installing-terraform"&gt;Installing Terraform&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Go to terraform's &lt;a href="https://developer.hashicorp.com/terraform/downloads"&gt;installation website&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy the link for Linux's AMD64 file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tUARw3a2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677043960701/f9352f96-1ed2-4385-a370-d5dac99c9955.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tUARw3a2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677043960701/f9352f96-1ed2-4385-a370-d5dac99c9955.png" alt="" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;  Go to the bin folder created previously and download the file using &lt;code&gt;wget&lt;/code&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;    &lt;span class="hljs-built_in"&gt;cd&lt;/span&gt; ~/bin
    wget https://releases.hashicorp.com/terraform/1.3.9/terraform_1.3.9_linux_amd64.zip
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To unzip this file, install the unzip package:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;    sudo apt-get install unzip
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now, unzip the file:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;    unzip terraform_1.3.9_linux_amd64.zip
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There will be a &lt;code&gt;terraform&lt;/code&gt; executable file extracted. You can delete the zip file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Since this file is in the bin folder and the bin folder is in the path, everything is set up.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Try running the following command to verify terraform's installation:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;    terraform --version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dD5wnrUe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677045726326/8039e644-41bd-4374-970a-6776ad6dbe91.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dD5wnrUe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677045726326/8039e644-41bd-4374-970a-6776ad6dbe91.png" alt="" width="685" height="61"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id="heading-creating-a-service-account"&gt;Creating a service account&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Go to IAM and Admin &amp;gt; Service Accounts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3oxm8Qwn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677046425534/dca26177-3aaf-4423-bb7f-3c4c6a91caba.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3oxm8Qwn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677046425534/dca26177-3aaf-4423-bb7f-3c4c6a91caba.png" alt="" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Click on the "Create Service Account" button at the top and provide the service account name.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Assign the following roles to this service account:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Lzp95lxW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677047523030/7c8ce8d5-be46-4c1d-83de-e77dea1789fe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Lzp95lxW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677047523030/7c8ce8d5-be46-4c1d-83de-e77dea1789fe.png" alt="" width="590" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Click Done. The service account is now created.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To generate keys for this service account, click on the 3-dot menu and then on "manage keys"&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QLx0WV_m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677047814762/1d1e9382-ac2c-4f7e-acd0-8a8a2a2a1553.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QLx0WV_m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677047814762/1d1e9382-ac2c-4f7e-acd0-8a8a2a2a1553.png" alt="" width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;On the following page, click on Add Key &amp;gt; Create new key&amp;gt; JSON format. Click Create.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This will download a JSON file. This is to be uploaded to the VM using SFTP.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the path of the downloaded file on the terminal and open an SFTP connection by running the command: &lt;code&gt;sftp &amp;lt;ssh-alias-name&amp;gt;.&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next, create a folder &lt;code&gt;.gcp&lt;/code&gt; and upload the credentials file into it by running the following commands:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;    mkdir .gcp
    put &amp;lt;credentials-file-name&amp;gt; .gcp/
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This will upload the file to VM. Here's a screenshot from my system:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MXBpKoHk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677048420681/6ed1273b-17fd-4fa4-b108-8fb4825e46cc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MXBpKoHk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677048420681/6ed1273b-17fd-4fa4-b108-8fb4825e46cc.png" alt="" width="800" height="131"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id="heading-authenticate-gcp-using-the-service-account-credentials"&gt;Authenticate GCP using the service account credentials&lt;/h4&gt;

&lt;p&gt;To authenticate GCP, we need to set an environment variable &lt;code&gt;$GOOGLE_APPLICATION_CREDENTIALS&lt;/code&gt; to point to the service account JSON file.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-built_in"&gt;export&lt;/span&gt; GOOGLE_APPLICATION_CREDENTIALS=/home/aditya/.gcp/&amp;lt;credentials-filename&amp;gt;.json
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next, authenticate GCP using the following command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;gcloud auth activate-service-account --key-file &lt;span class="hljs-variable"&gt;$GOOGLE_APPLICATION_CREDENTIALS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here's my example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gWnmnpvP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677048891324/d452eb5c-ba02-4da8-b3e1-fc8f220706b7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gWnmnpvP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677048891324/d452eb5c-ba02-4da8-b3e1-fc8f220706b7.png" alt="" width="800" height="56"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4 id="heading-installing-pyspark"&gt;Installing Pyspark&lt;/h4&gt;

&lt;p&gt;Instructions to install spark on Linux are well documented on the course's GitHub repository.&lt;/p&gt;

&lt;p&gt;You can follow the below 2 files in the mentioned order, to install Pyspark:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/blob/main/week_5_batch_processing/setup/linux.md"&gt;&lt;code&gt;linux.md&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/blob/main/week_5_batch_processing/setup/pyspark.md"&gt;&lt;code&gt;pyspark.md&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: You can write all the environment variables mentioned in the above 2 files to &lt;code&gt;.bashrc&lt;/code&gt; followed by the &lt;code&gt;source .bashrc&lt;/code&gt; command to make the variables permanent. Otherwise they'll need to be setup everytime you restart your VM.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id="heading-cloning-the-course-repo"&gt;Cloning the course repo&lt;/h4&gt;

&lt;p&gt;Clone the &lt;a href="https://github.com/DataTalksClub/data-engineering-zoomcamp"&gt;course repo&lt;/a&gt; into the home directory. Your final folder structure should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p1gfpbMH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677050146025/d9ddf857-5671-4c69-9bad-d9e4c1621149.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p1gfpbMH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677050146025/d9ddf857-5671-4c69-9bad-d9e4c1621149.png" alt="" width="503" height="44"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4 id="heading-open-a-remote-connection-from-visual-studio-code"&gt;Open a Remote Connection from Visual Studio Code&lt;/h4&gt;

&lt;p&gt;Install the "Remote-SSH" extension.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zQhGU7Hu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677049700312/6b32e4ce-5380-4787-bb12-23d1ed8fef67.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zQhGU7Hu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1677049700312/6b32e4ce-5380-4787-bb12-23d1ed8fef67.png" alt="" width="800" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press &lt;code&gt;F1&lt;/code&gt; and select "&lt;code&gt;Remote SSH: Connect to Host&lt;/code&gt;". It will show you the hosts configured on your system.&lt;/p&gt;

&lt;p&gt;Select the one associated with your Virtual Machine.&lt;/p&gt;

&lt;p&gt;Open the course repo folder and you're all set to start your development!&lt;/p&gt;




&lt;h2 id="heading-conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You can also refer to the course video for setting up the development environment &lt;a href="https://www.youtube.com/watch?v=ae-CV2KfoN0&amp;amp;list=PL3MmuxUbc_hJed7dXYoJw8DoCuVHhGEQb&amp;amp;index=13"&gt;here&lt;/a&gt;. Make sure to stop your VM once you're done with the setup or your development.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;


&lt;p&gt;This article is published w/ &lt;a href="https://scattr.io?ref=dev"&gt;Scattr  ↗️&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Linkedin Database Design</title>
      <dc:creator>Aditya Gupta</dc:creator>
      <pubDate>Thu, 30 Mar 2023 06:32:12 +0000</pubDate>
      <link>https://dev.to/itsadityagupta/linkedin-database-design-27pj</link>
      <guid>https://dev.to/itsadityagupta/linkedin-database-design-27pj</guid>
      <description>&lt;h2 id="heading-introduction"&gt;Introduction&lt;/h2&gt;

&lt;p&gt;LinkedIn stands out as a professional networking platform with a user base of over 750 million professionals worldwide. It handles an enormous amount of user data, including profiles, connections, job postings, and much more. In this blog post, I'll design a database for Linkedin and understand the various entities and relationships between them.&lt;/p&gt;

&lt;p&gt;To keep the scope of this blog limited, here are the requirements that we want our database to satisfy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Users can &lt;strong&gt;create their profile&lt;/strong&gt; with information like name, contact information, headline, summary, education, work experience and different skills.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Users can &lt;strong&gt;send connection requests&lt;/strong&gt; to other users, or &lt;strong&gt;follow them&lt;/strong&gt; only. They should also be able to &lt;strong&gt;accept or reject&lt;/strong&gt; the connection request.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Users can &lt;strong&gt;create posts&lt;/strong&gt;, and &lt;strong&gt;like&lt;/strong&gt; and &lt;strong&gt;comment&lt;/strong&gt; on others' posts.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;You can view the complete SQL code to create the below design along with the ER diagram on my github repo &lt;a href="https://github.com/Aditya-Gupta1/data-engineering-projects/tree/linkedin-db-design-oltp"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id="heading-user-profile"&gt;User Profile&lt;/h2&gt;

&lt;p&gt;Each user will have some basic information like name, current location, headline and summary. Along with this, each user can have multiple work experiences and multiple education details. Considering these things, we can have the following tables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;UserProfiles&lt;/strong&gt;:&lt;br&gt;  ▸ user_id (Primary Key)&lt;br&gt;  ▸ first_name&lt;br&gt;  ▸ middle_name&lt;br&gt;  ▸ last_name&lt;br&gt;  ▸ contact_information_id (Foreign Key to the &lt;em&gt;ContactInformation&lt;/em&gt; table)&lt;br&gt;  ▸ headline&lt;br&gt;  ▸ location&lt;br&gt;  ▸ about_section&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ContactInformation&lt;/strong&gt;:&lt;br&gt;  ▸ contact_information_id (Primary Key)&lt;br&gt;  ▸ email&lt;br&gt;  ▸ email_visibility&lt;br&gt;  ▸ phone_no&lt;br&gt;  ▸ phone_no_visibility&lt;br&gt;  ▸ website&lt;br&gt;  ▸ website_visibility&lt;br&gt;  Visibility can be one of &lt;em&gt;"PUBLIC", "PRIVATE" or "CONNECTIONS_ONLY".&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Skills&lt;/strong&gt;:&lt;br&gt;  ▸ skill_id (Primary Key)&lt;br&gt;  ▸ skill_name&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;EducationDetails&lt;/strong&gt;:&lt;br&gt;  ▸ education_id (Primary Key)&lt;br&gt;  ▸ school_id (Foreign Key to the &lt;em&gt;Schools&lt;/em&gt; table)&lt;br&gt;  ▸ degree&lt;br&gt;  ▸ field_of_study&lt;br&gt;  ▸ start_date&lt;br&gt;  ▸ end_date&lt;br&gt;  ▸ grade&lt;br&gt;  ▸ description&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Schools&lt;/strong&gt;:&lt;br&gt;  ▸ school_id (Primary Key)&lt;br&gt;  ▸ school_name&lt;br&gt;  ▸ location&lt;br&gt;  ▸ website&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;UserEducationDetails&lt;/strong&gt;:&lt;br&gt;  ▸ user_id (Primary Key, Foreign Key to the &lt;em&gt;UserProfiles&lt;/em&gt; table)&lt;br&gt;  ▸ education_id (Primary Key, Foreign Key to the &lt;em&gt;EducationDetails&lt;/em&gt; table)&lt;br&gt;  This table records the user and their corresponding education details.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;EducationSkills&lt;/strong&gt;:&lt;br&gt;  ▸ education_id (Primary Key, Foreign Key to the &lt;em&gt;EducationDetails&lt;/em&gt; table)&lt;br&gt;  ▸ skill_id (Primary Key, Foreign Key to the &lt;em&gt;Skills&lt;/em&gt; table)&lt;br&gt;  Associates skills with education.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;UserSkills&lt;/strong&gt;:&lt;br&gt;  ▸ user_id (Primary Key, Foreign Key to the &lt;em&gt;UserProfiles&lt;/em&gt; table)&lt;br&gt;  ▸ skill_id (Primary Key, Foreign Key to the &lt;em&gt;Skills&lt;/em&gt; table)&lt;br&gt;  Associate users with the skills.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Companies&lt;/strong&gt;:&lt;br&gt;  ▸ company_id (Primary Key)&lt;br&gt;  ▸ company_name&lt;br&gt;  ▸ company_website&lt;br&gt;  ▸ about_company&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Experiences&lt;/strong&gt;:&lt;br&gt;  ▸ experience_id (Primary Key)&lt;br&gt;  ▸ company_id (Foreign Key to the &lt;em&gt;Companies&lt;/em&gt; table)&lt;br&gt;  ▸ profile_headline&lt;br&gt;  ▸ employment_type&lt;br&gt;  ▸ start_date&lt;br&gt;  ▸ end_date&lt;br&gt;  ▸ location_type&lt;br&gt;  ▸ employment_location&lt;br&gt;  ▸ is_current_role&lt;br&gt;  ▸ employment_industry&lt;br&gt;  ▸ description&lt;br&gt;  Here, &lt;code&gt;employment_type&lt;/code&gt; can be one of "&lt;em&gt;FULL-TIME", "PART-TIME", "SELF-EMPLOYED", "FREELANCE", "INTERNSHIP" or "TRAINEE",&lt;/em&gt; and &lt;code&gt;location_type&lt;/code&gt; can be one of &lt;em&gt;"ON-SITE", "HYBRID" or "REMOTE"&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ExperienceSkills&lt;/strong&gt;:&lt;br&gt;  ▸ experience_id (Primary Key, Foreign Key to the &lt;em&gt;Experiences&lt;/em&gt; table)&lt;br&gt;  ▸ skill_id (Primary Key, Foreign Key to the &lt;em&gt;Skills&lt;/em&gt; table)&lt;br&gt;  Associates experience with skills.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;UserExperience&lt;/strong&gt;:&lt;br&gt;  ▸ user_id (Primary Key, Foreign Key to the &lt;em&gt;UserProfiles&lt;/em&gt; table)&lt;br&gt;  ▸ experience_id (Primary Key, Foreign Key to the &lt;em&gt;Experiences&lt;/em&gt; table)&lt;br&gt;  Associates users with experiences.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This completes a user's profile.&lt;/p&gt;

&lt;h2 id="heading-connection-requests"&gt;Connection Requests&lt;/h2&gt;

&lt;p&gt;We need to record who sent a connection request to whom, along with its status, and the followers of a user. Here are the tables to record this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Connections&lt;/strong&gt;:&lt;br&gt;  ▸ connection_id (Primary Key)&lt;br&gt;  ▸ request_sent_by (Foreign Key to the &lt;em&gt;UserProfiles&lt;/em&gt; table)&lt;br&gt;  ▸ request_sent_to (Foreign Key to the &lt;em&gt;UserProfiles&lt;/em&gt; table)&lt;br&gt;  ▸ request_status&lt;br&gt;  &lt;code&gt;request_status&lt;/code&gt; can be one of &lt;em&gt;"CONNECTED" or "PENDING"&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Followers&lt;/strong&gt;:&lt;br&gt;  ▸ followed_by (Primary Key, Foreign Key to the &lt;em&gt;UserProfiles&lt;/em&gt; table)&lt;br&gt;  ▸ following (Primary Key, Foreign Key to the &lt;em&gt;UserProfiles&lt;/em&gt; table)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These 2 tables can record information for connections and followers.&lt;/p&gt;

&lt;h2 id="heading-posts-and-comments"&gt;Posts and Comments&lt;/h2&gt;

&lt;p&gt;A user can create a post, and like and comment on others' posts. We can have the following tables for this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Posts&lt;/strong&gt;:&lt;br&gt;  ▸ post_id (Primary Key)&lt;br&gt;  ▸ user_id (Foreign Key to the &lt;em&gt;UserProfiles&lt;/em&gt; table)&lt;br&gt;  ▸ description&lt;br&gt;  ▸ created_at&lt;br&gt;  ▸ updated_at&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;PostReactions&lt;/strong&gt;:&lt;br&gt;  ▸ post_id (Primary Key, Foreign Key to the &lt;em&gt;Posts&lt;/em&gt; table)&lt;br&gt;  ▸ user_id (Primary Key, Foreign Key to the &lt;em&gt;UserProfiles&lt;/em&gt; table)&lt;br&gt;  ▸ reaction&lt;br&gt;  &lt;code&gt;reaction&lt;/code&gt; can be one of &lt;em&gt;'"LIKE'", "CELEBRATE", "SUPPORT", "FUNNY", "LOVE", or "INSIGHTFUL"&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Comments&lt;/strong&gt;:&lt;br&gt;  ▸ comments_id (Primary Key)&lt;br&gt;  ▸ post_id (Foreign Key to the &lt;em&gt;Posts&lt;/em&gt; table)&lt;br&gt;  ▸ user_id (Foreign Key to the &lt;em&gt;UserProfiles&lt;/em&gt; table)&lt;br&gt;  ▸ description&lt;br&gt;  ▸ created_at&lt;br&gt;  ▸ updated_at&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CommentReactions&lt;/strong&gt;:&lt;br&gt;  ▸ comments_id (Primary Key, Foreign Key to the &lt;em&gt;Comments&lt;/em&gt; table)&lt;br&gt;  ▸ user_id (Primary Key, Foreign Key to the &lt;em&gt;UserProfiles&lt;/em&gt; table)&lt;br&gt;  ▸ reaction&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="heading-complete-design"&gt;Complete Design&lt;/h2&gt;

&lt;p&gt;Here's a complete database design for the tables mentioned above:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Qj59WXoy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676631576153/3c4c27ce-fa6a-486c-9cb0-7f5e232b93bf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Qj59WXoy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1676631576153/3c4c27ce-fa6a-486c-9cb0-7f5e232b93bf.png" alt="" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="heading-conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Overall, there are &lt;strong&gt;18 tables&lt;/strong&gt; in this design. This design represents only the basic functionality. A lot more features can be added to this such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;LinkedIn Groups&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Linkedin Pages&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Linkedin Articles&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Block a User&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Featured Section in a user's profile&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and many more.&lt;/p&gt;

&lt;p&gt;Also, this design is for the &lt;strong&gt;OLTP database&lt;/strong&gt;. For analytical processing or warehousing, we would need a much more denormalized design. But that's a topic for another blog.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;


&lt;p&gt;This article is published w/ &lt;a href="https://scattr.io?ref=dev"&gt;Scattr  ↗️&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
