<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: lordz-md</title>
    <description>The latest articles on DEV Community by lordz-md (@lordzmd).</description>
    <link>https://dev.to/lordzmd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F761749%2F66a49ecc-6a79-407f-99c2-808174a757e8.jpeg</url>
      <title>DEV Community: lordz-md</title>
      <link>https://dev.to/lordzmd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lordzmd"/>
    <language>en</language>
    <item>
      <title>Amazon Deprecated My Email, So I Moved It to Switzerland</title>
      <dc:creator>lordz-md</dc:creator>
      <pubDate>Thu, 09 Jul 2026 17:06:48 +0000</pubDate>
      <link>https://dev.to/aws-builders/amazon-deprecated-my-email-so-i-moved-it-to-switzerland-d1m</link>
      <guid>https://dev.to/aws-builders/amazon-deprecated-my-email-so-i-moved-it-to-switzerland-d1m</guid>
      <description>&lt;p&gt;I am an ex AWS Community Builder. I had the badge, the Slack channel access, and the mild compulsion to explain to strangers that yes, actually, WorkMail was fine.&lt;/p&gt;

&lt;p&gt;And it &lt;em&gt;was&lt;/em&gt; fine, which is the tragedy, that isn't Stockholm syndrome talking. The pitch was narrow and it landed, at least for me: native Outlook support and Exchange-shaped behavior — calendars, free/busy, mobile sync that just worked — without buying into the entire Microsoft 365 apparatus and the licensing spreadsheet that comes with it. Four dollars a user. Fifty gigs. Data in a region I picked, encrypted with a KMS key I held. If you needed Outlook on the desk but did not need Teams, WorkMail was the only managed email service I ever used that appeared to grasp that a person might want &lt;em&gt;just email&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Then, on the last day of March, AWS put it on the list.&lt;/p&gt;

&lt;p&gt;Not a list. &lt;em&gt;The&lt;/em&gt; list. Fourteen services and features got sunset or shoved into maintenance mode in &lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/03/aws-service-availability/" rel="noopener noreferrer"&gt;a single availability update&lt;/a&gt;, and WorkMail went out alongside RDS Custom for Oracle, the WorkSpaces Thin Client, and something called the AWS Service Management Connector, which I had to read three times to satisfy myself it wasn't placeholder text somebody forgot to replace. In the words of Corey Quinn, "getting the Old Yeller treatment in one blog post is a bold move" — his read, in &lt;a href="https://www.lastweekinaws.com/newsletter/s3-gets-vectors-cloudfront-gets-sha-256-you-get-the-bill/" rel="noopener noreferrer"&gt;Last Week in AWS issue #466&lt;/a&gt;, being that somebody at Amazon had finally gone and checked the usage metrics. No new customers after April 30, 2026, console goes dark March 31, 2027.&lt;/p&gt;

&lt;p&gt;The community did get loud about it, which surprised me until I read what people were actually upset about. Nobody was mourning WorkMail. They were doing arithmetic on their own dependencies. App Runner's deprecation had leaked earlier in the year and then been walked back; CodeCommit was deprecated and subsequently resurrected. Fourteen at once reads less like lifecycle management and more like a policy change, and once you have had that thought you cannot stop having it about every service you rely on.&lt;/p&gt;

&lt;p&gt;The reaction to WorkMail specifically was closer to anthropological: &lt;em&gt;wait, people were using that?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Reader: I was people. I was, by some estimates, a statistically significant fraction of people.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stages of grief, abbreviated
&lt;/h2&gt;

&lt;p&gt;I sat on it for a while, in the manner of anyone handed a deadline eighteen months out. Then a weekend arrived with nothing in it and I made the mistake of opening the migration guide being on painkillers from a surgery I had in the weeks before.&lt;/p&gt;

&lt;p&gt;AWS's own guidance suggests third-party landing spots like Kopano Cloud and Zoho Mail. These are perfectly reasonable, WorkMail-shaped replacements from vendors who have every incentive to make the move painless. I looked at them for 6-7 minutes and went to Proton Mail instead, which is the kind of decision that in retrospect sounds principled for some people and looked a lot like spite at the time.&lt;/p&gt;

