<?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: Iduoad</title>
    <description>The latest articles on Forem by Iduoad (@iduoad).</description>
    <link>https://forem.com/iduoad</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%2F183113%2Ffe935cf6-2798-4664-9fa8-3ab906c74f13.jpg</url>
      <title>Forem: Iduoad</title>
      <link>https://forem.com/iduoad</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/iduoad"/>
    <language>en</language>
    <item>
      <title>I accidentally contributed to Terraform</title>
      <dc:creator>Iduoad</dc:creator>
      <pubDate>Mon, 26 Jan 2026 23:45:57 +0000</pubDate>
      <link>https://forem.com/iduoad/i-accidentally-contributed-to-terraform-4j2m</link>
      <guid>https://forem.com/iduoad/i-accidentally-contributed-to-terraform-4j2m</guid>
      <description>&lt;p&gt;Before I start telling you how I contributed to Terraform, I should admit that the title is a little clickbait 😅! Not totally, but just a little.&lt;/p&gt;

&lt;p&gt;The goal of this blog post is not to tell you that I contributed to Terraform or to brag. The goal is to share my take on Open Source contribution, which I came to after countless conversations with various people in the community and some small and humble contributions.&lt;/p&gt;

&lt;p&gt;This blog post is divided into four lessons I learned about contributing to open source throughout my humble career, while telling the story of how I contributed to Terraform.&lt;/p&gt;

&lt;h3&gt;
  
  
  I. OSS contribution always starts with involvement
&lt;/h3&gt;

&lt;p&gt;My story begins with LXC and Incus. If you know me, you know that I am a huge fan of containers in all their types and forms. One of the forms I like and use heavily is system containers, and in particular LXC containers.&lt;/p&gt;

&lt;p&gt;Since I use them so much, I started using Terraform to create reusable setups on LXC with Incus (a more open LXD alternative). &lt;/p&gt;

&lt;p&gt;After a while, I found myself redoing the same work, especially when it came to spinning up machine container clusters for experimentation. So I started looking for ready-to-use Terraform modules for Incus to make my life, and my setups easier.&lt;/p&gt;

&lt;p&gt;Not to my surprise, I found none, and this is normal since Incus is a fairly new fork of LXD and the awesome people behind it are still working on the ecosystem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✍️ ✍The first lesson here is that in order to contribute to open source, you should get involved first. Your first contribution to an OSS project is installing it on your machine and starting to use it.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Building your own OSS project counts as an Open Source contribution
&lt;/h3&gt;

&lt;p&gt;Now, I knew that something was missing, and I knew that at least someone needed it (I was the first user 😁). I started working on the first Terraform module to create instances. My end goal was to replicate my manual Kubernetes setup using the Terraform module and be able to create multiple clusters of all sizes and shapes with as few lines of code as possible, and with a single &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;While implementing a few features in my Terraform module, I discovered a bug. This links back to the first lesson: &lt;strong&gt;you need to get involved in order to find something you can contribute&lt;/strong&gt;. After a lot of troubleshooting and debugging, I figured out that the bug was not on my side but on the Terraform provider's side.&lt;/p&gt;

&lt;p&gt;So I headed to the Incus provider GitHub page and created an issue with a detailed description of the bug.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lxc/terraform-provider-incus/issues/180" rel="noopener noreferrer"&gt;Getting "Only image or source_instance can be set" even if source_instance defaults to null · Issue #180 · lxc/terraform-provider-incus&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Writing a detailed and, at the same time, on-point issue is very important. It helps the maintainers and developers of the project identify the problem faster and either point you to a solution or acknowledge the bug. It also avoids unnecessary back-and-forth.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;✍️ The second lesson is not to underestimate starting an OSS project of your own. It counts as and OSS contribution.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Take the initiative
&lt;/h3&gt;

