<?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: Sebastien Napoleon</title>
    <description>The latest articles on Forem by Sebastien Napoleon (@sebnapo).</description>
    <link>https://forem.com/sebnapo</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%2F1039583%2F9e0c97c6-7cdb-4a8a-b5eb-fdb559bbbb5d.jpg</url>
      <title>Forem: Sebastien Napoleon</title>
      <link>https://forem.com/sebnapo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/sebnapo"/>
    <language>en</language>
    <item>
      <title>From cloud noob to AWS re:Invent speaker : my story</title>
      <dc:creator>Sebastien Napoleon</dc:creator>
      <pubDate>Tue, 19 Dec 2023 15:10:26 +0000</pubDate>
      <link>https://forem.com/aws-builders/from-cloud-noob-to-aws-reinvent-speaker-my-story-1fig</link>
      <guid>https://forem.com/aws-builders/from-cloud-noob-to-aws-reinvent-speaker-my-story-1fig</guid>
      <description>&lt;p&gt;It's November 29, 2023, 2:30 PM in Las Vegas. I've just had a tattoo done, and here's what I hear: "Please let me welcome Sébastien on the stage, here at re:Invent." I'm not sure if a translation is needed, but here it is anyway: "Thank you for welcoming Sébastien to the stage, here at re:Invent." Before delving into this magical moment, let me tell you how I transitioned back into the cloud and secured a speaking role at the prestigious AWS re:Invent in less than 2 years.&lt;/p&gt;

&lt;p&gt;I won't go through the entire history since my inception in the cloud; I've already described that in &lt;a href="https://blog.ippon.fr/2022/06/08/reussir-sa-transition-depuis-le-monde-du-developpement-vers-le-monde-du-devops-cloud/" rel="noopener noreferrer"&gt;this article&lt;/a&gt;. Let me start a year ago.&lt;/p&gt;

&lt;h2&gt;
  
  
  The beginning of the story
&lt;/h2&gt;

&lt;p&gt;A year ago, a flame began to grow within me: the flame of sharing. I've always enjoyed sharing, whether through articles, with my colleagues on assignments, or during presentations. But everything started to accelerate from December 2022. While scrolling through LinkedIn, I came across a post promoting a program of a group of tech enthusiasts specializing in AWS. This program is called AWS Community Builder. Since registrations were not yet open, I inquired about the requirements, and bingo, I ticked all the boxes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Participated in the AWS community (blogging, open-source, presentations,...) - DONE&lt;/li&gt;
&lt;li&gt;Have a passion for sharing - DONE&lt;/li&gt;
&lt;li&gt;Have a specific expertise - DONE (serverless in my heart)&lt;/li&gt;
&lt;li&gt;Passionate about AWS - DONE DONE DONE&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, it was time to join the waiting list to receive a notification when registrations opened. One fine morning, I received an email saying that registrations were open. After filling out a form with a short letter of motivation, my application was submitted! Now, all that was left was to wait because not everyone is accepted as a Community Builder; you have to prove yourself.&lt;/p&gt;

&lt;p&gt;A few weeks later, another email arrived: congratulations, you are now a Community Builder! If only I knew that at that precise moment, my career would take a turn. From that point on, you're entitled to several benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Slack channel with over 3000 enthusiasts, including AWS employees and AWS Heroes&lt;/li&gt;
&lt;li&gt;$500 AWS credit just for you&lt;/li&gt;
&lt;li&gt;Goodies (swag, as we like to say)&lt;/li&gt;
&lt;li&gt;Access to the Community Builders group on dev.to to publish under this banner&lt;/li&gt;
&lt;li&gt;And many more!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExeWhlbmRrc2s0aHh4amw3eG5rY3RsY2IxZG1zcDBxbXY4OGVna2duZSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/3ohzdIuqJoo8QdKlnW/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExeWhlbmRrc2s0aHh4amw3eG5rY3RsY2IxZG1zcDBxbXY4OGVna2duZSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/3ohzdIuqJoo8QdKlnW/giphy.gif" alt="Awesome gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most significant aspect here is access to a community of enthusiasts. Picture a Slack filled with 3000 individuals producing content on AWS. It's a real goldmine, but not just that, it's also a fantastic way to expand your network.&lt;/p&gt;

&lt;p&gt;So, here I am, a Community Builder. With a bit of time on my hands during my assignments, I take advantage of it to write several articles, prepare Call for Papers (CFP) for the upcoming conference season, and create materials for presentations. I can already deliver these presentations at Ippon and local AWS User Group events.&lt;/p&gt;

&lt;h2&gt;
  
  
  When speaking becomes a passion
&lt;/h2&gt;

&lt;p&gt;I'm starting to gain some experience with AWS User Groups. I've given 2 talks in Nantes, one for the opening of the Ippon agency in Tours, and 3 internally at Ippon. I feel more and more comfortable speaking in public. One fine morning, I received a message on Slack. It's from Brian Tarbox, an AWS Community Hero and leader of the bAWSton user group. He saw one of the articles I wrote about the new Verified Access service. He asks if I'd be interested in presenting it in a talk for the AWS User Group in Boston (virtually, of course). The format? A 1-hour presentation with 5-10 minutes for questions, all with a little time difference, starting the presentation at 11:00 PM French time. Without hesitation, I accepted the offer, and here I am, a speaker... in English this time!&lt;/p&gt;

&lt;p&gt;It's not an easy challenge; you have to ignore your "French accent," get through the first 5 minutes, and then everything flows smoothly. You don't even realize you're giving the presentation in English (except when you can't find your words, of course...). However, this presentation has given me confidence in giving talks in English.&lt;/p&gt;

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

&lt;p&gt;The time for Call for Papers (CFP) arrives, it's the moment to send our famous "abstracts" everywhere we want to go. DevFest Nantes, Lille, ServerlessDay Zurich, Cloud Nord, AWS Community Day Hungary... Then comes the waiting time. The wait is long, but the results come in: rejection, rejection, rejection, rejection... Except for one, I got a slot for the AWS Community Day Hungary, the only conference in English! No problem, this one is also online, with a little twist; the sessions are pre-recorded (I found out on the day of the conference, too bad...). But another valuable experience to add to my speaker's CV. Unfortunately, no "major" French conferences wanted my topics. Too bad, but well, maybe next year!&lt;/p&gt;

&lt;h2&gt;
  
  
  The selection and the three months before re:Invent
&lt;/h2&gt;

&lt;p&gt;A few weeks pass, and then Jason Dunn, the leader of the AWS Community Builder program, posts a message on our Slack:&lt;/p&gt;

&lt;p&gt;"AWS re:Invent is coming! We are fortunate to have slots open for you builders (as well as AWS heroes) this year. Some of you will have the opportunity to have a talk at the Dev Lounge, right in the middle of the expo hall! We will open a Call for Papers (CFP) shortly. Choose up to three topics that you can propose; we will then make a selection and retain a little over a dozen. The format is 20 minutes with or without questions.&lt;br&gt;
What an announcement! I think I've never spent so much time on an abstract; I wanted to make it perfect. In addition to that, the abstract came with a letter of motivation. Why should we send you and not someone else? Because being selected here means you will be invited by AWS to re:Invent, no ticket to buy (except, of course, for the plane, hotel...). Here I am spending almost a day selling myself to the fullest and making this CFP a real teaser to spark interest in hearing the rest of the talk.&lt;/p&gt;

&lt;p&gt;The talk selection process then left us waiting for over a month. A month during which I completely forgot that I had participated. It was unthinkable for me that my topic would be selected by AWS. Me, the little cloud newbie, speaking at one of the world's biggest tech conferences? Never, it will never happen. And then, one beautiful evening in the middle of the week, I receive an email:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo7fmpi3jhox3k6ovve44.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%2Fo7fmpi3jhox3k6ovve44.PNG" alt="Acceptation mail"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'll spare you the details of the following two hours; I called everyone, I was over the moon. So, here I am, a speaker at re:Invent 2023! BOOM! As a bonus, I get to present the topic that interests me the most: event-driven architectures.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/MZocLC5dJprPTcrm65/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/MZocLC5dJprPTcrm65/giphy.gif" alt="It's happening gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The talk preparation
&lt;/h2&gt;

&lt;p&gt;Now that we are speakers, what exactly happens? Well, we begin a lengthy process with AWS internal teams. The first step involves administrative formalities, personal information, and so on.&lt;/p&gt;

&lt;p&gt;Nothing too complicated, but it needs to be done. We have access to a timeline to know the key dates. One month before having to submit my presentation, I received the official AWS template for the slides. There's no deviation from the template, no room for variations with copyrighted images (farewell to memes...). However, the template is really nice; I like the colors and fonts used.&lt;/p&gt;

&lt;p&gt;One month before departure, it's time to submit the finalized presentation. Quite pleased with my slides, it's now time to practice. The 20-minute format makes training much easier. You don't end up as exhausted as after a 1-hour presentation. After a few solo practice sessions, I extended an invitation to my colleagues in the company for an internal run-through and feedback. Everything goes smoothly; I receive great feedback, and my narrative about a bartender engages well, which is cool!&lt;/p&gt;

&lt;p&gt;As a speaker, we're invited to a tips &amp;amp; tricks session on how to capture an audience and ensure that our talk is a true experience for the listener. I learned a lot of great tips, with the best one being to always use the word "YOU." It should be everywhere to ensure that the person listening can relate. Pay attention to posture, gestures...&lt;/p&gt;