&lt;p&gt;To be clear about the principle, since everyone assumes they know it: I did not have a road-to-Damascus moment about surveillance capitalism ( in a similar move I decided to upgrade my iPhone instead of buying a Fairphone ). That's the reason people cite and it's a perfectly good one, but it isn't mine and I'm not going to borrow it to sound better. Mine was narrower and maybe nerdier. I wanted my mail under Swiss jurisdiction rather than in a bucket subject to whatever the current American legal weather happens to be. I also wanted the cryptography to be a library I could actually read ( not that I could, but I can feed the code to Claude using Fable and let it explain it to me ) — Proton maintains OpenPGP.js in the open, third parties audit it, and when I have a question about what is happening to my mail I can answer it by reading source instead of reading a trust page written by the marketing department.&lt;/p&gt;

&lt;p&gt;That's the whole thesis. Everything else was a tiebreaker.&lt;/p&gt;

&lt;h2&gt;
  
  
  The road not taken, and why I did not take it
&lt;/h2&gt;

&lt;p&gt;I bet you wonder why didn't I go self host!? I know. I could have run &lt;a href="https://www.iredmail.org/" rel="noopener noreferrer"&gt;iRedMail&lt;/a&gt; on an EC2 instance. Postfix, Dovecot, Rspamd, a Let's Encrypt cron job, and total sovereignty over every byte. It's good software. I've done it before. The knowledge is still in there somewhere, filed next to my Sendmail trauma.&lt;/p&gt;

&lt;p&gt;Here's what that weekend looks like instead. EC2 blocks outbound traffic on port 25 to public addresses by default, so before you can send a single message you get to sign in with your &lt;em&gt;root account credentials&lt;/em&gt; and fill in the Request to Remove Email Sending Limitations form, explaining to Amazon in the Use Case Description field why you, personally, should be trusted with SMTP. Approval is discretionary. People get denied. There are re:Post threads about this that read like hostage negotiations. You'll also want an Elastic IP with a reverse DNS record, which is a separate request, because the modern internet has decided that an EC2 IP address sending you mail is guilty until proven otherwise and Microsoft will cheerfully drop your messages into a void with no bounce and no appeal.&lt;/p&gt;

&lt;p&gt;Then you own it. Forever. Patching, spam filtering, backups, become your own problem, and at some point in a year or two a disk fills up at 3 a.m. and your mail server stops accepting mail and you find out about it because a client texts you, because you are a one man shop and you don't implement the observability you do for your clients.&lt;/p&gt;

&lt;p&gt;And then money wise it doesn't work. A right-sized instance, EBS, snapshots, and an Elastic IP land you in the neighborhood of a few Proton seats before you have spent one minute of your own time, which — and I say this with love for everyone who has ever hand-tuned a Postfix &lt;code&gt;main.cf&lt;/code&gt; — is not free. I'm too old for this. I'd rather pay someone in Switzerland.&lt;/p&gt;

&lt;p&gt;Self-hosting genuinely wins in exactly two cases. You need total control of the server, or you have data residency requirements tight enough that "Swiss company, Swiss datacenter" doesn't clear the bar and only "this rack, this jurisdiction, this contract" will. Those are real requirements and if you have them, none of the above applies and you should stop reading. Everyone else is choosing a hobby, not an architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step one: admit that WorkMail was doing three jobs
&lt;/h2&gt;

&lt;p&gt;WorkMail was three products in a trench coat, and only one of them is email.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Human mailboxes.&lt;/strong&gt; This is the part you migrate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email flow rules with a Run Lambda action.&lt;/strong&gt; This is not email. This is a workflow engine that convinced you it was email.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transactional sending for some app you shipped in 2019 and have not thought about since.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are number three: STOP. Go outside. That should have been going through SES the whole time, and the only reason it wasn't is that you had a working SMTP endpoint and no supervision. Verify the domain in SES, point the app at it, remove it from the migration checklist. Proton is not your application's SMTP relay, and treating it like one is how you end up reading bounce logs at 1 a.m. while questioning your career.&lt;/p&gt;

&lt;p&gt;If you are number two: keep SES as well, and take a moment to appreciate that you already were. Inbound mail arrives at Amazon SES first and is handed to WorkMail afterward — if SES blocks a message, your flow rules never see it. SES has been standing in front of your mail server this entire time, quietly, like a bouncer you forgot you hired.&lt;/p&gt;

&lt;p&gt;Proton, needless to say, has no equivalent to a Run Lambda rule. You configured those in the WorkMail console, picked your sender and recipient patterns, and your function fetched the message body through the &lt;code&gt;workmailmessageflow&lt;/code&gt; API. There is no CLI verb for any of it, which tells you something about how much AWS expected you to be doing this. It was an AWS-flavored appendage grafted onto email, not a feature of email, and nothing outside AWS is going to replace it. Route the automated addresses to SES receiving rules, keep the Lambda, let Proton handle the humans.&lt;/p&gt;