&lt;p&gt;After some back-and-forth in the issue and some encouragement from the Incus maintainers, I decided to take the initiative and start working on the fix. Although I didn't have experience with Terraform providers, I took it as a learning opportunity and created a PR.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lxc/terraform-provider-incus/pull/181" rel="noopener noreferrer"&gt;Add plugin framework conflicts_with and at_least_one_of to instance by Iduoad · Pull Request #181 · lxc/terraform-provider-incus&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At first, it was really hard. I didn't know where to start, and the LLMs were not very knowledgeable about the topic back then (or maybe I wasn't good at prompting). So I decided to start learning about Terraform providers, especially the validation part—where the bug was.&lt;/p&gt;

&lt;p&gt;I spent days learning a lot of new things, and reading a lot of code. Once I felt a bit confident, I started implementing the fix.&lt;/p&gt;

&lt;p&gt;My fix worked, and I was happy for a while... Until the Terraform provider started behaving in weird ways again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Go through the rabbit hole if needed
&lt;/h3&gt;

&lt;p&gt;After more back-and-forth on the PR, countless trials and errors, and doubting my Terraform provider understanding (then learning again) I decided to take it to the Terraform Plugin Framework itself and consider it a bug on their side.&lt;/p&gt;

&lt;p&gt;At first, I was still unsure whether the bug was actually in the Terraform Plugin Framework, so I started by creating an issue in their forum.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://discuss.hashicorp.com/t/conflicts-with-fails-the-plan-when-some-of-the-attributes-is-set-from-a-variable/71804" rel="noopener noreferrer"&gt;Conflicts_with fails the plan when some of the attributes is set from a variable - Terraform Providers / Plugin Development - HashiCorp Discuss&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I expected from the answer on the forum, this was most likely a bug on their side. So I created an issue in their GitHub repository and, BAM! It was a bug in &lt;code&gt;terraform-plugin-framework-validators&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/hashicorp/terraform-plugin-framework-validators/issues/251" rel="noopener noreferrer"&gt;Conflicts_with fails during Validate even if one of the values is Unknown · Issue #251 · hashicorp/terraform-plugin-framework-validators&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This time, I didn't fix it myself since the issue was already picked up by the Terraform team and was pending release.&lt;/p&gt;

&lt;p&gt;I waited for it to be released and then went back one layer up the recursion.&lt;/p&gt;

&lt;h3&gt;
  
  
  The fix
&lt;/h3&gt;

&lt;p&gt;When the version with the fix was released, I picked it up and continued working on my original fix. I even refactored a small part of the argument validation to make it a bit simpler.&lt;/p&gt;

&lt;p&gt;Fortunately, this time my Terraform test code was working. My PR was merged, and I moved back one layer of recursion to continue working on my Terraform module.&lt;/p&gt;

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

&lt;p&gt;Although I still haven't released the first Terraform module (it was delayed due to the provider release, and I got a bit busy with other work), starting work on it led me to discover a bug, engage with the community, and even contribute to fixing it.&lt;/p&gt;

&lt;p&gt;The most important message I have for people who want to start contributing to OSS is to get involved. Pick something you like and start using it. Read its code, read the issues, discuss in the forums, and you will slowly discover bugs and features you want to add, and maybe work on them yourself.&lt;/p&gt;

&lt;p&gt;Finally, I want to thank the awesome Incus community (and Terraform team) for their kindness and for building this amazing project and its entire ecosystem.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>terraform</category>
      <category>incus</category>
    </item>
    <item>
      <title>How to scrape Facebook events for fun and profit ?</title>
      <dc:creator>Iduoad</dc:creator>
      <pubDate>Wed, 19 Jun 2019 21:44:29 +0000</pubDate>
      <link>https://forem.com/iduoad/how-to-scrape-facebook-events-for-fun-and-profit-26lo</link>
      <guid>https://forem.com/iduoad/how-to-scrape-facebook-events-for-fun-and-profit-26lo</guid>
      <description>&lt;p&gt;** Disclaimer: This is a hobbyist work !!  there are better ways to do so !!**&lt;/p&gt;

&lt;p&gt;Last week, I spotted a post asking about how to scrape Facebook events, I've been wanting to do so for so long and I decided to give it a try and following is the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  1- The tooling !
&lt;/h2&gt;

&lt;p&gt;For this you'll need a: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modern browser dev tools&lt;/li&gt;
&lt;li&gt;Simple HTTP client to test things. &lt;/li&gt;
&lt;li&gt;JS/Browser HTTP client (optional).&lt;/li&gt;
&lt;li&gt;Web server to serve as a proxy to avoid CORS problems (optional).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2- Browser network tools 🔥🔥
&lt;/h2&gt;

&lt;p&gt;Network tools are the most underrated dev tools of all time. &lt;br&gt;
In this tutorial we'll use some of firefox's network tools features, namely filters and copy as cURL.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2F0x0.st%2Fzexr.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%2F0x0.st%2Fzexr.png" alt="Firefox dev tools" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
First thing you want to do is to visit fb.com/events and open the network tools. and put out some filter magic.&lt;br&gt;
Facebook makes a post request to /events and what we want is to intercept the request and analyze it's content.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;method: POST regexp:events/&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Then click the request line, and let's explore the results. &lt;br&gt;
If nothing appeared scroll down a bit until the browser makes another POST request.&lt;/p&gt;
&lt;h2&gt;
  
  
  3- Let's explore the thing
&lt;/h2&gt;

&lt;p&gt;The next step, is exploring the request's body and learning from it.&lt;br&gt;
To be honest, I don't know what all these params mean and are for but OH WELL ! &lt;/p&gt;

&lt;p&gt;First let's right click on the request and choose copy cURL from the copy menu. &lt;/p&gt;

&lt;p&gt;Alright let's do some more. Next copy paste the right into your terminal (you have curl or some curl compatible tool) and Tadaaa !! &lt;br&gt;
The is a little bit long isn't it ?? let's strip it down to :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl 'https://www.facebook.com/events/discover/query/' --data 'suggestion_token=%7B%22city%22%3A%22default_108085075885850%22%7D&amp;amp;timezone_id=1&amp;amp;__a=1' &amp;gt; events.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is all you need to get your events right away ! the most useful parameter is the &lt;em&gt;suggestion_token&lt;/em&gt; which contain preferences about the location you want events from, the topic and the date ...&lt;/p&gt;

&lt;h2&gt;
  
  
  4- Hakuna Matata ! Let's make something
&lt;/h2&gt;

&lt;p&gt;Now we need to use our events for something (display them in a nice web page for example.&lt;br&gt;
To do so, we need a browser http client library (or fetch API or just our old friend XHR).&lt;br&gt;
But this won't give us what we want, because the bloody browser will block our cross origin request (for our good ofc 🙄). &lt;br&gt;
So in this case we will need to setup a proxy (web server) which will do the fetching for us. Then we will get the json on the client via fetch or jquery or axios or whatever.&lt;br&gt;
Steps again:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Because we are too lazy to rewrite the http request in our favorite language we're gonna use this website to translate the from cURL syntax to whatever syntax :p . 
&lt;a href="https://curl.trillworks.com" rel="noopener noreferrer"&gt;curl to whatever&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests
data = {  'suggestion_token': '{"city":"default_108085075885850"}',  'timezone_id': '1',  '__a': '1'}
response = requests.post('https://www.facebook.com/events/discover/query/', data=data) 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;setup a web server to serve what has to be served. Again, we're too lazy to setup a proper proxy (using flask or whatever) or at least write a decent one. 
So we will write a sh*ty one in less than 1 or 2 minutes .. Daaaamn, this is so messed up :p
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from http.server import HTTPServer, BaseHTTPRequestHandler
import requests

class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):    
    def do_GET(self):        
        data = {
          'suggestion_token': '{"city":"default_108085075885850"}',
          'timezone_id': '1',
          '__a': '1'
        }
        response = requests.post('https://www.facebook.com/events/discover/query/', data=data)
        self.send_response(200)
        self.send_header("Access-Control-Allow-Origin", "*") # this is the key line of code !
        self.end_headers()
        self.wfile.write(response.text.encode())

PORT = 8080
httpd = HTTPServer(("", PORT), SimpleHTTPRequestHandler)
httpd.serve_forever()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;fetch it using axios or fetch or something and put it inside your beautiful web page.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;axios.get('localhost:8080')
  .then(function (response) {
    parseAndAddToMyBeautifulWebPage(response);    // beware the for(;;); in the beginning of the json data 
  })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it ! Thank you for reading :D, Sorry for the mess. &lt;/p&gt;

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