<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.2.0">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2024-10-13T11:35:35+00:00</updated><id>/feed.xml</id><title type="html">Iain Samuel McLean Elder</title><subtitle>AWS consultant. Certified solutions architect and security specialist.</subtitle><entry><title type="html">AWS UK Community Summit 2024</title><link href="/2024/10/04/AWS-UK-Community-Summit-2024.html" rel="alternate" type="text/html" title="AWS UK Community Summit 2024" /><published>2024-10-04T20:00:00+00:00</published><updated>2024-10-04T20:00:00+00:00</updated><id>/2024/10/04/AWS-UK-Community-Summit-2024</id><content type="html" xml:base="/2024/10/04/AWS-UK-Community-Summit-2024.html">&lt;p&gt;On 2024-09-26 I attended the &lt;a href=&quot;https://www.comsum.co.uk/&quot;&gt;AWS UK Community Summit&lt;/a&gt; in Manchester.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-10-04-AWS-UK-Community-Summit-2024-Banner.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-10-04-AWS-UK-Community-Summit-2024-Swag.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Of course it rained. Everyone warned me it would! I’m used to it living in Glasgow. It turns out Manchester isn’t even in the &lt;a href=&quot;https://www.showerstoyou.co.uk/latest/2021/03/top-20-rainiest-cities-uk/&quot;&gt;top 20 rainiest UK cities&lt;/a&gt;. I wonder why it gets such a wet reputation.&lt;/p&gt;

&lt;p&gt;My picks from the schedule were mostly from the Architects track, with some Builders and Leaders content too.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Damien Jones: Building and Automating Serverless Auto Scaling Data Pipelines in AWS&lt;/li&gt;
  &lt;li&gt;David Boyne: Avoiding the big ball of mud with serverless event driven architectures&lt;/li&gt;
  &lt;li&gt;Sheen Brisals: How do you measure your enterprise’s readiness for serverless?&lt;/li&gt;
  &lt;li&gt;Yan Cui: Money-saving tips for the frugal serverless developer&lt;/li&gt;
  &lt;li&gt;Vadym Kazulkin: Detect operational anomalies in Serverless applications with ML-based Amazon DevOps Guru&lt;/li&gt;
  &lt;li&gt;Sarah Hamilton: Operating Quality Event-Driven Serverless Architectures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full schedule:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-10-04-AWS-UK-Community-Summit-Schedule.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Serverless&lt;/strong&gt;: in 5 out of 6 talk titles, it looks like an unofficial theme of the event! It’s a broad term that refers to managed services that allow you to build robust solutions at a higher level of abstraction. You think about events and workflows rather than servers, storage, and requests. That stuff is obviously still important, but for most use cases there is no need to stray from the default behavior that services such as S3 and Lambda provide. I’m enthusiastic about serverless, because the less generic infrastructure I have to manage, the more I can focus on delivering value.&lt;/p&gt;

&lt;p&gt;Sarah’s talk was my favorite because she changed my mind and settled a doubt. It was a secondary point in her argument, but Sarah made me see the light on AWS’s CloudFormation Development Kit (CDK).&lt;/p&gt;

&lt;p&gt;A complex CloudFormation template may describe resources with patterns of interdependencies. Writing this all out by hand may be tedious and validating its correctness can be worse. If you’re tempted to parse the template into an object model to check its properties programmatically, you may as well go further and use a whole object model to generate the template. That’s what the CDK is: a programmatic object model for CloudFormation resources that also renders templates.&lt;/p&gt;

&lt;p&gt;AWS maintains the CDK so we trust the correctness of the object model and the template renderer. Then what remains for us to test the correctness of our templates is to test properties of an object model.&lt;/p&gt;

&lt;p&gt;Sarah’s revelation is what made this report take so long to write. After the conference I put her lesson into action. Last week I speedran a course in TypeScript and CDK to solve a problem for which bare CloudFormation offers ugly solutions. I hope to share more about that later.&lt;/p&gt;

&lt;p&gt;The rest of the article shares my main takeaways and dumps links or resources for further research.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Damien Jones&lt;/strong&gt;, a.k.a. the Amazon Web Shark, kicked us off with &lt;strong&gt;Building and Automating Serverless Auto Scaling Data Pipelines in AWS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;“Big Data” is often described by four V’s: &lt;strong&gt;Volume&lt;/strong&gt;, &lt;strong&gt;Variety&lt;/strong&gt;, &lt;strong&gt;Velocity&lt;/strong&gt;, and &lt;strong&gt;Veracity&lt;/strong&gt;. Damien added a 5th V: &lt;strong&gt;Value&lt;/strong&gt;: Is it important or useful? Does it have a monetary value?&lt;/p&gt;

&lt;p&gt;Damien told us the pros and cons of the building blocks he uses for his pipelines: S3, Lambda, Athena, Glue, Step Functions, and EventBridge.&lt;/p&gt;

&lt;p&gt;I hadn’t seen Glue in action before. I like its dashboards that present the data quality checks.&lt;/p&gt;

&lt;p&gt;Damien’s recorded demo of his pipelines was slick. I prefer recorded demos because controlling the screen doesn’t distract from the communication. He overlayed a timer, highlights, and blurring to guide the audience’s attention to the right place.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/in/mrdamienjones/&quot;&gt;Damien Jones on LinkedIn&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://amazonwebshark.com/&quot;&gt;The Amazon Web Shark blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/MrDamienJones/Community-Sessions&quot;&gt;Community Sessions repo on GitHub&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://web.archive.org/web/20210118085939/https://www.ibm.com/blogs/watson-health/the-5-vs-of-big-data/&quot;&gt;IBM defined a 5th V back in 2016&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://obsproject.com/&quot;&gt;Open Broadcaster Software (OBS)&lt;/a&gt; for recording&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.capcut.com/&quot;&gt;CapCut&lt;/a&gt; for video editing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;David Boyne&lt;/strong&gt; followed with &lt;strong&gt;Avoiding the big ball of mud with serverless event driven architectures&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Project Paradox: Why do we take the biggest decisions at a time when we have the least knowledge?&lt;/p&gt;

&lt;p&gt;Without conscious design, every project inevitably graduates toward a complex, unpleasant mess.&lt;/p&gt;

&lt;p&gt;Event-Driven Architecture (EDA) and Domain-Driven Design (DDD) give us patterns to control the complexity, but we need better tools to make them more discoverable.&lt;/p&gt;

&lt;p&gt;Links and resources to learn more:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/in/david-boyne?miniProfileUrn=urn%3Ali%3Afs_miniProfile%3AACoAAALFS9EBtEJmVHEiFArzaU8v3mZA_5JcpTk&amp;amp;lipi=urn%3Ali%3Apage%3Ad_flagship3_search_srp_all%3BnuAsnk7TSTqIN2IbVu3FeA%3D%3D&quot;&gt;David Boyne on LinkedIn&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.boyney.io/&quot;&gt;Boyney.io&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;More about the &lt;a href=&quot;https://beyond-agility.com/project-paradox/&quot;&gt;Project Paradox&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://laputan.org/mud/&quot;&gt;Big Ball of Mud&lt;/a&gt; article by Brian Foot and Joseph Yoder in 1999&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.eventcatalog.dev/&quot;&gt;EventCatalog&lt;/a&gt; David’s project to make EDA more discoverable&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.winglang.io/&quot;&gt;Wing Lang&lt;/a&gt;, David’s other project, a programming language for the cloud&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.eventstorming.com/&quot;&gt;EventStorming&lt;/a&gt;: a workshop format and book by Alberto Brandolini for collaborative exploration of complex business domains&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Robustness_principle&quot;&gt;Postel’s Law&lt;/a&gt;: “Be liberal in what you accept, and conservative in what you send”&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://martinfowler.com/articles/201701-event-driven.html&quot;&gt;Event-Carried State Transfer&lt;/a&gt;: a pattern that allows clients to update without needing to call back to the source system&lt;/li&gt;
  &lt;li&gt;Events tell story. Tables tell state.&lt;/li&gt;
  &lt;li&gt;Backwards compatibility, extra fields, parallel versions&lt;/li&gt;
  &lt;li&gt;David mentioned some patters: Open Host Service, Anti-Corruption Layer, Conformist&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://ddd-practitioners.com/&quot;&gt;Domain-Driven Design: A Practitioner’s Guide&lt;/a&gt; has a glossary to describe these&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/azure/architecture/patterns/&quot;&gt;Azure Cloud Design Patterns&lt;/a&gt; also describes them&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/introduction.html&quot;&gt;AWS Cloud design patter, architecture, and implementations&lt;/a&gt; also describes them&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://cloudevents.io/&quot;&gt;CloudEvents&lt;/a&gt;, a common specification for event data&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.asyncapi.com/en&quot;&gt;AsyncAPI&lt;/a&gt;, industry-standard asynchronous API definitions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sheen Brisals&lt;/strong&gt; asked &lt;strong&gt;How do you measure your enterprise’s readiness for serverless?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Serverless is not magic. You need to understand your use case, skills, and requirements. You need to assess its efficiency, cost, and trustworthiness.&lt;/p&gt;

&lt;p&gt;Afraid of vendor lock-in? AWS is not your vendor but a partner.&lt;/p&gt;

&lt;p&gt;I asked Sheen what he thought about David Heinemeier Hansson publicly quitting AWS for bare-metal servers because it’s cheaper, because he doesn’t need instant scaling. Sheen said that you need to look at the total cost of ownership and innovation.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/in/sheen-brisals/&quot;&gt;Sheen Brisals on LinkedIn&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Book: &lt;a href=&quot;https://www.amazon.com/Serverless-Development-AWS-Enterprise-Scale-Solutions/dp/1098141938&quot;&gt;Serverless Development on AWS: Building Enterprise-Scale Serverless Solutions&lt;/a&gt; by Sheen himself&lt;/li&gt;
  &lt;li&gt;Blog: &lt;a href=&quot;https://sbrisals.medium.com/the-serverless-book-2209ee13e619&quot;&gt;Introducing: The Serverless Book!&lt;/a&gt; by Sheen himself&lt;/li&gt;
  &lt;li&gt;Book: &lt;a href=&quot;https://learningsystemsthinking.com/&quot;&gt;Learning Systems Thinking&lt;/a&gt; by Diana Montalion&lt;/li&gt;
  &lt;li&gt;Talk: How to shift to a serverless mindset by David Anderson (various recording on YouTube)&lt;/li&gt;
  &lt;li&gt;Book: &lt;a href=&quot;https://www.amazon.com/Value-Flywheel-Effect-Accelerate-Organization-ebook/dp/B09V1RLRGG&quot;&gt;The Flywheel Value Effect&lt;/a&gt; also by David Anderson&lt;/li&gt;
  &lt;li&gt;Book: &lt;a href=&quot;https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215&quot;&gt;Domain-Driven Design&lt;/a&gt; by Eric Evans&lt;/li&gt;
  &lt;li&gt;Book: &lt;a href=&quot;https://www.amazon.es/Building-Microservices-Sam-Newman/dp/1491950358&quot;&gt;Building Microservices&lt;/a&gt; by Sam Newman&lt;/li&gt;
  &lt;li&gt;Article: &lt;a href=&quot;https://world.hey.com/dhh/we-have-left-the-cloud-251760fb&quot;&gt;We have left the cloud&lt;/a&gt; by David Heinemeier Hansson&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/partners/success/taco-bell-trek10/&quot;&gt;Taco Bell Migrates Ecommerce to AWS Serverless with Trk10, Cuts Cost by 90%&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Yan Cui&lt;/strong&gt; took us up to lunch with &lt;strong&gt;Money-saving tips for the frugal serverless developer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Yan gave a rapid fire of common costly mistakes and how to solve them. After each, a refrain: “I don’t mind spending money, but I want value!” All components need a purpose and a return on investment.&lt;/p&gt;