&lt;p&gt;What survives this triage is invariably smaller than what you feared. Mine went from eleven mailboxes to seven, and two of the four casualties were aliases nobody had sent to since the Trump administration. The first one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get your data out before you touch DNS
&lt;/h2&gt;

&lt;p&gt;WorkMail ships a native export. Use it — not because you'll import from it, but because you want a cold copy in S3 before you start moving load-bearing DNS records around.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws workmail start-mailbox-export-job &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--organization-id&lt;/span&gt; m-a123b4c5de678fg9h0ij1k2lm234no56 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--entity-id&lt;/span&gt; S-1-1-11-1111111111-2222222222-3333333333-3333 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-arn&lt;/span&gt; arn:aws:iam::111122223333:role/WorkmailMailboxExportRole &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--kms-key-arn&lt;/span&gt; arn:aws:kms:eu-west-1:111122223333:key/KEY-ID &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--s3-bucket-name&lt;/span&gt; my-workmail-escape-hatch &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--s3-prefix&lt;/span&gt; exports/alice/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--client-token&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;uuidgen&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prerequisites, because AWS would never let you have a nice thing unmediated: an S3 bucket with public access blocked, a &lt;em&gt;symmetric&lt;/em&gt; KMS key, and an IAM role that &lt;code&gt;export.workmail.amazonaws.com&lt;/code&gt; is permitted to assume. The trust policy scopes to &lt;code&gt;aws:SourceArn&lt;/code&gt; for a single organization, though you can drop that condition and reuse the role across several. It is about fifteen minutes of policy-wrangling per organization, not per user, which by AWS standards qualifies as a delightful surprise.&lt;/p&gt;

&lt;p&gt;Loop it over your entity IDs, walk away, return to a bucket full of &lt;code&gt;.zip&lt;/code&gt; files containing MIME-format messages.&lt;/p&gt;

&lt;p&gt;Now, two things before you call this a backup and go to bed.&lt;/p&gt;

&lt;p&gt;It exports email and calendar items &lt;strong&gt;only&lt;/strong&gt;. Contacts and tasks do not come along. This is documented, in the sense that it is written down somewhere you were never going to look. Also, the job runs over a period of time rather than capturing the mailbox at an instant, so it is emphatically not a snapshot — mail arriving mid-export lands wherever it lands. Poll &lt;code&gt;describe-mailbox-export-job&lt;/code&gt; for progress, and be aware that &lt;code&gt;list-mailbox-export-jobs&lt;/code&gt; will only show you the previous seven days, because retention is a feature you pay extra for elsewhere.&lt;/p&gt;

&lt;p&gt;Full procedure, trust policy JSON included: &lt;a href="https://docs.aws.amazon.com/workmail/latest/adminguide/mail-export.html" rel="noopener noreferrer"&gt;Exporting mailbox content&lt;/a&gt; and the &lt;a href="https://docs.aws.amazon.com/cli/latest/reference/workmail/start-mailbox-export-job.html" rel="noopener noreferrer"&gt;&lt;code&gt;start-mailbox-export-job&lt;/code&gt; CLI reference&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is your insurance. It is not your migration path. Your migration path is IMAP, like it was before Y2K.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration is boring, which is the entire point
&lt;/h2&gt;

&lt;p&gt;Proton's Easy Switch handles generic IMAP sources, not merely the Gmail and Outlook boxes its marketing page is excited about. WorkMail is a generic IMAP source. Feed it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Host: &lt;code&gt;imap.mail.&amp;lt;region&amp;gt;.awsapps.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Port: &lt;code&gt;993&lt;/code&gt;, TLS&lt;/li&gt;
&lt;li&gt;Username: the full address&lt;/li&gt;
&lt;li&gt;Password: the account password&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It runs server-side, chews through your folder hierarchy, and re-encrypts everything on arrival. For a 40 GB mailbox with a decade of nested folders named things like &lt;code&gt;Archive/2019/misc/actually-important&lt;/code&gt;, budget most of a day and — this is the important part — do not watch it. Nothing good has ever come from watching a mailbox sync.&lt;/p&gt;

&lt;p&gt;If you'd rather drive, &lt;code&gt;imapsync&lt;/code&gt; speaks fluently to both ends and gives you folder filtering and a log file that tells you the truth. I ran imapsync on the two mailboxes I actually cared about and let Easy Switch handle the rest, on the theory that a tool you can debug is worth more than a tool you can trust. Both landed clean, which was mildly disappointing. I prepared mentally I might need to tell my wife that I will not watch a movie with her that weekend.&lt;/p&gt;

