<?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>Forem: Daniel Caballero</title>
    <description>The latest articles on Forem by Daniel Caballero (@danielcaballero88).</description>
    <link>https://forem.com/danielcaballero88</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F979147%2F98ac863f-188b-45f0-8d2c-fbbc09f56275.jpeg</url>
      <title>Forem: Daniel Caballero</title>
      <link>https://forem.com/danielcaballero88</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/danielcaballero88"/>
    <language>en</language>
    <item>
      <title>How to: Deploy Angular Website to Github Pages</title>
      <dc:creator>Daniel Caballero</dc:creator>
      <pubDate>Mon, 18 Sep 2023 06:00:50 +0000</pubDate>
      <link>https://forem.com/danielcaballero88/how-to-deploy-angular-website-to-github-pages-jhb</link>
      <guid>https://forem.com/danielcaballero88/how-to-deploy-angular-website-to-github-pages-jhb</guid>
      <description>&lt;p&gt;With the following pages you can host an Angular website in github pages. I did this to host my personal website, which I created using Angular, in github pages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://danielcaballero88.github.io/" rel="noopener noreferrer"&gt;https://danielcaballero88.github.io/&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Step 1: Create GitHub Repository&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you haven't done this yet, create a GitHub repository for your Angular website project, it's a pre-requisite to deploy the website to github pages.&lt;/li&gt;
&lt;li&gt;Commit all your changes and push your project files to this repository.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Create gh-pages Branch&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a &lt;code&gt;gh-pages&lt;/code&gt; branch from your &lt;code&gt;master&lt;/code&gt; or &lt;code&gt;main&lt;/code&gt; branch.
```
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;$ git branch gh-pages&lt;br&gt;
  $ git checkout gh-pages&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
**Step 3: Build Your App**
- Build your Angular app in the `gh-pages` branch.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;$ ng build --configuration-production --base-href https://[username].github.io/[repo]/&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Note: Replace `[username]`, `[repo]`, and `[project-name]` with your GitHub username, repository name, and project name respectively. Make sure the project name matches what's in your `angular.json` file under `defaultProject`.
  Note 2: If this is your default github pages repo (`[username].github.io`) then you can skip `[repo]` in the `base-ref` above.

- Push the code and configure the repository to pick up the files from the build path (relative path to your local `/dist` directory in your repo, or whatever configuration you have in `angular.json`).

**Step 4: Visit the App Page**
- Visit your Angular app on GitHub Pages at the URL: `https://UserName.github.io/RepoName/`

Your application should now be live on GitHub Pages.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>angular</category>
      <category>githubpages</category>
      <category>github</category>
    </item>
    <item>
      <title>How to implement a simple scraper with Python</title>
      <dc:creator>Daniel Caballero</dc:creator>
      <pubDate>Wed, 21 Dec 2022 06:29:21 +0000</pubDate>
      <link>https://forem.com/danielcaballero88/how-to-implement-a-simple-scraper-with-python-57ic</link>
      <guid>https://forem.com/danielcaballero88/how-to-implement-a-simple-scraper-with-python-57ic</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Sometimes we need to get some information from a website as soon as possible after it is published, and just manually visiting the website every hour to check if the information is already there is just not a practical way to do it, specially if you are a programmer and have the ability to do it automatically for you.&lt;/p&gt;

&lt;p&gt;In my case, I need to know the date when new slots for appointments to complete some paperwork in the Consulate of Spain in Buenos Aires.&lt;/p&gt;

&lt;p&gt;They usually provide the date with a very short notice (or none at all) and the available slots for the appointments are&lt;br&gt;
filled sooner than I have time to even realize that the slots were available in the first place.&lt;/p&gt;

&lt;p&gt;So, I implemented a very simple but yet effective scraper that visits the website every hour, and parses the&lt;br&gt;
information. If a new date is found for the slots, it will notify me via GMail and Telegram, so I don't miss it.&lt;/p&gt;

&lt;p&gt;NOTE: The full code can be found in this &lt;a href="https://github.com/danielcaballero88/scrape-cgeonline"&gt;Github repo&lt;/a&gt;, so in this post I'll only share code snippets&lt;br&gt;
without many very important features that are not essential to understand the key concept, and that can also get in the&lt;br&gt;
way of it on a first read (e.g., file path resolution, error handling, logging, tests, etc). If you want to check out the program with all those features, do check the repo.&lt;/p&gt;
&lt;h1&gt;
  
  
  How to Implement the Scraper
&lt;/h1&gt;

&lt;p&gt;The website is just HTML with no need for rendering dynamic content or providing credentials of any kind. The objective&lt;br&gt;
is to scrape a table element, looking for the information contained in a single row that we can identify for the content&lt;br&gt;
of the first column.&lt;/p&gt;

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