&lt;p&gt;Favorite 1: CloudWatch Logs ingestion is expensive. If you use Lambda extensions and a telemetry API to bypass CloudWatch logs, CloudWatch Logs may still charge you for ingesting the default messages that every Lambda execution writes. Block all writes using an IAM policy.&lt;/p&gt;

&lt;p&gt;Favorite 2: Avoid using the RequestResponse (synchronous) calls for Lambda-to-Lambda, because you will pay twice for the execution time. For secondary tasks that aren’t in the critical path, use Event (asynchronous) invocation.&lt;/p&gt;

&lt;p&gt;I asked Yan whether I can avoid paying for Lambda to wait for a slow API response by using a Step Function, which waits for free. If the API was a webhook, then maybe a Step Function would make sense, but if it was a synchronous HTTP request/response, then Lambda or something needs to keep the connection open to receive the response.&lt;/p&gt;

&lt;p&gt;More resources:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/in/theburningmonk&quot;&gt;Yan Cui on LinkedIn&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://theburningmonk.com/&quot;&gt;The Burning Monk&lt;/a&gt;, Yan’s blog&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://speakerdeck.com/theburningmonk/money-saving-tips-for-the-frugal-serverless-developer-6894c183-11ed-4c14-b8b6-9e7ba5e79dd4&quot;&gt;Slide deck&lt;/a&gt; shared 2024-05-18 with all the examples&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://cloudwatchbook.com/&quot;&gt;The CloudWatch Book&lt;/a&gt; by Tobias Schmidt and Alessandro Volpicella&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs-advanced.html&quot;&gt;Lambda advanced logging controls&lt;/a&gt; allow you write JSON. “Not great, better than nothing.”&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.thefrugalarchitect.com/&quot;&gt;Laws of the Frugal Architect&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://theburningmonk.com/2023/12/direct-access-for-frontend-apps-to-aws-services/&quot;&gt;Configure direct end-user access to DynamoDB via identity tokens&lt;/a&gt; to avoid API gateway and Lambda costs. Not for fainthearts!&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gomomento.com/platform/cache/&quot;&gt;Momento&lt;/a&gt; cache charges per use (compare to ElastiCache, which charges per uptime)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Vadym Kazulkin&lt;/strong&gt; showed how to &lt;strong&gt;Detect operational anomalies in Serverless applications with ML-based Amazon DevOps Guru&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We learned what problems the Amazon DevOps Guru solves and saw it in action.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/es/devops-guru/&quot;&gt;DevOps Guru&lt;/a&gt; is a time-saver over standard CloudWatch. It takes the drudgery out of making alarms for every operational metric. It applies machine learning to Config, CloudWatch, X-Ray, and CloudTrail to provide faster operational insights.&lt;/p&gt;

&lt;p&gt;It categorizes notifications as “reactive” and “proactive”. Its proactive mode overlaps with Security Hub. Whereas Security Hub checks security configurations such as public access and encryption, DevOps Guru adds value by checking operational configurations such as read capacity or detailed monitoring.&lt;/p&gt;

&lt;p&gt;Vadym generated capacity notifications using a load testing tool called &lt;a href=&quot;https://github.com/rakyll/hey&quot;&gt;hey&lt;/a&gt;, a replacement for Apache Bench, or like a rapid-fire curl.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/in/vadymkazulkin&quot;&gt;Vadym Kazulkin on LinkedIn&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dev.to/vkazulkin&quot;&gt;Vadym’s blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sarah Hamilton&lt;/strong&gt; told a story about &lt;strong&gt;Operating Quality Event-Driven Serverless Architectures&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;She opened with an analogy, a restaurant called Sync’n’Serve that struggles to coordinate its chefs, waiters, and customers as its popularity grows. I like talks that start with stories.&lt;/p&gt;

&lt;p&gt;Sarah shared her opinions on testing, with reference to the “square of balance” popularized by Sheen’s book. The square describes a trade off among testing, observing, recovering, and delivering.&lt;/p&gt;

&lt;p&gt;Sarah prefers unit tests over integration tests and end-to-end tests. The further you get from each component, the more it costs to perform a test because of its complexity, and the less it tells you about exactly where a problem occurs and who is accountable. Unit tests of course can’t catch everything, so she prefers to cover only the critical paths using the more complex test types.&lt;/p&gt;

&lt;p&gt;Speedy reversal of a bad change is worth more than complete test coverage. Bugs and errors are inevitable, so we need to be ready to detect (observe) them and handle (recover from) them, and we need to keep delivering to stay relevant in the market.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/in/hamilton-sarah&quot;&gt;Sarah Hamilton on LinkedIn&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Sarah’s blog: &lt;a href=&quot;https://medium.com/serverless-transformation/bridge-integrity-integration-testing-strategy-for-eventbridge-based-serverless-architectures-b73529397251&quot;&gt;Bridge Integrity: Integration Test Strategy for EventBridge Based Serverless Architectures&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://level-out.com/&quot;&gt;Luke Hedger’s site&lt;/a&gt; (co-author of Sheen’s book)&lt;/li&gt;
  &lt;li&gt;Edsger Dijkstra: &lt;a href=&quot;https://blog.liw.fi/posts/2019/06/29/dijkstra_was_only_partially_correct_about_testing/&quot;&gt;testing show the presence, not the absence, of bugs&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Use Composite Alarms to avoid overload and fatigue. One is enough to prompt action.&lt;/li&gt;
  &lt;li&gt;Unit Testing doesn’t check inter-service contracts. Have each team and domain publish a schema that the others consume.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks to all the Summit team for delivering an exciting and satisfying full day of AWS learning and networking. &lt;a href=&quot;https://www.linkedin.com/in/joe-park-46b1b1266/&quot;&gt;&lt;strong&gt;Joe Park&lt;/strong&gt;&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/edmundcraske/&quot;&gt;&lt;strong&gt;Edmund Craske&lt;/strong&gt;&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/robinwford/&quot;&gt;&lt;strong&gt;Robin Ford&lt;/strong&gt;&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/pjphorn/&quot;&gt;&lt;strong&gt;Phil Horn&lt;/strong&gt;&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/tmisiukanis/&quot;&gt;&lt;strong&gt;Tom Misiukanis&lt;/strong&gt;&lt;/a&gt;, and the rest who I’ve yet to meet, you all deserve to be proud of what you did for the community this year.&lt;/p&gt;

&lt;p&gt;Looking forward to more, bigger, and better next year!&lt;/p&gt;</content><author><name></name></author><summary type="html">On 2024-09-26 I attended the AWS UK Community Summit in Manchester.</summary></entry><entry><title type="html">Edinburgh Python Meetup August 2024</title><link href="/2024/09/10/Edinburgh-Python-Meetup-August-2024.html" rel="alternate" type="text/html" title="Edinburgh Python Meetup August 2024" /><published>2024-09-10T20:00:00+00:00</published><updated>2024-09-10T20:00:00+00:00</updated><id>/2024/09/10/Edinburgh-Python-Meetup-August-2024</id><content type="html" xml:base="/2024/09/10/Edinburgh-Python-Meetup-August-2024.html">&lt;h1 id=&quot;python-meetup-edinburgh-august-2024&quot;&gt;Python Meetup Edinburgh August 2024&lt;/h1&gt;

&lt;p&gt;On 2024-08-27 I was the opening speaker at the reboot of the &lt;a href=&quot;https://www.meetup.com/python-edinburgh/events/302434042/&quot;&gt;Edinburgh Python Meetup&lt;/a&gt;. Thanks to &lt;strong&gt;Mark Smith&lt;/strong&gt;’s team for masterminding the event and inviting me to such a delightful evening.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-09-10-Edinburgh-Python-Meetup-Iain-introducing.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For me, starting out as a public speaker, this just the right size of event. About 40 people, some familiar faces, and a relaxed atmosphere. They warmed us with pizza and beer and general mingling in the open-plan kitchen for about 30 minutes before we all shuffled over to the seating and the projector to talk about snakes.&lt;/p&gt;

&lt;p&gt;I had the honor of opening with &lt;strong&gt;What year was that? Dating music posters with Python&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;My open-source work using Python in the AWS Community in theory gives me a lot to talk about, but to do it justice I wanted to more prep time than I was prepared to give myself in the last weeks of summer. This topic was simpler to prepare and a gentle opener that was well-received by Python newcomers and oldtimers alike. What follows is a bare-bones explanation of the solution. I’ll share more detail and insights from my research in a future blog post.&lt;/p&gt;

&lt;p&gt;“Saturday 2nd August. What year was that?”&lt;/p&gt;

&lt;p&gt;A Sunday afternoon WhatsApp rant from my Dad about the Creamfields 98 lineup inspired me to explore a modern answer to this question.&lt;/p&gt;

&lt;p&gt;After a lot of Googling, a scan of a paper-based perpetual calendar helped him solve the problem. It’s a fine solution. A 200-year range fits on the back of a phone book, needs no electricity, and solves the problem with some linear lookups. But it’s tedious, and doesn’t get me invited to Python talks.&lt;/p&gt;

&lt;p&gt;Can we solve the problem faster with today’s digital calendars? Almost. The user interface for repeating events is designed to solve problems such as “every Monday” or “every 4th Tuesday”. Google Calendar’s designer regrettably did not foresee my need to configure “Every Saturday 2nd of August”.&lt;/p&gt;

&lt;p&gt;The underlying standard that enables calendar interoperability is the &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc5545&quot;&gt;IETF’s iCalendar protocol&lt;/a&gt;. It’s easy to express using recurrence rule syntax:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;RRULE:
    FREQ=YEARLY;
    BYDAY=SA;
    BYMONTH=5;
    BYMONTHDAY=2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rrule&lt;/code&gt; class from Python’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dateutil&lt;/code&gt; module to evaluate it:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;dateutil.rrule&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rrule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;YEARLY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SA&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;dads_birthyear&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1954&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;month&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;creamfields_rule&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rrule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;freq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;YEARLY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;byweekday&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bymonthday&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bymonth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dtstart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dads_birthyear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;until&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;year&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;creamfields_rule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[1959, 1964, 1970, 1981, 1987, 1992, 1998, 2009, 2015, 2020]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You’ll always get a range of years. You need some cultural context to figure out which one it must be. A music festival attracts performers at the top of their game, so knowing when a band was big helps you to narrow it down.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-09-10-Edinburgh-Python-Meetup-Iain-with-the-audience.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.linkedin.com/in/judy2k/&quot;&gt;Mark Smith&lt;/a&gt;&lt;/strong&gt; followed to confess and apologize for &lt;strong&gt;Stupid Things I’ve Done With Python&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Mark has a folder on every computer called “stupid Python tricks”. It’s full of stuff that Python allows you to do, even if you probably shouldn’t.&lt;/p&gt;