&lt;p&gt;We also have the opportunity to have a session with a coach, this time personalized. I jumped at the opportunity without knowing exactly what to expect. We then have a 50-minute slot with someone more accustomed to the stage. The experience was really cool; I could do a live demo of my presentation with their highly relevant feedback. One of the feedback points that helped me the most was to engage the audience during the presentation. Asking questions, getting people to raise their hands in the room. Especially at the beginning of the presentation, it helps to hook the audience.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Arriving in Las Vegas and the Pre-Talk Experience
&lt;/h2&gt;

&lt;p&gt;The big day arrives, time to depart. Well, the journey from Nantes to Vegas was long, but after more than 16 hours of travel, I finally made it to Las Vegas. On the Sunday just before the conference kicks off, it's advisable to pick up your badge. So, I set foot for the first time in the Venetian convention center. Armed with my retrieved badge and a little AWS re:Invent speaker and AWS Community Builder pin in my pocket, I headed back to the hotel for some rest before the first day of the conference.&lt;/p&gt;

&lt;p&gt;The first day of the conference was fantastic. I won't go into a recap of all the sessions I attended (however, my colleagues have written two great articles on the subject, check them out: recap 1 and recap 2), but one thing struck me: I have never seen an event so well organized. You cannot get lost at re:Invent; it's impossible. Despite being 60,000 people strong, I never had to queue once for lunch. The only line I saw in Vegas was the one to attend my talk (just kidding, although I did have to wait a long time for tattoos!).&lt;/p&gt;

&lt;p&gt;Furthermore, on the second day, I had to go register as a speaker. This is a process that needs to be done at least 24 hours before our presentation, and at the latest, up to 4 hours before our slot. It's a large room (there are actually several, one in each major hotel), where you confirm your presence. You also have the opportunity to make 2-3 modifications to your slides. That worked out well; I had a few adjustments to make. After a 30-minute wait, it was my turn. 10 minutes later, I was done, perfect!&lt;/p&gt;

&lt;p&gt;Now, I'm all set for my talk tomorrow (theoretically). I had the chance to do 3 more rehearsals these days. I hadn't really practiced standing up without having to touch the mouse or pad to advance the slides. So, I downloaded a little app to simulate a mouse and be able to click from my phone while standing, as if I were addressing a crowd (well, in reality, I was talking to my bed and the 4 pillows in my room at the Horseshoe...).&lt;/p&gt;

&lt;p&gt;By the way, if like me, you have problems with jet lag (at least in this direction), the melatonin remedy works wonders for me. From that moment on, I slept like a baby (unlike waking up every hour without it...).&lt;/p&gt;

&lt;h2&gt;
  
  
  Show time !
&lt;/h2&gt;

&lt;p&gt;What better way to relax than getting a tattoo? So, there are three good hours spent – arriving an hour before the opening to wait in line, waiting to get your slot, and finally, getting the tattoo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs1tm7a3wkvqjygyboofx.jpg" 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%2Fs1tm7a3wkvqjygyboofx.jpg" alt="Tatoo image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then we go on to shoot some videos for Ippon and social media, and finally, we head to the meal.&lt;/p&gt;

&lt;p&gt;One hour before: we make our way to the Dev Lounge to watch the ongoing sessions. I meet the person accompanying the speakers; he tells me to meet behind the booth 10 minutes before the start of my talk. I comply. Here I am, backstage! The person just before me hands over, and now I have a magnificent microphone around my neck, getting acquainted with the environment (screens down here, on the stand, big presentation screen, the remote control, the stage as seen from here). Backstage again, the person helps us relax, we chat about our tattoos, we talk about everything and nothing, we almost forget that we're about to step on stage (or not)!&lt;/p&gt;

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

&lt;p&gt;And then suddenly, you hear, "We are now pleased to welcome Sebastien on stage, here at re:Invent." It's time to enter the stage. You are gently pushed onto the stage, you grab the remote control, and it's showtime, baby.&lt;/p&gt;

&lt;p&gt;I won't lie to you, the first 2 minutes, I was completely disoriented. Some slightly approximate English, and then I got into the groove. I managed to forget the present world (and there were a lot of people, about a hundred, it seems that event-driven architectures attract a crowd). Once that little moment passes, you find your bearings, you relax, you look at people in the audience, you think about articulation, gestures... And 20 minutes later, it's already done. Then, you go back through the backstage, remove the microphone, and decompress. It's time to savor it; we did it! We take the opportunity to grab a beer on the way out because it's now time for the happy hour in the expo hall. For the rest of the evening, as they say, what happens in Vegas stays in Vegas!&lt;/p&gt;

&lt;p&gt;To conclude my experience as a speaker and my little journey in the world of AWS cloud: the watchword for me is as follows: &lt;strong&gt;try&lt;/strong&gt;. Nothing ventured, nothing gained as they say! If I had listened to myself, I would never have submitted my CFP for re:Invent. I'm currently writing this conclusion on the return flight from re:Invent, and thankfully, I &lt;strong&gt;tried&lt;/strong&gt;. I undoubtedly had one of the most &lt;strong&gt;enriching experiences&lt;/strong&gt; of my life thanks to &lt;strong&gt;my boldness&lt;/strong&gt;. So, if you also enjoy sharing, if you also want to "accelerate your career," &lt;strong&gt;TRY&lt;/strong&gt;. Find a company that can support you in this path, as Ippon has done. Try to enter a program that suits you (go for the Community Builder program, there aren't enough French participants). You are then the &lt;strong&gt;master of your own destiny&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I'll finish with a photo of all the speakers at this year's Dev Chat. Congratulations to everyone; it's certainly an adventure we won't forget. I hope through these few lines, I've also inspired you to &lt;strong&gt;try&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1lg5f2h2x9effwpa2g04.JPG" 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%2F1lg5f2h2x9effwpa2g04.JPG" alt="All CB dev chat spaker picture"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>reinvent</category>
      <category>aws</category>
      <category>cloud</category>
      <category>speaking</category>
    </item>
    <item>
      <title>Simplified Retrieval of Terraform Output for Ansible via AWS SSM Parameter Store 🚀</title>
      <dc:creator>Sebastien Napoleon</dc:creator>
      <pubDate>Tue, 07 Nov 2023 13:24:17 +0000</pubDate>
      <link>https://forem.com/aws-builders/simplified-retrieval-of-terraform-output-for-ansible-via-aws-ssm-parameter-store-4fm8</link>
      <guid>https://forem.com/aws-builders/simplified-retrieval-of-terraform-output-for-ansible-via-aws-ssm-parameter-store-4fm8</guid>
      <description>&lt;p&gt;I recently had to work with Ansible for the first time. I needed to install software on several EC2 instances, and I was given an Ansible playbook that I needed to execute.&lt;/p&gt;

&lt;p&gt;When examining the structure of the Ansible folder, I noticed that I had to update certain variables within a file named 'specifics.yml.' These variables were formatted as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;nfs_mounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;myNfsDNSname:/"&lt;/span&gt;

&lt;span class="na"&gt;my_super_infra&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;10.10.10.10&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I initially began working with it. However, as I continued testing, I found myself making numerous deployments with Terraform. Each time, I had to modify my code to include the new EFS DNS name, which proved to be quite a hassle.&lt;/p&gt;

&lt;p&gt;For context, I had established a CI/CD pipeline using Gitlab-CI for this project, which had the following structure:&lt;/p&gt;

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

&lt;p&gt;From that point on, I needed to discover a way to dynamically obtain values generated during the Terraform 'apply' stage. Initially, I considered using Terraform to create a file at the end of the 'apply' stage containing the output of my modules. While this approach would work, it would also make the execution of the Ansible job dependent on the completion of the Terraform 'apply' stage. This was not what I wanted; I aimed to run the Ansible playbook without having to go through Terraform each time.&lt;/p&gt;

&lt;p&gt;So, I came up with this idea: introduce a Terraform module that stores the necessary variables in the AWS SSM Parameter Store. This module takes certain variables as input, which are the outputs of my modules that I wish to retrieve in Ansible, such as the EFS DNS name. To achieve this, you only need to include the following code snippet for each of your variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ssm_parameter"&lt;/span&gt; &lt;span class="s2"&gt;"efs_dns_url"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"OUTPUT_EFS_DNS_URL"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"String"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;efs_dns_url&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use count if you have a list (for example a list of IPs) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ssm_parameter"&lt;/span&gt; &lt;span class="s2"&gt;"my_ip"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my_ip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"OUTPUT_MY_IP_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"String"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my_ip&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&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;With this step, the initial part of my strategy was set up. The next challenge was to retrieve these values within my Ansible 'specifics.yml' file. As I delved into the documentation, I came across &lt;a href="https://docs.ansible.com/ansible/latest/collections/amazon/aws/ssm_parameter_lookup.html#ansible-collections-amazon-aws-ssm-parameter-lookup"&gt;a page&lt;/a&gt; in the Ansible documentation that was precisely what I needed! The first step was to include the requirements in the 'requirements.yml' file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amazon.aws&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;6.5.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you remember the specifics.yml ? it now looks like this :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;nfs_mounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;lookup('amazon.aws.aws_ssm',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'OUTPUT_EFS_DNS_URL',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;region='eu-central-1'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}:/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's as straightforward as that! You can now fetch specific values from Terraform directly into your Ansible playbook. This is incredibly convenient. The only drawback I've encountered here is the output when you execute the playbook, particularly when you utilize it for the host section, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;my_block&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;lookup('amazon.aws.aws_ssm',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'OUTPUT_MY_IP_0',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;region='eu-central-1'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;)}}"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;lookup('amazon.aws.aws_ssm',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'OUTPUT_MY_IP_1',&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;region='eu-central-1'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;)}}"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run the playbook with this kind of setup, you get an output which looks like this :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w620g6i2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/35n1gtk2idxymn6yke6u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w620g6i2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/35n1gtk2idxymn6yke6u.png" alt="Ansible host ugly output" width="703" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is not ideal, you can change it using variables, but I haven’t found a way yet to get the IP here instead of a placeholder. If you have an idea, feel free to reach out to me via the comments or on my socials.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>ansible</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Automatically start and stop EC2 instance leveraging EventBridge Scheduler</title>
      <dc:creator>Sebastien Napoleon</dc:creator>
      <pubDate>Wed, 16 Aug 2023 12:30:25 +0000</pubDate>
      <link>https://forem.com/aws-builders/automatically-start-and-stop-ec2-instance-leveraging-eventbridge-scheduler-2ph</link>
      <guid>https://forem.com/aws-builders/automatically-start-and-stop-ec2-instance-leveraging-eventbridge-scheduler-2ph</guid>
      <description>&lt;p&gt;&lt;em&gt;FYI: All of the code mentioned in this article can be found &lt;a href="https://github.com/sebnapo/ec2-instance-scheduler" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;During one of my recent assignments, I faced a requirement to schedule the start and stop of EC2 instances. The challenge was to design a mechanism that would be user-friendly and require minimal maintenance.&lt;/p&gt;