&lt;p&gt;Therefore, there is no need to use scraping frameworks like Scrapy, which are awesome but, for this particular case,&lt;br&gt;
also a huge overkill. Simply using the Python &lt;code&gt;requests&lt;/code&gt; library to get the website response will suffice, and parsing&lt;br&gt;
the information with the also amazing &lt;a href="https://beautiful-soup-4.readthedocs.io/en/latest/"&gt;Beautiful Soup&lt;/a&gt; will achieve the results beautifully (pun&lt;br&gt;
intended).&lt;/p&gt;

&lt;p&gt;To get the response and, specifically, the target row, we just need to use a few lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Fetch the response.
&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;soup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"html.parser"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Get the target row from the table.
&lt;/span&gt;&lt;span class="n"&gt;target_row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"tr"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="s"&gt;"Registro Civil-Nacimientos"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;target_cells&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"td"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Parse information into a dictionary.
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"servicio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"ultima_apertura"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"proxima_apertura"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"solicitud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cells&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"href"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it, now we have the information. Most tutorials I've found online for simple scrapers end here, but this is still&lt;br&gt;
completely useless (like those tutorials) unless we actually do something with that data.&lt;/p&gt;
&lt;h1&gt;
  
  
  Set up Notifications and Deploy to Linux VM in GCP
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Add notifications
&lt;/h2&gt;

&lt;p&gt;To make it actually useful in practice, we need to add notifications. You can check my other post on how to do that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[How to deploy your program to run periodically in a Linux VM in GCP Compute Engine][hot-to-deploy]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We need to implement the following simple logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="c1"&gt;# Code to scrape the website.
&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"proxima_apertura"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"fecha por confirmar"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Code to send the email.
&lt;/span&gt;    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course in your case you might need to adapt the code to the expected result that you'll have. And, also, instead of checking for equality or inequality, you might want to use a regex or something more flexible than a simple inequality (but for the purpose of this post, that is beyond the scope).&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy to VM
&lt;/h2&gt;

&lt;p&gt;Finally we can deploy it to a VM so it can run there periodically in the cloud. To see how to do that, you can check my other post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/danielcaballero88/how-to-set-up-email-and-message-notifications-for-a-python-application-5f3l"&gt;How to set up email and message notifications for a Python application&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once that is done, make sure that the cronjob that you set up to run your scraper is not aggressive on the target website, there is no need to put strain on their servers and force them to take measures against your scraper. Running it every hour or 30 min, in my case, is more than enough, and it's not something that will case the website any stress.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;And done! I showed you how to set up a Python scraper to automate the task of checking a website for you, and notify you whenever a desired change happens.&lt;/p&gt;

&lt;p&gt;Do check my &lt;a href="https://github.com/danielcaballero88/scrape-cgeonline"&gt;github repo&lt;/a&gt; to see the full code with logging, error handling, etc...&lt;/p&gt;

&lt;p&gt;And as usual, feel free to ask any questions in the comments and I'll check them out when I can.&lt;/p&gt;

&lt;p&gt;Cheers! &lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>python</category>
    </item>
    <item>
      <title>How to deploy your program to run periodically in a Linux VM in GCP Compute Engine</title>
      <dc:creator>Daniel Caballero</dc:creator>
      <pubDate>Sun, 18 Dec 2022 05:02:01 +0000</pubDate>
      <link>https://forem.com/danielcaballero88/how-to-deploy-your-program-to-run-periodically-in-a-linux-vm-in-gcp-compute-engine-4km6</link>
      <guid>https://forem.com/danielcaballero88/how-to-deploy-your-program-to-run-periodically-in-a-linux-vm-in-gcp-compute-engine-4km6</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Ok, so you have been developing a nice script or program, and it's now working just you we want it to, that's great! But it's also not enough... you need to host it somewhere!&lt;/p&gt;

&lt;p&gt;I'll show you here how to deploy your program (be it a script, a scraper, or whatever) to a Linux VM in Google Cloud Platform's Compute Engine service. &lt;/p&gt;