&lt;h2&gt;
  
  
  DNS: twenty minutes that determine your weekend
&lt;/h2&gt;

&lt;p&gt;Drop your MX TTL to 300 or smaller &lt;strong&gt;the day before&lt;/strong&gt; cutover. Everyone forgets this. You will forget this. Then, in Proton's admin console, collect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;protonmail-verification=&lt;/code&gt; TXT record&lt;/li&gt;
&lt;li&gt;MX at &lt;code&gt;mail.protonmail.ch&lt;/code&gt; (priority 10) and &lt;code&gt;mailsec.protonmail.ch&lt;/code&gt; (20)&lt;/li&gt;
&lt;li&gt;SPF: &lt;code&gt;v=spf1 include:_spf.protonmail.ch ~all&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Three DKIM CNAMEs&lt;/li&gt;
&lt;li&gt;A DMARC record, which you should have had for years, and we are not going to discuss it further&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add verification, SPF, and DKIM first. Leave MX pointed at WorkMail. Let it settle. &lt;em&gt;Then&lt;/em&gt; swap MX. Keep the WorkMail organization breathing for a week afterward — it's four dollars a mailbox, the cheapest insurance policy in the history of the discipline — and run a delta sync at the end to sweep up whatever landed on the old side while the internet made up its mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parts that will annoy you, ranked
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;No IMAP. No SMTP. Not really.&lt;/strong&gt; Proton is end-to-end encrypted, which means your mail client cannot simply talk to it. You run &lt;a href="https://proton.me/mail/bridge" rel="noopener noreferrer"&gt;Proton Bridge&lt;/a&gt; locally, which stands up a loopback IMAP/SMTP endpoint for your client to connect to. It works. It is also a daemon you must now think about, and if your team is on Thunderbird or Apple Mail, that conversation happens &lt;em&gt;before&lt;/em&gt; the migration, not during it, unless you enjoy conflict.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server-side search is different.&lt;/strong&gt; Proton cannot index what it cannot read, which is the whole selling point, so search is client-side against a locally built index you download once. This is fine. It is not Exchange. If your workflow is "search fourteen years of mail from a hotel wifi on a borrowed laptop," recalibrate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Storage math, honestly.&lt;/strong&gt; WorkMail: $4/user/month, 50 GB. Proton Mail Essentials: $6.99/user/month billed annually, 15 GB. Proton loses that comparison on paper and I'm not going to pretend otherwise by adding up "value" until the number comes out right. It wins the comparison back only if you were already paying separately for a VPN, a password manager, or file storage, because Workspace Standard bundles the lot at $12.99. If all you want is a mailbox, you are paying a premium for Swiss jurisdiction and zero-access encryption. That is a legitimate thing to buy. Just know that's the transaction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Calendar interop.&lt;/strong&gt; Remember those free/busy lookups I was bragging about four hundred words ago? Gone. Proton Calendar is end-to-end encrypted and handles invitations perfectly well, but there is no EWS endpoint for Outlook to interrogate and there is not going to be one. This was the single hardest thing to surrender. If your organization schedules meetings by staring at a grid of everyone's availability, price that in before you commit, and possibly consider scheduling fewer meetings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Was it worth it!?
&lt;/h2&gt;

&lt;p&gt;I did not leave WorkMail on principle. I left because Amazon deprecated it and I was given a deadline. The principle only decided where I landed, which I suspect is true of most principles.&lt;/p&gt;

&lt;p&gt;But the migration forced the triage I'd been avoiding for four years: the app mail that should have been SES, the alias nobody had touched since 2021, the "team" mailbox that was, on closer inspection, one person. The cutover consumed a weekend. The only casualty was an email flow rule I wrote in 2018 and am still not certain ever did anything.&lt;/p&gt;