&lt;p&gt;The specifications outlined that users should be able to initiate the starting and stopping of a specific EC2 instance through an API, based on provided parameters. A route like /provision using the HTTP POST method was chosen for this purpose. Parameters would be passed in the request body in JSON format:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;start_time&lt;/strong&gt; (timezone-less datetime): The date and time when the specified action should start for the EC2 instance, without considering timezones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;end_time&lt;/strong&gt; (timezone-less datetime): The date and time when the specified action should end for the EC2 instance, without accounting for timezones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;instance_id&lt;/strong&gt; (EC2 instance identifier): The unique identifier assigned to the EC2 instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AWS API is sufficient for starting and stopping EC2 instances. This task can be easily achieved with a Lambda function written in Python, using the Boto3 SDK.&lt;/p&gt;

&lt;p&gt;Now, triggering these Lambdas at specific dates and times is the next challenge. For this, the EventBridge scheduler is the perfect fit.&lt;/p&gt;

&lt;p&gt;To implement this, our API route will create two rules within EventBridge. The first rule will activate a Lambda function to start the EC2 instance, and the second to stop it. However, there's a potential problem with this approach: What happens to the rules we create that may have already been triggered?&lt;/p&gt;

&lt;p&gt;Not long ago, AWS introduced a solution to this dilemma – the capability to automatically delete rules post-trigger. You can read more about it in this article: &lt;a href="https://aws.amazon.com/blogs/compute/automatically-delete-schedules-upon-completion-with-amazon-eventbridge-scheduler/" rel="noopener noreferrer"&gt;https://aws.amazon.com/blogs/compute/automatically-delete-schedules-upon-completion-with-amazon-eventbridge-scheduler/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having conceptualized our architecture, it's time to dive in. Here's a brief outline, represented as a diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdw00y3vae927t4kfn71w.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%2Fdw00y3vae927t4kfn71w.png" alt="Architecture diagram of our little application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this scenario, we'll primarily use AWS and Python as our programming language. For deploying our infrastructure, we'll use code, specifically Terraform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hands-on
&lt;/h2&gt;

&lt;p&gt;Let's begin with the first step - two Lambdas: one to start and the other to stop.&lt;/p&gt;

&lt;p&gt;Here's the Lambda function to start one or more EC2 instances:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;eu-west-3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;ec2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ec2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_instances&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InstanceIds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instances&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instances&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Started your instances: &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's the function to stop one or more EC2 instances:&lt;br&gt;
&lt;/p&gt;

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


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;eu-west-3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;ec2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ec2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop_instances&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InstanceIds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instances&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instances&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Stopped your instances: &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once our instance management code is set up, the next task is creating our rules in EventBridge. We'll utilize a third Lambda function for this. This function will generate these rules using the AWS SDK, eliminating the need for manual intervention.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;body&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="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;region&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REGION&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;start_time&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;end_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;end_time&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;ec2_server_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ec2_instances_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;scheduler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;scheduler&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ActionAfterCompletion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DELETE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;FlexibleTimeWindow&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OFF&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ec2-schedule-start-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ec2_server_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;ScheduleExpression&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;at(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ScheduleExpressionTimezone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Europe/Paris&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Arn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;START_EC2_LAMBDA_ARN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RoleArn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EXECUTE_SCHEDULE_ROLE_ARN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;instances&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;: [&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ec2_server_id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;]}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ActionAfterCompletion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DELETE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;FlexibleTimeWindow&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OFF&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ec2-schedule-stop-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ec2_server_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;ScheduleExpression&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;at(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;end_time&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ScheduleExpressionTimezone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Europe/Paris&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Arn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;STOP_EC2_LAMBDA_ARN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RoleArn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EXECUTE_SCHEDULE_ROLE_ARN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;instances&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;: [&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ec2_server_id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;]}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;'&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;isBase64Encoded&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the Terraform code, please refer to this GitHub link where I've made my code public. Using this, you can deploy all components to your AWS account (follow the README for instructions).&lt;/p&gt;

&lt;p&gt;After deployment, you'll have an API in the API Gateway with a /provision route. You can access this with a POST request, using the following body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;“ec&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;_instances_id“:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;“xxxxxxxxxx“&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;“start_time”:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="mi"&gt;2023-04-01&lt;/span&gt;&lt;span class="err"&gt;T&lt;/span&gt;&lt;span class="mi"&gt;05&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;“end_time”:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="mi"&gt;2023-04-01&lt;/span&gt;&lt;span class="err"&gt;T&lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This API call facilitates the creation of two rules within the EventBridge Scheduler. One rule will start your application at the time specified in the start_time field, while the other will halt your instance at the time indicated in the end_time field.&lt;/p&gt;

&lt;p&gt;This streamlined setup makes orchestrating one or many EC2 instances incredibly straightforward. API-based interaction is immensely convenient, and the automated rule cleanup post-execution is a significant advantage.&lt;/p&gt;

&lt;p&gt;However, exercise caution. Currently, this stack doesn't manage "concurrency". For instance, if one event starts at 3:00 PM and ends at 6:00 PM, you can still have another event that begins at 5:00 PM and concludes at 8:00 PM. This overlap could result in the instance stopping at 6:00 PM during the second event. Pairing this stack with an app like a scheduler or calendar would be optimal (spoiler alert: this integration is part of my ongoing project).&lt;/p&gt;

&lt;p&gt;And there you have it! I hope you found this article helpful!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ec2</category>
      <category>api</category>
      <category>cloud</category>
    </item>
    <item>
      <title>How to optimize your lambda functions with AWS Lambda power tuning</title>
      <dc:creator>Sebastien Napoleon</dc:creator>
      <pubDate>Thu, 13 Apr 2023 09:53:41 +0000</pubDate>
      <link>https://forem.com/aws-builders/how-to-optimize-your-lambda-functions-with-aws-lambda-power-tuning-10h5</link>
      <guid>https://forem.com/aws-builders/how-to-optimize-your-lambda-functions-with-aws-lambda-power-tuning-10h5</guid>
      <description>&lt;p&gt;Whether you're a beginner or experienced with AWS, optimizing lambda functions is usually not the starting point for a serverless project. That's normal - at the beginning of a project, the priority is to get something functional, not necessarily optimized (at least, not right away). As a result, you often end up with one or more lambda functions that run, but could run either faster or for less cost!&lt;/p&gt;

&lt;p&gt;I have the opportunity to work on a personal project with a 100% serverless architecture. In this project, I have several functions that make API calls in parallel. We're talking about functions that make thousands, if not tens of thousands of calls. At the very beginning, we were close to the time quotas for a lambda in terms of execution time (15 minutes). So, we first added multithreading to manage the API calls (instead of doing them one by one, we now do them in parallel). We were able to cut the time in half, or even by a factor of three for certain functions. And with lambdas, time is money.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3ohuPd6vj81dNtF5YY/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3ohuPd6vj81dNtF5YY/giphy.gif" alt="Time is money gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Being satisfied with the improvement, we decided to leave the functions as they were and focus on other features. After a few months, I noticed that we had the same default memory on each of our lambdas (1024 MB), and I asked myself the following question: is it possible to increase or decrease this memory on the "big" lambdas and have a decrease in cost (and even an increase in performance)?&lt;/p&gt;

&lt;p&gt;That's when I remembered an tool I had heard about in an episode of the &lt;a href="https://stormacq.com/podcasts/index.html" rel="noopener noreferrer"&gt;AWS podcast in French&lt;/a&gt;: AWS Lambda Power Tuning. This is the utility that I'm going to talk about today, the one that allowed me to save over 30% on my AWS Lambda bill.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Lambda Power Tuning: How does it work?
&lt;/h2&gt;