&lt;p&gt;If you don't have a Linux VM in GCP, you can follow &lt;a href="https://dev.to/danielcaballero88/how-to-spin-up-a-free-linux-virtual-machine-on-google-cloud-platform-1olj"&gt;this other tutorial of mine on how to spin up a free VM in GCP&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you followed that, you should have a GCP project (I'll refer to it as &lt;code&gt;$PROJECT&lt;/code&gt;) and a VM (with a name that I'll reference as &lt;code&gt;$INSTANCE&lt;/code&gt;). Of course, you may already have that without having followed my other post, that's just as good.&lt;/p&gt;

&lt;p&gt;NOTE: This tutorial assumes that you are also working from a Linux machine (as I do). If you use Windows (please don't) or Mac, the same concepts apply but some commands may differ. Don't worry, there will be links to the official documentation where you can see the exact differences and adapt to your case.&lt;/p&gt;

&lt;h1&gt;
  
  
  Set up gcloud locally
&lt;/h1&gt;

&lt;p&gt;If you haven't done this yet, it's important to have &lt;code&gt;gcloud&lt;/code&gt; installed locally to be able to connect your local machine to all your GCP services (in this case, it will be just the VM).&lt;br&gt;
To do so, just follow the &lt;a href="https://cloud.google.com/sdk/docs/install-sdk" rel="noopener noreferrer"&gt;official docs for the gcloud sdk&lt;/a&gt;. All the steps are very easy to follow, and all the default configurations are good enough.&lt;/p&gt;

&lt;p&gt;To test that it's working properly, let's try to &lt;code&gt;ssh&lt;/code&gt; to our VM. To get the gcloud command to do it, go to the Compute Engine service in the [GCP console], see your VM listed and click on the 3 dots and then on "View gcloud command". &lt;br&gt;
This will prompt a dialog that displays the command that we will need to use to connect to the VM.&lt;/p&gt;

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

&lt;p&gt;Copy that command and run it locally in a terminal, you should see something like this:&lt;/p&gt;

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

&lt;p&gt;And that's it! Now we're ready to deploy our code to our VM using the &lt;code&gt;gcloud&lt;/code&gt; command.&lt;/p&gt;
&lt;h1&gt;
  
  
  Deploy the code
&lt;/h1&gt;

&lt;p&gt;I'll assume a simple structure for the code, because I want to focus on the concept of deploying the code and not on the nitty-gritty details of production-ready code (which is incredible important but not essential to explain how to perform a deployment, and will get in the way of the concept I want to explain here).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── src
│  └── ...
├── docs
│   └── ...
└── tests
    └── ...
├── main.py
├── deploy.sh
└── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means that there is a directory &lt;code&gt;src&lt;/code&gt; where the code is, some other directories not essential but usually present like &lt;code&gt;docs&lt;/code&gt; and &lt;code&gt;tests&lt;/code&gt;, an entry-point &lt;code&gt;main.py&lt;/code&gt; (this is the file that you execute to run your program, in this case it's a Python file but it could be any other kind), and a deployment script &lt;code&gt;deploy.sh&lt;/code&gt;. &lt;br&gt;
We'll focus, naturally, on the deployment script.&lt;/p&gt;
&lt;h2&gt;
  
  
  Copy the code over to the VM
&lt;/h2&gt;

&lt;p&gt;The first part is to copy the code over to the VM, so it runs there instead of on our local machine.&lt;/p&gt;

&lt;p&gt;Let's assume we have our program contained in the directory &lt;code&gt;/path/to/local/app&lt;/code&gt; in our local machine, and we want to put it in the directory &lt;code&gt;/path/to/remote/app&lt;/code&gt; in the VM.&lt;/p&gt;

&lt;p&gt;First we can zip the needed files into a temporary file &lt;code&gt;temp.tar.gz&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/loca/app
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;czf /tmp/temp.tar.gz src main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case I'm only taking the &lt;code&gt;src&lt;/code&gt; directory and the &lt;code&gt;main.py&lt;/code&gt; entry-point, but of course that you can include more directories and/or files if you need it for your program to run properly.&lt;/p&gt;

&lt;p&gt;After doing so, we can send that temporary file to the VM (you'll need to have &lt;code&gt;$ZONE&lt;/code&gt;, &lt;code&gt;$PROJECT&lt;/code&gt;, and &lt;code&gt;$INSTANCE&lt;/code&gt; as environment variables or simply replace them in the command):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud compute scp &lt;span class="nt"&gt;--zone&lt;/span&gt; &lt;span class="nv"&gt;$ZONE&lt;/span&gt; &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="nv"&gt;$PROJECT&lt;/span&gt; &lt;span class="nt"&gt;--recurse&lt;/span&gt; /tmp/temp.tar &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE&lt;/span&gt;&lt;span class="s2"&gt;:/tmp/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, extract the contents to the correct directory in the VM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud compute ssh &lt;span class="nt"&gt;--zone&lt;/span&gt; &lt;span class="nv"&gt;$ZONE&lt;/span&gt; &lt;span class="nv"&gt;$INSTANCE&lt;/span&gt;  &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="nv"&gt;$PROJECT&lt;/span&gt; &lt;span class="nt"&gt;--command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cd /path/to/remote/app &amp;amp;&amp;amp; tar xzf /tmp/temp.tar"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And done! Now the code is in the VM ready to run there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up the VM so the code can run
&lt;/h2&gt;

&lt;p&gt;Ok, so... the code is in the VM ready to be executed, but some configurations will be needed. For instance, if it's a Python program, it will need a Python interpreted of the right version installed in the VM and then the correct requirements installed with &lt;code&gt;pip&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To do so, we need to &lt;code&gt;ssh&lt;/code&gt; to our VM, and apply the proper configurations. For example, I'll assume our program uses Python 3.8 and the requirements are saved in a &lt;code&gt;requirements.txt&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;First we connect via SSH:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud compute ssh &lt;span class="nt"&gt;--zone&lt;/span&gt; &lt;span class="nv"&gt;$ZONE&lt;/span&gt; &lt;span class="nv"&gt;$INSTANCE&lt;/span&gt;  &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="nv"&gt;$PROJECT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we install Python 3.8 in the VM using apt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python3.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And install the requirements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python3.8 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally test our program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/remote/app
&lt;span class="nv"&gt;$ &lt;/span&gt;python3.8 main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And done! If it runs (and it should if it was running locally) it means that the VM is really ready to execute our program.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up a cronjob
&lt;/h2&gt;

&lt;p&gt;Now the program runs whenever we execute it, but that is hardly useful since we are still running it manually as we do in our local machine.&lt;br&gt;
The whole idea is to have it run periodically to do whatever it does hourly, or daily, or every 20 minutes, or whenever you need it to.&lt;/p&gt;

&lt;p&gt;For instance, I sometimes need some information from a website &lt;em&gt;as soon as it's published&lt;/em&gt;, so I set up a simple scraper and have it run hourly and notify me via email and telegram whenever there is a change. &lt;/p&gt;

&lt;p&gt;To achieve that, we'll set up a cronjob, which is quite easy to do. First we SSH to the VM and then we use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;crontab &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will open up an editor, where we need to add a line into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 * * * * cd /path/to/remote/app &amp;amp;&amp;amp; python3.8 main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case it will run hourly according to the cron expression that we used. If you're not familiar with cron expressions, it's quite simple, and you can understand it quickly &lt;a href="https://crontab.cronhub.io/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, we can check that the new cronjob is correctly set by checking the syslog:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ tail -f /var/log/syslog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see some CRON entries whenever your code is executed. If you're not sure, just change the cron expression so it runs every minute (&lt;code&gt;*/1 * * * *&lt;/code&gt;) and inspect the syslog again.&lt;/p&gt;

&lt;p&gt;And done!&lt;/p&gt;

&lt;p&gt;Now your code is running periodically in a Linux VM in GCP Compute Engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment script
&lt;/h2&gt;

&lt;p&gt;After having explained all the steps, it's easy to put them all together into the deployment script (&lt;code&gt;deploy.sh&lt;/code&gt;). The only addition is that we remove the current files in the remote directory before copying over the new files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# Project base path to reference files.&lt;/span&gt;
&lt;span class="nv"&gt;LOCAL_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/path/to/local/app"&lt;/span&gt;

&lt;span class="c"&gt;# Delete the current script and requirements files&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deleting remote files."&lt;/span&gt;
&lt;span class="nv"&gt;REMOTE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/path/to/remote/app"&lt;/span&gt;
&lt;span class="nv"&gt;FILES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"src main.py"&lt;/span&gt;

&lt;span class="nv"&gt;$ZONE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;...
&lt;span class="nv"&gt;$INSTANCE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;...
&lt;span class="nv"&gt;$PROJECT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;...

gcloud compute ssh &lt;span class="nt"&gt;--zone&lt;/span&gt; &lt;span class="nv"&gt;$ZONE&lt;/span&gt; &lt;span class="nv"&gt;$INSTANCE&lt;/span&gt;  &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="nv"&gt;$PROJECT&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cd &lt;/span&gt;&lt;span class="nv"&gt;$REMOTE_PATH&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; rm -rf &lt;/span&gt;&lt;span class="nv"&gt;$FILES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Send the current files&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Sending new files."&lt;/span&gt;
&lt;span class="nb"&gt;tar &lt;/span&gt;czf /tmp/temp.tar.gz &lt;span class="nv"&gt;$FILES&lt;/span&gt;

gcloud compute scp &lt;span class="nt"&gt;--zone&lt;/span&gt; &lt;span class="nv"&gt;$ZONE&lt;/span&gt; &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="nv"&gt;$PROJECT&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--recurse&lt;/span&gt; &lt;span class="s2"&gt;"/tmp/temp.tar.gz"&lt;/span&gt; &lt;span class="nv"&gt;$INSTANCE&lt;/span&gt;:/tmp/&lt;span class="s2"&gt;"

gcloud compute ssh --zone &lt;/span&gt;&lt;span class="nv"&gt;$ZONE&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE&lt;/span&gt;&lt;span class="s2"&gt;  --project &lt;/span&gt;&lt;span class="nv"&gt;$PROJECT&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
--command="&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$REMOTE_PATH&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;tar &lt;/span&gt;xzf /tmp/temp.tar&lt;span class="s2"&gt;"

# Install the latest requirements
echo "&lt;/span&gt;Installing requirements.&lt;span class="s2"&gt;"
gcloud compute ssh --zone &lt;/span&gt;&lt;span class="nv"&gt;$ZONE&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE&lt;/span&gt;&lt;span class="s2"&gt;  --project &lt;/span&gt;&lt;span class="nv"&gt;$PROJECT&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
--command="&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$REMOTE_PATH&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; python3.8 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt&lt;span class="s2"&gt;"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then whenever we want to deploy our code we simply execute our deployment script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/local/app
&lt;span class="nv"&gt;$ &lt;/span&gt;bash deploy.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;I showed you how to copy over your code from your local machine to a remote Linux VM in GCP Compute Engine, set it up to run the code properly and then set up a cronjob to execute it periodically.&lt;/p&gt;

&lt;p&gt;All the steps are quite simple and easy to follow, but if I overlooked anything or you are having trouble with any step, feel free to leave a comment and I'll reply as soon as I can.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>learning</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to set up email and message notifications for a Python application</title>
      <dc:creator>Daniel Caballero</dc:creator>
      <pubDate>Tue, 13 Dec 2022 10:01:20 +0000</pubDate>
      <link>https://forem.com/danielcaballero88/how-to-set-up-email-and-message-notifications-for-a-python-application-5f3l</link>
      <guid>https://forem.com/danielcaballero88/how-to-set-up-email-and-message-notifications-for-a-python-application-5f3l</guid>
      <description>&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;So, you've prepared a nice Python application that is now running (or almost there), but you still need to manually check the results, or see that it is actually running smoothly... so what happens if you're away and suddenly something stops working as it should? It will continue to be broken until you decide to check on it and fix it, but that's not very practical.&lt;/p&gt;

&lt;p&gt;It would be MUCH better to implement some kind of notification system so you're alerted when something happens. It might be something bad like your application being incapable of properly handling an unexpected error, or something good like some heavy program finishing running having the results ready for you to see.&lt;/p&gt;

&lt;p&gt;Here we'll go over a simple way to implement a quite good and reliable notification system using Python, GMail, and Telegram. &lt;br&gt;
You might want to ask "why two channels?". Well, a notification system might fail, and since the lack of a notification is not something that will be noted, then it might be completely overlooked and then you might miss an important notification. That would be a big fail. &lt;/p&gt;

&lt;p&gt;To avoid that case, it's important to have some redundancy. Of course that it's also possible for both notification systems to fail, but to do so simultaneously is way less likely, so we can feel a lot more secure on our notifications if we have more than one system implemented in parallel. If you're application is of critical importance to you, it's also possible to add more channels in parallel, but here we'll limit ourselves to two.&lt;/p&gt;

&lt;p&gt;Also, if on a given occasion we only receive the notification via one of the channels, we know that we need to check on the other one because it might be broken.&lt;/p&gt;

&lt;p&gt;NOTE: In this post I'll only share code snippets but I'll ignore very important features that are not essential to understand the key concept, and that can also get in the way of it on a first read (e.g., classes and functions definitions, file path resolution, error handling, logging, tests, etc). By all means, you should try to implement these code snippets together with all these other ingredients in a way that makes sense for your application.&lt;/p&gt;
&lt;h1&gt;
  
  
  Email notifications with GMail
&lt;/h1&gt;

&lt;p&gt;To send notifications using GMail we need, naturally, a Google account.&lt;br&gt;
I think that the best approach is to create a new account to send these emails, to avoid using one's personal Google account in case there is a security breach and someone gets access to the password app we'll create.&lt;br&gt;
Having a new account created just for these purposes means that even if someone gets access to the password app, there's no much harm that they can do, or non at all.&lt;/p&gt;

&lt;p&gt;So let's create a new Google account, and call it &lt;code&gt;notifier.bot@gmail.com&lt;/code&gt; (this exact address is probably taken but the actual account name is not important, you can create it with whatever name you prefer).&lt;/p&gt;

&lt;p&gt;After going over all the steps, we need to enable &lt;a href="https://support.google.com/accounts/answer/185839?hl=en&amp;amp;co=GENIE.Platform%3DDesktop"&gt;2 step verification&lt;/a&gt; first, because it is a pre-requisite that Google has before we are able to create a Password App.&lt;br&gt;
Simply go over the steps and use one of the methods provided for 2 step verification.&lt;/p&gt;

&lt;p&gt;Once that is set up, we can now &lt;a href="https://support.google.com/accounts/answer/185833?hl=en"&gt;create a App Password&lt;/a&gt;.&lt;br&gt;
The steps are quite simple, just a few clicks and we get a new App Password (let's call it &lt;code&gt;&amp;lt;app-password&amp;gt;&lt;/code&gt;) that we can use to authenticate to our account using it instead of our regular passowrd.&lt;br&gt;
The obvious benefit is that we can use this App Password in our app and we don't need to include our actual account password, so if by mistake it is filtered and someone gains access to it, we can simply revoke it and we never lose the control of the account.&lt;/p&gt;

&lt;p&gt;Now that we have a notifier Google account and we created a Password App for it, the code to create and send an email is quite simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;smtplib&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;email.message&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;EmailMessage&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Read email address and app password from environment variables.
&lt;/span&gt;&lt;span class="n"&gt;gmail_account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GMAIL_ACCOUNT"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;gmail_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GMAIL_PASSWORD"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create message.
&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EmailMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"To"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"danielcaballero88@gmail.com"&lt;/span&gt;
&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"From"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"danielcaballero88@gmail.com"&lt;/span&gt;
&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Subject"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;
&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Send email.
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SMTP_SSL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"smtp.gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;465&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;smtp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;smtp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gmail_account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gmail_password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;smtp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the secrets (GMail account and password) are read from environment variables that are previously loaded from a &lt;code&gt;.env&lt;/code&gt; file. It's VERY important that these secrets be kept out of the code for security reasons. &lt;br&gt;
If you're not familiar with this, the &lt;a href="https://pypi.org/project/python-dotenv/"&gt;official documentation of the &lt;code&gt;python-dotenv&lt;/code&gt; package&lt;/a&gt; is quite easy to follow.&lt;/p&gt;
&lt;h1&gt;
  
  
  Message notifications using Telegram
&lt;/h1&gt;

&lt;p&gt;To send notifications via Telegram we'll create a new Telegram bot, add it to the Telegram chat where we want to receive the notifications, and implement the code to send the notification message to that channel.&lt;/p&gt;

&lt;p&gt;The best approach is to follow the &lt;a href="https://core.telegram.org/bots/tutorial"&gt;official tutorial for creating and setting up a bot&lt;/a&gt;.&lt;br&gt;
Nonetheless, here I'll put a summary of the steps I followed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Create a Telegram bot
&lt;/h2&gt;

&lt;p&gt;Simply look for the &lt;code&gt;@BotFather&lt;/code&gt; in the Telegram messaging app, and ask to create a new bot by sending a &lt;code&gt;/newbot&lt;/code&gt;&lt;br&gt;
message to it.&lt;br&gt;
It will prompt for the bot name, we can give something like &lt;code&gt;NotifierBot&lt;/code&gt;, but this one will most likely be taken, so&lt;br&gt;
use a unique one (and by all means try to use CamelCase, just for consistency sake).&lt;br&gt;
The Bot Father will then provide a token for the bot (let's refer to it as &lt;code&gt;&amp;lt;token&amp;gt;&lt;/code&gt;), which is meant to be kept secret,&lt;br&gt;
just save it for now.&lt;/p&gt;

&lt;p&gt;Now you can look for your bot and start interacting with it, but since there is nothing implemented for it, it will be&lt;br&gt;
like talking to a wall (in the search bar look for &lt;code&gt;@NotifierBot&lt;/code&gt;, or whatever name you used preceded by a &lt;code&gt;@&lt;/code&gt; sign, and you should see it there).&lt;br&gt;
What we can do, however, is to get the chat id for this chat, or add the bot to another chat if we already have a chat dedicated to notifications, or we can create a new chat for it, whatever you choose is up to you, and it won't affect the next steps.&lt;/p&gt;

&lt;p&gt;We do need to get the chat id however, because we want the bot to send messages to that particular chat, so we need to&lt;br&gt;
visit the following url:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.telegram.org/&amp;lt;token&amp;gt;/getUpdates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just replace &lt;code&gt;&amp;lt;token&amp;gt;&lt;/code&gt; with your actual bot token and visit it in your browser.&lt;br&gt;
You'll get a JSON response displayed in the browser window and somewhere in it, something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
"my_chat_member":{"chat":{"id":&amp;lt;chat_id&amp;gt;,"
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where &lt;code&gt;&amp;lt;chat_id&amp;gt;&lt;/code&gt; is a positive or negative number.&lt;/p&gt;

&lt;p&gt;Copy that number and save it too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implement the notifications
&lt;/h2&gt;

&lt;p&gt;Now with both the &lt;code&gt;token&lt;/code&gt; and the &lt;code&gt;chat_id&lt;/code&gt; we can construct the call to send the message that we want, using the&lt;br&gt;
&lt;a href="https://core.telegram.org/method/messages.sendMessage"&gt;sendMessage&lt;/a&gt; method of the Telegram API:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Read token and chat_id from environment variables.
&lt;/span&gt;&lt;span class="n"&gt;telegram_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TELEGRAM_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;telegram_chat_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TELEGRAM_CHAT_ID"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"Proxy-Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Basic base64"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;data_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"chat_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;telegram_chat_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Replace &amp;lt;message&amp;gt; with the intended message.
&lt;/span&gt;    &lt;span class="s"&gt;"parse_mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"HTML"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"disable_notification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"https://api.telegram.org/bot&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;telegram_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/sendMessage"&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again note that we are reading the secrets from environment variables that are previously loaded from a &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Here I showed how we can set up notifications using GMail and Telegram, which can be incredibly useful for many practical applications when we need to be notified or alerted immediately when something (either good or bad) happens.&lt;/p&gt;

&lt;p&gt;Everything is explained and links to the official documentation for each case is also provided. &lt;/p&gt;

&lt;p&gt;You should be able to adapt these code snippets to your use case, and also feel free to ask questions and I'll do my best to answer them.&lt;/p&gt;

</description>
      <category>python</category>
      <category>notifications</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to spin up a free Linux Virtual Machine on Google Cloud Platform</title>
      <dc:creator>Daniel Caballero</dc:creator>
      <pubDate>Tue, 06 Dec 2022 10:59:38 +0000</pubDate>
      <link>https://forem.com/danielcaballero88/how-to-spin-up-a-free-linux-virtual-machine-on-google-cloud-platform-1olj</link>
      <guid>https://forem.com/danielcaballero88/how-to-spin-up-a-free-linux-virtual-machine-on-google-cloud-platform-1olj</guid>
      <description>&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;Ok, so you have been developing a nice script or application, and it's now working just you we want it to, that's great! But it's also not enough... you need to host it somewhere!&lt;/p&gt;

&lt;p&gt;There are many options to host your app, but we'll focus on GCP Compute Engine because it offers a lot of freedom. &lt;/p&gt;

&lt;p&gt;An important remark that is always valid is that we don't want to spend money unnecessarily, so keeping costs as low as possible is absolutely desirable, with extra points for keeping costs to actually zero.&lt;/p&gt;

&lt;p&gt;Many tutorials (pretty much all the ones that I found so far) propose to use the free trial that GCP offers, but that is a limited amount of money during a limited period of time, so it's only useful for first time users and for short timed projects, so if you've used that up already (I certainly have), it's not really an option for you anymore.&lt;/p&gt;

&lt;p&gt;Therefore, in this post I'll describe how to spin up a VM using GCP Compute Engine, and keep it within the &lt;a href="https://cloud.google.com/free" rel="noopener noreferrer"&gt;free tier&lt;/a&gt; so it's completely free. And I don't mean using your free trial credits to pay for the VM, which is virtually free but only for a given time until those credits run out. I mean a VM that will cost nothing to keep it running for as long as we need it to.&lt;/p&gt;

&lt;p&gt;NOTE: I'll assume that you're starting from scratch regarding Google Cloud Platform. For instance, I'll include the creation of a new GCP Project as a step, but of course you can just use an already existing project if you have one. Same for other things, but you should be able to adapt to each case only needing a bit of common sense.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why a Linux VM
&lt;/h1&gt;

&lt;p&gt;Like I said, there are countless options to host your app, way too many to even mention here a meaningful fraction of them, but whenever possible I try to always use a Linux VM. &lt;br&gt;
The reasons for my choice are also many, but I can enumerate some:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's Linux, and I love Linux. Every other solution or service will be a virtualization on top of a Linux machine, so being able to manage a Linux VM is a great skill to have.&lt;/li&gt;
&lt;li&gt;I can host many applications in the same VM, so I don't need to have many services running in GCP or any other cloud, just one VM is good enough for many solutions (until it runs out of CPU, RAM, or disk space, of course).&lt;/li&gt;
&lt;li&gt;I can have complete control over the machine, something that's not available with other services with high virtualization like Cloud Functions, App Engine, Cloud Run, etc.&lt;/li&gt;
&lt;li&gt;I can still implement different levels of virtualization if needed using Docker, so I don't lose anything in that regard.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Billing
&lt;/h1&gt;

&lt;p&gt;Even when using only the free tier, you need to create a GCP Project and a Billing Account, because that is a simply pre-requisite to enable the Compute Engine (VMs) service in GCP.&lt;br&gt;
On top of that, the Billing Account will require that you add a Credit or Debit card to it, because that is a pre-requisite to create a Billing Account.&lt;/p&gt;

&lt;p&gt;No need to worry, however, because everything is prepared to avoid being charged accidentally in case we make a mistake when setting up the VM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Billing Account
&lt;/h2&gt;

&lt;p&gt;The first step is to create the billing account.&lt;br&gt;
Just go to "Billing" &amp;gt; "Billing Accounts", click on "Add Billing Account" and follow the given steps (it's quite straight forward, but note that here you'll need to add your credit or debit card details):&lt;/p&gt;

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

&lt;p&gt;Again: we'll set up a Billing Account with a credit or debit card because it's a requisite to use some GCP services (like Compute Engine), but we'll remain within the free tier limits so we won't be charged.&lt;br&gt;
And even if we make a mistake and surpass the free tier limits, we'll assign a very low budget to the billing account, and set up alerts so we get notified immediately if we start spending any money.&lt;/p&gt;

&lt;p&gt;After creating the Billing Account, you should see it listed as follows:&lt;/p&gt;

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

&lt;p&gt;Let's set up a budget and some alerts to avoid spending money by mistake.&lt;br&gt;
Clicking on the Billing Account takes us to the Billing Account overview:&lt;/p&gt;

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

&lt;p&gt;Then go to "Budgets &amp;amp; alerts", click on "Create Budget" and follow the steps to set up a very low budget (10 USD is low enough in my opinion, but you can choose something different matching your idea of "very low") and some alerts (I use 50%, 90%, and 100%):&lt;/p&gt;

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

&lt;p&gt;This way if during any future month we start spending money by mistake, and reach 5 USD, we'll get a notification via&lt;br&gt;
email and we can go to our GCP Project and change or stop whatever service is spending money.&lt;br&gt;
Besides, we can always just go to our Billing Account overview and manually check if it's spending any money or not.&lt;/p&gt;

&lt;p&gt;Now we have to assign this billing account to the project, so go to Billing &amp;gt; Projects, and you'll see your GCP project&lt;br&gt;
there, which will have the billing disabled because so far no Billing Account has been assigned to it:&lt;/p&gt;

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

&lt;p&gt;To assign the billing account to it, click on the 3 dots, and click on Change Billing, and follow the steps:&lt;/p&gt;

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

&lt;p&gt;And that's it!&lt;br&gt;
We can now enable services that require Billing to be active, like Compute Engine.&lt;/p&gt;

&lt;h1&gt;
  
  
  Spin up a VM
&lt;/h1&gt;

&lt;p&gt;The GCP free tier specifications for Compute Engine might be changed by Google eventually, so by all means do check them&lt;br&gt;
out in the &lt;a href="https://cloud.google.com/free/docs/free-cloud-features#compute" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;.&lt;br&gt;
At the moment they are like this:&lt;/p&gt;

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

&lt;p&gt;So we can spin up an e2-micro VM with a 30 GB standard persistent disk.&lt;/p&gt;

&lt;p&gt;First, we need to enable the Compute Engine service:&lt;/p&gt;

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

&lt;p&gt;And then create a VM by clicking on "Create Instance":&lt;/p&gt;

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

&lt;p&gt;Follow the steps by selecting all the free options according to what is &lt;a href="https://cloud.google.com/free/docs/free-cloud-features#compute" rel="noopener noreferrer"&gt;currently free&lt;/a&gt;,&lt;br&gt;
and once that is finished the VM Instance will be listed in "Compute Engine" &amp;gt; "VM Instances" (as can be seen in the&lt;br&gt;
picture above, blurred).&lt;/p&gt;

&lt;p&gt;And that's it! The VM will be created and listed in the Compute Engine overview.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Here I showed you how to, starting from scratch, create a GCP project, add a Billing Account to it, and spin up a free Linux VM using GCP Compute Engine.&lt;br&gt;
The steps are quite easy and simple to follow, but do take into account that Google is prone to change things here and there as time passes, so if some time has passed between me writing this (December 2022) and you reading it, go and check the official documentation to see that everything is still valid (I left links to GCP official docs here and there throughout the post). &lt;br&gt;
And that's it, the VM is ready to be used!&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
  </channel>
</rss>