&lt;p&gt;Things like &lt;a href=&quot;https://github.com/judy2k/ish&quot;&gt;ish&lt;/a&gt;, which abuses the minus operator and Boolean values to make this code for testing “trueishness” into valid syntax:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'Yup'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ish&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'True-ish!'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The serious point he makes behind the fun and weird code is to understand &lt;a href=&quot;https://docs.python.org/3/reference/datamodel.html&quot;&gt;Python’s Data Model&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Python implements the basic operators using specially named methods on objects (for example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__sub__&lt;/code&gt; for left-side subtraction and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__rsub__&lt;/code&gt; for right-side subtraction). As with all programming, a solid understanding of the fundamental concepts makes it easier to reason about the higher-level behavior, and easier to write expressive code.&lt;/p&gt;

&lt;p&gt;Pathlib in the standard library overrides the division operator via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__truediv__&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__rtruediv__&lt;/code&gt; methods to allow path construction using syntax like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-pyconsole&quot;&gt;&amp;gt;&amp;gt;&amp;gt; Path(&quot;usr&quot;) / Path(&quot;local&quot;) / Path(&quot;bin&quot;)
PosixPath('usr/local/bin')
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It inspired me to deepen my understanding of the Python language. I’m a confident Python hacker and have middling skills, but I struggle to explain what &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self&lt;/code&gt; really is or what fundamentally defines an object. Mark promises that all the answers are in the data model doc, so add that to the reading list!&lt;/p&gt;

&lt;p&gt;No one recorded us, but YouTube shows Mark delivering this talk elsewhere, most recently at &lt;a href=&quot;https://www.youtube.com/watch?v=7tdcBGUtOY8&quot;&gt;PyCon Italia 2022&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href=&quot;https://www.trustpilot.com/&quot;&gt;TrustPilot&lt;/a&gt; for hosting, &lt;a href=&quot;https://www.travelperk.com/&quot;&gt;TravelPerk&lt;/a&gt; for sponsoring the event, to &lt;a href=&quot;https://www.linkedin.com/in/neilmillerlinkedin/&quot;&gt;&lt;strong&gt;Neil Miller&lt;/strong&gt;&lt;/a&gt; for giving us a opening pep talk,and to &lt;a href=&quot;https://www.linkedin.com/in/mardoig/&quot;&gt;&lt;strong&gt;Mar Doig&lt;/strong&gt;&lt;/a&gt; for taking the photos!&lt;/p&gt;

&lt;p&gt;After topping up on beer and pizza at TrustPilot HQ, we drifted into &lt;a href=&quot;https://www.belhaven.co.uk/pubs/midlothian/milnes-bar&quot;&gt;Milnes on Rose Street&lt;/a&gt; for some more pricey pints and joyously nerdy chatter. For me it was great way to reintegrate into Edinburgh’s tech scene.&lt;/p&gt;

&lt;p&gt;Looking forward to monthly meetups going forward. &lt;a href=&quot;https://www.meetup.com/python-edinburgh/events/303113223/&quot;&gt;September&lt;/a&gt; is already fully booked! Hope to see you there soon.&lt;/p&gt;

&lt;!-- links --&gt;</content><author><name></name></author><summary type="html">Python Meetup Edinburgh August 2024</summary></entry><entry><title type="html">How to find expiring RDS certificates across an AWS organization</title><link href="/2024/07/29/How-to-find-expiring-RDS-certificates-across-an-AWS-organization.html" rel="alternate" type="text/html" title="How to find expiring RDS certificates across an AWS organization" /><published>2024-07-29T16:00:00+00:00</published><updated>2024-07-29T16:00:00+00:00</updated><id>/2024/07/29/How-to-find-expiring-RDS-certificates-across-an-AWS-organization</id><content type="html" xml:base="/2024/07/29/How-to-find-expiring-RDS-certificates-across-an-AWS-organization.html">&lt;p&gt;Most AWS regions have a looming deadline (2024-08-22) to rotate an SSL/TLS certificate in AWS RDS/Aurora. An RDS instance that keeps using the old certificate beyond this date may stop working because clients may refuse the expired certificate.&lt;/p&gt;

&lt;p&gt;As the administrator an AWS organization, how do I identify instances across the organization that need attention?&lt;/p&gt;