&lt;p&gt;This tool, which is open source and available &lt;a href="https://github.com/alexcasalboni/aws-lambda-power-tuning" rel="noopener noreferrer"&gt;here&lt;/a&gt;, takes the form of a Step Function that is deployed on your AWS account. The purpose of this Step Function is to run your lambda with different memory configurations several times and output a comparison in the form of a graph (or JSON) to try to find the optimal balance between cost and execution time. There are three possible optimization modes: cost, execution time, or a "balanced" mode where it tries to find a balance between the two.&lt;/p&gt;

&lt;p&gt;To deploy it on your account, there are six different ways to do it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using AWS SAR (Serverless Application Repository), simply go to this link: &lt;a href="https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:451282441545:applications%7Eaws-lambda-power-tuning" rel="noopener noreferrer"&gt;https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:451282441545:applications~aws-lambda-power-tuning&lt;/a&gt; and install the application.&lt;/li&gt;
&lt;li&gt;With the SAM CLI.&lt;/li&gt;
&lt;li&gt;With AWS CDK.&lt;/li&gt;
&lt;li&gt;Via the open source AWS Power Tuner UI project (&lt;a href="https://github.com/mattymoomoo/aws-power-tuner-ui" rel="noopener noreferrer"&gt;https://github.com/mattymoomoo/aws-power-tuner-ui&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Via Terraform by deploying the SAR application.&lt;/li&gt;
&lt;li&gt;Via Terraform natively (without going through the SAR application).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find all the tutorials for deploying the application here: &lt;a href="https://github.com/alexcasalboni/aws-lambda-power-tuning/blob/master/README-DEPLOY.md" rel="noopener noreferrer"&gt;https://github.com/alexcasalboni/aws-lambda-power-tuning/blob/master/README-DEPLOY.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The tool is then very simple to use. You need to go to the AWS Step Functions service, find the step function with a name that starts with "powerTuningStateMachine-", and press the "start execution" button.&lt;/p&gt;

&lt;p&gt;Once you have pressed the button, you will be presented with this interface:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fio29zpcnd1cya4xmsnlr.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%2Fio29zpcnd1cya4xmsnlr.png" alt="Interface for the prompt of the Step Function"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The part that interests us is the input part, where we will set up what we want to test. Here is the list of possible inputs for our execution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;lambdaARN&lt;/strong&gt;: ARN of the lambda to execute&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;powerValues&lt;/strong&gt;: Array representing the memory configurations to test&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;num&lt;/strong&gt;: The number of lambda invocations to perform for each configuration (minimum 5, recommended between 10 and 100)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;payload&lt;/strong&gt;: Input parameters of the Lambda function&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;payloadS3&lt;/strong&gt;: Input parameters of the Lambda function (if too large for the lambda)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;parallelInvocation&lt;/strong&gt;: Allows invoking the lambda in parallel (watch out for throttling if enabled...)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;strategy&lt;/strong&gt;: The optimization strategy I mentioned earlier (cost, time or balanced)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;balancedWeight&lt;/strong&gt;: Parameter representing what you want to optimize (0 corresponds to the cost strategy and 1 to the time strategy)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;autoOptimize&lt;/strong&gt;: Automatically applies the best configuration at the end of the step function execution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;autoOptimizeAlias&lt;/strong&gt;: Creates or updates the alias with the new configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dryRun&lt;/strong&gt;: Allows testing the operation and call to the lambda function (IAM permission for example)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;preProcessorARN&lt;/strong&gt;: ARN of a lambda to execute before each execution of the lambda to be tested&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;postProcessorARN&lt;/strong&gt;: ARN of a lambda to execute after each execution of the lambda to be tested&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;discardTopBottom&lt;/strong&gt;: Allows removing the top and bottom 20% (to have a reliable representation of reality =&amp;gt; remove cold starts, for example)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sleepBetweenRunsMs&lt;/strong&gt;: Time between each execution of the function to be tested.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is a lot of information here, but we can focus on lambdaARN, num, and strategy to launch our first invocation. The rest can be explored after the first optimization if you are not yet satisfied.&lt;/p&gt;

&lt;p&gt;For example, an input would look like this :&lt;/p&gt;

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

{
  "lambdaARN": "arn:aws:lambda:eu-west-1:xxxxxxxx:function:lambda-power-tuning-snapoleon-article",
  "powerValues": [
    700,
    1000,
    1500,
    2000,
    2500,
    3000
  ],
  "num": 30,
  "payload": {},
  "parallelInvocation": false,
  "strategy": "balanced"
}



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

&lt;/div&gt;

&lt;p&gt;If everything goes well, you should have a nice state graph for the step function like this one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fje4mq0qiw77cjlbcfg41.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%2Fje4mq0qiw77cjlbcfg41.png" alt="State graph of the Step Function execution"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click on the last step "Optimizer", you will have access to the output of the step with, among other things, the results of the step function (also available under the Execution input and output panel). Here, in JSON format, you will have the output with the results for each configuration, as well as a link to a website to visualize the results in the form of a graph. Each result for a given configuration will look like this:&lt;/p&gt;

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

{
      "averagePrice": 0.00003504375000000001,
      "averageDuration": 2135.3594444444443,
      "totalCost": 0.0010591546875000002,
      "value": 1000
}


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

&lt;/div&gt;

&lt;p&gt;and the graph will look something like this :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwc7xkxvmsa0ibmssuw9i.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%2Fwc7xkxvmsa0ibmssuw9i.png" alt="Comparison result graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is now time to show you the power of the tool with a concrete example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing your Lambda function in practice
&lt;/h2&gt;

&lt;p&gt;A good use case for optimization would be for pure computation, using a library like Pandas in Python. So we will try to optimize a Lambda function that uses the Pandas library. The code is very simple:&lt;/p&gt;

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

import json
import pandas as pd
import numpy as np


def lambda_handler(event, context):
    # generate random data
    data = np.random.randn(1500000, 10)
    df = pd.DataFrame(data)

    # apply compute onto data
    df = df.apply(lambda x: x**2)
    df = df.apply(lambda x: x + 10)

    df = df.apply(lambda x: x**2)
    df = df.apply(lambda x: x + 10)

    df = df.apply(lambda x: x**2)
    df = df.apply(lambda x: x + 10)

    # print results
    print(df)
    return {
        'statusCode': 200,
        'body': json.dumps('Please optimize me !')
    }


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

&lt;/div&gt;

&lt;p&gt;If you want to perform the test by yourself, you need to deploy the function and don't forget to add the layer containing the required libraries (Pandas and Numpy). Here is the ARN of the layer used here: arn:aws:lambda:eu-west-1:336392948345:layer:AWSSDKPandas-Python39:5.&lt;/p&gt;

&lt;p&gt;Once the function is deployed and assuming that you have already deployed AWS Lambda Power Tuning on your account, all you have to do is to set up the execution of the function :&lt;/p&gt;

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

{
  "lambdaARN": "arn:aws:lambda:eu-west-1:xxxxxxx:function:lambda-power-tuning-snapoleon-article",
  "powerValues": [
    700,
    1000,
    1500,
    2000,
    2500,
    3000
  ],
  "num": 30,
  "payload": {},
  "parallelInvocation": false,
  "strategy": "balanced"
}


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

&lt;/div&gt;

&lt;p&gt;Here's what I used for our example. Please note that most of the time you will be limited to 3008MB of memory, which is a default "soft limit" for all AWS accounts. You can request an increase (up to 10,000 MB), but you will need to make a request to the support.&lt;/p&gt;

&lt;p&gt;Once the parameters are filled in, we launch the execution and all we have to do is wait.&lt;br&gt;
Two minutes later, everything is done and we have the results available :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4mwbfnmlvndzl3q6bjff.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%2F4mwbfnmlvndzl3q6bjff.png" alt="Concrete example chart results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We notice that if we allocate 700 MB, the execution time is 3097 ms for a cost of $0.000036. By increasing the memory to 1000 MB, we can reduce the execution time to 2135 ms for a similar cost of $0.000035. If we increase to 1500 MB, the execution time drops to 1422 ms and the cost remains the same. Starting from 2000 MB, we reach a plateau in terms of execution time (we won't go below 1149 ms). Since the time won't decrease anymore from this point, the cost will only increase. For 2000 MB, we have a cost of $0.000038, $0.000049 for 2500 MB and $0.000059 for 3000 MB. &lt;br&gt;
We can conclude from this execution that the sweet spot seems to be around 1500 or 2000 MB depending on what you're looking to optimize. If you want to save money, 1500 MB seems like a good candidate. On the other hand, if you're looking to optimize execution time, 2000 MB seems like the best choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  To conclude
&lt;/h2&gt;

&lt;p&gt;This tool is essential if you want to optimize the cost or execution time of your Lambda functions. It will allow you to make considerable savings on your AWS bill if you frequently use the Lambda service. It is also important to note that this tool can be integrated into a CI/CD and executed with each deployment of your application. This way, you can continuously update your Lambda functions through DevOps processes (but beware of drifts in the IaC).&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>serverless</category>
      <category>finops</category>
      <category>aws</category>
    </item>
    <item>
      <title>What if I were to tell you that a VPN is no longer useful</title>
      <dc:creator>Sebastien Napoleon</dc:creator>
      <pubDate>Mon, 03 Apr 2023 09:28:34 +0000</pubDate>
      <link>https://forem.com/aws-builders/what-if-i-were-to-tell-you-that-a-vpn-is-no-longer-useful-3hj</link>
      <guid>https://forem.com/aws-builders/what-if-i-were-to-tell-you-that-a-vpn-is-no-longer-useful-3hj</guid>
      <description>&lt;p&gt;Let's start with a scenario. It's a Monday morning, and we begin the week by connecting to our VPN. We can then access our emails, Teams channels, log in to the AWS console, etc. Just a typical Monday morning, right? An hour passes, and the connection is lost. "Well, I must have been disconnected from the VPN...," I'm sure that phrase sounds familiar to you.&lt;/p&gt;

&lt;p&gt;Yes, the VPN, this tool that everyone uses. But what if I told you that it was no longer useful using AWS ? Imagine a world where we could securely connect to our applications without going through a heavy client? What if, on top of that, we applied the principle of zero trust? And even further, if we could put restrictions on the type of device we use to connect and the geographic location?&lt;/p&gt;

&lt;p&gt;This is now possible. AWS announced its new AWS Verified Access service a few months ago, and today I will try to break it down for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  VPN, what is it and why does everyone use it?
&lt;/h2&gt;

&lt;p&gt;A VPN (Virtual Private Network) is a service that routes a user's web traffic to an internet destination via a secure remote server. This remote, secure server acts as an intermediary between the user and the internet network.&lt;/p&gt;

&lt;p&gt;The operation of a VPN is not very complicated (unless you really get into the details).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fix0ekf7ibjy4iastu3u6.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%2Fix0ekf7ibjy4iastu3u6.png" alt="How a VPN works"&gt;&lt;/a&gt;&lt;br&gt;
source : &lt;a href="https://blog.360totalsecurity.com/en/how-vpn-works/" rel="noopener noreferrer"&gt;https://blog.360totalsecurity.com/en/how-vpn-works/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;During VPN activation, a connection to the VPN server is established. All incoming and outgoing data from your device then passes through an encrypted tunnel via a process of encapsulation. Encryption secures the connection by making your traffic unreadable. Data is effectively indecipherable - except for those who possess the encryption key.&lt;/p&gt;

&lt;p&gt;No company wants unrestricted access to their internal applications. The https protocol is not sufficient here, so an additional layer of security is desired. Moreover, when someone now arrives on the Internet, the IP address of the person using the VPN will not be their ownIP address. Indeed, the person's request goes through the VPN server, so there has been a change of IP address indicating that the person went through the VPN.&lt;/p&gt;

&lt;p&gt;It is therefore possible to restrict access to our applications by limiting incoming IP addresses to that of the VPN server(s).&lt;/p&gt;

&lt;p&gt;We can see that having a VPN today is super interesting. However, there are still some disadvantages. Indeed, once connected to the VPN, it is much easier to harm an infrastructure, although there are still safeguards, but not always (authentication via user/password or via an SSO). If a malicious person manages to intrude on one of your servers, what about the rest?&lt;/p&gt;

&lt;p&gt;In addition, it is quite annoying to have to start your VPN client all the time, connect to it. Not to mention the moment when this VPN no longer works or randomly disconnects (session timeout on the server side, any network issue, etc).&lt;/p&gt;
&lt;h3&gt;
  
  
  The solution offered by AWS: Verified Access.
&lt;/h3&gt;

&lt;p&gt;Verified Access works by evaluating access requests against access policies attached to either an application or an application group. If all access policies are met, the user's request is accepted and sent to the application through a Verified Access-managed access point.&lt;/p&gt;

&lt;p&gt;The notable difference between Verified Access and a VPN is that each request is evaluated independently against access policies defined by the security team.&lt;/p&gt;

&lt;p&gt;Verified Access is composed of several components. Here is a diagram from the service's documentation that describes a high-level view of the service :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8a6nf0re54gak9nfonru.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%2F8a6nf0re54gak9nfonru.png" alt="Diagram showing how AWS Verified Access works"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verified Access instance (1)&lt;/strong&gt; - An instance that evaluates application requests and authorizes or denies access based on defined security criteria.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verified Access endpoint (2)&lt;/strong&gt; - Each endpoint represents an application. There are two possible endpoint types: a Load Balancer or an Elastic Network Interface (ENI).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verified Access group (3)&lt;/strong&gt; - A collection of Verified Access endpoints. It is recommended to group applications with similar security criteria into a single group. For example, applications for marketing in a marketing group.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Access Policies (4)&lt;/strong&gt; - A set of access policies defined by the client that determines whether access to an application is granted or denied. This can be a combination of things like the identity of the user trying to access the application, the properties of the device they are using (OS, browsers, updates, etc.), the location of the person, etc. An access policy can be assigned to a group to encompass a set of applications and, then a policy can be applied to a specific application within that group for more granularity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trust Providers (5)&lt;/strong&gt; - A service that handles user identity (such as IAM identity center, for example, or your on-premises identity provider) or device security compliance. AWS works with its own services, of course, but also with third-party services (including you, possibly as an IdP) and others, notably for metadata related to device compliance. It is mandatory to have a Trust Provider attached to a Verified Access instance to use the service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trust data (6)&lt;/strong&gt; - The data that passes between the Trust Provider and AWS Verified Access. This data can be an email address, an IP address, the browser used, your location, etc.&lt;/p&gt;

&lt;p&gt;Verified Access will then confront this data with the access policies defined by the user when it receives an access request for an application.&lt;/p&gt;

&lt;p&gt;For the rest of the article, I would like to dig into four important points from the perspective of a company that wants to migrate to AWS Verified Access: Access Policies, Trust Providers, Monitoring, and Security. Since VPNs are already often in place in companies, we would like to know how to integrate what already exists with the service. Additionally, a few lines are also necessary on how to write access policies, their formalism, as well as monitoring and security.&lt;/p&gt;

&lt;p&gt;It is true that I have not even talked about infrastructure management. You create a Verified Access Instance, and then what? Well, afterwards, AWS takes care of everything behind the scenes. It is a managed service, which is why I will not discuss this topic.&lt;/p&gt;

&lt;p&gt;You only pay for what you consume. As for the endpoints, I will not dwell on them. AWS manages the redirection to your application from the service. A CNAME will be created by AWS internally to redirect the endpoint to your application if the request is authorized.&lt;/p&gt;
&lt;h3&gt;
  
  
  Trust Providers: How to Migrate Legacy?
&lt;/h3&gt;

&lt;p&gt;We have seen that a trust provider is mandatory to use the Verified Access service. This trust provider can take two forms: one for user identity (Identity Provider, IdP for short) or one for managing the devices used to access our applications (computers, tablets, smartphones, etc.).&lt;/p&gt;

&lt;p&gt;Let's focus first on the identity of the users, as it is often the most relevant to us.&lt;/p&gt;

&lt;p&gt;Three different cases may exist when it comes to user identity to use the Verified Access service:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;You already use IAM Identity Center (successor to AWS Single Sign-on) and use this service as an IdP. You just need to connect IAM Identity Center with Verified Access and you're good to go.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You don't use IAM Identity Center and already have an on-prem IdP. It's simple, you'll need to create your space under IAM Identity Center. You can then easily connect the service with your on-prem IdP. It's also possible to use an Active Directory as an IdP. You just need to connect Verified Access with IAM Identity Center, and you're good to go.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You don't want to use the IAM Identity Center service and already have an on-prem IdP? No problem, if this IdP is compatible with the OpenID Connect (OIDC) protocol, you can directly connect the Verified Access service with your on-prem IdP via the OIDC protocol. It requires a bit of configuration, but it's possible.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What about support management now?&lt;/p&gt;

&lt;p&gt;Verified Access allows support management using external services. At the time of writing this article, 2 services are supported: Jamf and CrowdStrike.&lt;/p&gt;

&lt;p&gt;This list will likely expand, as a highlight of the service AWS wanted to emphasize at Re:Invent (&lt;a href="https://www.youtube.com/watch?v=Kkxn-bAIlnI" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=Kkxn-bAIlnI&lt;/a&gt;) is that they want to make it an "open" service. That is, to integrate as many reliable external solutions as possible, as this need is often raised with AWS. We will revisit this later, but the monitoring section is filled with integration with external solutions used everywhere.&lt;/p&gt;
&lt;h3&gt;
  
  
  Access Policies, what do they look like?
&lt;/h3&gt;

&lt;p&gt;The access policies used in the Verified Access service are written in AWS's proprietary language: Cedar (&lt;a href="https://www.cedarpolicy.com/" rel="noopener noreferrer"&gt;https://www.cedarpolicy.com/&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Even before seeing examples of access policies, it is important to note that if you create an application and start using Verified Access, the default rule is the Zero Trust Policy: everything is denied by default. Similar to the principle of least privilege in IAM policies, we want our users to only have access to what they need and nothing more. We do not want someone to escalate certain privileges because we have overly permissive access policies.&lt;/p&gt;

&lt;p&gt;Now that the basics are laid out, it's time to discover how an access policy written in Cedar is structured.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5dymqx24f93bfrl4o3i.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%2Fb5dymqx24f93bfrl4o3i.png" alt="Structure of an access policy written in Cedar"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Effect&lt;/strong&gt; - Allows or denies access based on the policy that follows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scope&lt;/strong&gt; - Specifies the principal, actions, and resources that the policy applies to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Condition clause&lt;/strong&gt; - Specifies the context in which the effects apply.&lt;/p&gt;

&lt;p&gt;It is important to note that in the context of access policies, the scope field should always be left blank. All that we are interested in is in the "condition clause" part, where we have access to the user's identity and the support they are using.&lt;/p&gt;

&lt;p&gt;We end up with an access policy that looks 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;permit(principal,action,resource)

when{
 context.&amp;lt;policy-reference-name&amp;gt;.&amp;lt;attribute&amp;gt; &amp;amp;&amp;amp;
 context.&amp;lt;policy-reference-name&amp;gt;.&amp;lt;attribute2&amp;gt;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We notice the possibility of using operators, here is the list of those available:&lt;/p&gt;

&lt;p&gt;!, ==, !=, &amp;lt;, &amp;lt;=, &amp;gt;, &amp;gt;=, in, &amp;amp;&amp;amp;, ||, .exists(), has, like, .contains(), .containsAll(), .containsAny()&lt;/p&gt;

&lt;p&gt;If one wishes to access certain information, particularly about the support used by the user, it is necessary for the user to have a browser extension to transmit the information to Verified Access. However, for any information related to the user's identity, there is no need for this browser extension.&lt;/p&gt;

&lt;p&gt;Let's see what we can do through two examples taken from the AWS documentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;permit(principal,action,resource)
when {
    context.identity.groups.contains("finance") &amp;amp;&amp;amp;
    context.identity.email_verified == true &amp;amp;&amp;amp;
    ip(context.http_request.client_ip).isInRange(ip("10.0.0.0/8"))
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we access the interesting data using the keyword "context". It is important to note that anything added to the "http_request" field will always be present. It contains certain information about the HTTP request such as the client's IP address, the HTTP method used, etc. (documentation on this subject can be found here: &lt;a href="https://docs.aws.amazon.com/verified-access/latest/ug/trust-data-default-context.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/verified-access/latest/ug/trust-data-default-context.html&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The "identity" field allows us to know that the data comes from the IAM Identity Center service (and thus, that you are using it as an IdP). If you are not using this IdP, the documentation does not mention how to retrieve the fields (except for services that manage supports such as Jamf and CrowdStrike).&lt;/p&gt;

&lt;p&gt;Returning to our example, we see that we will verify that the person's group (retrieved through IAM Identity Center) is indeed the "finance" group, then ensure that the email they are using is verified, and finally, that their IP address is in the range 10.0.0.0/8. If all this information is evaluated positively, the person's request will be accepted (thanks to the "permit" keyword at the top).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;permit(principal,action,resource)
when {
     context.crwd.assessment.overall &amp;gt; 50 
     &amp;amp;&amp;amp; context.jamf.risk == "LOW"
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second example which is actually a combination of two examples from the documentation. Here, we see the use of the keywords "crwd" and "jamf". These correspond to the two third-party services authorized by AWS for support management.&lt;/p&gt;

&lt;p&gt;The first one is CrowdStrike, characterized by the keyword "crwd". In this example, we will verify that the overall rating of the support used is greater than 50. For the second one, which is none other than Jamf, characterized by "jamf", we will verify that the risk associated with the support is equal to "LOW".&lt;/p&gt;

&lt;p&gt;We can see that the service allows for a large part of what businesses want in terms of options to secure access to their applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do we monitor all of this? And what about security?
&lt;/h3&gt;

&lt;p&gt;There is still a very important point that we have not yet discussed. Let's talk about monitoring and security. As a VPN is often a bottleneck and the gateway for all traffic to the internal network of a company, it is necessary that it be well monitored with a hermetic security.&lt;/p&gt;

&lt;p&gt;For security, all data at rest is encrypted by KMS with a key held and managed by the service itself. We can use CloudTrail to audit the use of the service. For data in transit, the service uses TLS 1.2 for encryption.&lt;/p&gt;

&lt;p&gt;As for traffic between multiple networks, it is possible to configure Verified Access to restrict access to specific resources in a Virtual Private Cloud (VPC). It is also possible to restrict access to portions of the network based on the user's group.&lt;/p&gt;

&lt;p&gt;Let's now talk about monitoring. In August 2022, AWS released the Open Cybersecurity Schema Framework (OCSF). In collaboration with various industry players, they defined a standard log schema to enable those who produce logs to have the same view as those who consume them. AWS Verified Access currently uses this log format to allow for simplified integration with globally recognized monitoring services, including the following list of partner solutions (at present):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Datadog&lt;/li&gt;
&lt;li&gt;IBM&lt;/li&gt;
&lt;li&gt;netskope&lt;/li&gt;
&lt;li&gt;new relic&lt;/li&gt;
&lt;li&gt;RAPID7&lt;/li&gt;
&lt;li&gt;sumo logic&lt;/li&gt;
&lt;li&gt;Trellix&lt;/li&gt;
&lt;li&gt;Splunk&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How much does it cost?
&lt;/h3&gt;

&lt;p&gt;Certainly, one of the sensitive issues concerning this service is its price. The bill is divided into two parts: hourly charges for each application, and data that has passed through during the use of the service.&lt;/p&gt;

&lt;p&gt;The hourly price of the service per application, in the Ireland region (eu-west-1), amounts to $0.31/hour per application up to 148,800 hours per application (which corresponds to approximately 200 applications per month, running all the time). The rate is lower for hours per application above 148,800 and costs $0.23/hour thereafter.&lt;/p&gt;

&lt;p&gt;Processed data costs $0.02/GB.&lt;/p&gt;

&lt;p&gt;If we take an example with 210 applications that process 1 GB of data each for 31 days, we arrive at the following calculation:&lt;/p&gt;

&lt;p&gt;148,800 * 0.31 + 7,400 * 0.23 + 1 * 210 * 0.02 = $47,834.2&lt;/p&gt;

&lt;h3&gt;
  
  
  To conclude,
&lt;/h3&gt;

&lt;p&gt;To add a personal opinion about this service, I think it is a super useful service. I believe there is a good chance that this kind of service will replace the VPN solutions we currently know. Given the complexity of maintaining a VPN today, there is a big gain to be had here (because yes, when the VPN goes down, it's a big part of the day's revenue that goes up in smoke for many companies with the democratization of teleworking). Here, the VPN is managed by AWS, we no longer have to worry about its robustness. In addition, the access policy part seems to be very user-friendly, it is much easier to centralize policies and manage access to them using IAM. So, of course, all these advantages come with a cost.&lt;/p&gt;

&lt;p&gt;Can we say that the VPN is no longer useful? Not really. Indeed, some use cases will still be relevant, I think. What about SSH access to a database (a very good alternative being AWS SSM with the session manager if the resource is on AWS)? Access to a NAS? The service doesn't seem to be made for that yet. It remains to be seen what the future holds.&lt;/p&gt;

&lt;p&gt;If you are interested in setting up this service, I found this article to be very well detailed: &lt;a href="https://medium.com/@chaim_sanders/a-visual-guide-to-setting-up-aws-verified-access-1333466f7222" rel="noopener noreferrer"&gt;https://medium.com/@chaim_sanders/a-visual-guide-to-setting-up-aws-verified-access-1333466f7222&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>aws</category>
      <category>vpn</category>
      <category>security</category>
    </item>
    <item>
      <title>What is an event-driven architecture and why storing events is important ?</title>
      <dc:creator>Sebastien Napoleon</dc:creator>
      <pubDate>Mon, 06 Mar 2023 13:31:39 +0000</pubDate>
      <link>https://forem.com/aws-builders/what-is-an-event-driven-architecture-and-why-storing-events-is-important--om2</link>
      <guid>https://forem.com/aws-builders/what-is-an-event-driven-architecture-and-why-storing-events-is-important--om2</guid>
      <description>&lt;p&gt;In this post, we are going to see how to create a serverless analytics architecture based off an event-driven architecture. This is made possible with the help of services like Kinesis Data Firehose, EventBridge, Athena, Glue, Lambda and S3.&lt;/p&gt;

&lt;p&gt;To avoid rewriting yet another definition of this type of architecture, let's turn to AWS's definition: "An event-driven architecture relies on events to trigger communication between decoupled services. This type of architecture is common in modern applications based on microservices. An event can be a state change or update, such as adding an item to the cart on an e-commerce site. Events can refer to a state (purchased item, modified price, and entered delivery address) or consist of identifiers (shipment notification of an order).&lt;/p&gt;

&lt;p&gt;Event-driven architectures have three key elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event producers&lt;/li&gt;
&lt;li&gt;Event routers&lt;/li&gt;
&lt;li&gt;Event consumers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A producer sends an event to the router, which filters the events and then passes them on to consumers. The producer and consumer services are decoupled, which allows them to be scaled, updated, and deployed independently." (source: &lt;a href="https://aws.amazon.com/event-driven-architecture/" rel="noopener noreferrer"&gt;https://aws.amazon.com/event-driven-architecture/&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Now that you are familiar with this type of architecture (or at least understand the principle), it is important to understand that events become the heart of our application. Without them, the application no longer makes sense, and it stops functioning as it is no longer receiving input. We quickly realize that storing these events is essential. The marketing team would like to know how many products were sold in the last few hours, days, months, or years. On the other hand, architects would like to create a new service that would consume one or more existing events. They would therefore like to know the number of events produced in x time.&lt;/p&gt;

&lt;p&gt;If you are also convinced that storing and analyzing these events is essential, you're lucky because you've come to the right place.&lt;/p&gt;

&lt;p&gt;The complete code presented in this article is available here : &lt;a href="https://gitlab.com/bootstrap-stack/serverless-analytics" rel="noopener noreferrer"&gt;https://gitlab.com/bootstrap-stack/serverless-analytics&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Producing events on AWS&lt;/p&gt;

&lt;p&gt;The central service of our architecture is AWS EventBridge, the event bus service released by AWS in July 2019. This service is widely used and in fact, used internaly by other AWS services. Config rules, for example, are triggered by events passing through AWS event buses. Each service has its own events that are emitted into EventBridge (from the success/failure of a Lambda function to a scale up or down in an auto scaling group), and a lot of information passes through these buses (example for auto scaling events: &lt;a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/automating-ec2-auto-scaling-with-eventbridge.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/autoscaling/ec2/userguide/automating-ec2-auto-scaling-with-eventbridge.html&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;But how do we create our own data bus in EventBridge and then send events into? It's simple - you can do it directly in the console or using your favorite infrastructure-as-code tool. In this article, I will use AWS Cloud Development Kit (CDK) to deploy my infrastructure (the code used will be in TypeScript)&lt;/p&gt;

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


&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;businessEventsBus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EventBus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;IpponBlogDemo&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;Now that we have created our bus, let me tell you about the formalism to use when sending events to EventBridge. An event consists of 5 fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;time&lt;/strong&gt;: the time and date of our event that we are producing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;source&lt;/strong&gt;: the source corresponds to the domain from which your event was sent (e.g. sales domain).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;detailType&lt;/strong&gt;: defines the action related to the event. If we stick to the perspective of an e-commerce store, we could have "item.sold" for example in this field.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;detail&lt;/strong&gt;: the content of the event, this is where we will put specific fields (product name, price, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;event bus name&lt;/strong&gt;: the name of the bus in which we want to send the event.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these fields are encapsulated in a JSON object and in our case, sent via a TypeScript class that uses the AWS SDK to call the EventBridge API.&lt;/p&gt;

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


&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EventBridgeClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PutEventsCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/opt/nodejs/node_modules/@aws-sdk/client-eventbridge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EventSenderEventBridge&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EventBridgeClient&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;busName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;myBusName&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;

    &lt;span class="nx"&gt;send&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutEventsCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;Entries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
                &lt;span class="na"&gt;EventBusName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;busName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;Source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;sales&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;DetailType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sold&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;Detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;toothbrush&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;9.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="err"&gt;”&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="p"&gt;}],&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;How to store our data?&lt;/p&gt;

&lt;p&gt;As I am a huge fan of the serverless world and managed services (yes, it does make life so much easier), having a small wallet, I turned to a flexible stack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn6phbv0oh19bizgtj299.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%2Fn6phbv0oh19bizgtj299.png" title="S3 logo on the left and Kinesis Data Firehose logo on the right&amp;lt;br&amp;gt;
" alt="Alt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will use S3 to store our data. This support is perfect for our use case. It's cheap, ultra-flexible, and 100% managed by AWS.&lt;/p&gt;

&lt;p&gt;We have the support for our data, we know how to easily produce events. Now, we need that little glue between our receptacle and the events.&lt;/p&gt;

&lt;p&gt;This glue will be provided by the Kinesis service, particularly Kinesis Data Firehose. Indeed, there is an existing integration between Kinesis Data Firehose and EventBridge with S3 bucket as the destination!&lt;/p&gt;

&lt;p&gt;First, you may not know the Kinesis Data Firehose service. Here's the AWS definition: Amazon Kinesis Data Firehose is an Extract, Transform, and Load (ETL) service that captures, transforms, and reliably delivers streaming data to data lakes, data stores, and analytics services. (&lt;a href="https://aws.amazon.com/kinesis/data-firehose/" rel="noopener noreferrer"&gt;https://aws.amazon.com/kinesis/data-firehose/&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;It may not be very clear yet, so here's a diagram illustrating how the service works:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo75a1as6z8292ivzur2z.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%2Fo75a1as6z8292ivzur2z.png" title="Diagram illustrating how the Kinesis Data Firehose service works" alt="Alt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see on the left, the "20+ AWS Services" that contains the EventBridge service. So, our events will be transmitted to KDF (Kinesis Data Firehose), where we can easily transform them using a lambda or tools provided by AWS. The result will finally be transmitted to the destination, in our case, an S3 bucket.&lt;/p&gt;

&lt;p&gt;How to create and configure a Kinesis Data Firehose stream&lt;/p&gt;

&lt;p&gt;Now, let's move on to the creation of our infrastructure with CDK, and I'll try to explain the different fields required when creating our stream.&lt;/p&gt;

&lt;p&gt;First, you need to create an IAM role with certain permissions. To avoid writing too much code, I will only create the role and add an IAM policy to it to show you how to do it:&lt;/p&gt;

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


&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deliveryStreamRole&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventTypeUppercased&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DeliveryStreamRole&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;assumedBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ServicePrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firehose.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;deliveryStreamRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;logs:CreateLogGroup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;logs:PutLogEvents&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;logs:CreateLogStream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Now, you need to add certain IAM policies (depending on your needs):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For the S3 service&lt;/strong&gt;: AbortMultipartUpload, GetBucketLocation, GetObject, ListBucket, ListBucketMultipartUploads, PutObject.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For the Lambda service&lt;/strong&gt; (if you want to transform your input objects into a format different from that of EventBridge): InvokeFunction, GetFunctionConfiguration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For the Glue service&lt;/strong&gt; (we will see the usefulness of this service later): GetDatabase, GetTable, GetPartition*, GetTableVersions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Permissions are now created. It's time to create our S3 bucket to store our data:&lt;/p&gt;

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


&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;destinationBucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;BlogIpponDemoBucket&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;blog&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;ippon&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;repository&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Let's move on to the big part, the creation of our Kinesis Data FireHose (KDF) stream. I will break down the creation into several steps to explain what each of them corresponds to.&lt;/p&gt;

&lt;p&gt;First, in the CDK document for the KDF service, we need to choose a "destination configuration" to pass as an object in the constructor. Here is a link to the documentation: &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_kinesisfirehose.CfnDeliveryStream.html#construct-props" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_kinesisfirehose.CfnDeliveryStream.html#construct-props&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are different destinations that can be found there, but the one we are interested in is S3, more specifically the extendedS3DestinationConfiguration object. The following code will be the configuration of this object.&lt;/p&gt;

&lt;p&gt;For readability reasons, we will add intermediate variables for certain parts. Two variables will be needed, the first concerns the processingConfiguration field and the second concerns the &lt;strong&gt;dataFormatConversionConfiguration&lt;/strong&gt; field.&lt;/p&gt;

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


&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processingConfiguration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;processors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Lambda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;parameterName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LambdaArn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;parameterValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;BlogIpponDemoLambdaTransformationArn&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AppendDelimiterToRecord&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;parameterName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Delimiter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;parameterValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;We are now in the &lt;strong&gt;processingConfiguration&lt;/strong&gt; section. This section comes into play once the data has passed through the buffer (which I will explain later). Either we do nothing, in which case the data goes directly to our destination, or we decide to transform it before storing it. In our case, our source data is an EventBridge event.&lt;/p&gt;

&lt;p&gt;We would like to be able to transform the source event into something more meaningful, something that will make sense when we come to analyze it. In this case, we will use a small lambda that we will have built. It's a very basic lambda that takes JSON as input and transforms it into another JSON as output (our business event).&lt;/p&gt;

&lt;p&gt;As a result, we have streamlined the source data by removing unnecessary fields. The second processor is just there to add a delimiter between each of our records. The data is now ready to go to our S3 bucket.&lt;/p&gt;

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


&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dataFormatConversionConfiguration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;inputFormatConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;deserializer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;openXJsonSerDe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;outputFormatConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;serializer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;parquetSerDe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;schemaConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;catalogId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;glueDatabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;catalogId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;roleArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deliveryStreamRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roleArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;databaseName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;glueDatabaseName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;glueTableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;Our second variable is &lt;strong&gt;dataFormatConversionConfiguration&lt;/strong&gt;. It allows us to configure the format part of data conversion (input and output) and to define a schema for our data (this is where Glue comes in).&lt;/p&gt;

&lt;p&gt;Here, our input events use the &lt;strong&gt;openXJsonSerDe&lt;/strong&gt; serializer and for the output, in order not to store the data as is, we will store it in Apache Parquet format, which is a columnar format (which will reduce costs). We will then use the parquetSerDe deserializer.&lt;/p&gt;

&lt;p&gt;Now it's time to define a schema for our data. This schema is contained in a Glue table, which itself is contained in a database. Here we only specify where our schema is stored (part &lt;strong&gt;schemaConfiguration&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;It is now time to assemble everything in our configuration object by adding the other fields.&lt;/p&gt;

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


&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deliveryStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CfnDeliveryStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventTypeUppercased&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DeliveryStream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;extendedS3DestinationConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;bucketArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destinationBucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucketArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;roleArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deliveryStreamRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roleArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;bufferingHints&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;intervalInSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;cloudWatchLoggingOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;logGroupName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kinesis-log-group&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;logStreamName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kinesis-logs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;year=!{timestamp:yyyy}/month=!{timestamp:MM}/day=!{timestamp:dd}custom_partition=!{partitionKeyFromLambda:custom_partition}/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;errorOutputPrefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;errors/!{firehose:error-output-type}/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;processingConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;processingConfiguration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;dataFormatConversionConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dataFormatConversionConfiguration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;dynamicPartitioningConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;retryOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;durationInSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;The first two fields correspond to the ARN of the destination S3 bucket and the ARN of the IAM role to use.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;bufferingHints&lt;/strong&gt; field is interesting as it allows us to configure the management of our stream buffer. The buffer corresponds to either the data retention time or the minimum amount of data to be sent.&lt;/p&gt;

&lt;p&gt;Two fields are available, &lt;strong&gt;intervalInSeconds&lt;/strong&gt; which corresponds to the time part (how long to wait). The value of this field can range from 60s to 900s and defaults to 300s (we wait at least 1 minute between each send and up to 15 minutes). Then we have the &lt;strong&gt;sizeInMBs&lt;/strong&gt; field, which corresponds to the amount of data we should wait for before sending. This amount varies between 1 and 128 with a default value of 5.&lt;/p&gt;

&lt;p&gt;We now move on to the part concerning the logs. Here we have a &lt;strong&gt;cloudWatchLoggingOptions&lt;/strong&gt; object that allows us to activate logs in CloudWatch by setting the name of the CloudWatch Group and Stream.&lt;/p&gt;

&lt;p&gt;We now have one of the crucial parts to define our stream, the prefix to use (&lt;strong&gt;prefix&lt;/strong&gt;) to know where to send our data in our S3 bucket (and especially, with which partitions). Here, I wanted to have a separation by datetime with a compartment per year/month/day and then by a custom partition that we will see later. We use a partitioning mode based on Hive to take advantage of dynamic partitioning. It's just necessary to know that we need to define our partition keys with this formalism "partition_key=!{We retrieve the key here}". We do the same for the output of processing or sending errors with the &lt;strong&gt;errorOutputPrefix&lt;/strong&gt; keyword. The fact that errors are sent directly to the S3 bucket is super convenient, we can easily see if there are errors. We can also automate the detection of these errors by building a lambda that will be triggered when a file is added to this error compartment.&lt;/p&gt;

&lt;p&gt;To finish creating our stream, we will activate and configure dynamic partitioning using the &lt;strong&gt;dynamicPartitioningConfiguration&lt;/strong&gt; keyword. Dynamic partitioning is recent and offers many advantages. The first is to be able to query our data according to keys that we define ourselves. These keys will allow us to build SQL queries with WHERE clauses. This will allow Athena (the service we will use later to query) to only pick the data we really care about (and therefore not scan unnecessary data). So we will have an improvement in performance, cost, and the number of scanned data. The downside is that this option is recent and not necessarily well documented at the moment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/firehose/latest/dev/dynamic-partitioning.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/firehose/latest/dev/dynamic-partitioning.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How to create and configure a Glue table to use with KDF&lt;/p&gt;

&lt;p&gt;Here, the use of Glue that we make is based on the definition of a data schema in a database. It should be noted that Glue is a serverless data integration service that allows analytics users to easily discover, prepare, move, and integrate data from multiple sources. So it is much more complete and complex than the use we make of it here.&lt;/p&gt;

&lt;p&gt;We are almost there, just two more steps and we can finally query our business events from EventBridge. Let's move on to the creation of a Glue database and table:&lt;/p&gt;

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


&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;glueDatabase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CfnDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;glue-database&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;catalogId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;IpponBlogDemoAwsAccountId&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;databaseInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="nx"&gt;IpponBlogDemoGlueDatabase&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;There's no need to explain this step, as the code is pretty basic with what you have seen in the article by now.&lt;/p&gt;

&lt;p&gt;For the creation of the table, the interesting configuration will be located in the &lt;strong&gt;tableInput&lt;/strong&gt; object.&lt;/p&gt;

&lt;p&gt;Since this object is quite large, we will have to use a few intermediate variables. These variables will be &lt;strong&gt;partitionKeys&lt;/strong&gt; and &lt;strong&gt;columns&lt;/strong&gt;.&lt;/p&gt;

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


&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;partitionKeys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;year&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;month&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;day&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;custom_partition&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;This first object allows us to define partition keys. These keys correspond to what we put in the prefix section when configuring our stream earlier. The "custom_partition" field here is just to remind us of the partition that we added thanks to the transformation lambda.&lt;/p&gt;

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


&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Name of the item’,
        ‘type’: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;
    },
    {
       ‘name’: ‘price’,
       ‘comment’: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;Price&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="nx"&gt;Quantity&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;This field will allow us to define the schema that our data will have in our S3 bucket. Here we will have a name, a price, and a quantity for each item.&lt;/p&gt;

&lt;p&gt;We can now assemble everything in our CDK object.&lt;/p&gt;

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


&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;glueTable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CfnTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;glue-table-for-athena&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;databaseName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="nx"&gt;IpponBlogDemoGlueDatabase&lt;/span&gt;&lt;span class="err"&gt;’’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;catalogId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;IpponBlogDemoAwsAccountId&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;tableInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;IpponBlogDemoGlueTable&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;tableType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EXTERNAL_TABLE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;partitionKeys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;partitionsKeys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;storageDescriptor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;compressed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;inputFormat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;outputFormat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;serdeInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;serializationLibrary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;IpponBlogDemoS3BucketUrl&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;storageDescriptor&lt;/strong&gt; field will allow us to describe how the data will be stored. Here, we will specify that our data will be compressed, our input format will be Apache Parquet, and our output format will also be Apache Parquet. As I am not an expert in this area, I will not go into the details of this configuration, but it has the merit of working. We will simply need to define the location of our data (where it is stored) using the &lt;strong&gt;location&lt;/strong&gt; keyword (we will pass in the URL of our S3 bucket).&lt;/p&gt;

&lt;p&gt;We still need to describe to Glue what these partition keys correspond to (i.e. the associated type, possible values, and the number of digits possible, for example). To do this, we will use the &lt;strong&gt;addPropertyOverride()&lt;/strong&gt; function on the table we created earlier.&lt;/p&gt;

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


&lt;span class="nx"&gt;glueTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPropertyOverride&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TableInput.Parameters.projection&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.enabled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;glueTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPropertyOverride&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TableInput.Parameters.projection&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.year&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;integer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;glueTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPropertyOverride&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TableInput.Parameters.projection&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.year&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.range&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2022,2050&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;glueTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPropertyOverride&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TableInput.Parameters.projection&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.year&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.digits&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;glueTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPropertyOverride&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TableInput.Parameters.projection&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.month&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;integer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;glueTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPropertyOverride&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TableInput.Parameters.projection&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.month&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.range&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1,12&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;glueTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPropertyOverride&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TableInput.Parameters.projection&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.month&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.digits&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;glueTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPropertyOverride&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TableInput.Parameters.projection&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.day&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;integer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;glueTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPropertyOverride&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TableInput.Parameters.projection&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.day&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.range&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1,31&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;glueTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPropertyOverride&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TableInput.Parameters.projection&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.day&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.digits&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;glueTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPropertyOverride&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TableInput.Parameters.projection&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.custom_partition&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;injected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;glueTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPropertyOverride&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TableInput.Parameters.storage&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.location&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.template&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;s3://ippon-blog-demo-analytics-repository/year=${year}/month=${month}/day=${day}/custom_partition=${custom_partition}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;In our case, we specify that the &lt;strong&gt;year&lt;/strong&gt; partition is a 4-digit integer between 2022 and 2050. We repeat the process for months and days and let Glue define the type of our custom partition itself using the &lt;strong&gt;injected&lt;/strong&gt; keyword. It's very convenient but has a big disadvantage: we can only perform equalities in SQL queries on this field afterwards (which can be annoying when we want to retrieve information for a specific period and not just a fixed date, for example).&lt;/p&gt;

&lt;p&gt;And there you have it, the job is done.&lt;/p&gt;

&lt;p&gt;How to query data in S3?&lt;/p&gt;

&lt;p&gt;We now have a functional stack that allows us to retrieve events sent to EventBridge, transform them into a business-oriented format, and send them to an S3 bucket with beautiful partitions. The only thing left to do is to query all this data. When it comes to analytics and S3, we often think of the AWS Athena service. Athena is a service that allows you to easily query an S3 bucket (and many other data sources), using SQL language and Apache Spark under the hood. In addition to that, Athena is a completely managed service by AWS, no infrastructure management required and that's awesome.&lt;/p&gt;

&lt;p&gt;The best part of the story? You don't need to do anything: once you get to the Athena console, simply choose your Glue database, select the table in question, configure the destination for query results and you'll be ready to query your data.&lt;/p&gt;

&lt;p&gt;What's magical is that thanks to storing our data in the parquet format combined with dynamic data partitioning, this entire stack will cost you very little money. Unless, of course, you produce a huge number of events (because yes, Athena can be expensive). And when I say huge, I really mean a lot. On a personal project, I'm at around 100,000 events per day and the stack doesn't cost me a penny with AWS's free plan. The query over 10 days, or ~1m events, represents 80MB of scanned data. Considering that Athena's price is €5 per terabyte of scanned data, this query represents a little less than 0.0005 cents. Of course, it all depends on the size of your events.&lt;/p&gt;

&lt;p&gt;In conclusion, it is possible to optimize performance when querying with Athena. Indeed, the fewer files we have in our S3 destination bucket, the faster Athena will execute the query. It is therefore possible, with the help of an EMR (Elastic MapReduce) job, to join the files together to have fewer, but larger files.&lt;/p&gt;

&lt;p&gt;In terms of costs, you also need to be careful with the Athena query result bucket and not hesitate to set lifecycle configurations on the buckets to expire objects that you no longer need (queries can quickly reach gigabytes of results). You should also think carefully about what you want to store. Storing just for the sake of storing is not very useful (either for the business or for the wallet). Only store what can add value to the queries you are going to make, the rest is certainly not useful for analytics.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>analytics</category>
      <category>cloud</category>
    </item>
  </channel>
</rss>