&lt;p&gt;The Community Builder badge expired anyway, but who knows maybe this article will get me back into the program next year?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you're still using WorkMail, you have until March 2027 and you should not use all of it. DNS propagation is patient. Your users are not. And AWS has now established, conclusively, that "it's been running for a decade" is not a support commitment.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>email</category>
      <category>privacy</category>
      <category>devops</category>
    </item>
    <item>
      <title>Deploying a Cloudfront Lambda Image Optimization stack with Terraform Part 1 - Cloudfront</title>
      <dc:creator>lordz-md</dc:creator>
      <pubDate>Sun, 09 Feb 2025 14:06:52 +0000</pubDate>
      <link>https://dev.to/aws-builders/deploying-a-cloudfront-lambda-image-optimization-stack-with-terraform-part-1-cloudfront-3ocf</link>
      <guid>https://dev.to/aws-builders/deploying-a-cloudfront-lambda-image-optimization-stack-with-terraform-part-1-cloudfront-3ocf</guid>
      <description>&lt;p&gt;Ever since AWS announced CloudFront supporting Origin Access Control (OAC) for Lambda function URL origins I was thinking of improving the  image optimization system used by one of my projects that was based on the infrastructure described &lt;a href="https://aws.amazon.com/blogs/networking-and-content-delivery/image-optimization-using-amazon-cloudfront-and-aws-lambda/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
The problem with this approach was that it's security was based on a secret key in a Custom origin header, which is validated in the Lambda function before processing the image. This approach was triggering the AWS Security rules to flag the Lambda Public Access rule. At the moment of publishing this post this has been already been fixed by the team maintaining the &lt;a href="https://github.com/aws-samples/image-optimization.git" rel="noopener noreferrer"&gt;aws-samples&lt;/a&gt; although the official blog post still mentions the Custom origin header.&lt;/p&gt;

&lt;p&gt;Anyway, as I was not much into CDK and JavaScript I also decided to rewrite the CDK code used to deploy the stack with Terraform and make it available to the public via Githbub &lt;a href="https://github.com/lordz-ei/terraform-cloudfront-image-optimization/" rel="noopener noreferrer"&gt;terraform-cloudfront-image-optimizatio&lt;/a&gt; repository.&lt;/p&gt;

&lt;p&gt;This is the sample architecture the image-optimization stack creates.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F01r0wklikxx15m6q5hkt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F01r0wklikxx15m6q5hkt.png" alt="Sample image-optimization architecture" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;
Image from AWS Networking &amp;amp; Content Delivery blog



&lt;p&gt;So let's start.&lt;/p&gt;

&lt;p&gt;We have a couple of components in this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Original images S3 bucket&lt;/li&gt;
&lt;li&gt;Transformed images S3 bucket&lt;/li&gt;
&lt;li&gt;Cloudfront&lt;/li&gt;
&lt;li&gt;Cloudfront funtion&lt;/li&gt;
&lt;li&gt;Image optimization Lambda function&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Setting up AWS Terraform provider
&lt;/h2&gt;

&lt;p&gt;Let's set up the AWS Terraform provider first. I ussualy do it by creating a file called versions.tf but I have seen also configurations using providers.tf&lt;/p&gt;

&lt;p&gt;It is done using the following block&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~&amp;gt; 5.0"  #Allow only the right-most version component to increment 
    }
  }

}

provider "aws" {
  region = var.aws_region
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Defining necessary Terraform variables
&lt;/h2&gt;

&lt;p&gt;Let's define the required variables&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variable "aws_region" {
  description = "The AWS region to deploy resources."
  default     = "eu-west-1"
}

variable "create_origin_bucket" {
  description = "Create an S3 bucket for original images"
  type        = bool
  default     = false #Assume there is already a bucket used to serve images
}

variable "original_image_bucket_name" {
  description = "Name of the original image bucket"
  type        = string
}

variable "transformed_image_bucket_name" {
  description = "Name of the transformed image bucket"
  type        = string
}

variable "cloudfront_log_bucket_name" {
  description = "S3 bucket for CloudFront logs"
  type        = string
}

variable "min_ttl" {
  description = "Minimum TTL for CloudFront cache"
  type        = number
  default     = 86400
}

variable "default_ttl" {
  description = "Default TTL for CloudFront cache"
  type        = number
  default     = 604800
}

variable "max_ttl" {
  description = "Maximum TTL for CloudFront cache"
  type        = number
  default     = 2592000
}

variable "max_image_size" {
  description = "Maximum image size in bytes"
  type        = number
  default     = 4700000
}

variable "image_cache_ttl" {
  description = "TTL for transformed images in seconds"
  type        = string
  default     = "max-age=31622400"
}