&lt;p&gt;The AWS News Blog article &lt;a href=&quot;https://aws.amazon.com/blogs/aws/rotate-your-ssl-tls-certificates-now-amazon-rds-and-amazon-aurora-expire-in-2024/&quot;&gt;Rotate Your SSL/TLS Certificates Now - Amazon RDS and Amazon Aurora Expire in 2024&lt;/a&gt; explains the problem well. To summarize:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A DB instance created before 2024-01-25 by default uses the certificate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rds-ca-2019&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rds-ca-2019&lt;/code&gt; certificate expires on 2024-08-22&lt;/li&gt;
  &lt;li&gt;Since 2022, AWS lets you use certificates valid for 40 years (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rds-ca-rsa2048-g1&lt;/code&gt;) and 100 years (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rds-ca-rsa4096-g1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rds-ca-ecc384-g1&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;The issue affects plain old RDS instances and plain old RDS clusters&lt;/li&gt;
  &lt;li&gt;The issue &lt;strong&gt;does not affect&lt;/strong&gt; Aurora Serverless v1&lt;/li&gt;
  &lt;li&gt;The issue &lt;strong&gt;does affect&lt;/strong&gt; Aurora Serverless v2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The article isn’t clear on why Aurora Serverless v1 is an exception, so I had to read around to find out. From the documentation I learned the distinguishing features.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;An Aurora Serverless v2 cluster uses the special DB instance class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db.serverless&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;An Aurora Serverless v2 cluster, like a plain old RDS cluster, uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;provisioned&lt;/code&gt; engine mode&lt;/li&gt;
  &lt;li&gt;An Aurora Serverless v1 cluster uses no DB instances&lt;/li&gt;
  &lt;li&gt;An Aurora Serverless v1 cluster uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serverless&lt;/code&gt; engine mode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sources: &lt;a href=&quot;https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_CreateDBCluster.html&quot;&gt;CreateDBCluster EngineMode&lt;/a&gt;, &lt;a href=&quot;https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.upgrade.html&quot;&gt;Migrating to Aurora Serverless v2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Confusingly, the &lt;a href=&quot;https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.DBInstanceClass.html&quot;&gt;DBInstanceClass documentation&lt;/a&gt; doesn’t list &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db.serverless&lt;/code&gt; at all.&lt;/p&gt;

&lt;p&gt;To to find certificates that need rotation, I need to look just at the DB instances. The certificate isn’t a property of the cluster, but of the instances in the cluster.&lt;/p&gt;

&lt;p&gt;Now that I understand the theory, I’m ready to identify the instances that need attention.&lt;/p&gt;

&lt;p&gt;The main article shows how to do this in one account, but I have hundreds. How do I find all the problem DB instances across my organization?&lt;/p&gt;

&lt;p&gt;Because I’ve set up &lt;strong&gt;AWS Config&lt;/strong&gt; with a configuration aggregator across my organization, I have a quick way to solve this using the advanced query interface.&lt;/p&gt;

&lt;p&gt;I read the &lt;a href=&quot;https://github.com/awslabs/aws-config-resource-schema/blob/master/config/properties/resource-types/AWS%3A%3ARDS%3A%3ADBInstance.properties.json&quot;&gt;resource schema in GitHub&lt;/a&gt; to discover that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AWS::RDS::DBInstance&lt;/code&gt; type has a property called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;configuration.cACertificateIdentifier&lt;/code&gt;. Watch out for the weird capitalization!&lt;/p&gt;

&lt;p&gt;Use a query like this to list all the DB instances across your organization.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;accountId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;awsRegion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;resourceName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cACertificateIdentifier&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;resourceType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'AWS::RDS::DBInstance'&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;accountId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;awsRegion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;resourceName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first three columns uniquely identify and locate each instance in the in the organization. The last column is the property I need to check.&lt;/p&gt;

&lt;p&gt;You’ll get a result set shaped like this:&lt;/p&gt;

&lt;!-- vale off --&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;accountId&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;awsRegion&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;resourceName&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;configuration.cACertificateIdentifier&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;111111111111&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;eu-west-1&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;demoinstance1&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;rds-ca-rsa2048-g1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;222222222222&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;eu-central-1&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;demoinstance2&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;rds-ca-rsa2048-g1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;333333333333&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;eu-central-1&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;demoinstance3&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;rds-ca-2019&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;!-- vale on --&gt;

&lt;p&gt;The example result allows me to split the instance into two groups, &lt;strong&gt;ignore&lt;/strong&gt; and &lt;strong&gt;inform&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I &lt;strong&gt;ignore&lt;/strong&gt; demoinstance1 and demoinstance2. They need no more attention because they use one of the long-lived certificates.&lt;/p&gt;

&lt;p&gt;I &lt;strong&gt;inform&lt;/strong&gt; the responsible team about demoinstance3 because it uses the certificate &lt;strong&gt;rds-ca-2019&lt;/strong&gt; that is about to expire. Now they can plan a remediation.&lt;/p&gt;

&lt;p&gt;In this article I’ve discussed the upcoming deadline to rotate a SSL/TLS certificate for RDS/Aurora DB instances. You’ve seen where to get more information to understand the problem both from the AWS News blog and from the RDS service documentation. Finally you’ve seen how to use AWS Config’s organization aggregator and advanced query feature to report on the certificate configuration across all the DB instances in the AWS organization.&lt;/p&gt;</content><author><name></name></author><summary type="html">Most AWS regions have a looming deadline (2024-08-22) to rotate an SSL/TLS certificate in AWS RDS/Aurora. An RDS instance that keeps using the old certificate beyond this date may stop working because clients may refuse the expired certificate.</summary></entry><entry><title type="html">Geomob Edinburgh June 2024</title><link href="/2024/06/29/Geomob-Edinburgh.html" rel="alternate" type="text/html" title="Geomob Edinburgh June 2024" /><published>2024-06-29T12:00:00+00:00</published><updated>2024-06-29T12:00:00+00:00</updated><id>/2024/06/29/Geomob-Edinburgh</id><content type="html" xml:base="/2024/06/29/Geomob-Edinburgh.html">&lt;h1 id=&quot;geomob-edinburgh-june-2024&quot;&gt;Geomob Edinburgh June 2024&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://thegeomob.com/post/june-25th-2024-geomobedi-details&quot;&gt;The Edinburgh Geomob&lt;/a&gt; met again on Tuesday the 25th of June 2024 at &lt;a href=&quot;https://www.thisiscodebase.com/&quot;&gt;Codebase&lt;/a&gt;, in the shadow of Edinburgh Castle. Since the &lt;a href=&quot;https://www.isme.es/2024/03/30/Geomob-Edinburgh.html&quot;&gt;first session in March&lt;/a&gt; was so engaging, I had been looking forward to this one even more. Thanks always to &lt;a href=&quot;https://www.linkedin.com/in/galacamacho/&quot;&gt;Gala Camacho&lt;/a&gt; for fostering a relaxed and welcoming community.&lt;/p&gt;

&lt;p&gt;Geospatial tech is for me not a career but a hobby. Despite (or maybe because of) that it’s the tech meetup that I get most excited about. Every visit expands my horizon of the interesting problems that are out there and how open data and tools help solve them. Some of the technical details and jargon go over my head, and that’s fine. My own niche of security and cost engineering in AWS could be just as confusing to someone not immersed in it.&lt;/p&gt;

&lt;p&gt;Maps are as much about understanding how we relate to each other as they are about physical measurements.&lt;/p&gt;

&lt;p&gt;Just as important as the talks themselves is the mingling. It’s all about sharing stories and tech gossip! From one attendee I learned of the &lt;strong&gt;&lt;a href=&quot;https://www.scottishtecharmy.org/&quot;&gt;Scottish Tech Army&lt;/a&gt;&lt;/strong&gt;, a not-for-profit organization that brings together NGOs and volunteer techies to help the third sector improve its understanding and use of technology. He lent his skills to a project that assists newly visually impaired people with audio cues to maintain their mobility and confidence.&lt;/p&gt;

&lt;p&gt;The rest of the post sums up my notes and thoughts sparked by the three talks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://mapstodon.space/@stevefaeembra&quot;&gt;Steven Kay&lt;/a&gt;&lt;/strong&gt; kicked off the session with his explorations of &lt;strong&gt;Orthogonal Strip Maps&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Tube_map&quot;&gt;London Underground map&lt;/a&gt; is probably the most famous modern example of a &lt;strong&gt;schematic map&lt;/strong&gt;. It throws out almost everything you might expect a map to show. It ignores real curves and distances between stops. But with one notched line it answers the most important traveller’s question: where can I go from here?&lt;/p&gt;

&lt;p&gt;It’s obvious in the 21st century, but its realization took a creative leap in the 20th, and builds on the idea of an &lt;strong&gt;orthogonal map&lt;/strong&gt; which goes back centuries.&lt;/p&gt;

&lt;p&gt;The earliest orthogonal example Steven showed was a page from &lt;a href=&quot;https://archive.org/details/bim_eighteenth-century_patersons-british-itine_paterson-daniel_1785_2/page/n59/mode/2up&quot;&gt;&lt;strong&gt;Paterson’s Itinerary&lt;/strong&gt; (Volume 2, maps 113-114)&lt;/a&gt;, published in 1785. It shows the road route from Cambridge to Ely, and optimizes the layout for a traveller marking progress from one place to the next.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-06-29-Patersons-Itinerary.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It draws the route from top to bottom, split into two vertical strips to give more space to details along each half of the route. You read down from Cambridge on the left and up to Ely on the right. Most maps put north at the top. This one has a compass to show that the route runs south-south-west. It draws and names settlements close to the route, but the side roads to reach them are faint. The main route shows mile markers to estimate travel time and plan rests. It retains curves in the road and nearby rivers that may serve as navigational aids.&lt;/p&gt;

&lt;p&gt;A more recent example is &lt;a href=&quot;https://somethingaboutmaps.wordpress.com/2015/09/28/a-matter-of-perspective/&quot;&gt;Daniel Huffman’s 2015 linearized map of Lake Michigan&lt;/a&gt;. He illustrates the basic idea:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-06-29-Geomob-Ediburgh-Unfurling-Lake-Michigan.webp&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;See Daniel’s article for the complete map. It’s beautiful and reveals how much humans organize  around water. He gets into more history of such maps, and deep into the why and the how of his own effort.&lt;/p&gt;

&lt;p&gt;I won’t attempt to reproduce the math needed to draw Daniel’s map, because even he admits that he no longer understands it (a friend helped). Although Steven’s own equations were much simpler, he got a similar result, and it’s still pleasant to look at.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-06-29-Geomob-Edinburgh-Orthogonal-Strip-Map.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Steve’s basic approach is to take a strip of the map on each side of the route and lay them out in a straight line.&lt;/p&gt;

&lt;p&gt;The V-shaped artifacts indicate where a right-angle turn has been flattened out, as if diagonally cutting an L shape and laying each part flat.&lt;/p&gt;

&lt;p&gt;I imagine Daniel’s more complex equations are what avoid such artifacts in his version. Also Lake Michigan doesn’t have any sharp corners.&lt;/p&gt;

&lt;p&gt;One of the audience asked about scaling each segment of the displayed route to the expected speed, so that you may have greater levels of detail for areas where you spend more time (slower parts would have bigger segments). The speaker thought it might be worth exploring until another in crowd countered: “Do you really want to see that much of Princes Street!?”&lt;/p&gt;

&lt;p&gt;Apart from the visuals, the takeaway here is that it was all possible using data from OpenStreetMap (with a little manual cleaning) and open-source tools such as QGIS for the mapping and Blender for some animations.&lt;/p&gt;

&lt;p&gt;One more miscellaneous detail: The Ordnance Survey published an open data product called OS Open Zoomstack that now competes with OpenStreetMap. I wonder which use cases it would be better fit for and how compatible it would be with OSM. There’s a whole other research topic!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.openstreetmap.org/user/Chris%20Fleming&quot;&gt;Chris Fleming&lt;/a&gt;&lt;/strong&gt; next gave us a retrospective of &lt;strong&gt;20 years of OpenStreetMap: Why we’re still mapping&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Chris is a local legend of open mapping. Always enthusiastic, warm, and welcoming to newcomers like me, and always finding new ways to make OpenStreetMap (OSM) more useful for people.&lt;/p&gt;

&lt;p&gt;And how useful is OSM for people in 2024? Chris suggested &lt;a href=&quot;https://www.strava.com/&quot;&gt;Strava&lt;/a&gt;, &lt;a href=&quot;https://www.instagram.com/&quot;&gt;Instagram&lt;/a&gt;, &lt;a href=&quot;https://pokemongolive.com/&quot;&gt;Pokémon Go&lt;/a&gt;, and &lt;a href=&quot;https://cycle.travel/&quot;&gt;Cycle Travel&lt;/a&gt;. (That last one was new to me, but it looks great!)&lt;/p&gt;

&lt;p&gt;I laughed when Chris told how one Pokémon Go player added a pond to his back garden in OSM so that he could catch more water pokémon. It’s such a popular game hack that the OSM Wiki now gives &lt;a href=&quot;https://wiki.openstreetmap.org/wiki/Tips_for_new_(Pokemon_Go)_mappers&quot;&gt;tips to do it responsibly&lt;/a&gt;, considering the needs of other map users.&lt;/p&gt;

&lt;p&gt;In the early days Chris turned to Linux User Groups to find early evangelists and volunteers, because they already understood the value of open source development.&lt;/p&gt;

&lt;p&gt;The first UK “mapping parties” brought people together to map city centers. To help divvy up the work they would split a city’s neighborhoods using a “cake diagram”. The &lt;a href=&quot;https://wiki.openstreetmap.org/wiki/Cake_diagram&quot;&gt;Mapping Cake Diagram for the Manchester Mapping Party 2006&lt;/a&gt; is even pleasingly circular.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-06-29-Geomob-Edinburgh_Manchester_Cake_Diagram.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Between 2007 and 2011 Yahoo allowed OpenStreetMap to trace its satellite images for maps. The OSM Wiki &lt;a href=&quot;https://wiki.openstreetmap.org/wiki/Yahoo!_Aerial_Imagery&quot;&gt;lists several achievements&lt;/a&gt; that would have been almost impossible at the time without it. The agreement continued until Yahoo shut down its mapping unit.&lt;/p&gt;

&lt;p&gt;By time I had started contributing to OSM in 2013 through the Humanitarian OpenstreetMap Team (&lt;a href=&quot;https://www.hotosm.org/projects/mapping-for-climate-ready-cities-east-and-southern-africa/&quot;&gt;still active now&lt;/a&gt;), I recall that Bing had a similar arrangement.&lt;/p&gt;

&lt;p&gt;By 2012 OSM had mapped 95% of Scotland’s roads. At this point Chris showed us some screenshots of Edinburgh’s city center to compare 2013 and 2024. Although 2013’s maps seem crude and sparse now, the Scottish team deserved to be proud to get that far.&lt;/p&gt;

&lt;p&gt;I think Chris used the OSM Comparator from the &lt;a href=&quot;https://mappingedinburgh.org/tools/&quot;&gt;Mapping Edinburgh Toolkit&lt;/a&gt;. Today &lt;a href=&quot;https://mappingedinburgh.org/tools/osm-comparator/#zoom=16&amp;amp;lon=-3.19595&amp;amp;lat=55.95344&quot;&gt;its view of Edinburgh&lt;/a&gt; looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-06-29-Geomob-Edinburgh_OSM-Side-by-Side.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;By now the map was complete enough to support analyses in other fields such as a survey of &lt;a href=&quot;https://mappingedinburgh.org/tools/examples/medical_practice/&quot;&gt;medical occupations in 1909 and 1910&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since then the level of detail has only increased. Almost all the street addresses are numbered. In one area someone even mapped out all the plant pots and trees. At some point you have to stop and wonder how much detail is too much!&lt;/p&gt;

&lt;p&gt;But then, it all depends on the use case. What might be mapjunk for one person may be a critical navigational aid for another. Chris like me is sighted, and so we can’t always anticipate how visually impaired people may think about the world. I may have thought that tracking the kerb heights of all pavements was makework for bored mappers. Then Chris shared the story of a blind OSM user who asked for more of that data, because it helps him to walk around new city streets more safely.&lt;/p&gt;

&lt;p&gt;Since I discovered OSM in 2013, smartphones have taken over the world. A new generation of mobile-first mapping tools such as &lt;a href=&quot;https://every-door.app/&quot;&gt;Every Door&lt;/a&gt; and &lt;a href=&quot;https://streetcomplete.app/&quot;&gt;Street Complete&lt;/a&gt; are now available in our pockets at all times to fill in the small details that you don’t get from satellite images or GPS traces. When I tried Every Door in my local area, it set up 100 “quests”, such as identifying a road surface or the denomination of a place of worship. Geodata comes from the real world, and these tools make it easier to collect while you’re out there.&lt;/p&gt;

&lt;p&gt;The Edinburgh OSM user group meets monthly. The OSM wiki &lt;a href=&quot;https://wiki.openstreetmap.org/wiki/Edinburgh#Social_Events&quot;&gt;shares details of the next events&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.linkedin.com/in/tristan-goss-5473a2a6/&quot;&gt;Tristan Goss&lt;/a&gt; from &lt;a href=&quot;https://earthwave.co.uk/&quot;&gt;Earthwave&lt;/a&gt;&lt;/strong&gt; increased the scale with cs2eo.org, which provides &lt;strong&gt;intersection processing for glaciological satellite altimetry&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The polar ice is melting at a rate of 1.2 gigatons per year.&lt;/p&gt;

&lt;p&gt;We know this because of &lt;a href=&quot;https://www.esa.int/Enabling_Support/Operations/CryoSat-2_operations&quot;&gt;CryoSat-2&lt;/a&gt;, a European Space Agency (ESA) satellite dedicated to measuring polar sea ice thickness. It launched in 2010 with a 3-year mission. 14 years later, it’s still sending 1TB per month of measurements.&lt;/p&gt;

&lt;p&gt;Tristan’s team uses that data to build a website called &lt;a href=&quot;https://cs2eo.org/&quot;&gt;cs2eo&lt;/a&gt;, which serves “High performance geospatial-temporal queries for Earth Observation”. It allows you search over regions of the polar ice caps within a time range to get all the relevant ice height measurements captured by the satellite.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-06-29-Geomob-Edinburgh_cs2eo.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you sketched all the approaches the satellite makes over the poles, you would draw lots of lines in random directions getting close to a central point. Only some intersect. The cleverness that Tristan’s team brings is to correlate these “strips” of data into something coherent for analysis.&lt;/p&gt;

&lt;p&gt;Tristan looks forward to the new mission &lt;a href=&quot;https://www.eoportal.org/satellite-missions/cristal-copernicus-polar-ice-and-snow-topography-altimeter-&quot;&gt;CRISTAL (Copernicus Polar Ice and Snow Topography)&lt;/a&gt;. When the audience asked which instruments he would like a new satellite to have, he wished for one magic satellite with colocation of all the sensors! He also admitted it may cost more than the GDP of a small country, so it depends on how far the mission budget stretches.&lt;/p&gt;

&lt;p&gt;The generalization of this use case is &lt;a href=&quot;https://en.wikipedia.org/wiki/Point_cloud&quot;&gt;point cloud analysis&lt;/a&gt;. Tristan would like to correlate the data with other big-scale sources where each point lies kilometers from the next. Earthwave’s &lt;a href=&quot;https://specklia.earthwave.co.uk/&quot;&gt;Specklia&lt;/a&gt; service exists to enable this for more use cases.&lt;/p&gt;

&lt;p&gt;Funding for the polar project is hard to get because so few people live there. So if you want to support Tristan’s work, move to &lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_northernmost_settlements&quot;&gt;Svalbard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you to the &lt;strong&gt;&lt;a href=&quot;https://www.thehangingbat.com/&quot;&gt;Hanging Bat&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;a href=&quot;https://diagonal.works/&quot;&gt;Diagonal&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href=&quot;https://opencagedata.com/&quot;&gt;OpenCage&lt;/a&gt;&lt;/strong&gt; for hosting the geobeers and geobanter.&lt;/p&gt;

&lt;p&gt;Over a pint of &lt;a href=&quot;https://www.campervanbrewery.com/shop/p/leith-pils-can-330ml&quot;&gt;Leith Pils&lt;/a&gt; I heard about a Discord server for the Scottish tech community. I wonder, is it the one run by the &lt;strong&gt;&lt;a href=&quot;https://www.scottishtechnology.club/&quot;&gt;Scottish Technology Club&lt;/a&gt;&lt;/strong&gt;?  That Club site looks like a great source for local tech news and events. They also have a &lt;a href=&quot;https://www.scottishtechnology.club/mentoring&quot;&gt;mentoring program&lt;/a&gt;. Either way, I need to &lt;a href=&quot;https://discord.com/invite/bgHpBkq24p&quot;&gt;join and find out&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If any of this sounds fun, come to the next Geomob, probably in September or October. Watch &lt;a href=&quot;https://thegeomob.com/events&quot;&gt;the Geomob website&lt;/a&gt; for updates.&lt;/p&gt;</content><author><name></name></author><summary type="html">Geomob Edinburgh June 2024</summary></entry><entry><title type="html">Report from AWS Summit Berlin 2024</title><link href="/2024/05/20/Report-from-AWS-Summit-in-Berlin.html" rel="alternate" type="text/html" title="Report from AWS Summit Berlin 2024" /><published>2024-05-20T00:00:00+00:00</published><updated>2024-05-20T00:00:00+00:00</updated><id>/2024/05/20/Report-from-AWS-Summit-in-Berlin</id><content type="html" xml:base="/2024/05/20/Report-from-AWS-Summit-in-Berlin.html">&lt;p&gt;The AWS Summit in Berlin 2024 was another great opportunity for learning and networking. As an official AWS event, of course it began with a flashy keynote to bring good news. And it was Big News.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stefan Höchbauer&lt;/strong&gt;, Managing Director of AWS Germany and Europe Central, announced the &lt;strong&gt;AWS Sovereign Cloud&lt;/strong&gt;. It’s the EU GovCloud. It may be the foundation of the next 100 years of utility computing. I’m excited about it, even though I won’t get to work with it (only EU residents may operate it), because it allows all European governments to use the same AWS technology that its private companies do, even for the most restricted and regulated workloads.&lt;/p&gt;

&lt;p&gt;As I was only able to attend the second day of this two-day affair, I missed the representative of the Brandenburg local government making the same announcement. I bet they are pleased with the billions that AWS will invest to make it happen!&lt;/p&gt;

&lt;p&gt;The two-day format was my only major complaint about the Summit, as it made planning to meet with other community builders more difficult. Some attended on Wednesday, others on Thursday. But I understand why they did it like that. The venue was so full that the Community stage was tucked into the corner of a side building that was hard to find and looked like it was in the middle of being rebuilt or knocked down! I was pleased to learn at the end of the keynote that the next host city will be Hamburg, one of Europe’s largest ports, with a fine maritime mercantile tradition. An excuse to visit more of Germany!&lt;/p&gt;

&lt;p&gt;I had hope to write more about all the talks I enjoyed, but life got in the way. So the rest of this post shares my notes and thoughts on my favorite of the day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sana Shah&lt;/strong&gt;, Cloud Operations Lead at &lt;a href=&quot;https://www.demicon.de/en/&quot;&gt;DEMICON&lt;/a&gt;, opened the Community Stage with &lt;strong&gt;GenAI Deep Dive: Build a GenAI assistant for a women-in-cloud initiative&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This was probably the best session of the day because how it changed my thinking. I was skeptical before of generative AI services and this changed my mind. Sana maintained a confident delivery to share a clear use case and a practical solution.&lt;/p&gt;

&lt;p&gt;Sana started the &lt;a href=&quot;https://www.linkedin.com/company/aws-womens-user-group-berlin/?originalSubdomain=de&quot;&gt;AWS Women’s User Group in Berlin&lt;/a&gt;. Running a user group is a lot of work. If you also need to fit it around the day job, then you need all the help you can get.&lt;/p&gt;

&lt;p&gt;Sana showed us how to configure the AWS Bedrock service to act as her personal assistant so that it would answer her questions about the user group and about the global trends in the employment gender gap. She configured the service to combine the basic language skills of a base model (I think it was &lt;a href=&quot;https://en.wikipedia.org/wiki/Claude_(language_model)&quot;&gt;Anthropic’s Claude&lt;/a&gt;) with information from curated documents about the user group and a report from an NGO report about the global trends (if I remember more about these documents I’ll update here).&lt;/p&gt;

&lt;p&gt;It turns out that this is an example of retrieval-augmented generation (RAG). The “retrieval-augmented” part just means making the generated responses more useful by getting parts of the answer from a given document.&lt;/p&gt;

&lt;p&gt;Educational too was her summary of the economics of engineering solutions using generative technology.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Prompt engineering ($)&lt;/strong&gt;: Query the base model directly and add any extra data you may need into every query.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;*Retrieval-augmented generation (RAG) ($$))&lt;/strong&gt;: Configure extra inputs (databases, documents) that the system may consult if the base model has no useful answers.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Pre-train ($$$)&lt;/strong&gt;: I’m still not sure what this is. Sana’s focus was on RAG, assuming that this stage is complete.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;New model ($$$$)&lt;/strong&gt;: Make a new base model to rival Claude or ChatGPT. Very few people in the world have resources to do this.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What’s the difference between including a PDF in the prompt and making it available via RAG? Query time simplicity and consistency. My analogy in SQL: You can write an SQL query that contains all the source data using CTEs with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VALUES&lt;/code&gt; clauses or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT ... UNION ALL SELECT ...&lt;/code&gt; segments. But if you write this more than twice you probably want to just store it in a table so that you can just reference the table directly the next time. Where the BedRock system goes further is that it will automatically consult the configured RAG sources.&lt;/p&gt;

&lt;p&gt;I would have loved to see what the model answered to a question that fell outside the use case, such as “How is the weather today in Berlin?”. Does the base model know anything about Berlin, or anything about sunshine, or a Thursday in Spring?&lt;/p&gt;

&lt;p&gt;Sana ended by showing that Bedrock can not only respond to questions, but it can perform actions on our behalf. A Bedrock Agent is basically a way to invoke a command using a text chat interface instead of a command line interface or a graphical user interface. So Sana instructed her agent to send an email via name and address prompting to the MC, who proudly showed off her new message thanking her for her awesome contributions!&lt;/p&gt;

&lt;p&gt;Huge thanks to the “&lt;strong&gt;Förderverein AWS Community DACH&lt;/strong&gt;” for running the Community Stage that made possible this event-within-an-event. The “Förderverein” acts as a “group of user groups” in Germany and promotes the interests of the AWS community across Germany, Switzerland, and Austria. They ran the event smoothly. I haven’t seen them all in action yet, but I believe that it’s one of the best-organized AWS user communities. There’s a lot we can learn from them.&lt;/p&gt;</content><author><name></name></author><summary type="html">The AWS Summit in Berlin 2024 was another great opportunity for learning and networking. As an official AWS event, of course it began with a flashy keynote to bring good news. And it was Big News.</summary></entry><entry><title type="html">Geomob Edinburgh March 2024</title><link href="/2024/03/30/Geomob-Edinburgh.html" rel="alternate" type="text/html" title="Geomob Edinburgh March 2024" /><published>2024-03-30T21:00:00+00:00</published><updated>2024-03-30T21:00:00+00:00</updated><id>/2024/03/30/Geomob-Edinburgh</id><content type="html" xml:base="/2024/03/30/Geomob-Edinburgh.html">&lt;p&gt;&lt;a href=&quot;https://thegeomob.com/post/mar-26th-2024-geomobedi-details&quot;&gt;The &lt;strong&gt;Edinburgh Geomob&lt;/strong&gt;&lt;/a&gt; met for the first time on Tuesday the 26th of March 2024 at &lt;a href=&quot;https://www.thisiscodebase.com/&quot;&gt;Codebase&lt;/a&gt;, just off Lothian Road. Thanks to &lt;a href=&quot;https://www.linkedin.com/in/galacamacho/&quot;&gt;Gala Camacho&lt;/a&gt; for hosting.&lt;/p&gt;

&lt;p&gt;Of all the technical communities I’ve encountered, the geospatial data community is the most inclusive, diverse and welcoming. Maybe that’s why I keep coming back despite it being for me more of a hobby than a career. Gala’s closing remarks helped me to understand why I feel this way: “&lt;strong&gt;Geo comes from the real world&lt;/strong&gt;.”&lt;/p&gt;

&lt;p&gt;To collect or to make use of geodata you have to get off the internet, get out the house, and get into the real world. Maybe I’m reading too much into that, but it makes sense to me.&lt;/p&gt;

&lt;p&gt;The Geomob is the first tech meetup I’ve attended in Edinburgh for almost a decade, and to get back in the mood it was just right. A relaxed half hour of mingling, remembering old faces, and snacking before talks began. 15 minutes for each speaker, enough to tell a story without overwhelming. Done after an hour with plenty of time for more chat over afterbeers.&lt;/p&gt;

&lt;p&gt;Looking forward to the next one on Tuesday the 25th of June 2024.&lt;/p&gt;

&lt;p&gt;The rest of this post shares my thoughts on the three talks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.linkedin.com/in/robertcoup/&quot;&gt;Robert Coup&lt;/a&gt; from &lt;a href=&quot;https://koordinates.com/&quot;&gt;Koordinates&lt;/a&gt;&lt;/strong&gt; presented “&lt;strong&gt;Kart: Practical versioning for geodata&lt;/strong&gt;”.&lt;/p&gt;

&lt;p&gt;Robert said few geodata projects have a robust versioning system. Maybe they use one static copy of the data for the lifetime of the project, or they make changes willy-nilly and end up with files named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my_geodata_2024-03-30_edited_version3_FINAL.shp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As a software engineer Robert is used to versioning all his program files using tools such as Git. &lt;a href=&quot;https://koordinates.com/products/kart/&quot;&gt;Kart&lt;/a&gt; builds on top of Git versioning with features that make it easier to work with geodata. Robert didn’t give us a demo, so my understanding of Kart remains theoretical. I hope to see it in action to answer some questions.&lt;/p&gt;

&lt;p&gt;I asked, “Why not just work directly with Git?” If you track changes to your geodata in a Git repo you’ll already be in a better place. One consideration is size and selectivity. Some of Robert’s customers store several terabytes of data and work on different sections at different times. Basic Git is clumsy for handling large files. Kart supports this use case with a spatially filtered clone.&lt;/p&gt;

&lt;p&gt;The spatial filter sounds like a compelling feature. I wanted to know more but missed the chance to ask more details at the geobeers. Does it work like a geoindex over Git’s own index? Does it partition the source file into smaller spatial chunks?&lt;/p&gt;

&lt;p&gt;In 2017 the Windows source code contained 3.5M files and when added to a Git repo used 300GB of space. At that scale, many Git commands would take 30 minutes up to hours and a few would never complete. So to make it usable &lt;a href=&quot;https://devblogs.microsoft.com/bharry/the-largest-git-repo-on-the-planet/&quot;&gt;Microsoft extended Git with a Virtual File System&lt;/a&gt; that avoids downloading all the data. Does Kart reuse any of that work?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;http://www.linkedin.com/in/andrew-bladon-baa69854&quot;&gt;Andrew Bladon&lt;/a&gt; from &lt;a href=&quot;https://developers.arcgis.com/&quot;&gt;Esri&lt;/a&gt;&lt;/strong&gt; presented “&lt;strong&gt;How mapping technology can optimize route planning for climbers&lt;/strong&gt;”.&lt;/p&gt;

&lt;p&gt;Andrew loves climbing mountains, less so when it’s wet and windy. It’s not just unpleasant to be outside but dangerous on such difficult terrain. It might be sunny somewhere nearby, but checking all the possible locations and local conditions is too much for one person.&lt;/p&gt;

&lt;p&gt;After enough miserable ascents and cancelled plans, Andrew did something about it. He made a mobile app that he described as a “conditions navigator”. I’d call it a “good weather” map. It comes preloaded with Andrew’s favorite peaks, pulls in weather forecasts from OpenMeteo, and builds on Esri’s own mapping technology to apply traffic-light coloring to the peaks. Red: stay away; Yellow: only the brave; Green: perfect conditions, get up there!&lt;/p&gt;

&lt;p&gt;I loved this talk because of how personal it was. We learned a lot about Andrew and what drove him to build this app. He told a great story and showed some beautiful pictures! In a way, this was just an excuse for a mountain climber to show off his beautiful clear-day summit photography. And it’s welcome because we have geodata to thank for it.&lt;/p&gt;

&lt;p&gt;The audience asked whether it could be used for other outdoor activities such as camping. Andrew agreed although he’d need to reload the app with different points of interest or include a feature that allows different users to have their own points.&lt;/p&gt;

&lt;p&gt;When I tried it for my own trip this weekend, I found the app on &lt;a href=&quot;https://github.com/AndrewBladon/QtWeatherApp&quot;&gt;GitHub&lt;/a&gt;, and you need a lot of developer tools to get it up and running. I’d love to see this up on the Google Play Store or the Apple App Store so more people can try it.&lt;/p&gt;

&lt;p&gt;Read Andrew’s own story at the Esri Developers blog, “&lt;strong&gt;&lt;a href=&quot;https://www.esri.com/arcgis-blog/products/sdk-qt/developers/creating-app-to-identify-climbing-locations-using-weather-api/&quot;&gt;Creating an application to identify climbing locations using a weather forecast API&lt;/a&gt;&lt;/strong&gt;”, and see the views that make it all worthwhile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.linkedin.com/in/moral-masuoka/&quot;&gt;Moral Masuoka&lt;/a&gt; from &lt;a href=&quot;https://www.beautifyearth.com/&quot;&gt;Beautify Earth&lt;/a&gt;&lt;/strong&gt; (BE) presented “&lt;strong&gt;Art Walls: Mapping potential points of connection through murals&lt;/strong&gt;”.&lt;/p&gt;

&lt;p&gt;BE is part mural catalog, part agency for talent and real estate. The company’s mission is “street art connections”: connecting people with street art across the US and UK.&lt;/p&gt;

&lt;p&gt;The company’s transatlantic scope is due to Moral’s personal journey from Los Angeles (LA) to Edinburgh. Each place challenges our ideas of what a city should be. While Edinburgh may beat LA for walkability, LA has a lot more art going up on its walls.&lt;/p&gt;

&lt;p&gt;We toured Moral’s favorite murals through the &lt;a href=&quot;https://art.beautifyearth.com/murals&quot;&gt;BE mural map&lt;/a&gt;, the &lt;a href=&quot;https://www.jcmap.org/&quot;&gt;Jersey City Mural Arts Program (JCMAP)&lt;/a&gt; powered by &lt;a href=&quot;https://www.canvsart.com/&quot;&gt;CANVS&lt;/a&gt;, and &lt;a href=&quot;https://streetartcities.com/&quot;&gt;Street Art Cities&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Maybe it was just a fluke, but it surprised me in LA how clean the walls next to the murals were! I compared it to central Barcelona’s Raval neighborhood, where if you don’t paint the shop shutters then some vandal is going to do it for you. So you might as well pay an artist to do it for you and make it look nice.&lt;/p&gt;

&lt;p&gt;I made a note from Moral’s slide deck: “&lt;strong&gt;Seeing North Edinburgh through the eyes of the Internet&lt;/strong&gt;”. This appears to have been an event &lt;a href=&quot;https://www.edinburghmuseums.org.uk/whats-on/seeing-north-edinburgh-through-eyes-internet&quot;&gt;promoted by Edinburgh Council&lt;/a&gt; in February 2023 hosted by the Data Civics team at the Edinburgh Futures Institute exploring North Edinburgh via Instagram, Twitter, Google Earth, street photography and community archive.&lt;/p&gt;

&lt;p&gt;I asked how BE designs for two competing use cases, that of the graphic designer who wants the mural to look great on a digital display and that of the touring visitor who needs to find the physical original. They’re still figuring it out as they find the best way to run a sustainable business while serving the needs of both groups.&lt;/p&gt;

&lt;p&gt;Anything that may brighten up our drab public spaces and encourage civic pride is welcome. Today the BE site shows just a few murals in UK, so I hope the venture takes off and gives us a whole-island catalog of street art to enjoy.&lt;/p&gt;

&lt;p&gt;After the talks there was &lt;strong&gt;a quick word from the sponsor &lt;a href=&quot;https://geovation.uk/&quot;&gt;Geovation&lt;/a&gt;&lt;/strong&gt;. It’s a sort of business incubator for geospatial startups. I think they get funding from the Scottish government and the UK Ordnance Survey. I’m grateful to them for enabling such an entertaining evening.&lt;/p&gt;

&lt;p&gt;Finally we poured ourselves into the &lt;a href=&quot;https://www.thehangingbat.com/&quot;&gt;Hanging Bat&lt;/a&gt; pub to compare notes and mingle with the community over geobeers. The pub is on Lothian Road, perfectly placed to relax with a pint before getting the bus home.&lt;/p&gt;</content><author><name></name></author><summary type="html">The Edinburgh Geomob met for the first time on Tuesday the 26th of March 2024 at Codebase, just off Lothian Road. Thanks to Gala Camacho for hosting.</summary></entry><entry><title type="html">Print start and end timestamps for each Bash command</title><link href="/2024/03/30/print-start-and-end-timestamps-for-each-Bash-command.html" rel="alternate" type="text/html" title="Print start and end timestamps for each Bash command" /><published>2024-03-30T05:20:00+00:00</published><updated>2024-03-30T05:20:00+00:00</updated><id>/2024/03/30/print-start-and-end-timestamps-for-each-Bash-command</id><content type="html" xml:base="/2024/03/30/print-start-and-end-timestamps-for-each-Bash-command.html">&lt;p&gt;Today I learned to make Bash print start and end timestamps for tracking execution times.&lt;/p&gt;

&lt;p&gt;To be honest the first result looks ugly, but it’s an achievement. Read on to learn how I did it.&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;lsb_release &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sleep &lt;/span&gt;3
&lt;span class=&quot;go&quot;&gt;----------------------------------------------------------------------------------------------------------------------
2024-03-31T06:07:20+02:00
----------------------------------------------------------------------------------------------------------------------
LSB Version:	core-11.1.0ubuntu2-noarch:printing-11.1.0ubuntu2-noarch:security-11.1.0ubuntu2-noarch
Distributor ID:	Ubuntu
Description:	Ubuntu 20.04.6 LTS
Release:	20.04
Codename:	focal
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2024-03-31T06:07:23+02:00
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;It’s bugged me a for while that Bash doesn’t display the times a command starts and ends.&lt;/p&gt;

&lt;p&gt;It bugs me most when I run a command and later realize I need to track its duration for performance measurements or the execution timestamps for auditing.&lt;/p&gt;

&lt;p&gt;I knew the variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PS1&lt;/code&gt; sets the prompt string. And I knew how to embed a command so Bash would rerun it time to display the latest output. I could use that to display the end time of the previous command, but without the start time it’s useless.&lt;/p&gt;

&lt;p&gt;While searching for something completely different in &lt;a href=&quot;https://www.gnu.org/software/bash/manual/bash.html&quot;&gt;the Bash manual&lt;/a&gt;, I stumbled across the missing piece &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PS0&lt;/code&gt;. Its value is expanded like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PS1&lt;/code&gt; and displayed by interactive shells &lt;strong&gt;after reading a command and before the command is executed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PS0&lt;/code&gt; can print the start time and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PS1&lt;/code&gt; can print the end time. That provides automatic execution timestamp auditing and allows computing the duration.&lt;/p&gt;

&lt;p&gt;Its discovery carried me away. I wanted to know how to draw a horizontal rule to make a clear saparation between the injected output and the command’s normal output. I googled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash draw a horizontal line&lt;/code&gt; for good results. &lt;a href=&quot;https://medium.com/@patrickdeyoreo/draw-a-line-between-bash-commands-fc51d16fe95e&quot;&gt;Patrick DeYoreo&lt;/a&gt; shows how to draw a line between Bash commands. &lt;a href=&quot;https://www.cyberciti.biz/faq/repeat-a-character-in-bash-script-under-linux-unix/&quot;&gt;Vivek Gite&lt;/a&gt; shows how to repeat a character ‘n’ times in Bash, which amounts to the same thing. Below I show how to adapt both solutions as a Bash function.&lt;/p&gt;

&lt;p&gt;Patrick’s insight is to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COLUMNS&lt;/code&gt; variable to know the terminal width, or how many characters (columns) fit in one line of text, and so draw exactly enough characters. It uses a loop to print each line segment and increments a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;line&lt;/code&gt; counter to stop after printing a segment for each column.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;horizontal_line &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;line++ &amp;lt; COLUMNS&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
      &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;done
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'\n'&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Vivek’s insight is to trick the printf builtin into drawing a line. The format specifier contains the line segment. The trick is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%.0s&lt;/code&gt; which I think means “print 0 characters of the argument” and to pass a sequence of arguments as long as the terminal width. It draws the same line.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;horizontal_line &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'%.0s'&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;seq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$COLUMNS&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'\n'&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I prefer Patrick’s solution because it’s easier for me to understand.&lt;/p&gt;

&lt;p&gt;My own contribution is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;${1:--}&lt;/code&gt;. It’s the first parameter, the character to draw the line with. The default character is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt;. It needs braces to use the syntax &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:-&lt;/code&gt; that separates the parameter name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; from the default.&lt;/p&gt;

&lt;p&gt;After defining the function here’s how I set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PS0&lt;/code&gt;and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PS1&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;PS0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'$(horizontal_line; date --iso=seconds; horizontal_line)\n'&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;PS1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'$(horizontal_line &quot;!&quot;; date --iso=seconds; horizontal_line &quot;!&quot;)\n$ '&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(Probable bug: For some reason the second &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;horizontal_line&lt;/code&gt; call doesn’t put a baked-in newline and so first line of the command output fused to the prompt. The workaround is to put another literal newline at the end of the prompt.)&lt;/p&gt;

&lt;p&gt;The next step is to write the timestamp and rule in one horizontal line to save space. Maybe the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;horizontal_line&lt;/code&gt; function needs a parameter to set the length, or a new function takes a string and a horizontal position to overwrite the line.&lt;/p&gt;</content><author><name></name></author><summary type="html">Today I learned to make Bash print start and end timestamps for tracking execution times.</summary></entry><entry><title type="html">Download and analyze Amplify Hosting access logs</title><link href="/2024/03/13/Analyze-Amplify-Hosting-Access-Logs.html" rel="alternate" type="text/html" title="Download and analyze Amplify Hosting access logs" /><published>2024-03-13T13:00:00+00:00</published><updated>2024-03-13T13:00:00+00:00</updated><id>/2024/03/13/Analyze-Amplify-Hosting-Access-Logs</id><content type="html" xml:base="/2024/03/13/Analyze-Amplify-Hosting-Access-Logs.html">&lt;h2 id=&quot;amplify-hosting-has-no-ready-made-analytics&quot;&gt;Amplify Hosting has no ready-made analytics&lt;/h2&gt;

&lt;p&gt;AWS Amplify Hosting is good for running a static site, but it doesn’t do analytics.&lt;/p&gt;

&lt;p&gt;That means it doesn’t offer easy insights into visitor traffic or behavior.&lt;/p&gt;

&lt;p&gt;It does share access logs from which you can build your own analytics.&lt;/p&gt;

&lt;p&gt;Acquiring the access logs is a complicated process, and I learned so much while figuring it out that I need to write a series of articles to explain it all.&lt;/p&gt;

&lt;p&gt;In this first article I describe a basic solution and provide some missing documentation for the Amplify Hosting service.&lt;/p&gt;

&lt;p&gt;In a future article I’ll present a complete solution to download all available access logs and show the queries I use to analyze my own site’s performance.&lt;/p&gt;

&lt;h2 id=&quot;read-access-logs-in-the-console--or-dont&quot;&gt;Read access logs in the console … or don’t&lt;/h2&gt;

&lt;p&gt;The short story here is you don’t use the console. The experience is so bad it almost made me give up on Amplify. &lt;a href=&quot;https://developers.cloudflare.com/pages/&quot;&gt;Cloudflare Pages&lt;/a&gt; was about to win my business.&lt;/p&gt;

&lt;p&gt;In the AWS console you go to the Amplify app, then go to the monitoring page, and then to the access logs tab.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-03-14-Analyze-Amplify-Hosting-Access-Logs_2024-03-13-17-25-24.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then you get frustrated because you realise the view is close to useless for analysis.&lt;/p&gt;

&lt;p&gt;15 records per page? Give me 100 at least.&lt;/p&gt;

&lt;p&gt;Only 4 columns? The full format contains over 30 fields.&lt;/p&gt;

&lt;p&gt;It’s as if the service team ran out of time to finish this feature or they don’t want you to use it. Am I missing something?&lt;/p&gt;

&lt;p&gt;It gets worse when you click “Edit time range”.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/2024-03-14-Analyze-Amplify-Hosting-Access-Logs_2024-03-13-17-22-25.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You can access logs for any two week period starting with the date and time provided below. If you wish to access older logs, you will need to setup a Lambda function that runs every two weeks and stores the results in S3.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Did AWS just tell me to &lt;a href=&quot;/assets/fault-tolerance.png&quot;&gt;write a distributed map reduce function in Erlang&lt;/a&gt;?&lt;/p&gt;

&lt;h2 id=&quot;download-the-access-logs-with-the-generateaccesslogs-api&quot;&gt;Download the access logs with the GenerateAccessLogs API&lt;/h2&gt;

&lt;p&gt;So what can you do? If you’re like me, you’ll try to write a Bash script to automate around the problem.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://docs.aws.amazon.com/amplify/latest/userguide/access-logs.html#using-access-logs&quot;&gt;Amplify user guide’s Monitoring page&lt;/a&gt; reassures us that Amplify Hosting retains all access logs for an app until you delete the app, and repeats the console’s limitations. It doesn’t refer to any API to collect the logs.&lt;/p&gt;

&lt;p&gt;The API exists. Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenerateAccessLogs&lt;/code&gt; to reproduce the console’s functionality, with all its limitations. In a future article I’ll show how to automate around it to build the log export feature you wish existed. Here I’ll show the basic solution.&lt;/p&gt;

&lt;p&gt;You call the API with your app ID, your domain name, and the access time range in &lt;a href=&quot;https://en.wikipedia.org/wiki/ISO_8601&quot;&gt;ISO format&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws amplify generate-access-logs &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--app-id&lt;/span&gt; dddamplifyapp1 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--domain-name&lt;/span&gt; example.org &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--start-time&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'2024-03-09T00:00:00'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--end-time&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'2024-03-10T00:00:00'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Amplify Hosting responds with a signed URL to an object in an S3 bucket. I redacted the sensitive values in the &lt;a href=&quot;https://docs.aws.amazon.com/STS/latest/APIReference/CommonParameters.html&quot;&gt;signed URL’s common parameters&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;logUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://
               aws-amplify-prod-eu-west-1-access-logs
               .s3.eu-west-1.amazonaws.com
               /dddamplifyapp1
               /example.org
               /Sat%20Mar%2009%2022%3A41%3A50%20UTC%202024
               ?X-Amz-Security-Token=...
               &amp;amp;X-Amz-Algorithm=AWS4-HMAC-SHA256
               &amp;amp;X-Amz-Date=20240313T213714Z
               &amp;amp;X-Amz-SignedHeaders=host
               &amp;amp;X-Amz-Expires=3599
               &amp;amp;X-Amz-Credential=ASIA...%2Feu-west-1%2Fs3%2Faws4_request
               &amp;amp;X-Amz-Signature=...&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The Amplify Hosting service owns the log file bucket. It appears to generate the object on demand.&lt;/p&gt;

&lt;p&gt;You have an hour to request the URL before the credentials expire. If you request within that time, S3 responds with a chunk of the access log. Keep reading to see how to request it with curl.&lt;/p&gt;

&lt;h2 id=&quot;the-amplify-hosting-log-format-has-quirks&quot;&gt;The Amplify Hosting log format has quirks&lt;/h2&gt;

&lt;p&gt;Amplify Hosting is a layer above a range of AWS services including CloudFront, AWS’s content distribution network (CDN) that serves the site content.&lt;/p&gt;

&lt;p&gt;Amplify Hosting creates a CloudFront distribution dedicated to your app. You have no direct access to the distribution because Amplify Hosting hides it from you. CloudFront provides Amplify Hosting with its access logs.&lt;/p&gt;

&lt;p&gt;But the format that Amplify Hosting shares via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenerateAccessLogs&lt;/code&gt; API isn’t identical to &lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html&quot;&gt;CloudFront’s standard log format&lt;/a&gt;. Amplify Hosting has small differences.&lt;/p&gt;

&lt;p&gt;The key format is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;app_id&amp;gt;/&amp;lt;domain&amp;gt;/&amp;lt;timestamp&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After URL-decoding the example timestamp is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sat Mar 09 22:41:50 UTC 2024&lt;/code&gt;. That’s a weird format. Why is the year apart from the month and date? Why not use the ISO format?&lt;/p&gt;

&lt;p&gt;The timestamp marks the first time you request the chunk. Days later if you request the same chunk you get the same key.&lt;/p&gt;

&lt;p&gt;The log file format contains not tab-separated but comma-separated values (CSV).&lt;/p&gt;

&lt;p&gt;Despite those differences I’m able to analyze the logs with any tool that reads the CSV format.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://www.s3stat.com/&quot;&gt;S3Stat&lt;/a&gt; and &lt;a href=&quot;https://goaccess.io/&quot;&gt;GoAccess&lt;/a&gt; tools promise ready-made analytics for CloudFront logs. I haven’t yet tested these so I don’t know whether they handle Amplify’s variant.&lt;/p&gt;

&lt;h2 id=&quot;now-you-can-automate-the-log-collection&quot;&gt;Now you can automate the log collection&lt;/h2&gt;

&lt;p&gt;To fetch just one chunk of the logs you complete three main steps:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Make the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenerateAccessLogs&lt;/code&gt; request&lt;/li&gt;
  &lt;li&gt;Extract the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logUrl&lt;/code&gt; value from the response&lt;/li&gt;
  &lt;li&gt;Download the log file at the URL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can automate all these steps in Bash with a command like this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;
  aws amplify generate-access-logs &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--app-id&lt;/span&gt; dddamplifyapp1 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--domain-name&lt;/span&gt; example.org &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--start-time&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'2024-03-08T00:00:00'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--end-time&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'2024-03-09T00:00:00'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  | jq &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'.logUrl'&lt;/span&gt;
&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; logs.csv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;First the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws&lt;/code&gt; command runs. This is the same as before.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;| jq -r '.logUrl'&lt;/code&gt; passes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws&lt;/code&gt;’s output through the &lt;a href=&quot;https://jqlang.github.io/jq/&quot;&gt;JSON processor jq&lt;/a&gt; to extract the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logUrl&lt;/code&gt; value from the JSON response. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-r&lt;/code&gt; switch writes the “raw” value, without the quotes that normally surround a JSON string.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;$(...)&quot;'&lt;/code&gt; is a command substitution. The output I just described is collected into a Bash string and used as a parameter for what comes next as if it were a literal.&lt;/p&gt;

&lt;p&gt;So you can imagine that at this point the command becomes the much simpler:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;s2&quot;&gt;&quot;https://...&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; logs.csv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://curl.se/&quot;&gt;curl&lt;/a&gt; requests the content at the URL and just writes it out.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;gt; logs.csv&lt;/code&gt; saves the content to a log file with the standard CSV file extension.&lt;/p&gt;

&lt;p&gt;Now you just have a CSV file, so you can use your favorite tool to analyze these.&lt;/p&gt;

&lt;p&gt;My current favorite tool is &lt;a href=&quot;https://duckdb.org/&quot;&gt;DuckDB&lt;/a&gt;. My &lt;a href=&quot;https://github.com/duckdb/duckdb/discussions/10876&quot;&gt;love for DuckDB&lt;/a&gt; deserves its own article. Here I’ll just show the commands to do the simplest analysis.&lt;/p&gt;

&lt;p&gt;Read the CSV into a SQL table and start the DuckDB CLI.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;duckdb &lt;span class=&quot;nt&quot;&gt;-cmd&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'CREATE TABLE logs AS SELECT * FROM logs.csv;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In SQL, count the requests in the access log.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request_count&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;avoid-common-errors-from-generateaccesslogs&quot;&gt;Avoid common errors from GenerateAccessLogs&lt;/h2&gt;

&lt;p&gt;The API has no default date range. If you omit one, it gives an error.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws amplify generate-access-logs &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--app-id&lt;/span&gt; dddamplifyapp1 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--domain-name&lt;/span&gt; example.org
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;An error occurred (BadRequestException) when calling the GenerateAccessLogs operation: Unable to complete request for the given time range, reduce time range and try again&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The API fails if you request more than two weeks. Here I request one month.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws amplify generate-access-logs &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--app-id&lt;/span&gt; dddamplifyapp1 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--domain-name&lt;/span&gt; example.org &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--start-time&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'2024-01-01'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--end-time&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'2024-02-01'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;An error occurred (BadRequestException) when calling the GenerateAccessLogs operation: Time range must be no greater than 2 weeks&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You’re lucky if you can get a full two-week response. The API may fail with what looks like a timeout. The error text is the same as when you omit the time range.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws amplify generate-access-logs &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--app-id&lt;/span&gt; dddamplify1 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--domain-name&lt;/span&gt; example.org &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--start-time&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'2024-01-01'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--end-time&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'2024-01-15'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;An error occurred (BadRequestException) when calling the GenerateAccessLogs operation: Unable to complete request for the given time range, reduce time range and try again&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I find it’s more reliable to use a range of just one week.&lt;/p&gt;

&lt;p&gt;That means a single chunk contains even less data. That’s why you’ll want to read the next article in the series, where I show you how to download all the chunks!&lt;/p&gt;

&lt;h2 id=&quot;aws-can-do-better-here&quot;&gt;AWS can do better here&lt;/h2&gt;

&lt;p&gt;I don’t understand why Amplify Hosting puts that straitjacket over the CloudFront logs.&lt;/p&gt;

&lt;p&gt;Please, AWS, consider making the console experience more like that of AWS Config Advanced Query or even Athena. Those services show all the available columns and more than 15 rows.&lt;/p&gt;

&lt;p&gt;Even better, consider making the CloudFront access logs directly accessible so I don’t need to spend my evenings and weekends figuring out a custom solution for this! Even if I had to pay for the storage or the requests, it would be a preferable solution, because I could go straight to Athena to derive insights from my data.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this article I explained showed you how to download and analyze Amplify Hosting access logs.&lt;/p&gt;

&lt;p&gt;I started by explaining that Amplify Hosting provides no analytics features. I showed how it’s impractical to use the console to read the access logs and, how it’s difficult to download more than two weeks of log data.&lt;/p&gt;

&lt;p&gt;I introduced the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GenerateAccessLogs&lt;/code&gt; API that allows you to collect the access logs using the CLI, explained the format of the response, and explained the format of the log file and how it varies from the CloudFront standard log file format.&lt;/p&gt;

&lt;p&gt;I showed how to use Bash to automate the collection of a single chunk of the access logs and how to analyze the result using DuckDB. I shows some common errors and what you need to do to avoid them.&lt;/p&gt;

&lt;p&gt;Before I finished, I shared my opinion on how AWS can improve this experience from the Amplify Hosting service.&lt;/p&gt;

&lt;p&gt;Finally I promised in the next article to show you how to go beyond the basic solution to download all the access logs.&lt;/p&gt;

&lt;h2 id=&quot;read-denis-gladkikhs-solution&quot;&gt;Read Denis Gladkikh’s solution&lt;/h2&gt;

&lt;p&gt;After I started writing this, I found &lt;a href=&quot;https://www.outcoldman.com/about/&quot;&gt;Denis Gladkikh&lt;/a&gt;’s blog series on Analyzing AWS Amplify Access Logs. It identifies the same problem with Amplify, proposes a similar solution, gives a tour of the different analytics tools you can use on the logs. I recommend you read these too!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.outcoldman.com/en/archive/2021/01/14/analyzing-aws-amplify-access-logs-part-1/&quot;&gt;Analyzing AWS Amplify Access logs. Part 1.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.outcoldman.com/en/archive/2021/02/04/analyzing-aws-amplify-access-logs-part-2/&quot;&gt;Analyzing AWS Amplify Access logs. Part 2.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><summary type="html">Amplify Hosting has no ready-made analytics</summary></entry><entry><title type="html">The blog is back</title><link href="/2024/03/12/the-blog-is-back.html" rel="alternate" type="text/html" title="The blog is back" /><published>2024-03-12T21:00:00+00:00</published><updated>2024-03-12T21:00:00+00:00</updated><id>/2024/03/12/the-blog-is-back</id><content type="html" xml:base="/2024/03/12/the-blog-is-back.html">&lt;p&gt;The blog is back! 2024 is the year that I take my writing more seriously so that I can contribute more effectively to the community.&lt;/p&gt;

&lt;p&gt;I love writing code and hacking on open source projects. But code can be a bit impenetrable for the people unfamiliar with the problem it solves.&lt;/p&gt;

&lt;p&gt;So this year I want to spend more time writing articles to support and explain the open source work that I do and to highlight interesting problems that I solve at work.&lt;/p&gt;

&lt;p&gt;Just today someone contacted me to ask for advice about AppSync and DynamoDB. It’s not my speciality, so I recommended other AWS Community Builders to contact to ask for advice based on what they have written about those topics:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://benoitboure.com/series/appsync&quot;&gt;Benoît Bouré&lt;/a&gt; has a whole AppSync series on his blog&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://community.aws/posts/amplify-flutter-serverless-with-appsync&quot;&gt;Johannes Koch&lt;/a&gt; published a tutorial to build a serverless app using AppSync and DynamoDB&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sethorell.substack.com/p/why-dynamodb-is-still-my-first-pick&quot;&gt;Seth Orell&lt;/a&gt; writes about why his first pick for a database is DynamoDB&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.ibd.com/about-2/&quot;&gt;Robert J Berger&lt;/a&gt;’s Omnyway stack uses Amplify&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although I’ve not worked directly with any of them yet, I was able to recommend them based on their writing. I want people to be able to do the same for me! So I need to start writing again so that people can see what problems I like to work on and what I can help them with.&lt;/p&gt;

&lt;p&gt;Why haven’t I published more? I take a lot of technical notes at work, notes that I could adapt into a lot of technical articles. But it would take time to generalize from confidential customer data and use cases or redact other things I cannot share publicly.&lt;/p&gt;

&lt;p&gt;10 years ago I was single and more energetic and so I had time explore miscellaneous topics on &lt;a href=&quot;https://basetable.wordpress.com/2014/11/13/convert-json-to-csv-with-jq-and-recordstream/&quot;&gt;my old blog&lt;/a&gt;. My favorite article from my previous blogging life is  Now I’m married with dogs and so I have to manage my time more carefully.&lt;/p&gt;

&lt;p&gt;This year I hope to involve my customers in the learning-in-public process and work with them to write about the problems we solve in a way that they are proud of sharing online, either on my own blog or on their company blog if they have one.&lt;/p&gt;

&lt;p&gt;Another reason that seems more silly but did psychologically block me was the difficulty of knowing who reads what I write. My block has no comments, and the Amplify Hosting backend I use has no built-in analytics.&lt;/p&gt;

&lt;p&gt;Amplify’s missing analytics almost made me tear up the architecture and start again. But last weekend I hacked together a script that downloads the access logs and uses SQL to find the most requested pages, the top referrers, and other basic insights.&lt;/p&gt;

&lt;p&gt;That analytics solution for Amplify Hosting will be the topic for the next article!&lt;/p&gt;

&lt;p&gt;I’ll add comments too, using utteranc.es, a comment manager backed by GitHub issues. I using  GitHub to host my code and manage my projects, so I’ll try it for comments too.&lt;/p&gt;

&lt;p&gt;Hope to see you round here more often!&lt;/p&gt;</content><author><name></name></author><summary type="html">The blog is back! 2024 is the year that I take my writing more seriously so that I can contribute more effectively to the community.</summary></entry><entry><title type="html">How to install binaries from GitHub</title><link href="/2023/09/11/how-to-install-binaries-from-github.html" rel="alternate" type="text/html" title="How to install binaries from GitHub" /><published>2023-09-11T00:00:00+00:00</published><updated>2023-09-11T00:00:00+00:00</updated><id>/2023/09/11/how-to-install-binaries-from-github</id><content type="html" xml:base="/2023/09/11/how-to-install-binaries-from-github.html">&lt;h1 id=&quot;how-to-install-binaries-from-github&quot;&gt;How to install binaries from GitHub&lt;/h1&gt;

&lt;p&gt;Ways to install binaries from GitHub Releases:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jooola/gh-release-install&quot;&gt;jooola/gh-release-install&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/Rishang/install-release&quot;&gt;Rishang/install-release&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/devops-works/binenv&quot;&gt;devops-works/binenv&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/redraw/gh-install&quot;&gt;redraw/gh-install&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/giantswarm/install-binary-action&quot;&gt;giantswarm/install-binary-action&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dvershinin/lastversion&quot;&gt;dvershinin/lastversion&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/wimpysworld/deb-get&quot;&gt;wimpysworld/deb-get&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ways to release binaries to GitHub Releases:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/actions/create-release&quot;&gt;actions/create-release&lt;/a&gt; (Archived, but references active projects)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/softprops/action-gh-release&quot;&gt;softprops/action-gh-release&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><summary type="html">How to install binaries from GitHub</summary></entry></feed>