variable "lambda_layer_arn" {
  description = "ARN of the Lambda layer" #used for adding the Pillow library layer
  type        = string
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating Cloudfront distribution
&lt;/h2&gt;

&lt;p&gt;To create the Cloudfront distribution we use the &lt;a href="https://registry.terraform.io/modules/terraform-aws-modules/cloudfront/aws/latest" rel="noopener noreferrer"&gt;terraform-aws-modules/cloudfront/aws&lt;/a&gt; from AWS Hero Anton Babenko. &lt;/p&gt;

&lt;p&gt;For an easy distinction of the distribution let's set &lt;code&gt;comment = "Image Optimization CloudFront with Failover"&lt;/code&gt;, obviously this can be changed to the description of your wish.&lt;/p&gt;

&lt;p&gt;As we will use Cloudfront Origin Access Control (OAC) we will enable it using following configuration block&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;origin_access_control = {
    s3_oac = {
      description      = "CloudFront access to S3"
      origin_type      = "s3"
      signing_behavior = "always"
      signing_protocol = "sigv4"
    }

    lambda_oac = {
      description      = "CloudFront access to Lambda"
      origin_type      = "lambda"
      signing_behavior = "always"
      signing_protocol = "sigv4"
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the cloudfront module doesn't have the posibility to create the cache policy and response header policy we will use Terraform resources&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "aws_cloudfront_cache_policy" "image_optimization_cache_policy" {
  name        = "image-optimization-cache-policy"
  comment     = "Cache policy for image optimization"
  default_ttl = var.default_ttl
  min_ttl     = var.min_ttl
  max_ttl     = var.max_ttl
  parameters_in_cache_key_and_forwarded_to_origin {
    cookies_config {
      cookie_behavior = "none"
    }
    headers_config {
      header_behavior = "none"
    }
    query_strings_config {
      query_string_behavior = "none"
    }
  }
}

resource "aws_cloudfront_response_headers_policy" "image_optimization_response_header_policy" {
  name    = "image-optimization-response-header-policy"
  comment = "Response header policy for image optimization"

  cors_config {
    access_control_allow_credentials = false

    access_control_allow_headers {
      items = ["*"]
    }

    access_control_allow_methods {
      items = ["GET"]
    }

    access_control_allow_origins {
      items = ["*"]
    }
    access_control_expose_headers {
      items = ["-"]
    }

    access_control_max_age_sec = 600
    origin_override            = true
  }

  custom_headers_config {
    items {
      header   = "x-aws-image-optimization"
      override = true
      value    = "v1.0"
    }

    items {
      header   = "vary"
      override = true
      value    = "accept"
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's return to the cloudfront module and add the origins and the origin group&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;origin = {
    s3 = {
      domain_name           = module.transformed_s3_bucket.s3_bucket_bucket_regional_domain_name
      origin_access_control = "s3_oac"
      origin_shield = {
        enabled              = true
        origin_shield_region = var.aws_region
      }
    }

    lambda = {
      domain_name           = "${module.image_optimization_lambda.lambda_function_url_id}.lambda-url.${data.aws_region.current.name}.on.aws"
      origin_access_control = "lambda_oac"
      custom_origin_config = {
        http_port              = 80
        https_port             = 443
        origin_protocol_policy = "https-only"
        origin_ssl_protocols   = ["TLSv1.2"]
      }
      origin_shield = {
        enabled              = true
        origin_shield_region = var.aws_region
      }
    }
  }

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

&lt;/div&gt;



&lt;p&gt;To make this work we will also need the default cache behavior and the Cloudfront function association&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;default_cache_behavior = {
    target_origin_id       = "lambda_failover"
    viewer_protocol_policy = "redirect-to-https"
    allowed_methods        = ["GET", "HEAD"]
    cached_methods         = ["GET", "HEAD"]

    use_forwarded_values       = false
    cache_policy_id            = aws_cloudfront_cache_policy.image_optimization_cache_policy.id
    response_headers_policy_id = aws_cloudfront_response_headers_policy.image_optimization_response_header_policy.id

    function_association = {
      # Valid keys: viewer-request, viewer-response
      viewer-request = {
        function_arn = aws_cloudfront_function.cloudfront_url_rewrite.arn
      }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Last pieces of Cloudfront configurations are logging and viewer certificate which we will use the default and also we don't need any geo restrictions and will setup a dependency on the lambda function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;logging_config = {
      include_cookies = false
      bucket          = module.cloudfront_logs.s3_bucket_bucket_domain_name
      prefix          = "cloudfront-logs/"
    }


    geo_restriction = {
      restriction_type = "none"
    }


    viewer_certificate = {
      cloudfront_default_certificate = true
    }
  }

  depends_on = [module.image_optimization_lambda]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the next part we will deploy the Cloudfront function and Lambda&lt;/p&gt;

</description>
      <category>cloudfront</category>
      <category>security</category>
      <category>aws</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Operation Fire Valley</title>
      <dc:creator>lordz-md</dc:creator>
      <pubDate>Fri, 15 Dec 2023 21:44:45 +0000</pubDate>
      <link>https://dev.to/aws-builders/operation-fire-valley-3b7h</link>
      <guid>https://dev.to/aws-builders/operation-fire-valley-3b7h</guid>
      <description>&lt;p&gt;&lt;strong&gt;Ho ho ho!!!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's that time of the year again!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Welcome to Operation “FireValleyRocks”!&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Many thanks to Jason Dunn, Lily Kerns, and the other wonderful AWS Community managers.&lt;br&gt;
It's been a wonderful year and you guys made it even greater. Have wonderful holidays and thanks for everything you are doing for us the community!!!&lt;/p&gt;

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

</description>
      <category>communitybuilders</category>
      <category>stepfunctions</category>
      <category>thankfulforest2024</category>
      <category>firevalleyrocks</category>
    </item>
    <item>
      <title>Operation Ho Ho Ho, or how AWS Community Builders saved the holidays!</title>
      <dc:creator>lordz-md</dc:creator>
      <pubDate>Tue, 20 Dec 2022 06:00:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/operation-ho-ho-ho-or-how-aws-community-builders-saved-christmas-52e5</link>
      <guid>https://dev.to/aws-builders/operation-ho-ho-ho-or-how-aws-community-builders-saved-christmas-52e5</guid>
      <description>&lt;h2&gt;
  
  
  What surprises you most about the community builders program?
&lt;/h2&gt;

&lt;p&gt;I love learning new things, but mostly I like socializing with builders around the world and I am very surprised on how builders help each other, doesn't matter if it's a problem with an AWS service or a Vegas hotel not accepting your card when you arrived for re:Invent.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s your background and your experience with AWS?
&lt;/h2&gt;

&lt;p&gt;I come from a sysadmin/networking background which I did for about 10 years before finding out about AWS. I enjoy AWS because it gives me the freedom to build things and tear them down fast. I also enjoy sharing knowledge about AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s the biggest benefit you see from the program?
&lt;/h2&gt;

&lt;p&gt;This year was my second in person re:Invent, and thanks to the builders I didn't feel lonely in Vegas. I think networking and knowledge sharing is the best benefit of the AWS Community Builders.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s the next swag item that you would like to get?
&lt;/h2&gt;

&lt;p&gt;My apartment is full of swag items, so I donated a lot of swag I received this year, except the hoodie and the oversized coffee mug ( which is good also for self-defense and I don't have to justify why I carry a coffee mug in my car :D :D :D ). I even gave my claw won Kindle to a builder that needed it more than me. I would love access to books, maybe an Audible yearly subscription and access to Cloud Academy that can be donated to someone who needs it more.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are you eating for dinner today? Share the recipe!
&lt;/h2&gt;

&lt;p&gt;Borscht (Beet Soup )&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---qwhziAy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oshyze0x9fzg744b9ws3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---qwhziAy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oshyze0x9fzg744b9ws3.jpg" alt="Image description" width="880" height="479"&gt;&lt;/a&gt;&lt;br&gt;
(Not actual image of my borscht)&lt;/p&gt;

&lt;p&gt;According to Wikipedia, Borscht is neither Ukrainian or Russian. It is national Slavic dish that has a history of centuries. Borsch is traditional beet soup cooked in every household of any former republic that belonged to USSR – Ukraine, Russia, Moldova, Belorussia etc. Not to mention all over Eastern Europe&lt;/p&gt;

&lt;p&gt;The iconic red beet soup is made with beef ribs, cabbage, potatoes, carrots, garlic and dill, and then served with a dollop of sour cream and rye bread, but I replaced the beef ribs with pork ones. So let's go.&lt;/p&gt;

&lt;p&gt;You will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Red beets&lt;/li&gt;
&lt;li&gt;Cabbage&lt;/li&gt;
&lt;li&gt;Potatoes&lt;/li&gt;
&lt;li&gt;Carrots&lt;/li&gt;
&lt;li&gt;Onion&lt;/li&gt;
&lt;li&gt;Garlic&lt;/li&gt;
&lt;li&gt;Dill&lt;/li&gt;
&lt;li&gt;Beef or Pork ribs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Step 1. Put the ribs in boiling water and let them boil for 30-40 minutes.&lt;br&gt;
Step 2. Add cabbage in broth with bay leaves and peppercorns for 20 minutes after bringing to a boil.&lt;br&gt;
Meanwhile you can chop the rest of the vegetables.&lt;br&gt;
Step 3. Sauté the onion and carrots in a bit of olive oil until translucent, about 5 minutes. This makes onion flavorful making entire borscht recipe more delicious. &lt;strong&gt;Do not skip.&lt;/strong&gt;&lt;br&gt;
Step 4. Add beets and a bit more oil, cook for another 5 minutes.&lt;br&gt;
Step 5. Put the sautéed veggies to the pot along with potatoes, tomato paste and salt. Cook covered for 20 minutes. In the meantime, prep garlic, dill and other seasonings.&lt;/p&gt;

&lt;p&gt;Season borscht with white vinegar, garlic, sugar and pepper. Red wine vinegar, apple cider vinegar, white wine vinegar or lemon juice can also be used. Traditional choice is borsch for sour soup - a liquid created with fermented wheat or barley brans, giving the dish a distinctively sour and slightly pungent taste ( can be bought in US and Western Europe at traditional eastern european shops ) but if not the best choice is white vinegar though.&lt;/p&gt;

&lt;p&gt;Stir, turn off heat and let borscht soup stand for 10 minutes covered to allow flavors to “marry” each other. Add dill and your borscht is ready to serve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is there anything else you would like to say about the community builders program in 2022?
&lt;/h2&gt;

&lt;p&gt;This year was amazing, the CB mixer at re:Invent, the people I met gave me a lot of inspiration for 2023. So I will be launching a local AWS UG in Moldova and will work more on trainings and labs with local students who want to learn more about cloud and AWS.&lt;br&gt;
Thanks to all the AWS CB managers, developer advocates and to all the CB around the world for a wonderful experience. Wish you Happy Holidays! Let's us all have a wonderful 2023!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>communitybuilder</category>
      <category>cbchristmas2022</category>
    </item>
    <item>
      <title>What I learned from “failing” to teach my first AWS course</title>
      <dc:creator>lordz-md</dc:creator>
      <pubDate>Fri, 11 Nov 2022 09:56:08 +0000</pubDate>
      <link>https://dev.to/aws-builders/what-i-learned-from-failing-to-teach-my-first-aws-course-1085</link>
      <guid>https://dev.to/aws-builders/what-i-learned-from-failing-to-teach-my-first-aws-course-1085</guid>
      <description>&lt;p&gt;I have been working with AWS for more than 5 years already and this spring I was selected by AWS to become an AWS Community Builder in Republic of Moldova. I have teamed up with Tekwill Academy who already has a lot of amazing IT courses and organize a lot of events for the local community, to give an AWS course to a local bank and in the future have courses to students and people that wish to transition into cloud and DevOps. I have started to teach my AWS course at the end of August 2022 and finished it a couple of days ago mid-November (initially it was planned to finish it end of October, but I needed to postpone some lessons because of personal issues).&lt;/p&gt;

&lt;p&gt;This are the lessons I learned from this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Being an AWS Certified Cloud Engineer and having lots of years of experience, doesn’t make you a great teacher — no matter how many projects you have done in the past, it doesn’t mean you can teach, it gives you the weight of having experience but every small mistake in the way of your students gives them the benefit of doubt of your qualifications.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Great trainers prepare the courses well in advance — never leave preparing the slides for the last moment, this course came unexpected, so I had literally been preparing slides and material in the day of the lesson or the day before. This combined with working on real projects made me do them as fast as possible, so I am sure I have omitted important thing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Nether underestimate your audience — on-line training and off-line differ a lot, if you are off-line, you can expect questions of situations that you have never encountered, and you are expected to answer immediately, thus on-line you have some time to prepare and give an answer&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On-line video courses are totally different from a course with a trainer — this actually relates to the point above, I have taken many on-line courses for my work and sometimes I wish I could have asked the trainer a question that related to a real issue I had at the moment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Community matters — AWS Community Builders and folks at AWS helped me a lot in getting answers on issues I have never faced but students asked.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improvement nether stops — after the lessons learned from this attempt I will improve, I have already ideas on making the slides more fun, the labs and the hands-on task much interactive.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You may have noticed that the title had failing in quotes, though I feel I have could done better, I consider this a learning experience and actually I have learned a lot from preparing this course so it’s not a failure at all. I know what can be done better and in 3 months I will have a better course and will be prepared to share the knowledge with other.&lt;/p&gt;

</description>
      <category>awscommunity</category>
      <category>teaching</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
