<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="https://calzone.proofofpizza.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://calzone.proofofpizza.com/" rel="alternate" type="text/html" /><updated>2023-03-19T17:38:21+00:00</updated><id>https://calzone.proofofpizza.com/feed.xml</id><title type="html">ProofOfPizza</title><subtitle>Blog by ProofOfPizza about programming and tech talk about aws, docker, terraform, java, typescript, angular, spring and more. Also fun things about music theory and math and so on.</subtitle><author><name>{&quot;avatar&quot;=&gt;&quot;/assets/images/bio-photo.jpg&quot;, &quot;bio&quot;=&gt;&quot;Curiosity killed the cat but at least she learned something!&quot;, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Buy me a coffee&quot;, &quot;icon&quot;=&gt;&quot;fas fa-coffee&quot;, &quot;url&quot;=&gt;&quot;https://www.buymeacoffee.com/ProofOfPizza?ref=footer&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/ProofOfPizza/&quot;}, {&quot;label&quot;=&gt;&quot;GitLab&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-gitlab&quot;, &quot;url&quot;=&gt;&quot;https://gitlab.com/ProofOfPizza&quot;}]}</name></author><entry><title type="html">Bye G***le, Hi Plausible - Analytics with a clear conscience</title><link href="https://calzone.proofofpizza.com/tech/opinion/bye-google-hi-plausible-analytics-with-a-clear-conscience/" rel="alternate" type="text/html" title="Bye G***le, Hi Plausible - Analytics with a clear conscience" /><published>2022-04-11T00:00:00+00:00</published><updated>2022-04-11T00:00:00+00:00</updated><id>https://calzone.proofofpizza.com/tech/opinion/bye-google-hi-plausible-analytics-with-a-clear-conscience</id><content type="html" xml:base="https://calzone.proofofpizza.com/tech/opinion/bye-google-hi-plausible-analytics-with-a-clear-conscience/">&lt;p&gt;So you have a web page, or maybe even a bunch of pages, and you are interested to optimize them, either for better user experience, more sales revenue, or you just want to know if anyone reads your blog that you spend all these hours on. Well technology is here to save you! If you dive in this topic the first second third and fourth thing (do I keep counting?) you will find is G***gle Analytics. It is free, and installation is easy and it will give you all the data &lt;em&gt;you might ever need.&lt;/em&gt; But what does “free” mean? And why not look at what you need first, and then see if you can get just that? In this blog we explore an alternative, &lt;a href=&quot;https://plausible.io/&quot;&gt;Plausibe analytics&lt;/a&gt;, why and how we at &lt;a href=&quot;https://inquisitive.nl?ref=proofofpizza&quot;&gt;InQuisitive&lt;/a&gt; transitioned.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you are not paying for it, you’re not the customer; you’re the product being sold.
&lt;cite&gt;blue_beetle&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a famous quote by now, but in this context it is actually worse. How much worse? Well that depends on your success. Because your site is free to visit for anyone, and you have G***le Analytics for free, but who is the product here? Not just you, all the people who visit your site in good faith. Every friend, customer, family member you show it to, your colleagues that you send a link etc. They are all products, you give away their data, so that all of us, including all those people who never even went to your site are being targeted with ever growing precision: manipulating our buying decisions, voting decisions, deciding on the information that reaches us so we’ll form our opinions aligned with the Big Buyers interests… Is it that bad ? Yes it is!&lt;/p&gt;

&lt;p&gt;You should understand that this incredible power lies not in specific data you provide, it lies in the immense amounts of data that organisations such as G***le or F***book (or Meta!) have of us. Their algorithms find patterns, and using those patterns they’re able to predict so many things about us that in many ways, they know us better then we know ourselves or our loved ones. Our freedoms are at stake, and even though we can not fix this problem by ourselves, we can choose to not be an accomplice in giving them away… for free!&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;There is, simply, no way, to ignore privacy. Because a citizenry’s freedoms are interdependent, to surrender your own privacy is really to surrender everyone’s. You might choose to give it up out of convenience, or under the popular pretext that privacy is only required by those who have something to hide. But saying that you don’t need or want privacy because you have nothing to hide is to assume that no one should have, or could have to hide anything – including their immigration status, unemployment history, financial history, and health records. You’re assuming that no one, including yourself, might object to revealing to anyone information about their religious beliefs, political affiliations and sexual activities, as casually as some choose to reveal their movie and music tastes and reading preferences. Ultimately, saying that you don’t care about privacy because you have nothing to hide is no different from saying you don’t care about freedom of speech because you have nothing to say. Or that you don’t care about freedom of the press because you don’t like to read. Or that you don’t care about freedom of religion because you don’t believe in God. Or that you don’t care about the freedom to peaceably assemble because you’re a lazy, antisocial agoraphobe. Just because this or that freedom might not have meaning to you today doesn’t mean that that it doesn’t or won’t have meaning tomorrow, to you, or to your neighbor – or to the crowds of principled dissidents I was following on my phone who were protesting halfway across the planet, hoping to gain just a fraction of the freedom that my country was busily dismantling.
&lt;cite&gt;Edward Snowden&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;how-does-that-work-&quot;&gt;How does that work ?&lt;/h2&gt;
&lt;p&gt;You installed G***gle Analytics on your website. Now every time someone comes to your site, navigates, loads a widget, clicks on a link etc a call is made to G***gle to inform them. This is how they get their data, and how they get you to work for them… for free! Well not entirely: they give you some insights into that data of course. Obviously, as said before, data is much more valuable if it exists in greater quantities so the same data is probably more worth to them than to you.&lt;/p&gt;

&lt;p&gt;A simple &lt;a href=&quot;https://duckduckgo.com/?q=how+to+make+sense+of+data+in+G\*\*\*gle+Analytics&amp;amp;t=brave&amp;amp;ia=web&quot;&gt;search&lt;/a&gt; on the internet shows us that G***le is retrieving so much data it is hard to understand, there are countless plugins and other software to link it to just to make sense of it! But why ? Well, because it was never designed for you or your purpose of it.&lt;/p&gt;

&lt;h2 id=&quot;are-there-alternatives-&quot;&gt;Are there alternatives ?&lt;/h2&gt;
&lt;p&gt;Yes there are! There are lots of them actually, just none as famous and big as G***le Analytics, and therefore you have to seek them out. To name but a few, we have:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://matomo.org/&quot;&gt;Matomo&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.openwebanalytics.com/&quot;&gt;Open Web Anaytics&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://count.ly/&quot;&gt;Countly&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://awstats.sourceforge.io/&quot;&gt;AWStats&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://plausible.io/&quot;&gt;Plausible&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is just a small list, there are many more. These five are all open source. So you see there is plenty to choose from! For a comparison you can for instance see sites like &lt;a href=&quot;https://rigorousthemes.com/blog/open-source-google-analytics-alternatives/&quot;&gt;rigorous themes&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;where-to-go-from-here-&quot;&gt;Where to go from here ?&lt;/h2&gt;
&lt;p&gt;I started looking into this, because I was interested to know if anyone reads my blogs. As simple as that. If there were only people from the Netherlands looking I might consider changing the language to Dutch. And as a privacy geek I wanted to know what possibilities exist that might enable me to get some insights into my visitors, without selling it all out to the big guys, and without me feeling guilty for becoming just like them!
After some research into ease of use, easy of install, data provided, price, documentation and type of project/company, I settled on Plausible.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Plausible Analytics is an open-source project dedicated to making web analytics more privacy-friendly.”&lt;/em&gt; it says on their &lt;a href=&quot;https://plausible.io/about&quot;&gt;about&lt;/a&gt; page. I like that! And just reading through the site and the documentation was a fresh breeze. So I signed up for the 30 days trial, to see if I could indeed get it working, and to see if I would find it worth while. It turns out that configuration for my jekyll site was almost to easy to mention. I had to add two lines to my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_config.yaml&lt;/code&gt;:&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;analytics:
  provider: &quot;custom&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then include a small, one line script that Plausible provided for me in the folder &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_includes&lt;/code&gt;. That. Was. It.&lt;/p&gt;

&lt;p&gt;I looked into the statistics and immediately understood what I was looking and found directly what it was I was looking for. I could see how many people visit my pages. If I want I can send a url with a small query parameter so that I can easily recognise from where the people came, and which actions I take, such as sharing it on LinkedIn, or directly with my colleagues, or referencing it in StackOverflow answers, result in people actually reading my work.&lt;/p&gt;

&lt;p&gt;I was very satisfied, and decided that this would be a good step, not just for me and my blog but also for the company I work with, &lt;a href=&quot;https://inquisitive.nl?ref=proofofpizza&quot;&gt;InQuisitive&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Please note that while I am very enthusiastic about their product I am in no way affiliated to Plausible.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;price-and-possibilities&quot;&gt;Price and possibilities&lt;/h2&gt;
&lt;p&gt;I choose to go for the easiest way, the hosted version where I do not have to manage any servers and they promise to keep my data in Europe, and all in accordance with the &lt;a href=&quot;https://en.wikipedia.org/wiki/General_Data_Protection_Regulation&quot;&gt;GDPR&lt;/a&gt;. This is important because we see countries moving towards stricter interpretations and enforcements when it comes to data protection. Cases in Austria and France had led to the &lt;a href=&quot;https://techstory.in/eu-declares-google-analytics-illegal-heres-why/&quot;&gt;EU declaring G***le Analytics illegal&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The software itself is free and open source, so if you want, or need to you can host it all yourself and keep the data in house. They made script for you make it easy to install and run everything, and if you see a possible improvement, just make a PR! But I really think for the money you do not need to, a basic subscription is as low as 9 Euros per month. But if you do and you use it heavily, by all means please consider their suggestion:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;If you’re self-hosting Plausible, sponsoring us is a great way to give back to the community and to contribute to the long-term sustainability of the project. Thank you for supporting independent creators of Free Open Source Software!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;introducing-it-in-our-companys-websites&quot;&gt;Introducing it in our company’s websites&lt;/h2&gt;
&lt;p&gt;Next I called my colleague Maxime who works as the chief marketeer in our office. I said “Hey Maxime, remember those ideas we all wrote down about wanting to do some good with this company? Well I may have an idea to put our money where our mouth is, and I’d like to discuss it with you”. So we set up a meeting and I asked her a bunch of questions like, what are we using now for our analytics? What are the data points you actually look at and use? Do you get all the info you need? Is it cluttered with stuff you do not actually want to collect?&lt;/p&gt;

&lt;p&gt;Then I proceeded to show my screen and show her what Plausible looked like for my site, and asked if that might work for her. She liked the idea (duh, nobody wants to be part of …), and said she would look into it. We agreed she’d read up on it, and if she wanted to proceed with a trial next to the existing analytics, we’d meet up and I’d help her set it up. Except, she did not.&lt;/p&gt;

&lt;p&gt;I waited, and waited a bit more. And then I saw a message “Hey, just wanted to let you know that I managed to get it working for both our sites with the Plausible plugin [for Wordpress]. Looking good!” Now that was nice, no need for any techie to assist, just fiddle around a bit and you have it working!&lt;/p&gt;

&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;We at &lt;a href=&quot;https://inquisitive.nl?ref=proofofpizza&quot;&gt;InQuisitive&lt;/a&gt; now have all our sites set up with Plausible, and G***le is out. It may not seem much but the important thing is that it is &lt;em&gt;something.&lt;/em&gt; A step in the right direction, in the direction we want to go. Where we are autonomous to choose what we do, instead of walking on some big data bully’s leash. Also remember that just “your little bit of data” is not the problem, but all those little bits make up a really big problem. So take control, keep yourself, your company and the people around you safe. Or at least: refuse to be part of the threat.
It’s fun, it’s really not that hard, so spend a day on it!&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A well spent day brings happy sleep.
&lt;cite&gt;Leonardo da Vinci&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content><author><name>{&quot;avatar&quot;=&gt;&quot;/assets/images/bio-photo.jpg&quot;, &quot;bio&quot;=&gt;&quot;Curiosity killed the cat but at least she learned something!&quot;, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Buy me a coffee&quot;, &quot;icon&quot;=&gt;&quot;fas fa-coffee&quot;, &quot;url&quot;=&gt;&quot;https://www.buymeacoffee.com/ProofOfPizza?ref=footer&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/ProofOfPizza/&quot;}, {&quot;label&quot;=&gt;&quot;GitLab&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-gitlab&quot;, &quot;url&quot;=&gt;&quot;https://gitlab.com/ProofOfPizza&quot;}]}</name></author><category term="tech" /><category term="opinion" /><category term="analitics" /><category term="data" /><category term="gdpr" /><summary type="html">So you have a web page, or maybe even a bunch of pages, and you are interested to optimize them, either for better user experience, more sales revenue, or you just want to know if anyone reads your blog that you spend all these hours on. Well technology is here to save you! If you dive in this topic the first second third and fourth thing (do I keep counting?) you will find is G***gle Analytics. It is free, and installation is easy and it will give you all the data you might ever need. But what does “free” mean? And why not look at what you need first, and then see if you can get just that? In this blog we explore an alternative, Plausibe analytics, why and how we at InQuisitive transitioned.</summary></entry><entry><title type="html">#AutomaticAlley #3 - Safe and easy AWS with 2FA and scripted login</title><link href="https://calzone.proofofpizza.com/tech/automaticalley/AutomaticAlley-3-safe-and-easy-aws-with-2FA-and-scripted-login/" rel="alternate" type="text/html" title="#AutomaticAlley #3 - Safe and easy AWS with 2FA and scripted login" /><published>2022-03-13T00:00:00+00:00</published><updated>2022-03-13T00:00:00+00:00</updated><id>https://calzone.proofofpizza.com/tech/automaticalley/AutomaticAlley-3-safe-and-easy-aws-with-2FA-and-scripted-login</id><content type="html" xml:base="https://calzone.proofofpizza.com/tech/automaticalley/AutomaticAlley-3-safe-and-easy-aws-with-2FA-and-scripted-login/">&lt;p&gt;And here is the third in the series “AutomaticAlley” where I share some tips, tricks, bits and bites to make day to day life easier. Especially for us nerds on the command line. In this episode we’ll have a look at enforcing AWS IAM users to setup 2FA and then how to make that bearable by scripting the login. This writing is sort an extension of my &lt;a href=&quot;https://dev.to/matrixersp/how-to-use-fzf-with-ripgrep-to-selectively-ignore-vcs-files-4e27&quot;&gt;earlier blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As I have &lt;a href=&quot;https://dev.to/matrixersp/how-to-use-fzf-with-ripgrep-to-selectively-ignore-vcs-files-4e27&quot;&gt;said before&lt;/a&gt; 2FA (or MFA in general) is of crucial importance when working with cloud providers such as AWS, GCP, or Azure. A simple risk analysis says that:&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;Risk = probability * impact
Risk = really quite possible though not immediately likely * selling your house and living in debt for the rest of your days = worth the trouble of using 2FA
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;See? Math is easy!&lt;/p&gt;

&lt;p&gt;Now then how do we make sure our IAM users (being your colleagues) to use 2FA ? We setup a policy for that! IAM let’s us create users or roles, and attach policies to them which describe exactly:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;What the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;effect&lt;/code&gt; is: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;allow&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deny&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;What &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;action&lt;/code&gt; we are talking about&lt;/li&gt;
  &lt;li&gt;What the relevant resources are for that action&lt;/li&gt;
  &lt;li&gt;Which version of the language syntax rules.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are more options, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;conditions&lt;/code&gt; to make even more fine grained rules for you users. Specifying &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sid&lt;/code&gt; can help you document and make for easier understanding. All of this is just in plan &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;json&lt;/code&gt; structure so for most of us reading this, and even remotely interested in setting this up, quite readable. If you find it hard, there is always the option to specify it in the AWS Console. (In fact in that case I would recommend selecting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;json&lt;/code&gt;, the copy paste this document, click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Policy Summary&lt;/code&gt;.)&lt;/p&gt;

&lt;h2 id=&quot;the-policy-to-deny-everything-if-no-mfa-is-present&quot;&gt;The policy to deny everything if no MFA is present&lt;/h2&gt;

&lt;p&gt;In our case the policy would like:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;{
    &quot;Version&quot;: &quot;2012-10-17&quot;,
    &quot;Statement&quot;: [
        {
            &quot;Sid&quot;: &quot;AllowViewAccountInfo&quot;,
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: [
                &quot;iam:ListUsers&quot;,
                &quot;iam:ListMFADevices&quot;,
                &quot;iam:GetAccountPasswordPolicy&quot;,
                &quot;iam:GetAccountSummary&quot;
            ],
            &quot;Resource&quot;: &quot;*&quot;
        },
        {
            &quot;Sid&quot;: &quot;AllowChangeOwnPasswordsOnFirstLogin&quot;,
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: [
                &quot;iam:ChangePassword&quot;,
                &quot;iam:GetUser&quot;
            ],
            &quot;Resource&quot;: &quot;arn:aws:iam::*:user/${aws:username}&quot;
        },
        {
            &quot;Sid&quot;: &quot;AllowChangeOwnPasswordsAfterMFAEnabled&quot;,
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: [
                &quot;iam:GetLoginProfile&quot;,
                &quot;iam:UpdateLoginProfile&quot;
            ],
            &quot;Resource&quot;: &quot;arn:aws:iam::*:user/${aws:username}&quot;
        },
        {
            &quot;Sid&quot;: &quot;AllowManageOwnVirtualMFADevice&quot;,
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: [
                &quot;iam:CreateVirtualMFADevice&quot;,
                &quot;iam:DeleteVirtualMFADevice&quot;
            ],
            &quot;Resource&quot;: &quot;arn:aws:iam::*:mfa/${aws:username}&quot;
        },
        {
            &quot;Sid&quot;: &quot;AllowManageOwnUserMFA&quot;,
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: [
                &quot;iam:DeactivateMFADevice&quot;,
                &quot;iam:EnableMFADevice&quot;,
                &quot;iam:ListMFADevices&quot;,
                &quot;iam:ResyncMFADevice&quot;
            ],
            &quot;Resource&quot;: &quot;arn:aws:iam::*:user/${aws:username}&quot;
        },
        {
            &quot;Sid&quot;: &quot;DenyAllExceptListedIfNoMFA&quot;,
            &quot;Effect&quot;: &quot;Deny&quot;,
            &quot;NotAction&quot;: [
                &quot;iam:ListUsers&quot;,
                &quot;iam:ListMFADevices&quot;,
                &quot;iam:ChangePassword&quot;,
                &quot;iam:GetUser&quot;,
                &quot;iam:CreateVirtualMFADevice&quot;,
                &quot;iam:DeleteVirtualMFADevice&quot;,
                &quot;iam:DeactivateMFADevice&quot;,
                &quot;iam:EnableMFADevice&quot;,
                &quot;iam:ListMFADevices&quot;,
                &quot;iam:ResyncMFADevice&quot;
            ],
            &quot;Resource&quot;: &quot;*&quot;,
            &quot;Condition&quot;: {
                &quot;BoolIfExists&quot;: {
                    &quot;aws:MultiFactorAuthPresent&quot;: &quot;false&quot;
                }
            }
        }
    ],
    &quot;Id&quot;: &quot;MFA Required IAM Policy&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Shall we do quick run through of this document? First we describe rights to see account info: we &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;allow&lt;/code&gt; some list and read access to users, devices, and policies. We specify this for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Resource&quot;: &quot;arn:aws:iam::*:user/${aws:username}&quot;&lt;/code&gt;. This is an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ARN&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Amazon Resource Name&lt;/code&gt;, a way to identify well, Amazon resources. It is made up of several components, usually something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arn:aws:&amp;lt;the service&amp;gt;:&amp;lt;the account number&amp;gt;:&amp;lt;resource-type&amp;gt;&lt;/code&gt;. In this case it is simply within service &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iam&lt;/code&gt;, for any (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt;) account you have access to, the current user. In short you can do all this, as long as it is about your current user.&lt;/p&gt;

&lt;p&gt;Then the following blocks are somewhat similar: We allow the user to change their password on first login, after setting up MFA, and of course we allow the user to setup MFA and use it. If you understood the structure of the first block these blocks are sort of self-evident.&lt;/p&gt;

&lt;p&gt;Now the spice in the salsa here is found in the last block &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DenyAllExceptListedIfNoMFA&lt;/code&gt;. Contrary to the other statements this one has effect &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deny&lt;/code&gt;. Then it has another negation: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NotAction&lt;/code&gt;, which means that &lt;em&gt;we deny not the following actions&lt;/em&gt; or &lt;em&gt;we deny all but the following actions&lt;/em&gt; or in even more plain english: &lt;em&gt;we allow ONLY the following actions&lt;/em&gt;. Then we list those actions: all basic actions that will allow our user to to nothing important except setup their MFA.&lt;/p&gt;

&lt;p&gt;The last part to point your attention to is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Condition&lt;/code&gt; block. It basically says: “All the above, is only valid under this condition: ‘If the user has no MFA present’”&lt;/p&gt;

&lt;p&gt;Pretty neat right ? If you want to play along: just create a user in IAM, and attach this policy to them. Then either login into the console, or make sure you create access keys and log in on the cli. You will find it nice and boring! Nothing to do, except set up you MFA! But once you do, well… then who knows? Meaning: You should also specify more policies for the user. Normally we would do this in a separate document, because you can attach multiple policies to a user. This way you can reuse this policy for all your users, or those in certain groups. And at the same time, specify specific access to users based on what they need to be doing in AWS.&lt;/p&gt;

&lt;h2 id=&quot;login-on-the-cli&quot;&gt;Login on the cli&lt;/h2&gt;

&lt;p&gt;The AWS cli is a very useful and powerful tool. It gives you direct access to the AWS api: you can list and look at resources, create update and delete them. As such it should be clear why it is important to be careful with your credentials!&lt;/p&gt;

&lt;p&gt;Running a simple command such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws ec2 describe-instances&lt;/code&gt; will be successful only if you can be authenticated (you are who you say you are) and you have sufficient authorization (you are allowed to do what you want to do). How does this work on the cli ?&lt;/p&gt;

&lt;p&gt;There are multiple options:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Provide your credentials in the command line with the command every time. (Who even does this ?)&lt;/li&gt;
  &lt;li&gt;Make use of the &lt;a href=&quot;https://help.ubuntu.com/community/EnvironmentVariables&quot;&gt;environment or shell variables&lt;/a&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AWS_ACCESS_KEY_ID&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt; and if using MFA &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AWS_SESSION_TOKEN&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Use the credentials stored in the default credential file: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.aws/credentials&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is also the order in which the possibilities are evaluated. The last option is easiest to setup using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws configure&lt;/code&gt; &lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html&quot;&gt;command&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Lets have a look at that file. If you open it in your favorite editor you should see something like:&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;[default]
aws_access_key_id=AKIAXXXXXXXXXXXXXXXX
aws_secret_access_key=someunreadablemumbojumboxxxxxxxxxxxxxxx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Almost nothing to explain there: it shows the credentials and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[default]&lt;/code&gt; tells us this is the default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;profile&lt;/code&gt;. We can set up different profiles and name them as we want. For instance if we have a different user that has read only rights. To use that user we would add a part to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.aws/credentials&lt;/code&gt;:&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;[default]
aws_access_key_id=AKIAXXXXXXXXXXXXXXXX
aws_secret_access_key=someunreadablemumbojumboxxxxxxxxxxxxxxx

[readonly]
aws_access_key_id=AKIAYYYYYYYYYYYYYYYY
aws_secret_access_key=someotherunreadablemumbojumboxxxxxxxxxx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;but-what-about-mfa-&quot;&gt;But what about MFA ?&lt;/h2&gt;

&lt;p&gt;But if you have been impatient, curious or both and you set up the policy as described above and you tried to run the command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws ec2 describe-instances&lt;/code&gt; you probably found:&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;An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note: This assumes you have your credentials set up in the credentials file. Otherwise you will not even be authenticated!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This tells us we were in fact authenticated, but we were not allowed to perform this operation. Makes sense, we explicitly denied all that!
But maybe you were smart and actually set up MFA. For instance using your phone and an authenticator app. Now you want to login but how ??&lt;/p&gt;

&lt;p&gt;Not to worry, this is how: From the above options we ignore option 1. simply because that is insane.
Options 2 and 3 are somewhat more doable.&lt;/p&gt;

&lt;p&gt;To login we normally need our username, password and MFA token. That temporary token you get from your authenticator app. To login using the cli we use the access_key, the secrets_access_key and the session_token.
And we can get the session_token using the command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws sts get-session-token&lt;/code&gt;. It takes a number of arguments:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;The ARN of your MFA device. You can find it by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws iam list-mfa-devices&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Token code. You find this in your authenticator app.&lt;/li&gt;
  &lt;li&gt;Duration in seconds. You should set this to a sane value. We’ll get to this later on.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the final command could be:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;aws sts get-session-token --serial-number arn:aws:iam::111222333444:mfa/My_User --token-code 192952 --duration-seconds 72000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The answer will look something like:&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;Credentials:
  AccessKeyId: ASIAXXXXXXXXXXXXXXXX
  Expiration: '2022-03-14T07:07:36+00:00'
  SecretAccessKey: /19dlJorSomeothing+like_this4tFdicOkBJ1I
  SessionToken: IQoJb3JpZ2luX2VjEMv//////////wEaDGV1LWNlbnRyYWwtMSJIMEYCIQDUiP18GiJROFi19/TSRJBVG1ZYmpBCNEcZOmEUPEyXEQIhAPtHSOWW27fpk5fGyttOoHx1KKshS4LjbAEKb62hWWK1Ku8BCEQQARoMNjQwNzUzMjQ0NDk4IgzYyM9Lhvtb097v57oqzAHKmj784kousxlPGZIHt6Rkn5fN+FYMhQUquk+g7dauTngCIskvOxvgUjTUSIit6Fg8r2EcGMIKD+vdKNwdkchZkfvgxypioVmb1t70NTMrqgPoMjexBqiGVq9SxUKofxnhBKe5lphfseqpXUKk/QEXHqUVqrZ2P9XCeMyf/clT+Q4npL1NhMLsXXeS5fvoYMlCfZcXkTvfau3WEwlDVGEZQQiUP+yF9j4wdhtZX3saGMVUrqwUO6mEJphxnKavVoNAxljmtrM/cj2bBcYw+KC3kQY6lwEUWU3FDrG+R0IIjrV4lNP9E9Fx6JODMGktCoIgpfTewoHhli0IBEhrHU7U7900ACNAtMIsjT2ezFK2kNmhyH2p+OUORC56eeLVEv53+QHgjLNdbpUITHobFTrwjjmH+LAxzTdZh89BrNRyWsiOaFLRsJuCqpKliAJyuHldrRoA1DXRAojB37ihrZTz1h0ShML8sYE1OjaX
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Great! … But how do we now use that ?&lt;/p&gt;

&lt;h2 id=&quot;environment-variables&quot;&gt;Environment Variables&lt;/h2&gt;
&lt;p&gt;As we saw, the second option was to use environment variables.
Setting them on the command line is easy! Just run:&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;export AWS_ACCESS_KEY_ID=ASIAXXXXXXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=/19dlJorSomeothing+like_this4tFdicOkBJ1I
export AWS_SESSION_TOKEN=IQoJb3JpZ2luX2VjEMv//////////wEaDGV1LWNlbnRyYWwtMSJIMEYCIQDUiP18GiJROFi19/TSRJBVG1ZYmpBCNEcZOmEUPEyXEQIhAPtHSOWW27fpk5fGyttOoHx1KKshS4LjbAEKb62hWWK1Ku8BCEQQARoMNjQwNzUzMjQ0NDk4IgzYyM9Lhvtb097v57oqzAHKmj784kousxlPGZIHt6Rkn5fN+FYMhQUquk+g7dauTngCIskvOxvgUjTUSIit6Fg8r2EcGMIKD+vdKNwdkchZkfvgxypioVmb1t70NTMrqgPoMjexBqiGVq9SxUKofxnhBKe5lphfseqpXUKk/QEXHqUVqrZ2P9XCeMyf/clT+Q4npL1NhMLsXXeS5fvoYMlCfZcXkTvfau3WEwlDVGEZQQiUP+yF9j4wdhtZX3saGMVUrqwUO6mEJphxnKavVoNAxljmtrM/cj2bBcYw+KC3kQY6lwEUWU3FDrG+R0IIjrV4lNP9E9Fx6JODMGktCoIgpfTewoHhli0IBEhrHU7U7900ACNAtMIsjT2ezFK2kNmhyH2p+OUORC56eeLVEv53+QHgjLNdbpUITHobFTrwjjmH+LAxzTdZh89BrNRyWsiOaFLRsJuCqpKliAJyuHldrRoA1DXRAojB37ihrZTz1h0ShML8sYE1OjaX
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And that is it! Now if you run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws ec2 describe-instances&lt;/code&gt; you should get a pretty answer that shows you all your ec2 machines and their properties.&lt;/p&gt;

&lt;h2 id=&quot;the-credentials-file&quot;&gt;The credentials file&lt;/h2&gt;

&lt;p&gt;The other option is to tweak the credentials file. We can add a profile &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[MFA]&lt;/code&gt; there and list the data we got from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws sts get-session-token&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;[default]
aws_access_key_id=AKIAXXXXXXXXXXXXXXXX
aws_secret_access_key=someunreadablemumbojumboxxxxxxxxxxxxxxx

[MFA]
aws_access_key_id=ASIAXXXXXXXXXXXXXXXX
aws_ecret_access_key=/19dlJorSomeothing+like_this4tFdicOkBJ1I
aws_session_token=IQoJb3JpZ2luX2VjEMv//////////wEaDGV1LWNlbnRyYWwtMSJIMEYCIQDUiP18GiJROFi19/TSRJBVG1ZYmpBCNEcZOmEUPEyXEQIhAPtHSOWW27fpk5fGyttOoHx1KKshS4LjbAEKb62hWWK1Ku8BCEQQARoMNjQwNzUzMjQ0NDk4IgzYyM9Lhvtb097v57oqzAHKmj784kousxlPGZIHt6Rkn5fN+FYMhQUquk+g7dauTngCIskvOxvgUjTUSIit6Fg8r2EcGMIKD+vdKNwdkchZkfvgxypioVmb1t70NTMrqgPoMjexBqiGVq9SxUKofxnhBKe5lphfseqpXUKk/QEXHqUVqrZ2P9XCeMyf/clT+Q4npL1NhMLsXXeS5fvoYMlCfZcXkTvfau3WEwlDVGEZQQiUP+yF9j4wdhtZX3saGMVUrqwUO6mEJphxnKavVoNAxljmtrM/cj2bBcYw+KC3kQY6lwEUWU3FDrG+R0IIjrV4lNP9E9Fx6JODMGktCoIgpfTewoHhli0IBEhrHU7U7900ACNAtMIsjT2ezFK2kNmhyH2p+OUORC56eeLVEv53+QHgjLNdbpUITHobFTrwjjmH+LAxzTdZh89BrNRyWsiOaFLRsJuCqpKliAJyuHldrRoA1DXRAojB37ihrZTz1h0ShML8sYE1OjaX
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To use this profile we have to specify it in the commands we use. So now it becomes:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;aws ec2 describe-instances --profile MFA
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And again: works like a charm! We can use this with any aws cli command, and we will have to if we want to use this profile.&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;/h2&gt;
&lt;p&gt;Now then, what is the problem  with this ? Well… Do you know anyone who wants to do this all the time ? Each time you open up a new terminal get the credentials and set all the environment variables?
Or time and time again editing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.aws/credentials&lt;/code&gt; file copy pasting stuff ? Well I don’t. So the results will be:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;People start complaining. And with people I mean us too!&lt;/li&gt;
  &lt;li&gt;People will try to minimize the effort by putting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;duration&lt;/code&gt; as long as possible, effectively reducing security.&lt;/li&gt;
  &lt;li&gt;If the above happens enough and pressure gets high enough you may be forced to undo this whole exercise and return to a situation with no enforced MFA. A very &lt;a href=&quot;https://dev.to/matrixersp/how-to-use-fzf-with-ripgrep-to-selectively-ignore-vcs-files-4e27&quot;&gt;dangerous situation&lt;/a&gt; indeed!&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;the-solution&quot;&gt;The solution&lt;/h2&gt;
&lt;p&gt;Of course the solution is what it always is for these type of manual nuisances: automate them into oblivion!&lt;/p&gt;

&lt;p&gt;So without further ado let get our hands dirty and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; away (Sorry for that, could not resist!).
Find a good location to store bash scripts, and create a new file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws-set-auth.sh&lt;/code&gt;. In it we write:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;#/bin/bash

TOKEN=${1}
if [[ -z $TOKEN ]]
then
  echo &quot;please provide a session token&quot;
  exit 1
fi
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_SESSION_TOKEN
CREDS=$(aws sts get-session-token --serial-number arn:aws:iam::640753244498:mfa/Chai_inQuisitive --token-code $TOKEN --duration-seconds 14400 --output json | jq .Credentials )
if [[ -z $CREDS ]]
then
  echo &quot;Invalid token. Access Denied&quot;
  exit 1
fi
AWS_ACCESS_KEY_ID=$(echo $CREDS | jq -r .AccessKeyId)
AWS_SECRET_ACCESS_KEY=$(echo $CREDS | jq -r .SecretAccessKey)
AWS_SESSION_TOKEN=$(echo $CREDS | jq -r .SessionToken)
echo &quot;export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID&quot; &amp;gt;&amp;gt; tmp.env
echo &quot;export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY&quot; &amp;gt;&amp;gt; tmp.env
echo &quot;export AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN&quot; &amp;gt;&amp;gt; tmp.env
echo &quot;creds are set in ENVIRONMENT VARS&quot;

CRED_FILE_NAME=~/.aws/credentials
OUTPUT_FILE=()
readarray -t CRED_FILE &amp;lt; &quot;${CRED_FILE_NAME}&quot;
for LINE in &quot;${CRED_FILE[@]}&quot;
do
  OUTPUT_FILE+=( &quot;${LINE}&quot; )
  if [[ &quot;${#OUTPUT_FILE[@]}&quot; &amp;gt;1 &amp;amp;&amp;amp; &quot;${OUTPUT_FILE[-2]}&quot; = &quot;[MFA]&quot; ]]
  then
    unset OUTPUT_FILE[-1]
  fi
  if [[ &quot;${OUTPUT_FILE[-1]}&quot; = &quot;[MFA]&quot; &amp;amp;&amp;amp; -z $LINE ]]
  then
    OUTPUT_FILE+=( &quot;aws_access_key_id=${AWS_ACCESS_KEY_ID}&quot; )
    OUTPUT_FILE+=( &quot;aws_secret_access_key=${AWS_SECRET_ACCESS_KEY}&quot; )
    OUTPUT_FILE+=( &quot;aws_session_token=${AWS_SESSION_TOKEN}&quot; )
    OUTPUT_FILE+=( &quot;${LINE}&quot; )
  fi
done

printf &quot;%s\n&quot; &quot;${OUTPUT_FILE[@]}&quot; &amp;gt; &quot;${CRED_FILE_NAME}&quot;
echo &quot;profile [MFA] updated ~./aws/credentials as profile&quot;

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

&lt;p&gt;Okay, so let’s have a look at what we have here. The first line just tells us we want this file to be interpreted as a bash file.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TOKEN=${1}&lt;/code&gt; means store the first command line argument (after the script name!) in a variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TOKEN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then we check if that argument was given and if not we tell the user to provide it and we exit.
If all is good we proceed to the next part where we &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unset&lt;/code&gt; the environment variables.&lt;/p&gt;

&lt;p&gt;The next line is what we have seen before: Getting the credentials. In this case we &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pipe&lt;/code&gt; them into &lt;a href=&quot;https://stedolan.github.io/jq/&quot;&gt;jq&lt;/a&gt; to parse them and store the result in a variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CREDS&lt;/code&gt;. This means that you need to have &lt;a href=&quot;https://stedolan.github.io/jq/&quot;&gt;jq&lt;/a&gt; installed on your machine. If you don’t it will not work. But you probably should anyway because it is one of the most useful and crucial tools we have on the command line.&lt;/p&gt;

&lt;p&gt;Then we do another check to see if all went well, if not, we tell the user and exit. Just like before.&lt;/p&gt;

&lt;p&gt;After this we will use the credentials in the ways we read about before: exporting them as environment variables and editing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.aws/credentials&lt;/code&gt; file.
There is a bit of weird magic here. We first simply export the environment variables as we did before. Nothing strange about that, we know how that works. But then we also write these &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;export&lt;/code&gt; instructions themselves to a temporary file. We will use these later! Just read on for now!&lt;/p&gt;

&lt;p&gt;The last bit is probably the most difficult to read if you are not very familiar with bash scripting. In general what it does is this:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Create an empty output file&lt;/li&gt;
  &lt;li&gt;Read the credentials file&lt;/li&gt;
  &lt;li&gt;Go over it line by line and
    &lt;ul&gt;
      &lt;li&gt;Add the line to the output file&lt;/li&gt;
      &lt;li&gt;Check if the previous line was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[MFA]&lt;/code&gt;. So we identify the part that we need to change.&lt;/li&gt;
      &lt;li&gt;If so add the credentials.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Overwrite the credentials file with the output file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note: If you carefully followed this part you see why this script can handle any number of profiles in your credentials file but also why the [MFA] profile MUST be the last one there! There are probably ways to improve. There always are. But to balance complexity with usability brought me to this point if you want to improve on it: go right ahead!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Also please note that I have set the duration to 14400 seconds which is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;60 sec * 60 minutes * 4 hours&lt;/code&gt;. For me 4 hours sounded reasonable. If you need it to be stricter, just turn it down to 1 hour or whatever meets your needs.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;using-the-script&quot;&gt;Using the script&lt;/h2&gt;
&lt;p&gt;So we are now ready to use the script. We run:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;bash path-to-script-directory/aws-set-auth.sh &amp;lt;token&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Where we replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;token&amp;gt;&lt;/code&gt; with the token we get from the authenticator app. It will print out:&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;creds are set in ENVIRONMENT VARS
profile [MFA] updated ~./aws/credentials as profile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So nice! Right? But can we make it even easier? Sure! Because first of all this command is slightly longer then a lazy person likes, and more importantly if we have to look for that script and the path every time… well… Eeeuw!&lt;/p&gt;
&lt;div style=&quot;width:100%;height:0;padding-bottom:56%;position:relative;pointer-events:none;&quot;&gt;&lt;iframe src=&quot;https://giphy.com/embed/hshZwZemt0r28&quot; width=&quot;100%&quot; height=&quot;100%&quot; style=&quot;position:absolute&quot; frameborder=&quot;0&quot; class=&quot;giphy-embed&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;

&lt;h2 id=&quot;wrap-it-all-in-one-simple-command&quot;&gt;Wrap it all in one simple command&lt;/h2&gt;
&lt;p&gt;Bash, zsh or fish whatever our shell of choice is, will allow us to call this script. This enables us to use a single, easy command wherever we are on the command line (provided that in that location we have access to the aws cli and jq of course, but most people would have those installed globally).&lt;/p&gt;

&lt;p&gt;The normal and quick way to do such things is: Create an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alias&lt;/code&gt; instruction in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bashrc&lt;/code&gt; (or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.zshrc&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.fishrc&lt;/code&gt;) file. Something like:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alias authaws=&quot;bash ~/script-directory/aws-set-auth.sh&quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Doing this will give you no problems or errors. The only downside is that the script actually does not really work. Sad! The reason is somewhat technical in that bash will start a new session, execute the script there, set all the environment variables, and then finish the script and kill the session. Then returning to where you were it will happily say all was well. But you are left without proper environment variables set of course!&lt;/p&gt;

&lt;p&gt;To get around this we adapt &lt;a href=&quot;https://askubuntu.com/a/53179&quot;&gt;this solution&lt;/a&gt;:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;In our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws-set-auth.sh&lt;/code&gt; script we write export instruction to a temporary file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmp.env&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bashrc&lt;/code&gt; we write a function that calls the script, we use the command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt; to execute the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmp.env&lt;/code&gt; file and then remove that file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bashrc&lt;/code&gt; this looks like:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;alias authaws='_authaws(){bash ~/.config/nixpkgs/scripts/aws-set-auth.sh &quot;$1&quot; &amp;amp;&amp;amp; . ./tmp.env &amp;amp;&amp;amp; rm ./tmp.env;}; _authaws'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This looks a bit like tricky magic but it isn’t. Inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alias&lt;/code&gt; &lt;em&gt;authaws&lt;/em&gt; we define a function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_authaws()&lt;/code&gt; which executes our script and uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt; instruction to source the file (see the &lt;a href=&quot;https://askubuntu.com/a/53179&quot;&gt;mentioned answer on stack exchange&lt;/a&gt;). Then in the end it actually calls that function.&lt;/p&gt;

&lt;p&gt;So now if you followed along you can just run:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;authaws &amp;lt;token&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And we can do this in any directory, wherever we are. If it runs out, after the specified time, just rerun it and &lt;em&gt;boom&lt;/em&gt;, that is it! Easy as eating chocolate and safe as milk!&lt;/p&gt;

&lt;h2 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;There is always a conflict of interests when it comes to easy of use or comfort on the one hand and security and or privacy on the other. This can lead to very ugly situations.&lt;/p&gt;

&lt;p&gt;Some of you may recall a famous incident with a company named &lt;a href=&quot;https://en.wikipedia.org/wiki/DigiNotar&quot;&gt;digiNotar&lt;/a&gt; that was doing great and went bankrupt in a month. This was NOT because they had not thought about security, or because they had no security measures in place. In fact they had very tight security in place. But it was just… so annoying! So they decided to make life easier with an extra cable and some settings…&lt;/p&gt;

&lt;p&gt;This was not all that was wrong but the bottom line of the story is: Security measures are only useful when people follow them, and the chances of people following them are increased greatly if it is made &lt;em&gt;easy&lt;/em&gt; for them. Therefore automation in some cases is not just to speed up things, make them easier or less error-prone, in some cases it actually &lt;em&gt;increases security&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you have any suggestions, comments or improvements reach out! Have a nice week!&lt;/p&gt;</content><author><name>{&quot;avatar&quot;=&gt;&quot;/assets/images/bio-photo.jpg&quot;, &quot;bio&quot;=&gt;&quot;Curiosity killed the cat but at least she learned something!&quot;, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Buy me a coffee&quot;, &quot;icon&quot;=&gt;&quot;fas fa-coffee&quot;, &quot;url&quot;=&gt;&quot;https://www.buymeacoffee.com/ProofOfPizza?ref=footer&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/ProofOfPizza/&quot;}, {&quot;label&quot;=&gt;&quot;GitLab&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-gitlab&quot;, &quot;url&quot;=&gt;&quot;https://gitlab.com/ProofOfPizza&quot;}]}</name></author><category term="tech" /><category term="AutomaticAlley" /><category term="automatic-alley" /><category term="linux" /><category term="automation" /><category term="bash" /><category term="scripting" /><category term="devops" /><category term="security" /><category term="IAM" /><category term="AWS" /><category term="passwords" /><summary type="html">And here is the third in the series “AutomaticAlley” where I share some tips, tricks, bits and bites to make day to day life easier. Especially for us nerds on the command line. In this episode we’ll have a look at enforcing AWS IAM users to setup 2FA and then how to make that bearable by scripting the login. This writing is sort an extension of my earlier blog post.</summary></entry><entry><title type="html">How to learn new skills, without magic promises</title><link href="https://calzone.proofofpizza.com/tech/opinion/how-to-learn-new-skills-wihtout-magic-promises/" rel="alternate" type="text/html" title="How to learn new skills, without magic promises" /><published>2022-03-07T00:00:00+00:00</published><updated>2022-03-07T00:00:00+00:00</updated><id>https://calzone.proofofpizza.com/tech/opinion/how-to-learn-new-skills-wihtout-magic-promises</id><content type="html" xml:base="https://calzone.proofofpizza.com/tech/opinion/how-to-learn-new-skills-wihtout-magic-promises/">&lt;p&gt;“Professor”, my colleague  asks me jokingly, “Why don’t you write a blog about how I can learn Javascript.” That got me thinking, since I am eternally learning new things, always studying something: Music theory, playing an instrument, Spanish, Turkish, Math, Programming, and the list goes on. Have I learned anything about the process of learning? Did I distill any rules that I now apply either consciously or unconsciously?&lt;/p&gt;

&lt;p&gt;The question of how to learn is asked by many, but only few of us actually listen to the answers, and even fewer of us put them into practice. That is because even if you do all the right things, the right ways (assuming we could clearly define those) it still costs us. It takes effort, time, energy, focus. And for us working people with a life that means it takes up space in a life that is already full and something will need to give. You can learn effectively, sure, but here is the first consideration: There are no shortcuts.&lt;/p&gt;

&lt;h2 id=&quot;1-there-are-no-shortcuts&quot;&gt;1. There are no shortcuts&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;div style=&quot;width:100%;height:0;padding-bottom:75%;position:relative;pointer-events:none;&quot;&gt;&lt;iframe src=&quot;https://giphy.com/embed/TZjY28zYHoize&quot; width=&quot;100%&quot; height=&quot;100%&quot; style=&quot;position:absolute&quot; frameborder=&quot;0&quot; class=&quot;giphy-embed&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
  &lt;p&gt;&lt;cite&gt;&lt;a href=&quot;https://youtu.be/2kArCRjT29w&quot;&gt;Or maybe…&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While there are things to avoid, and things to strive for it is good to be as realistic as possible with your expectations. You want to learn javascript in three weeks? Well of course you can spend three weeks learning javascript, but chances of you mastering it are very slim, and even that might still be an exaggeration. You will have to put in the effort, and understanding this before you start gives you a much better chance at achieving your goals.&lt;/p&gt;

&lt;h2 id=&quot;2-set-yourself-a-goal&quot;&gt;2. Set yourself a goal&lt;/h2&gt;
&lt;p&gt;This is an obvious one, that you probably have seen everywhere. Goal setting. Now before you do this, let’s think a bit about why that would be useful and what kind of goals are helpful. In fact is it true that any goal is better than none? Any goal is a good goal ? I beg to differ. Goals are points at the horizon. You define them, so you have something to look at, a point on which to focus so that your energy and efforts are getting aligned with achieving that goal. And that means there are do’s and don’ts!&lt;/p&gt;

&lt;p&gt;A goal that is too big, or in terms of our image of the “horizon”: too far.. is a goal that will not help you. It is a dream to keep dreaming about, but if you put it that far, it is past the horizon, you can not see it so it will not help you focus and thinking about the work necessary to achieve it will only paralyze and / or demotivate you.&lt;/p&gt;

&lt;p&gt;A goal that is too small, or “too close”, will not be interesting enough, ambitious enough, to inspire you to do the work… and therefore the unintuitive result is that goals that like this are also hard to achieve!&lt;/p&gt;

&lt;p&gt;A good goal should be in the middle, ambitious enough to scare you a bit, to make you feel excited and a bit anxious. At the same time it should be attainable, you should be able to imagine the necessary work and effort it will cost you. You should see it at the horizon, so you can direct yourself towards it.&lt;/p&gt;

&lt;p&gt;And since setting goals, as well as achieving is a habit that is trained, the goals naturally become more audacious as you continue. There is no fun in staying safe folk
!&lt;/p&gt;

&lt;h2 id=&quot;3-get-support&quot;&gt;3. Get support&lt;/h2&gt;
&lt;p&gt;As said in the beginning, it takes time, effort, and focus. So unless your life is empty and boring now, that means some other parts of your life will have to give that space. Trying to learn something new, without changing anything in your life is doomed to fail. Talk to your partner, talk to your colleagues, talk to your employer, talk to your friends. If people support you, and help you that is a great advantage, a super power you have got there. If they don’t then it is up to you to decide: either you still go for it, and others, while not supportive will at least know what you’re working for and why that changes the way you spend your time and energy. Or decide to just not do it.&lt;/p&gt;

&lt;p&gt;I’ll say it again, trying to learn something without creating this space, without a way to even put in the necessary work is a really good way to cheat yourself and then be disappointed.&lt;/p&gt;

&lt;h2 id=&quot;4-get-in-the-game&quot;&gt;4. Get in the game&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;Live as if you were to die tomorrow. Learn as if you were to live forever.
&lt;cite&gt;Mahatma Gandhi&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You still did not get to do all before mentioned points, so you’re waiting for the right moment? Waiting until work gets calmer? Waiting until new year ? Be careful: you will do what you practice, always. So be afraid to practice procrastination! Just stop pretending you will get to it. And when you are serious about it, when you feel enthusiastic about learning something new: do it. Sort out a way to get started and get in the game. Tweak  the other points as you go. Preparation is great, but it does not have to be perfect!&lt;/p&gt;

&lt;h2 id=&quot;5-immerse-yourself&quot;&gt;5. Immerse yourself&lt;/h2&gt;
&lt;p&gt;Learning something by doing it an hour a time, two times a week is very, very hard. You will find that you are wasting a lot of time and energy bringing back up what you already learned, what you were doing. If you want to learn effectively it is much better to take a period in which you spend as much time on it as possible. Don’t try to learn five things at the same time. Choose one, and immerse yourself as much as possible. This is why #3 is important!.&lt;/p&gt;

&lt;p&gt;Immersion will also speed up learning, and that will do all kinds of wonderful things: You notice you’re improving which fuels enthusiasm. It can bring you to a point where the subject really becomes central in your thoughts. And while that may make you somewhat less attentive to other things, it fortifies your learning massively. Learning something is all about associations. The more associations you create in your brain, the stronger. As a simple example: Say you’re learning coding, and you already know how to play some guitar. At some point, if you may get to the point that you go to rehearsal with your friends and suddenly it occurs to you that the guitar solo is just:&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;const base_sequence = &quot;Am Am G&quot;
for (let i = 1, i&amp;lt;=8, i++) {
  if i%2 == 1 { // i the uneven rounds
    const play(base_sequence + &quot;E7&quot;)
  else {
    const play(base_sequence + &quot;Am&quot;)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or what about the days:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;public abstract class Day {
  boolean breakfast = true;

  boolean kidsGoToSchool;

  String todaysCook = &quot;Mom&quot;;

  public void brushYourTeeth() {
    System.out.println(&quot;Kids come brush your teeeeeeeth!&quot;);
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;public class Monday extends Day {
  boolean kidsGoToSchool = true;
  String todaysCook = &quot;Dad&quot;;

  public static void main(String... args) {
    Day today = new Monday();
    System.out.println(today.breakfast);
    System.out.println(today.todaysCook == &quot;Dad&quot;); // is it ?
    System.out.println(today.kidsGoToSchool);
    today.brushYourTeeth();
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;These type of associations and abstractions will come as a result of immersing yourself. It is then that your learning continues throughout the day, also when doing completely different things. It is a very powerful tool to accelerate and solidify your learning.&lt;/p&gt;

&lt;p&gt;We all marvel at the way kids seem to learn things effortlessly and quick. Well, did you ever try to observe how many hours of their waking day are spend going back and forth through the alphabet? How many times the repeat their multiplication tables? Riding a bike? Right: Now imagine sitting down working through your lessons in Java for as long as you see your kids doing any of these. Would that make you learn faster? You betcha! (But yes, please observe #3 or I will get angry comments from abandoned loved ones!)&lt;/p&gt;

&lt;h2 id=&quot;6-fake-it-till-you-make-it&quot;&gt;6. Fake it ‘till you make it&lt;/h2&gt;
&lt;p&gt;We generally know this is important, recommend it to others, but fail to apply it to ourselves. Did you ever meet anyone from another country that is now learning your language? “Just go out and practice. Talk to people, that is a great way to learn”. This is often the first advice given.&lt;/p&gt;

&lt;p&gt;So what about when you learn something new, Docker for example, then what ? Here is one of the most scary and at the same time most effective suggestions I have for you: Get involved in &lt;a href=&quot;https://stackoverflow.com/&quot;&gt;Stack Overflow&lt;/a&gt;. And I mean &lt;em&gt;involved&lt;/em&gt;. Create an account. (You would be surprised how many of us techies use Stack Overflow all day every day, but never bothered to make an account!). Before anything else: take 5 minutes to familiarize yourself with the &lt;a href=&quot;https://stackoverflow.com/conduct&quot;&gt;community guidelines&lt;/a&gt;. You’ll be disappointed if you don’t! Then there are three things to do:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Search for questions on the subject you are learning (use the filters for that). You can filter on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;highest score&lt;/code&gt; and you will without exception find questions and answers that will teach you something new, dive just that bit deeper into the subject you are busy with!&lt;/li&gt;
  &lt;li&gt;Ask questions: Asking good questions is a valuable skill, so train it! Also in accordance with Stack Overflow’s &lt;a href=&quot;https://stackoverflow.com/help/how-to-ask&quot;&gt;“How to ask a good question”&lt;/a&gt; setting up a minimal reproducible example solves half of your problems without asking, and for the other half you will learn to view your issue from another side, and when you get answers they will not only fix the problem you had, but teach you something more general about the topic as well.&lt;/li&gt;
  &lt;li&gt;This is the most important: Filter on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;newest&lt;/code&gt; and find questions that you know the answer to. Or think you know. Or think you should know. And then go ahead and &lt;em&gt;answer them&lt;/em&gt;. This again costs you some time and effort but I assure you it is worth it: You will research just a bit more, and force yourself to organise your thoughts, reaffirming what you’re learning again. And also: you will see that quite quickly you are able to actually help others and you find you know more than you thought! If you’re like me, then you’ll find it very motivating to continue.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On that same note: I recently upped this game for myself. Browse around on this blog and you will see a bunch of topics I wrote about even though most likely there are others who are more knowledgeable. Why? Because for one, I do not want to forget what I just spend days figuring out, and two after writing it all down I invariably learn more about it: To get all the steps and lines in place I want to make sure it is correct.. &lt;em&gt;and why&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;On many occasions we are satisfied with knowing &lt;em&gt;something works&lt;/em&gt;. “Fake it ‘till you make it” makes sure we also dig into &lt;em&gt;why it works&lt;/em&gt;. An added bonus is that I get to learn because of &lt;a href=&quot;https://www.reddit.com/r/linux/comments/t3dxio/blog_terminal_file_managers_and_my_vifm_setup/?utm_source=share&amp;amp;utm_medium=web2x&amp;amp;context=3&quot;&gt;other people’s tips and suggestions&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Pro tip: There are many ways to put this principle into practice: Take up relevant tasks at work, create a project for yourself or with friends/colleagues, give a presentation … .. .&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;7-have-fun&quot;&gt;7. Have fun&lt;/h2&gt;
&lt;p&gt;Learning something only so that you can enjoy it, after you have mastered it makes no sense. I repeat: No sense, at all. Why would you enjoy it when you are good at it… when you do not even enjoy it when it is a shiny new adventure ? That does not happen. So allow yourself to have fun, and when it weighs on you: take a break. Don’t stop, just take a break and relax, or shift your attention on something on the side. You’re frustrated because the RxJs exercise just does not seem to work no matter what you do ? Relax, take a coffee read a it on Reddit, maybe someone writes something interesting about Reactive Programming in Java (Wow is that a thing?, interesting…!). This way, you keep immersed, you keep busy… and you can because it keeps being fun!&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you hoped I had some super trick to learn anything you want in a breeze you might be a bit demoralized now. If so: I do not apologize. I did not make it that way, I am just telling it the way I see it. If you’re serious about learning stuff then hopefully you found something here to inspire you to take the next step. It may not be easy, but at least it is simple; After all, if you want to get to that spot on the horizon, you can do it:&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;While not at goal:
  Take the step in front of me.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>{&quot;avatar&quot;=&gt;&quot;/assets/images/bio-photo.jpg&quot;, &quot;bio&quot;=&gt;&quot;Curiosity killed the cat but at least she learned something!&quot;, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Buy me a coffee&quot;, &quot;icon&quot;=&gt;&quot;fas fa-coffee&quot;, &quot;url&quot;=&gt;&quot;https://www.buymeacoffee.com/ProofOfPizza?ref=footer&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/ProofOfPizza/&quot;}, {&quot;label&quot;=&gt;&quot;GitLab&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-gitlab&quot;, &quot;url&quot;=&gt;&quot;https://gitlab.com/ProofOfPizza&quot;}]}</name></author><category term="tech" /><category term="opinion" /><category term="problem-solving" /><category term="programming" /><category term="creativity" /><category term="learning" /><category term="skills" /><category term="devops" /><summary type="html">“Professor”, my colleague asks me jokingly, “Why don’t you write a blog about how I can learn Javascript.” That got me thinking, since I am eternally learning new things, always studying something: Music theory, playing an instrument, Spanish, Turkish, Math, Programming, and the list goes on. Have I learned anything about the process of learning? Did I distill any rules that I now apply either consciously or unconsciously?</summary></entry><entry><title type="html">Terminal file managers and my vifm setup</title><link href="https://calzone.proofofpizza.com/tech/Terminal-file-managers-and-vifm-setup/" rel="alternate" type="text/html" title="Terminal file managers and my vifm setup" /><published>2022-02-27T00:00:00+00:00</published><updated>2022-02-27T00:00:00+00:00</updated><id>https://calzone.proofofpizza.com/tech/Terminal-file-managers-and-vifm-setup</id><content type="html" xml:base="https://calzone.proofofpizza.com/tech/Terminal-file-managers-and-vifm-setup/">&lt;p&gt;Another blog about some nice tools and tricks to make life easier. This time let’s look at terminal managers, why would we use them ? And I’ll dive into my setup of choice with Vifm. It was supposed to go in the AutomaticAlley series but it became a bit more elaborate so I’ll just put it out on it’s own.&lt;/p&gt;

&lt;p&gt;This is another very opionated blog, and I feel that that is what it should be when it comes to these type of tools. If you use your laptop for anything more than just plain browsing and reading emails, I think you should have a terminal file manager. That is, somehow there exist people who do things like programming, sys admin or devops stuff and they do not touch the command line / terminal. This is crazy to me, I would not know how, and even less why …
 There are many discussions on this topic such as &lt;a href=&quot;https://www.reddit.com/r/AskProgramming/comments/ov7yu4/do_you_use_git_from_command_line_or_from_gui/&quot;&gt;this one on reddit&lt;/a&gt;. Some comments include:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;In my experience, people who rely on Git plugins in their IDE will often never truly understand what the plugin is doing under the hood. It provides an abstraction for you so you don’t have the understand the details. That is, until you run into some situation where the plugin can’t do what you want, or it’s not obvious how to do it via the plugin. This will happen eventually.
&lt;cite&gt;/u/bears-repeating&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;This. The CLI changes far less often than GUI tools, is the same across any system and also works over ssh
&lt;cite&gt;/u/CharacterUse&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some people comment that they prefer the UI / plugins, mostly for comfort or out out laziness:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;UI because laziness. I know how to git on command line but for must use cases a UI is much more convenient.
&lt;cite&gt;/u/YMK1234&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And this sums up the basic distrust of UI’s that I absolutely feel. As said, it is opinionated:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;I use the CLI. It’s the way I learned git and the one that feels most comfortable to me. I am always wary of a UI doing something other than what I intended.
&lt;cite&gt;/u/MrSloppyPants&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And obviously these discussions are repeated for many other things we use as techs like docker, editing configs, checking logs etc. For all of these we know that once we need to be on remote servers through SSH for instance, we’re thrown back on the command line.&lt;/p&gt;

&lt;p&gt;Another very strong argument is that whatever you do on the cli can be automated. You’re writing scripts? Pipelines ? Just automate the commands you already know and love.&lt;/p&gt;

&lt;p&gt;So I say: Why not make the command line the default, and in order to do that, take time to learn and configure it so it does exactly what we want. Because it seems to me that those are the two points that stand in the way for most people.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;The time and energy to configure stuff&lt;/li&gt;
  &lt;li&gt;The time to learn things&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And yes it is a matter of taste, but I personally enjoy figuring out things and then making sure I can navigate and use my system quickly and with easy (please no mousing and clicking… eeeeuw!).&lt;/p&gt;

&lt;p&gt;One of the most important tools for that is the file manager. If you use a terminal file manager it works seamlessly with the terminal. Jump in, navigate, jump out. And most decent file managers do much much more, quickly create files, move or copy files, split into multiple panes to make that even easier, navigate using short cuts, opening files with predefined specific tools etc etc.&lt;/p&gt;

&lt;p&gt;Also, most of us use keyboard shortcuts, right ? Why not leverage that idea into everything we can find? Apparently it helps us be more productive in our favorite tools, why would it not do the same everywhere else ?&lt;/p&gt;

&lt;p&gt;I have so far used two of them: &lt;a href=&quot;https://github.com/ranger/ranger&quot;&gt;Ranger&lt;/a&gt; and &lt;a href=&quot;https://vifm.info/&quot;&gt;Vifm&lt;/a&gt;. Both of them use Vi key bindings, which as a Vim user I like. I used Ranger first, and it was great, but then I found Vifm which is even more Vim-like, and I tried it, and stayed.&lt;/p&gt;

&lt;h2 id=&quot;nuff-with-the-banter-show-us-the-setup&quot;&gt;‘Nuff with the banter, show us the setup!&lt;/h2&gt;
&lt;p&gt;Okay okay, I have tortured you enough with my opinions. If you were not interested you probably never made it here, if you were interested in Vifm, you probably did not need any convincing. So thanks for indulging me. Now let’s get to the nerdy stuff!&lt;/p&gt;

&lt;p&gt;If you’re spending your time on the command line, I strongly suggest looking into a terminal emulator you want like &lt;a href=&quot;http://software.schmorp.de/pkg/rxvt-unicode.html&quot;&gt;URXVT&lt;/a&gt;, &lt;a href=&quot;https://konsole.kde.org/&quot;&gt;Konsole&lt;/a&gt; or my current favorite &lt;a href=&quot;https://alacritty.org/&quot;&gt;Alacritty&lt;/a&gt;. Then choose a shell you like: good ol’ &lt;a href=&quot;https://www.gnu.org/software/bash/&quot;&gt;bash&lt;/a&gt;, my current love &lt;a href=&quot;https://ohmyz.sh/&quot;&gt;zsh&lt;/a&gt;, or the new &lt;a href=&quot;https://fishshell.com/&quot;&gt;fish&lt;/a&gt; on the block. And while you are at it: Setup a decent font. You’ll spend hours looking to find tiny mistakes in files etc, so make it easy on the eyes!&lt;/p&gt;

&lt;p&gt;Good, that out of the way let’s get to Vifm. Installing it is easy on mac or linux. On windows ? Who knows ? You probably need a few more things first, and ten it should not be too hard.&lt;/p&gt;

&lt;p&gt;Once you have it, just run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vifm&lt;/code&gt; and tadaa, it opens up. And it is probably quite ugly. So time to tweak your theme! Choose a nice one &lt;a href=&quot;https://vifm.info/colorschemes.shtml&quot;&gt;here&lt;/a&gt; and let’s get going. I currently use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zenburn_1&lt;/code&gt;. We locate the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.vifmrc&lt;/code&gt;. It is most likely in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/vifm/.vifmrc&lt;/code&gt;. As with most of these terminal things, all important settings are in the rc-file. It has a section about color schemes:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;&quot; Selected color scheme
colorscheme zenburn_1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Reopen Vifm and check it out! Beautiful! Alright up next let’s navigate a bit. Use whatever you want: Arrow keys, hjkl-vi keys, or even, and we’ll see this more: hit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:&lt;/code&gt; and just type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd some-directory&lt;/code&gt;. So easy! So now quit vifm, annnnd so sad! We are back at the home directory!&lt;/p&gt;

&lt;p&gt;That is useless right ? No worries we can fix it. Unfortunately I did not find any native way to do that (let me know if you do!) so my solution was to write a simple function and an alias in  my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.zshrc&lt;/code&gt;. So open up your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.zshrc&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bashrc&lt;/code&gt; , depending on your choice and add:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;alias r = &quot;vicd ./&quot;

vicd()
{
  local dst=&quot;$(command ~/.vifm/scripts/vifm -- choose-dir -&quot;$@&quot;)&quot;
  if [ -z &quot;$dst&quot; ]; then
    echo 'Directory picking cancelled/failed'
    return 1
  fi
  cd &quot;$dst&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This function stores the last directory you where at and on closing vifm directs you right there. The alias, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;r&lt;/code&gt;, well yeah, that is because I got used to it from my time using Ranger, and I did not want to change it, but you might use another alias obviously.
Save it and reload your shell, either by closing and reopening the terminal or by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zsh&lt;/code&gt;. I will just use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zsh&lt;/code&gt; from now on, if you use another shell, just replace that.&lt;/p&gt;

&lt;p&gt;Now let’s type in our alias &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;r&lt;/code&gt; and enter. Navigate to your prettiest directory and quit. And tadaa, you exit to the terminal exactly where you were. Great improvement right ?
Another way to achieve this is by using the command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:shell&lt;/code&gt;, which would make it easier to automate, but then you startup a lot of shells inside of each other and that gets messy!&lt;/p&gt;

&lt;p&gt;By the way, if you are lazy like me: check out the bottom of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vifmrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;&quot; Sample mappings
&quot; quit on q
nnoremap q :q&amp;lt;cr&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nice! Just a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;q&lt;/code&gt; and as you see, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nnoremap&lt;/code&gt; is just like vim, key mappings, so we can leverage that for some quick navigation as well! In my case for instance:&lt;/p&gt;

&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;nnoremap nx  :cd ~/.config/nixpkgs&amp;lt;cr&amp;gt;
nnoremap gd  :cd ~/Downloads&amp;lt;cr&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And by the way, to make life quick ‘n’ easy I have similar mappings in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.zshrc&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;alias gd = &quot;cd ~/Downloads &amp;amp;&amp;amp; r&quot;
alias gh = &quot;cd ~ &amp;amp;&amp;amp; r&quot;
alias nx = &quot;~/.config/nixpkgs&quot;
alias nxr = &quot;nx &amp;amp;&amp;amp; r&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This way navigating in Vifm, and on the command line itself, jumping in and out of Vifm happens fast and intuitively. You would have to make some aliases that make sense for you of course, keys you hit quickly and things that require no effort to remember.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Pro tip: remember the blog about &lt;a href=&quot;/tech/tutorial/using-sops-with-aws-and-terraform/&quot;&gt;sops&lt;/a&gt;? Well then&lt;/em&gt;&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nnoremap s   :!sops %f&amp;lt;cr&amp;gt;&lt;/code&gt; &lt;strong&gt;&lt;em&gt;should make your again a lot easier! But check if you do not have other mappings for&lt;/em&gt;&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s&lt;/code&gt; &lt;strong&gt;&lt;em&gt;to avoid collisions.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;copying-renaming-creating-files-and-other-such-fun-stuff&quot;&gt;Copying, renaming, creating files and other such fun stuff!&lt;/h2&gt;
&lt;p&gt;Now let’s have a look at some other basic things you might want to do.
To copy a file: navigate, hit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yy&lt;/code&gt; to copy to clipboard, navigate and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt; to paste. As expected.
To delete ? Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dd&lt;/code&gt; (and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt; somewhere else for moving it of course)
Rename? Hit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cc&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt; and see!
And for making selections to perform these things? &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;V&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In general on the &lt;a href=&quot;https://vifm.info/&quot;&gt;Vifm&lt;/a&gt; site under “What users are saying…” it is said best:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;“Thank you for this great Vifm tool whose slogan could be: “If you don’t know how to do it, don’t look at the docs, just think how you would do it in vi.””
&lt;cite&gt;Carlos Pita&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;multiple-panes&quot;&gt;Multiple panes&lt;/h2&gt;
&lt;p&gt;Something that I had to get used to for a bit, but resulted to be incredibly useful were the dual panes.
Just hit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctrl-w v&lt;/code&gt; to open the second pane, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crtl-w o&lt;/code&gt; to close it. (Just like…). Vifm will remember your last choice in this.
If you have two panes switch between them with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;space&lt;/code&gt;. Now imagine if I want to copy a few things from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Downloads&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.config/nixpkgs/scripts&lt;/code&gt;:
I hit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;r&lt;/code&gt; anywhere. Then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gd&lt;/code&gt;, some ups or downs to get to my file, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yy&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;space&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nx&lt;/code&gt;, navigate, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;space&lt;/code&gt; … rinse and repeat. Now to read this, is cumbersome. But to do this, once you have a feel for these shortcuts (and if you are a Vim user this costs you exactly zero effort) is really really quick and intuitive. No menus, no mouse, no clicking, chosing options from secondary menus nothing. And then you want to edit the file? Just hit enter and go.&lt;/p&gt;

&lt;p&gt;That is… if you set up the correct programs to open files…&lt;/p&gt;
&lt;h2 id=&quot;setting-up-default-programs-to-open-your-files&quot;&gt;Setting up default programs to open your files&lt;/h2&gt;
&lt;p&gt;Again we open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.vifmrc&lt;/code&gt;. Find the section on opening files, and adjust to your liking. I for instance have:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;filetype *.wav,*.mp3,*.flac,*.m4a,*.wma,*.ape,*.ac3,*.og[agx],*.spx,*.opus,*.aiff
        \ vlc %f

filextype *.pdf
        \ evince %c
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Everything that is not matched against any definitions will be opened with your default Vim. If you would like to use something else, like VI or even something else completely like codium you can edit this line in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.vifmrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;set vicmd=vi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;file-preview&quot;&gt;File Preview&lt;/h2&gt;
&lt;p&gt;Another nice thing to have in a file manager are file previews. This is also possible, and configurable to suit your wishes in Vifm. To start: Open Vifm, and make sure you have two panes. Then hitting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;w&lt;/code&gt; key will toggle the preview. Nice right ? I agree!
So anything that is previewable (if only that were a word!) in Vim should be immediately working now.&lt;/p&gt;

&lt;p&gt;But for some files it is a bit more involved to get the previews working nicely. And luckily others have done some work for us, and we need only extend it a bit.
For images we find &lt;a href=&quot;https://wiki.vifm.info/index.php?title=How_to_preview_images&quot;&gt;here in the docs&lt;/a&gt; a suggestion to use &lt;a href=&quot;https://github.com/seebye/ueberzug&quot;&gt;Ueberzug&lt;/a&gt;. The link to the &lt;a href=&quot;https://www.reddit.com/r/linux/comments/aviu08/ueberzug_v1810_released/ehfj0s4/&quot;&gt;reddit&lt;/a&gt; shows some scripts that the author of Ueberzug, &lt;em&gt;/u/seebye&lt;/em&gt; wrote. It also has a link to youtube to a video by &lt;a href=&quot;https://www.youtube.com/watch?v=qgxsduCO1pE&quot;&gt;DistroTube&lt;/a&gt; that give a little more explanation, and where in the comments of that video, we find a variation of those scripts to support videos as well. I then took that and extended it to be able to preview pdfs as well.
Here is how to do it:&lt;/p&gt;

&lt;p&gt;Create two files in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.vifm/scripts&lt;/code&gt; (by the way, finding things in Vifm is just like finding them in Vim right, so use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt; as you would!):&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;vifmimg&lt;/li&gt;
  &lt;li&gt;vifmrun&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both will contain bash scripts:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vifmimg&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;#!/usr/bin/env bash
readonly ID_PREVIEW=&quot;preview&quot;

if [ -e &quot;$FIFO_UEBERZUG&quot; ]; then
    if [[ &quot;$1&quot; == &quot;draw&quot; ]]; then
        declare -p -A cmd=([action]=add [identifier]=&quot;$ID_PREVIEW&quot;
                           [x]=&quot;$2&quot; [y]=&quot;$3&quot; [width]=&quot;$4&quot; [height]=&quot;$5&quot; \
                           [path]=&quot;${PWD}/$6&quot;) \
            &amp;gt; &quot;$FIFO_UEBERZUG&quot;
    elif [[ &quot;$1&quot; == &quot;videopreview&quot; ]]; then
        [[ ! -f &quot;/tmp/$6.png&quot; ]] &amp;amp;&amp;amp; ffmpegthumbnailer -i &quot;${PWD}/$6&quot; -o &quot;/tmp/$6.png&quot; -s 0 -q 10
        declare -p -A cmd=([action]=add [identifier]=&quot;$ID_PREVIEW&quot;
                           [x]=&quot;$2&quot; [y]=&quot;$3&quot; [width]=&quot;$4&quot; [height]=&quot;$5&quot; \
                           [path]=&quot;/tmp/$6.png&quot;) \
            &amp;gt; &quot;$FIFO_UEBERZUG&quot;
    elif [[ &quot;$1&quot; == &quot;pdfpreview&quot; ]]; then
        [[ ! -f &quot;/tmp/$6.jpg&quot; ]] &amp;amp;&amp;amp; pdftoppm -singlefile -jpeg  &quot;${PWD}/$6&quot; &quot;/tmp/$6&quot;
        declare -p -A cmd=([action]=add [identifier]=&quot;$ID_PREVIEW&quot;
                           [x]=&quot;$2&quot; [y]=&quot;$3&quot; [width]=&quot;$4&quot; [height]=&quot;$5&quot; \
                           [path]=&quot;/tmp/$6.jpg&quot;) \
            &amp;gt; &quot;$FIFO_UEBERZUG&quot;
    elif [[ &quot;$1&quot; == &quot;clear&quot; ]]; then
        declare -p -A cmd=([action]=remove [identifier]=&quot;$ID_PREVIEW&quot;) \
            &amp;gt; &quot;$FIFO_UEBERZUG&quot;
    fi
fi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This basically says, if you have Ueberzug ready and you call this script with an argument of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draw&lt;/code&gt; then draw the preview of a image file (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$6&lt;/code&gt;) with the dimensions given in the rest of the arguments (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$2&lt;/code&gt; … &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$5&lt;/code&gt;). If the argument is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdfpreview&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;videopreview&lt;/code&gt; then make an image of that, and draw that. If the argument is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clear&lt;/code&gt; then clear up the screen.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vifmrun&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;#!/usr/bin/env bash
export FIFO_UEBERZUG=&quot;/tmp/vifm-ueberzug-${PPID}&quot;

function cleanup {
    rm &quot;$FIFO_UEBERZUG&quot; 2&amp;gt;/dev/null
    pkill -P $$ 2&amp;gt;/dev/null
}

rm &quot;$FIFO_UEBERZUG&quot; 2&amp;gt;/dev/null
mkfifo &quot;$FIFO_UEBERZUG&quot;
trap cleanup EXIT
tail --follow &quot;$FIFO_UEBERZUG&quot; | ueberzug layer --silent --parser bash &amp;amp;

vifm --choose-dir - &quot;$@&quot;
cleanup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This file basically runs Vifm with Ueberzug prepared. I adapted it in this line: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vifm --choose-dir - &quot;$@&quot;&lt;/code&gt; to be able to work with our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vicd()&lt;/code&gt; function. So that means we need to make two more small changes. But before that:
Save these files and make them executable by running:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;chmod +x vifmrun
chmod +x vifmimg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now let’s edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.zshrc&lt;/code&gt; and make sure we have:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;vicd()
{
  local dst=&quot;$(command ~/.vifm/scripts/vifmrun &quot;$@&quot;)&quot;
  if [ -z &quot;$dst&quot; ]; then
    echo 'Directory picking cancelled/failed'
    return 1
  fi
  cd &quot;$dst&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This way we call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vimrun&lt;/code&gt; script with that same argument, whenever we call&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vicd()&lt;/code&gt; which again is whenever we call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;r&lt;/code&gt; or whatever your personal alias was.&lt;/p&gt;

&lt;p&gt;Finally we adapt the sections in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vifmrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;fileviewer *.pdf
           \ vifmimg pdfpreview %px %py %pw %ph %c
           \ %pc
           \ vifmimg clear

fileviewer *.bmp,*.jpg,*.jpeg,*.png,*.gif,*.xpm
           \ vifmimg draw %px %py %pw %ph %c
           \ %pc
           \ ~/.vifm/scripts/vifmimg clear

           fileviewer *.avi,*.mp4,*.wmv,*.dat,*.3gp,*.ogv,*.mkv,*.mpg,*.mpeg,*.vob,
        \*.fl[icv],*.m2v,*.mov,*.webm,*.ts,*.mts,*.m4v,*.r[am],*.qt,*.divx
        \ vifmimg videopreview %px %py %pw %ph %c
        \ %pc
        \ vifmimg clear
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fileviewer&lt;/code&gt; sections are for the previews, where the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filetype&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filextype&lt;/code&gt; are for actually opening the files.
Lets try it out! And boom there you go, previews of PDF, images and videos all working nicely!&lt;/p&gt;

&lt;h2 id=&quot;bonus-add-vifm-to-vim&quot;&gt;Bonus: add Vifm to Vim!&lt;/h2&gt;
&lt;p&gt;Sounds like inception right ? Well kind of, but then in an awesome way. I have tried things like &lt;a href=&quot;https://github.com/preservim/nerdtree&quot;&gt;nerd tree&lt;/a&gt; and never really got the hang of it, but now we can use Vifm, exactly as we configured it with all it’s magic directly in Vim.
For this we use a plugin called &lt;a href=&quot;https://github.com/voldikss/vim-floaterm&quot;&gt;Floaterm&lt;/a&gt;. It spins up a floating terminal window, and we can tell it what we want to do with it. And what do we want? V.I.F.M ! Vifm! (Ah imagine the dance that goes with that!). In our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vimrc&lt;/code&gt; (mind you not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vifmrc&lt;/code&gt; this time of course!). We add configuration for it:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;&quot;Floaterm
let g:floaterm_opener = 'edit'
vnoremap &amp;lt;leader&amp;gt;m :FloatermNew --autoclose=2 vifm&amp;lt;CR&amp;gt;
nnoremap &amp;lt;leader&amp;gt;m :FloatermNew --autoclose=2 vifm&amp;lt;CR&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;I use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;space+m&lt;/code&gt; to open the vifm window and set it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;edit&lt;/code&gt; meaning if I choose to open a file it just opens it in a new buffer in Vim with out splitting into two panes which is the default. Obviously you might have to add something to load the plugin and maybe use a different configuration, but all of that can be found on &lt;a href=&quot;https://github.com/voldikss/vim-floaterm&quot;&gt;Floatterm’s&lt;/a&gt; page.&lt;/p&gt;

&lt;p&gt;Now if we put all of that together we can get something like this. Pretty sweet right ? Let me know what you think, and if you have suggestions I am always ready to ear them!&lt;/p&gt;

&lt;figure&gt;
  
&lt;img src=&quot;/assets/images/vifm.gif&quot; alt=&quot;Gif demo of vifm setup&quot; /&gt;

  &lt;figcaption&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;</content><author><name>{&quot;avatar&quot;=&gt;&quot;/assets/images/bio-photo.jpg&quot;, &quot;bio&quot;=&gt;&quot;Curiosity killed the cat but at least she learned something!&quot;, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Buy me a coffee&quot;, &quot;icon&quot;=&gt;&quot;fas fa-coffee&quot;, &quot;url&quot;=&gt;&quot;https://www.buymeacoffee.com/ProofOfPizza?ref=footer&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/ProofOfPizza/&quot;}, {&quot;label&quot;=&gt;&quot;GitLab&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-gitlab&quot;, &quot;url&quot;=&gt;&quot;https://gitlab.com/ProofOfPizza&quot;}]}</name></author><category term="tech" /><category term="linux" /><category term="automation" /><category term="vifm" /><category term="vim" /><category term="file-managers" /><category term="dotfiles" /><category term="bash" /><summary type="html">Another blog about some nice tools and tricks to make life easier. This time let’s look at terminal managers, why would we use them ? And I’ll dive into my setup of choice with Vifm. It was supposed to go in the AutomaticAlley series but it became a bit more elaborate so I’ll just put it out on it’s own.</summary></entry><entry><title type="html">#AutomaticAlley #2 - FZF and Ripgrep: fuzzy find files infuriatingly fast!</title><link href="https://calzone.proofofpizza.com/tech/automaticalley/AutomaticAlley-2-fzf-fuzzy-finder-and-ripgrep/" rel="alternate" type="text/html" title="#AutomaticAlley #2 - FZF and Ripgrep: fuzzy find files infuriatingly fast!" /><published>2022-02-21T00:00:00+00:00</published><updated>2022-02-21T00:00:00+00:00</updated><id>https://calzone.proofofpizza.com/tech/automaticalley/AutomaticAlley-2-fzf-fuzzy-finder-and-ripgrep</id><content type="html" xml:base="https://calzone.proofofpizza.com/tech/automaticalley/AutomaticAlley-2-fzf-fuzzy-finder-and-ripgrep/">&lt;p&gt;E voila: the second in the series, doubtfully dubbed #AutomaticAlley. It’s where I share small bits and bites of scripts and other tiny tricks to automate away boring and error prone stuff. The things we keep doing all the time, the things that annoy us that we need to to automate especially if we want to keep working on the command line a sane practice. Today let’s have a look at FZF and Ripgrep to fuzzily find files!&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“If you just remembered where you put it, you wouldn’t have to look for it!”
&lt;cite&gt;Every mom ever&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that would be nice, but it is simply not feasible, and we have accepted this a long time ago.
What can we do ? Well we’ll just find our file, and then open it! Let’s try this the good ol’ bash way:
To find something with a lot of matches: open the terminal in your home directory &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/&lt;/code&gt; and run:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;find . | grep bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or something with few or no matches:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;find . | grep bashIsGreatButNotTooFast
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It takes a while, and if you have bad luck you’re trying to find something in “one of those sytem directory thingies”  … was it /sys ? /run? /etc ? Yah one of those right? So you try:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;cd /
find . | grep bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now you really need to be patient!&lt;/p&gt;

&lt;h2 id=&quot;enter-fzf-the-fuzzy-finder-and-ripgrep&quot;&gt;Enter FZF the Fuzzy finder and Ripgrep!&lt;/h2&gt;
&lt;p&gt;Some of us are not that patient, so they did something about it and wrote &lt;a href=&quot;https://github.com/junegunn/fzf&quot;&gt;FZF&lt;/a&gt;. It is a command line fuzzy finder. That means it will find what you are looking for, even if you do not exactly know what you’re looking for!&lt;/p&gt;

&lt;p&gt;To install it there are a bunch of options, so choose whatever fits you best! If you use Vim I definitely recommend installing the Vim plugin as well. Because as they in the &lt;a href=&quot;https://github.com/junegunn/fzf.vim/&quot;&gt;README&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;h2 id=&quot;why-you-should-use-fzf-on-vim&quot;&gt;Why you should use fzf on Vim&lt;/h2&gt;
  &lt;p&gt;Because you can and you love fzf.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then let us also install &lt;a href=&quot;https://github.com/BurntSushi/ripgrep&quot;&gt;Ripgrep&lt;/a&gt;. This will enable us to search content inside files.
Again an easy process, just use whatever package manager you use for your system.&lt;/p&gt;

&lt;p&gt;Now Ripgrep and FZF work awesomely together so lets tweak some settings here.
There are a lot of options, and it has many things you might want. What I wanted was:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;A sensible default to search and find files&lt;/li&gt;
  &lt;li&gt;A way to find files by searching for content&lt;/li&gt;
  &lt;li&gt;The search results displayed in a nice way&lt;/li&gt;
  &lt;li&gt;A way to quickly find some file and open it in Vim&lt;/li&gt;
  &lt;li&gt;Ability to use FZF in Vim to find files&lt;/li&gt;
  &lt;li&gt;Ability to find stuff inside files in Vim&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Number 2 I almost do not use, because I generally have an idea of where to look, and I generally look for stuff when I am working on a project. For instance I will have a code file open in Vim and then think, “Wait, where did I set that variable?” And then go for option 6.&lt;/p&gt;

&lt;p&gt;For this I added to my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.zshrc&lt;/code&gt; (if you use bash or fish use the appropriate rc file):&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;export FZF_DEFAULT_OPTS=&quot;-m&quot;
FZF_DEFAULT_OPTS+=&quot; --color='light'&quot;
FZF_DEFAULT_OPTS+=&quot; --bind 'ctrl-/:toggle-preview'&quot;
FZF_DEFAULT_OPTS+=&quot; --preview 'head -500 {}' --height 80%&quot;
FZF_DEFAULT_OPTS+=&quot; --preview-window=up:40%:hidden&quot;
FZF_DEFAULT_OPTS+=&quot; --height=80%&quot;
FZF_DEFAULT_OPTS+=&quot; --layout=reverse&quot;
FZF_DEFAULT_OPTS+=&quot; --border&quot;
export FZF_DEFAULT_COMMAND='rg --files --hidden --follow --no-ignore-vcs'
export FZF_CTRL_T_COMMAND=&quot;$FZF_DEFAULT_COMMAND&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;So a bunch of default options for the preview screen, actually those are for the use in Vim.
And a sensible default. Use ripgrep, look for hidden files as well, follow symbolic links and do not ignore vcs files.&lt;/p&gt;

&lt;p&gt;I also add a few custom aliases:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;fzfi = &quot;rg --files --hidden --follow --no-ignore-vcs -g '!{node_modules,.git}' | fzf&quot;
o = &quot;x=$(fzfi); if [[ ! -z $x ]]; then vim $x; fi&quot;
nx = &quot;~/.config/nixpkgs&quot;
nxo = &quot;nx &amp;amp;&amp;amp; o&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;An alias &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fzfi&lt;/code&gt; to use the default FZF command except that it does not search in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node_modules&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.git&lt;/code&gt; directories. Generally I am not looking for files there, because they are generated or managed for me, so I do not not need to edit those. Then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;o&lt;/code&gt; to look for a file using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fzfi&lt;/code&gt;, and if I find it, open it with Vim. Finally an example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nxo&lt;/code&gt; (there are many more examples, for each directory I commonly use and want to go quicly to edit files) it refers to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nx&lt;/code&gt; which changes to my directory of nix configurations, and then runs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;o&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;configs-in-vim&quot;&gt;Configs in Vim&lt;/h2&gt;
&lt;p&gt;Now for the ones interested in using Vim, I added these configs in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.vimrc&lt;/code&gt;:&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;    &quot;search
    nnoremap &amp;lt;C-g&amp;gt; :GFiles?&amp;lt;CR&amp;gt;
    nnoremap &amp;lt;C-h&amp;gt; :History&amp;lt;CR&amp;gt;
    nnoremap &amp;lt;C-l&amp;gt; :Rg&amp;lt;CR&amp;gt;
    nnoremap &amp;lt;C-b&amp;gt; :BLines&amp;lt;CR&amp;gt;
    nnoremap &amp;lt;C-p&amp;gt; :All&amp;lt;CR&amp;gt;
    nnoremap &amp;lt;leader&amp;gt;b :Buffers&amp;lt;CR&amp;gt;
    &quot;set grepprg=rg\ --vimgrep\ --smart-case\ --hidden\ --follow
    let g:fzf_preview_window = ['up:50%:hidden', 'ctrl-/']
    command! -bang -nargs=*  All
      \ call fzf#run(fzf#wrap({'source': 'rg --files --hidden --no-ignore-vcs --glob &quot;!{node_modules/*,.git/*}&quot;', 'options': '--expect=ctrl-t,ctrl-x,ctrl-v --multi --reverse' }))

    &quot;============ copied from source: https://github.com/junegunn/fzf.vim/blob/master/plugin/fzf.vim =========
    &quot;============ can possibly be removed  after update ======================================================
    command! -bang -nargs=* Rg
      \ call fzf#vim#grep(&quot;rg --column --line-number --no-heading --color=always --smart-case -- &quot;.shellescape(&amp;lt;q-args&amp;gt;),
      \ 1, s:p(), &amp;lt;bang&amp;gt;0)

    command! -bang -nargs=* History
      \ call s:history(&amp;lt;q-args&amp;gt;, s:p(), &amp;lt;bang&amp;gt;0)'])

    command! -bar -bang -nargs=? -complete=buffer Buffers
      \ call fzf#vim#buffers(&amp;lt;q-args&amp;gt;, s:p({ &quot;placeholder&quot;: &quot;{1}&quot; }), &amp;lt;bang&amp;gt;0)

    function! s:p(...)
      let preview_args = get(g:, 'fzf_preview_window', ['right', 'ctrl-/'])
      if empty(preview_args)
        return { 'options': ['--preview-window', 'hidden'] }
      endif

      &quot; For backward-compatiblity
      if type(preview_args) == type(&quot;&quot;)
        let preview_args = [preview_args]
      endif
      return call('fzf#vim#with_preview', extend(copy(a:000), preview_args))
    endfunction

    function! s:history(arg, extra, bang)
      let bang = a:bang || a:arg[len(a:arg)-1] == '!'
      if a:arg[0] == ':'
        call fzf#vim#command_history(bang)
      elseif a:arg[0] == '/'
        call fzf#vim#search_history(bang)
      else
        call fzf#vim#history(a:extra, bang)
      endif
    endfunction

    &quot;=========================================================================================================
    &quot;=========================================================================================================
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see most of this is copied from the &lt;a href=&quot;https://github.com/junegunn/fzf.vim/blob/master/plugin/fzf.vim&quot;&gt;docs&lt;/a&gt;. I also found some useful ideas in &lt;a href=&quot;https://dev.to/matrixersp/how-to-use-fzf-with-ripgrep-to-selectively-ignore-vcs-files-4e27&quot;&gt;this blog&lt;/a&gt;.
Basically in here I chose some key shortcuts that made sense to me, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctrl-p&lt;/code&gt; to search files, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctrl-l&lt;/code&gt; to search contents (lines) etc. Also if looking for contents (content&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctrl-l&lt;/code&gt;) then we can toggle the preview with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctrl-/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;All in all with this you can now do all of those five things summed up above, and you get number three for free! Have a look and let me know what you think!&lt;/p&gt;

&lt;figure&gt;
  
&lt;img src=&quot;/assets/images/fzf.gif&quot; alt=&quot;Gif demo of FZF and Ripgrep&quot; /&gt;

  &lt;figcaption&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;</content><author><name>{&quot;avatar&quot;=&gt;&quot;/assets/images/bio-photo.jpg&quot;, &quot;bio&quot;=&gt;&quot;Curiosity killed the cat but at least she learned something!&quot;, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Buy me a coffee&quot;, &quot;icon&quot;=&gt;&quot;fas fa-coffee&quot;, &quot;url&quot;=&gt;&quot;https://www.buymeacoffee.com/ProofOfPizza?ref=footer&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/ProofOfPizza/&quot;}, {&quot;label&quot;=&gt;&quot;GitLab&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-gitlab&quot;, &quot;url&quot;=&gt;&quot;https://gitlab.com/ProofOfPizza&quot;}]}</name></author><category term="tech" /><category term="AutomaticAlley" /><category term="automatic-alley" /><category term="linux" /><category term="automation" /><category term="bash" /><category term="scripting" /><category term="fzf" /><category term="ripgrep" /><category term="fuzzy-finder" /><category term="vim" /><summary type="html">E voila: the second in the series, doubtfully dubbed #AutomaticAlley. It’s where I share small bits and bites of scripts and other tiny tricks to automate away boring and error prone stuff. The things we keep doing all the time, the things that annoy us that we need to to automate especially if we want to keep working on the command line a sane practice. Today let’s have a look at FZF and Ripgrep to fuzzily find files!</summary></entry><entry><title type="html">Angular - A minimalistic approach to runtime configurations in docker containers</title><link href="https://calzone.proofofpizza.com/tech/tutorial/Angular-environment-variables-at-runtime/" rel="alternate" type="text/html" title="Angular - A minimalistic approach to runtime configurations in docker containers" /><published>2022-02-13T00:00:00+00:00</published><updated>2022-02-13T00:00:00+00:00</updated><id>https://calzone.proofofpizza.com/tech/tutorial/Angular-environment-variables-at-runtime</id><content type="html" xml:base="https://calzone.proofofpizza.com/tech/tutorial/Angular-environment-variables-at-runtime/">&lt;p&gt;Build your Angular app, run it inside a container and move it across environments. How do we manage our runtime configurations?&lt;/p&gt;

&lt;p&gt;Build once, deploy everywhere! This idea relies on the ability to inject configuration settings at runtime. In backend applications there is a way of working for this, and most modern frameworks such as &lt;a href=&quot;https://spring.io/&quot;&gt;spring&lt;/a&gt; support these out of the box. They rely on reading environment variables. But what about the frontend, where code is executed in your mom’s browser at home and such environment variables are therefore not available? This was my weeks exploration, and is the topic of this blog.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Tip: If you like to look at the code while reading along you can &lt;a href=&quot;https://github.com/ProofOfPizza/example-angular-runtime-config&quot;&gt;clone the repo&lt;/a&gt;.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Angular has a nice &lt;a href=&quot;https://angular.io/guide/build&quot;&gt;system of environments&lt;/a&gt; and settings when it comes to build time. You specify the environments you want, and all their settings, and then you build using a flag to build for that environment:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;ng build --configuration production
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This allows you to create a build for local development, and a different build for the production environment. Things like application urls or database connection strings are almost certainly going to be different for these environments.
Now we can also run a different build for our test, acceptance and staging environments. Problem solved ?&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;ng build --configuration test
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Not quite!&lt;/p&gt;

&lt;h2 id=&quot;build-once-deploy-everywhere-and-runtime-configs&quot;&gt;Build once, deploy everywhere and runtime configs&lt;/h2&gt;
&lt;p&gt;There are many opinions, and strategies for building applications, making them available on different environments for various types of testing, or production use. Running a new build for each environment is definitely an option,
and I know people who are in favor of that. I disagree. In my opinion and experience it has a few important drawbacks:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It makes testing results less reliable. “Only config changes, it should work exactly the same”. The fact that it &lt;em&gt;should&lt;/em&gt; work, but actually is &lt;em&gt;not guaranteed&lt;/em&gt; is why testing is an important effort in software development. So while in a vast majority of cases this is true, once in awhile you encounter unexpected side effects of running a new build.&lt;/li&gt;
  &lt;li&gt;It makes the pipelines from local development all the way to production slower. Extra builds means extra time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There might be more reasons, such as the need to store more artifacts etc, but these would be my most important ones. As said, feel free to disagree with my in the comments or over coffee!
I like the idea of “Run once, build everywhere”, also known as &lt;a href=&quot;https://12factor.net/build-release-run&quot;&gt;build, release, run&lt;/a&gt; in the 12-factor app methodology.&lt;/p&gt;

&lt;p&gt;So what we need is a way to have different settings available at runtime. For the purpose of this blog I will assume we have an Angular app, that runs inside a docker container, at least on environments other than the local machine.&lt;/p&gt;

&lt;h2 id=&quot;available-solutions-in-angular&quot;&gt;Available solutions in Angular&lt;/h2&gt;
&lt;p&gt;So what are the options we have in angular? Well, surprisingly there is no simple standard way to do this. On the other hand, Angular is a very powerful framework so it is very possible to create solutions with the tools available.
A quick search on the internet will return explanations on how to do that. Most involve more or less the following:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Make the configurations available in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dist&lt;/code&gt; folder after building without minifying.&lt;/li&gt;
  &lt;li&gt;A javascript function (ususally one of those cute “self invoking” functions) that makes those variables available globally. For Angular, this means setting them on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window&lt;/code&gt; object.&lt;/li&gt;
  &lt;li&gt;An angular service to read the variables, and this service can then be injected wherever we need access to the variables&lt;/li&gt;
  &lt;li&gt;A way to provide the service and run it’s method to load the variables before/while bootstrapping Angular. Usually it involves &lt;a href=&quot;https://angular.io/api/core/APP_INITIALIZER&quot;&gt;APP_INITIALIZER&lt;/a&gt; and patterns like service factory and service provider.&lt;/li&gt;
  &lt;li&gt;…some have even more steps for nice things such as custom error handling etc…&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now this is definitely doable, and possibly an elegant solution. However, to me it seems like a lot of boiler plate code, and something as simple as reading some variables there should not be that hard!&lt;/p&gt;
&lt;div style=&quot;width:100%;height:0;padding-bottom:56%;position:relative;pointer-events:none;&quot;&gt;&lt;iframe src=&quot;https://giphy.com/embed/XGJqYmrDrmonHAIx0b&quot; width=&quot;100%&quot; height=&quot;100%&quot; style=&quot;position:absolute&quot; frameborder=&quot;0&quot; class=&quot;giphy-embed&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;

&lt;h2 id=&quot;a-minimalistic-approach&quot;&gt;A minimalistic approach&lt;/h2&gt;
&lt;p&gt;So in addition to all the previous variations on the solution, I will here offer a different, simple version.
I asked myself &lt;em&gt;“If I do step 1. and 2. and all variables are available globally, why do we need step 3-x?”&lt;/em&gt; Well, we do not. We might want to, but we don’t need to. So lets look at the first steps and make them as easy as we can.&lt;/p&gt;

&lt;p&gt;Let’s start by making some config files. We to make these to be available to read and manipulate after compilation. Because we need them to be copied &lt;em&gt;as-is&lt;/em&gt; and not compiled and or minified we write them as simple old javascript files. We create a folder &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/config&lt;/code&gt; and in it we create files with some sample configurations:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.js&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;__config = {
  apiUrl: 'http://localhost'
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.test.js&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;__config = {
  apiUrl: 'https://test.amazing-app.com'
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.prod.js&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;__config = {
  apiUrl: 'https://amazing-app.com'
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next we edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;angular.json&lt;/code&gt; in the project’s root folder. It has a section about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assets&lt;/code&gt; under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;projects architect build options&lt;/code&gt;, here we specify all files and folders that we want copied as-is to the build output folder (by default: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dist&lt;/code&gt;). So we make this part look like:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;    &quot;assets&quot;: [&quot;src/favicon.ico&quot;, &quot;src/assets&quot;, &quot;src/config&quot;],
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To check if all is well so far we run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm run build&lt;/code&gt; (or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ng build&lt;/code&gt;). We go into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dist&lt;/code&gt; folder and next to some minified js files we should find the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assets&lt;/code&gt; folder as well as our new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config&lt;/code&gt; folder with containing the files we just created.&lt;/p&gt;

&lt;p&gt;So far so good, time to make them available to us! For this we just use the oldest trick in the javascript book. We load a script in our HTML! So edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; in our main folder and load the script &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section. It is important to put it there right at the top. The browser will load whatever is in that html file from top to bottom, and we want it to load our settings &lt;em&gt;before&lt;/em&gt; doing anything with Angular because otherwise our app will not load or work correctly! So go ahead and edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; to look somewhat like this:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;script src=&quot;config/config.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;meta charset=&quot;utf-8&quot; /&amp;gt;
    &amp;lt;title&amp;gt;ExampleAngularRuntimeConfig&amp;lt;/title&amp;gt;
    &amp;lt;base href=&quot;/&quot; /&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot; /&amp;gt;
    &amp;lt;link rel=&quot;icon&quot; type=&quot;image/x-icon&quot; href=&quot;/favicon.ico&quot; /&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;app-root&amp;gt;&amp;lt;/app-root&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we use Angular with typescript we will have to let it know that the variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__config&lt;/code&gt; exists. We do this by specifying &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;declare let __config: any;&lt;/code&gt;
This really should be all that is necessary to load your configs at runtime! You can access your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apiUrl&lt;/code&gt; by using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__config.apiUrl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: I have seen people use variable name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__env&lt;/code&gt; and claim that it is a &lt;strong&gt;special&lt;/strong&gt; variable. It is not. It is just a variable with a funky name. Call it pancakesWithHotsause if you want and you’ll see it work as well. The usefulness of starting the variable with __ is mostly to avoid conflicts. It is unlikely, and should be unlikely, that we’ll create a variable in our normal code somewhere called __something, so in that way it helps.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I feel that it might be smart to be a bit more specific about our data structure. If you work together with more people, and or your code grows etc, it is a good practice to use stricter typings to make your code more maintainable.
If you use a code editor that is a bit smart you also get type-ahead support this way. All in all just basic coding hygiene.
If you also feel that way you can go ahead with me and create an interface for the configs/ environment:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/app/environment.interface.ts&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;export interface Environment {
  apiUrl: string;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And we change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;declare&lt;/code&gt; statement to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;declare let data: Environment&lt;/code&gt;
If you have cloned the &lt;a href=&quot;https://github.com/ProofOfPizza/example-angular-runtime-config&quot;&gt;repo&lt;/a&gt; you will find in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/app/app.component.ts&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;declare let data: Environment;

@Component({
  selector: 'app-root',
  template: '&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  apiUrl: string = __config.apiUrl;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So for now, we do not do anything too complex with our configuration variable, in fact we don’t do anything. Just throw it on the screen!&lt;/p&gt;

&lt;h2 id=&quot;running-in-a-docker-container-and-changing-the-config-at-runtime&quot;&gt;Running in a docker container and changing the config at runtime&lt;/h2&gt;
&lt;p&gt;Now we still have a last thing to do and that is to see that we get a feel for how we can change the config at runtime. As far as we have seen now, we have not been changing anything!&lt;/p&gt;

&lt;p&gt;For this I will assume we run our app inside of a docker container. Today I will not dive to deep into docker, but just to give a bit of info about the setup in the &lt;a href=&quot;https://github.com/ProofOfPizza/example-angular-runtime-config&quot;&gt;repo&lt;/a&gt;:
We have a dockerfile, in our case just the default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;. This is where we specify the build of our app. It is used to build a docker image. That image is what we want to build once, move along all our environments and maybe even ship to customers.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;FROM node:14-alpine as build

# copy code and run build
WORKDIR /app
COPY ./*.json ./
COPY ./src ./src
RUN npm install &amp;amp;&amp;amp; npm run build

# run app with nginx
FROM nginx:stable-alpine
COPY --from=build /app/dist/example-angular-runtime-config /usr/share/nginx/html
COPY ./default.conf /etc/nginx/conf.d/default.conf
WORKDIR /start
COPY ./start-app.sh .
CMD [ &quot;sh&quot;, &quot;start-app.sh&quot; ]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we take a closer look we see some instructions for a base image, copying files running a build, and then putting the result (everything in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dist&lt;/code&gt;) inside a nginx folder so it can be served. But where does it take our environment variables into account? In the last step. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CMD&lt;/code&gt; instruction is where the image receives what is the command to run by default when sinning up a container. In this case, and that is a fairly common pattern it is running a small shell script &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;start-app.sh&lt;/code&gt;. Lets have a look:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;#/bin/sh
if [ &quot;${ENVIRONMENT}&quot; = &quot;prod&quot; ]
then
  echo &quot;starting app prod&quot;
  mv /usr/share/nginx/html/config/config.prod.js /usr/share/nginx/html/config/config.js
elif [ &quot;${ENVIRONMENT}&quot; = &quot;test&quot; ]
then
  echo &quot;starting app test&quot;
  mv /usr/share/nginx/html/config/config.test.js /usr/share/nginx/html/config/config.js
else
  echo &quot;starting app default / local&quot;
fi
nginx -g &quot;daemon off;&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Also pretty straight forward: We check for an environment variable called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENVIRONMENT&lt;/code&gt; and see if it is either &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prod&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; or anything else. Then if necessary it overwrites &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.js&lt;/code&gt; with the prod or test version. When that is done it instructs nginx to serve our app. Pretty neat, now let’s see it in action!&lt;/p&gt;

&lt;h2 id=&quot;lets-build-once-and-run-&quot;&gt;Let’s build once, and run …&lt;/h2&gt;

&lt;p&gt;To build the image run:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;docker build -t amazing-app . # Mind the dot in the end!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We have now built the image, and gave it a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tag&lt;/code&gt; &lt;em&gt;amazing-app&lt;/em&gt;. You can see it by listing the images you now have: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image ls&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now we want to run a container based on this image, this is our runtime where our configs come into play! The plot thickens!
When you’re ready for the magic, run:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;docker run -p 4200:4200 -d --env ENVIRONMENT=local amazing-app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This command tells docker to run a container, bind the port 4200 in the container to 4200 on our machine, run in detached mode (so we can easily close it and it does not block our terminal), and pass it an environment variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENVIRONMENT=local&lt;/code&gt;. Lastly we specified the image that we want to use to create our container: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;amazing-app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now go to your favorite browser and navigate to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:4200&lt;/code&gt; and tadaa! Is it not amazing ? Well I am sure you can build even more amazing apps than these, but it does what we want!&lt;/p&gt;

&lt;p&gt;Let us stop our container and see some more wonders:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;docker stop $(docker ps -q)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This will stop all containers that we have.&lt;/p&gt;

&lt;p&gt;Now lets imagine that we were a pipeline and wanted deploy our app to the test environment then we would just use the same image and simply run:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;docker run -p 4200:4200 -d --env ENVIRONMENT=test amazing-app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Try it and check your browser! So easy and so powerful!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Tip: If you are disappointed, try a hard refresh (shift+F5) or an incognito window!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While we know that all out solutions can be useful and have their place, many times a bit more minimalistic solutions are preferable. It keeps our projects small, and easy to maintain. Also adding code when necessary is a normal task that is never forgotten (duh) while removing unnecessary code is much more difficult. So to keep technical debt to a minimum, be sure to avoid over engineering!&lt;/p&gt;

&lt;p&gt;I hope you liked this small demo. And if you see anything that could better, or maybe you if just completely disagree with my opinions on devops and &lt;em&gt;build once, deploy everywhere&lt;/em&gt; then please feel a warm welcome to leave a comment or reach out!&lt;/p&gt;</content><author><name>{&quot;avatar&quot;=&gt;&quot;/assets/images/bio-photo.jpg&quot;, &quot;bio&quot;=&gt;&quot;Curiosity killed the cat but at least she learned something!&quot;, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Buy me a coffee&quot;, &quot;icon&quot;=&gt;&quot;fas fa-coffee&quot;, &quot;url&quot;=&gt;&quot;https://www.buymeacoffee.com/ProofOfPizza?ref=footer&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/ProofOfPizza/&quot;}, {&quot;label&quot;=&gt;&quot;GitLab&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-gitlab&quot;, &quot;url&quot;=&gt;&quot;https://gitlab.com/ProofOfPizza&quot;}]}</name></author><category term="tech" /><category term="tutorial" /><category term="typescript" /><category term="angular" /><category term="devops" /><category term="docker" /><category term="configuration-management" /><category term="coding" /><category term="programming" /><summary type="html">Build your Angular app, run it inside a container and move it across environments. How do we manage our runtime configurations?</summary></entry><entry><title type="html">#AutomaticAlley - Kill any program anytime with ease</title><link href="https://calzone.proofofpizza.com/tech/automaticalley/AutomaticAlley-kill-any-program-anytime/" rel="alternate" type="text/html" title="#AutomaticAlley - Kill any program anytime with ease" /><published>2022-02-07T00:00:00+00:00</published><updated>2022-02-07T00:00:00+00:00</updated><id>https://calzone.proofofpizza.com/tech/automaticalley/AutomaticAlley-kill-any-program-anytime</id><content type="html" xml:base="https://calzone.proofofpizza.com/tech/automaticalley/AutomaticAlley-kill-any-program-anytime/">&lt;p&gt;This is the first in a series to come, doubtfully dubbed #AutomaticAlley. It’s where I will share small bits and bites of scripts and other tiny tricks to automate away boring and error prone stuff. The things we keep doing all the time, the things that annoy us that we need to to automate especially if we want to keep working on the command line a sane practice.&lt;/p&gt;

&lt;p&gt;In today’s episode: How to kill any program anytime with ease. For this we create a simple shell alias that kills any program that looks like the one that is bothering us. No need to go to activity screens or whatnot. In fact for those of us using tiling window managers or who otherwise generally live on the command line we just stay in our homes!&lt;/p&gt;

&lt;p&gt;We use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkill&lt;/code&gt; command for this. It is based on the loved &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; command that finds anything by regex or name. Then from the manual (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man pkill&lt;/code&gt;) we learn:&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;pgrep looks through the currently running processes and lists the process IDs which match the selection criteria to stdout.
[..]
pkill will send the specified signal (by default SIGTERM) to each process instead of listing them on stdout.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;So we learn that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkill&lt;/code&gt; is based on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pgrep&lt;/code&gt; which is a command that will list processes that are running currently. Now we need to be sure that the one we want is easily matched, and we do not want to have to remember exact spelling of even type all of it. After all we want to be as lazy as we can!&lt;/p&gt;

&lt;h2 id=&quot;lets-use-pgrep-to-get-the-right-matches&quot;&gt;Let’s use pgrep to get the right matches&lt;/h2&gt;
&lt;p&gt;By the way: If you are using a mac and pkill or pgrep are not available you can install &lt;a href=&quot;https://unix.stackexchange.com/questions/225/pgrep-and-pkill-alternatives-on-mac-os-x&quot;&gt;proctools&lt;/a&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install proctools&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let’s ask the system what we have: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkill --help&lt;/code&gt;. We find:&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;-i, --ignore-case         match case insensitively
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Yes! We’ll take it. We’re not particular about case!
So let’s try it! We run spotify. And then type:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;pgrep -i spotify
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;pgrep -i SpoTify
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;pgrep -i spoti
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;pgrep -i otif
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Looks good right ? It matches all! But wait… when we run some selenium tests for instance we need to run the selenium server. Usually in a repo where we use it we run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npx selenium-standalone install &amp;amp;&amp;amp; npx selenium-standalone start&lt;/code&gt; or similar,generally automated away in a nice &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm &amp;lt;selenium-command&amp;gt;&lt;/code&gt; in package.json.
Now these have a tendency to clog up the system, or hang in zombie state somehow. So lets see if we could &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pgrep&lt;/code&gt; and thus &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkill&lt;/code&gt; it.&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;pgrep -i selenium
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Nothing! Nothing!? Unfortunately it does not work. So lets see what else we have:&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;-f, --full                use full process name to match
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;That sounds good. Full whatever is probably more than what we had! And indeed:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;pgrep -i -f selenium
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And yes there we have something! If we would like to compare it, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-a&lt;/code&gt; option will display the full command line:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;pgrep -i -f -a selenium
pgrep -i -f -a spotify
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;So now lets kill ‘em!&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;pkill -i -f selenium
pkill -i -f spotify
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And if you feel like it is too silent now, and you like more feedback, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkill -h&lt;/code&gt; tells us:&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;-e, --echo                display what is killed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;So now we have:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;pkill -i -f -e selenium
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;lets-now-make-it-easier-simple-is-good&quot;&gt;Let’s now make it easier! Simple is good!&lt;/h2&gt;
&lt;p&gt;Who wants to remember all of that, or look it up every time? Not us smart (lazy) kids!
So now, depending on your shell we have something like a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.zshrc&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bashrc&lt;/code&gt; usually in our home directory.
In it we want to make an alias, so we add a line it:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;alias dieSpotify='pkill -i -f -e spotify'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Save it and reload the shell, or open a new terminal. Then just run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dieSpotify&lt;/code&gt;. And yes!
But wait… do we have to do that for each program we might ever want to kill ? That is crazy!
And of course the answer is no, we automated it, now we parametrize it! We can pass an argument to the alias. So we change the line into:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;die='pkill -i $1 -f -e'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$1&lt;/code&gt; part here refers to the argument we pass so now we can kill everything at our convenience. Go Rambo!&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;die spoti
die chrom
die KeePass
[..etc..]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;One time effort, but now hanging programs, or programs that clog up the system with processes that do not get closed properly etc. Bam… Kill them with ease and comfort :)&lt;/p&gt;

&lt;p&gt;Was it useful ? You have a better plan ? Let me know in the comments!&lt;/p&gt;</content><author><name>{&quot;avatar&quot;=&gt;&quot;/assets/images/bio-photo.jpg&quot;, &quot;bio&quot;=&gt;&quot;Curiosity killed the cat but at least she learned something!&quot;, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Buy me a coffee&quot;, &quot;icon&quot;=&gt;&quot;fas fa-coffee&quot;, &quot;url&quot;=&gt;&quot;https://www.buymeacoffee.com/ProofOfPizza?ref=footer&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/ProofOfPizza/&quot;}, {&quot;label&quot;=&gt;&quot;GitLab&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-gitlab&quot;, &quot;url&quot;=&gt;&quot;https://gitlab.com/ProofOfPizza&quot;}]}</name></author><category term="tech" /><category term="AutomaticAlley" /><category term="automatic-alley" /><category term="linux" /><category term="automation" /><category term="bash" /><category term="scripting" /><summary type="html">This is the first in a series to come, doubtfully dubbed #AutomaticAlley. It’s where I will share small bits and bites of scripts and other tiny tricks to automate away boring and error prone stuff. The things we keep doing all the time, the things that annoy us that we need to to automate especially if we want to keep working on the command line a sane practice.</summary></entry><entry><title type="html">Setup SOPS with AWS KMS and Terraform to encrypt your secrets in git</title><link href="https://calzone.proofofpizza.com/tech/tutorial/using-sops-with-aws-and-terraform/" rel="alternate" type="text/html" title="Setup SOPS with AWS KMS and Terraform to encrypt your secrets in git" /><published>2022-01-31T00:00:00+00:00</published><updated>2022-01-31T00:00:00+00:00</updated><id>https://calzone.proofofpizza.com/tech/tutorial/using-sops-with-aws-and-terraform</id><content type="html" xml:base="https://calzone.proofofpizza.com/tech/tutorial/using-sops-with-aws-and-terraform/">&lt;p&gt;In this post we’ll explore Mozilla SOPS to manage configuration secrets.&lt;/p&gt;

&lt;p&gt;There are a number of things to take into consideration, and there are different possible solutions each with their own advantages and problems. Some things however are important and should be possible in any solution:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;secrets should never be stored in plain text in git (duh!).&lt;/li&gt;
  &lt;li&gt;it should be possible to specify who has access to which secrets. For instance some members may have access only to dev secrets while others have access to all secrets.&lt;/li&gt;
  &lt;li&gt;access to the secrets should be revocable quickly and easily.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basically there are two different approaches:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Keep the secrets in an external system like &lt;a href=&quot;https://www.hashicorp.com/products/vault&quot;&gt;Hashicorp Vault&lt;/a&gt;, &lt;a href=&quot;https://azure.microsoft.com/en-us/services/key-vault/#product-overview&quot;&gt;Azure Key Vault&lt;/a&gt; or &lt;a href=&quot;https://aws.amazon.com/secrets-manager/&quot;&gt;AWS SecretsManager&lt;/a&gt; and find a way to inject them at runtime. Preferably the agent running the app has some rights based on identity for this. This means we need to setup identity management as well as a vault to keep the secrets. This adds overhead and some risks because it is not always directly clear what the potential risks are. But the advantage is that we can rotate secrets easily, and also by identity management assign or revoke access as the situation demands.&lt;/li&gt;
  &lt;li&gt;Keep the secrets encrypted in git. For this we need to manage access to the encryption key(s). The obvious advantage is that we do not need an external vault, and this may drive development speed and reduces complexity somewhat on that end. The disadvantage is that the deployment secrets are now coupled to the code in git, and in case secrets need to be rotated it involves a new deployment. If you have some gitops and automated pipelines in place this might not be a big problem, but in some situations this is a more serious issue. It also means that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git bisect&lt;/code&gt; will no longer reliably return an answer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this blog post we’ll look at the second scenario. Not because it is inherently better, but because there are valid usecases for it and there is a really nice solution in the form of &lt;a href=&quot;https://github.com/mozilla/sops&quot;&gt;Mozilla SOPS&lt;/a&gt;. SOPS supports encrypting files as binaries, but apart from that it also has the great feature of encrypting only the values of config files as long as they are in the correct formats, being json, yaml, .env or .ini.&lt;/p&gt;

&lt;p&gt;I’ve made a demo project on git to explore how we can set up this solution using terraform to provision the AWS users and keys, and then we configure and use SOPS to encrypt/decrypt a few example configuration secrets.&lt;/p&gt;

&lt;h2 id=&quot;lets-set-up-the-repo-and-initialize-terraform&quot;&gt;Let’s set up the repo and initialize terraform!&lt;/h2&gt;

&lt;p&gt;There is a git repo here: &lt;a href=&quot;https://github.com/ProofOfPizza/example-sops-terraform-aws&quot;&gt;ProofOfPizza&lt;/a&gt;. There are some prerequisites to be able to follow along:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;You need to have a valid &lt;a href=&quot;https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/&quot;&gt;AWS account&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;You need to have the &lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html&quot;&gt;AWS cli installed&lt;/a&gt; and&lt;/li&gt;
  &lt;li&gt;You need to have &lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/reference/configure/&quot;&gt;credentials configured&lt;/a&gt; (usually in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.aws/credentials&lt;/code&gt;) for a user (preferably NOT the root user but an IAM user with sufficient rights).&lt;/li&gt;
  &lt;li&gt;You need to have terraform installed.&lt;/li&gt;
  &lt;li&gt;To follow along please do not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clone&lt;/code&gt; the repo but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fork&lt;/code&gt; it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To install the repo simply go to the &lt;a href=&quot;https://github.com/ProofOfPizza/example-sops-terraform-aws&quot;&gt;repo&lt;/a&gt; in your browser and click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fork&lt;/code&gt;. You are then taken to your own github account and will there see your fork of the repo. Then you just click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;code&lt;/code&gt; and copy the link. In your terminal type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone &amp;lt;link&amp;gt;&lt;/code&gt; where &lt;link /&gt; should be replaced by the link you just copied.&lt;/p&gt;

&lt;p&gt;Then run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd terraform &amp;amp;&amp;amp; terraform init&lt;/code&gt;. This should get you the providers and initialize the terraform project. If you run into issues here please verify your terraform version with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform --version&lt;/code&gt;. In the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;providers.tf&lt;/code&gt; the required version is specified: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;required_version = &quot;~&amp;gt; 1.1.0&quot;&lt;/code&gt;. Match this with your version if necessary.&lt;/p&gt;

&lt;p&gt;We also see this block specifying the aws provider:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;provider &quot;aws&quot; {
  region  = &quot;eu-central-1&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you want you can specify any &lt;a href=&quot;https://aws.amazon.com/about-aws/global-infrastructure/regions_az/&quot;&gt;aws region&lt;/a&gt; you want. If you have MFA set up for your user then you have some additional steps to get a token and set up a profile for that user. (I should write another quick post about that). Also if you have multiple users configured in aws-cli you can specify the profile you want to use. You can do this by adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;profile = my_profile&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;deeper-look-into-the-terraform-code-to-create-the-aws-users-and-keys&quot;&gt;Deeper look into the terraform code to create the AWS users and keys&lt;/h2&gt;

&lt;p&gt;Now to use SOPS we need an encryption key. SOPS is compatible with many backends, but for now we will stick to one. However, SOPS supports using multiple backends simultaneously out of the box. We will create keys in &lt;a href=&quot;https://aws.amazon.com/kms/&quot;&gt;AWS Key Management Service (KMS)&lt;/a&gt;. This key can be rotated automatically, and with a policy we can specify which users have which rights to it. This way we can also easily revoke the rights if a team member leaves the project for instance.
So for this example we will need three users:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;A test user with rights to the test key to use sops on the test secrets&lt;/li&gt;
  &lt;li&gt;A prod users with rights to the prod key to use sops on both prod and test secrets&lt;/li&gt;
  &lt;li&gt;A KMS key admin user who has rights to update the keys if necessary. AWS will not allow us to create a key without someone attached to it that could perform these actions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The terraform code’s layout is pretty straightforward:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;providers.tf&lt;/code&gt; defines the AWS provider&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;outputs&lt;/code&gt; describes all the outputs that our terraform operations will provide. We can later see them in the console using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform output&lt;/code&gt; command.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.tf&lt;/code&gt; contains the definitions for the resources. It uses the terraform &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for_each&lt;/code&gt; syntax to loop over value definitions to reduce code duplication.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;locals.tf&lt;/code&gt; contains those value definitions. The values for the three users and the two keys we create are found here.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;policies/*.json&lt;/code&gt; are policy documents for the users. Here the allowed actions and resources are specified. To avoid circular dependencies the users have rights on all keys &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; and then the keys have only specific users assigned to them. Otherwise we would have to first create the keys, which would require us to first create the users, which would require us to first create the keys … well you get the point… or actually, that point never gets fully made :)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key-policy.tpl&lt;/code&gt; leverages the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.tpl&lt;/code&gt; terraform template format. It is basically a json file with interpolations. We use the terraform &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jsonencode&lt;/code&gt; function to interpolate more complex values than just strings. In this case lists of users:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;  &quot;Principal&quot;: {
&quot;AWS&quot;: ${jsonencode(key_users)}
},
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you want to learn more about all the options you can set for keys, and what they mean you can read more in the &lt;a href=&quot;https://docs.aws.amazon.com/service-authorization/latest/reference/list_awskeymanagementservice.html&quot;&gt;AWS docs&lt;/a&gt;.
    To learn more about how to specify these settings in terraform read the docs for &lt;a href=&quot;https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user&quot;&gt;iam_users&lt;/a&gt; and &lt;a href=&quot;https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key&quot;&gt;kms_keys&lt;/a&gt; in terraform.&lt;/p&gt;

&lt;h2 id=&quot;terraform-apply-and-creating-the-aws-resources&quot;&gt;Terraform apply and creating the aws resources&lt;/h2&gt;

&lt;p&gt;The KMS keys have a policy that defines who has which rights on them, this also goes for the actions create, update and delete key. For this reason we should add the IAM user that we use for terraform to the KMS keys. Otherwise terraform will be unable to manage them! In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;locals.tf&lt;/code&gt; we see:&lt;/p&gt;

&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;locals {
  my_aws_user = &quot;arn:aws:iam::12345678999:user/iam_user_name&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You will have to replace it with your user’s ARN. You can get it from the cli by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws sts get-caller-identity&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once you feel ready, we can run a speculative plan by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform plan&lt;/code&gt;.
If it all looks good to you you can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform apply&lt;/code&gt; and when asked type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yes&lt;/code&gt; to confirm. After a few moments terraform will inform you of what resources have been created.&lt;/p&gt;

&lt;h2 id=&quot;setup-profiles-for-aws-cli&quot;&gt;Setup profiles for AWS cli&lt;/h2&gt;

&lt;p&gt;Now we can use these users to test SOPS, but for this we need to setup the aws cli because SOPS relies on the users being present in the aws cli configuration. AWS cli credentials are kept in a file at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.aws/credentials&lt;/code&gt;. You can edit it with your preferred text editor and add the following sections:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;[KMS_ADMIN]
aws_access_key_id=AKIAZKL66TFJJ3EX6UVA
aws_secret_access_key=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

[KMS_TEST_USER]
aws_access_key_id=AKIAZKL66TFJHY2VOR4D
aws_secret_access_key=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

[KMS_PROD_USER]
aws_access_key_id=AKIAZKL66TFJAW7AGEVG
aws_secret_access_key=JRcWCkG8CuTV4XbluAgB0aCO/WuPKMGZ72QfiHRd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These keys will need to be replaced with theones we just created obviously.
Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform output&lt;/code&gt; to display our created resources. You will notice that for the access_keys we specified &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sensitive = true&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;outputs.tf&lt;/code&gt;. This ensures it does not get displayed in the console by default. If you want to display it anyway you have to specify the resource: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform output access_keys&lt;/code&gt;. Here we can find all the necessary details in the fields &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secret&lt;/code&gt; for the respective users.&lt;/p&gt;

&lt;p&gt;To test if our profiles are working we can for instance run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws sts get-caller-identity --profile KMS_ADMIN&lt;/code&gt;. Any command in the aws cli can be run using a profile in this manner.
If this all works we are now ready to finally use SOPS and see the magic!&lt;/p&gt;

&lt;h2 id=&quot;deeper-look-into-the-sops-configuration&quot;&gt;Deeper look into the SOPS configuration&lt;/h2&gt;

&lt;p&gt;We can easily just encrypt and decrypt files in place specifying the kms key in the command line…. but why would we ? The real use is for projects where we work together and want to store our configs in git and so on. So let’s just get directly to a more realistic configuration.
SOPS looks recursively in our directory path for a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.sops.yaml&lt;/code&gt;. In this file we can precisely specify which files or paths we want to encrypt, and with which keys. For this project our configuration looks like this:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;creation_rules:
    - path_regex: .*config/test/.*
    kms: arn:aws:kms:eu-central-1:640753212234:key/f9024130-ba9c-458d-ba4a-ca05b06f6f2c
    aws_profile: KMS_TEST_USER

    - path_regex: .*config/prod/.*
    kms: arn:aws:kms:eu-central-1:640753212234:key/02b01f77-35ac-4f24-a23c-87684b6cc01b
    aws_profile: KMS_PROD_USER
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What does this mean ? It means that any files matching the regex &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.*config/test/.*&lt;/code&gt; meaning all files inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/test&lt;/code&gt; in this case, will be encrypted using the key &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arn:aws:kms:eu-central-1:640753212234:key/f9024130-ba9c-458d-ba4a-ca05b06f6f2c&lt;/code&gt;. And thus only those with access to that key can and see and edit those files.&lt;/p&gt;

&lt;p&gt;We suppose for instance that you just joined the team, and got your keys. If you look into the files you will see that for any JSON, YAML, .env file the values are encrypted, and for instance the ssh keys are encrypted as binaries.
    You can decrypt any file in place using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sops -d -i filename&lt;/code&gt;, -d for decrypt and -i for in-place. But then you would have to remember to encrypt them later, and who wants to do that ? So the developers of sops made it easy for us. We just run: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sops filename&lt;/code&gt; and it will decrypt it in memory and open it in our default editor (the one specified in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$EDITOR&lt;/code&gt; environment variable). Now we see the file in cleartext, we can edit and save it, and when we exit the file it gets reencrypted and saved to the file. How awesome is that !?&lt;/p&gt;

&lt;p&gt;Now we assumed we came in the team just fresh, but we got both the prod and test keys. In a real situation we might be developers with only access to the test env. To simulate this edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.aws/credentials&lt;/code&gt; and change a profile name:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;[KMS_PROD_USER_XXX]
aws_access_key_id=AKIAZKL66TFJAW7AGEVG
aws_secret_access_key=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
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now go into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/test&lt;/code&gt; and run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sops default.yaml&lt;/code&gt;. No problem, right? Now try the same in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/prod&lt;/code&gt;. You will probably see something like:&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;Failed to get the data key required to decrypt the SOPS file.

Group 0: FAILED
arn:aws:kms:eu-central-1:640753212234:key/02b01d66-35ac-4f24-a23c-87684b6cc01b: FAILED
- | Error decrypting key: NoCredentialProviders: no valid
| providers in chain. Deprecated.
| 	For verbose messaging see
| aws.Config.CredentialsChainVerboseErrors
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pretty neat right ?&lt;/p&gt;

&lt;h2 id=&quot;git-diff&quot;&gt;Git diff&lt;/h2&gt;

&lt;p&gt;Now let’s assume you were hired to actually do something. And you need to add a property, so you run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sops some.env.json&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/test&lt;/code&gt;. You add an application url:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;{
  &quot;database&quot;: {
    &quot;server&quot;: &quot;important-test-sql.database.net&quot;,
    &quot;catalog&quot;: &quot;important-test-sqldb-application-api&quot;,
    &quot;username&quot;: &quot;application-test-api-username&quot;,
    &quot;password&quot;: &quot;application-test-api-password&quot;
  },
  &quot;applicationUrl&quot;: &quot;app.test-env.awesome-stuff.com&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After saving and quitting would like to see what has changed in your files compared to the way things were. And so you run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git diff&lt;/code&gt;. But yeah…. Not quite as informative as you would like it to be ?&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;[...]
&quot;username&quot;: &quot;ENC[AES256_GCM,data:XYqrySLKWxIJifFP3vB3T7XZM9vjN6KUxTItO6s=,iv:D4Qd4KvAmApEpqjaAStF9IPR7H4HoiPzdOomh1HpYCo=,tag:3ApMYz4t41ni5qeBXv6XMw==,type:str]&quot;,
&quot;password&quot;: &quot;ENC[AES256_GCM,data:1vGBvFRCjhTIbbnmolmmjBh1d+eCP5E5Wa8zwG8=,iv:tuoLwKlDRV5wley3CWllgwz03MU619Y9DVeyzRGOxr4=,tag:XiMK5P2/0nEbI+rMnAIaBQ==,type:str]&quot;
},
+       &quot;applicationUrl&quot;: &quot;ENC[AES256_GCM,data:f7ce62NuwHVAchtTRSi8slaawvnXSiks5EtfSLg1,iv:UKMRt8XAncqF+qrQmBOxFOCM15XwEMl5jsUchH9dOAY=,tag:+lNZEhXa6+0L7TRvzYNHjQ==,type:str]&quot;,
[...]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nope… Not very useful. But we’re in luck because even this has been thought of! We create a file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitattributes&lt;/code&gt; in the root of our project and in it we write:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;config/** diff=sopsdiffer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This tells git that for every file matching the path &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/**&lt;/code&gt;, meaning any file under this directory, we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sopsdiffer&lt;/code&gt; when we run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git diff&lt;/code&gt;. But what is sopsdiffer? It is just an arbitrary name that could be anything, but we can give it some meaning by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git config diff.sopsdiffer.textconv &quot;sops -d&quot;&lt;/code&gt;. This command adds a bit to our git config file found in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.git.config&lt;/code&gt;. It now has a part:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;[diff &quot;sopsdiffer&quot;]
textconv = sops -d --config /dev/null
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we now run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git diff&lt;/code&gt; we find something a lot more satisfying:&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;[...]
&quot;catalog&quot;: &quot;important-test-sqldb-application-api&quot;,
&quot;username&quot;: &quot;application-test-api-username&quot;,
&quot;password&quot;: &quot;application-test-api-password&quot;
-       }
+       },
+       &quot;applicationUrl&quot;: &quot;app.test-env.awesome-stuff.com&quot;
[...]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sops gets called every time we call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git diff&lt;/code&gt; before displaying it in the console. Didn’t I tell you? Awesome stuff!. This should work for most IDE’s because they all call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git diff&lt;/code&gt; under the hood. Unfortunately it only partly works if you use vscode as your IDE and try diffing in there. If you want to read more about why it fails, or want to help working out a solution you can track the issue &lt;a href=&quot;https://github.com/mozilla/sops/issues/959&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;revoking-access-to-kms-keys&quot;&gt;Revoking access to KMS keys&lt;/h2&gt;

&lt;p&gt;Now let’s say you did an awsesome job and are now ready to leave the team on to greater things! Now we need to revoke your access to the KMS keys. With our terraform set up this is a simple two step that can be run by the key_admin user we created:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;locals.tf&lt;/code&gt; and from the line 46:&lt;/li&gt;
&lt;/ol&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;policy = templatefile(&quot;./policies/key-policy.tpl&quot;, {key_users = [aws_iam_user.user[&quot;test&quot;].arn, aws_iam_user.user[&quot;prod&quot;].arn], key_admins = [aws_iam_user.user[&quot;key_admin&quot;].arn, local.my_aws_user]})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We remove the test user so it becomes:&lt;/p&gt;
&lt;div class=&quot;code-header&quot;&gt;
  &lt;button class=&quot;copy-code-button&quot; aria-label=&quot;Copy code to clipboard&quot;&gt;&lt;/button&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;policy = templatefile(&quot;./policies/key-policy.tpl&quot;, {key_users = [aws_iam_user.user[&quot;prod&quot;].arn], key_admins = [aws_iam_user.user[&quot;key_admin&quot;].arn, local.my_aws_user]})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;Edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;providers.tf&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;profile = KMS_ADMIN&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;We run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform apply&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And there, our test user now no longer can decrypt our configurations! Mind you, for now, he still has a user and everything in the cli set up. But our secrets are safe.
Within 2 minutes, everything is safe again. Of course you know up front someone will leave the team, but say a lost laptop could be a more sudden event in which case you need to be able to mitigate these risks quickly!
Obviously if the person not just switched teams but left the company entirely, then we would also remove the user.&lt;/p&gt;

&lt;h2 id=&quot;conclusion-and-cleanup&quot;&gt;Conclusion and cleanup&lt;/h2&gt;

&lt;p&gt;Don’t forget to clean up properly. DO NOT JUST THROW AWAY THE FOLDER. This will bring you a headache in the AWS console. Instead just:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;providers.tf&lt;/code&gt; again and remove the profile so we run the cleanup as our own IAM user.&lt;/li&gt;
  &lt;li&gt;run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform plan -destroy&lt;/code&gt; and confirm. This will clean up all the resources in AWS.&lt;/li&gt;
  &lt;li&gt;Edit your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.aws/credentials&lt;/code&gt; and remove the now redundant profiles.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s it!
This concludes this write up and example on how to use SOPS for encrypting and decrypting secrets in our configs (and other files), and how to manage all the required resources in AWS with terraform.
I hope you liked it, and please leave any comments or suggestions in the comment section or reach out to me.&lt;/p&gt;</content><author><name>{&quot;avatar&quot;=&gt;&quot;/assets/images/bio-photo.jpg&quot;, &quot;bio&quot;=&gt;&quot;Curiosity killed the cat but at least she learned something!&quot;, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Buy me a coffee&quot;, &quot;icon&quot;=&gt;&quot;fas fa-coffee&quot;, &quot;url&quot;=&gt;&quot;https://www.buymeacoffee.com/ProofOfPizza?ref=footer&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/ProofOfPizza/&quot;}, {&quot;label&quot;=&gt;&quot;GitLab&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-gitlab&quot;, &quot;url&quot;=&gt;&quot;https://gitlab.com/ProofOfPizza&quot;}]}</name></author><category term="tech" /><category term="tutorial" /><category term="security" /><category term="devops" /><category term="sops" /><category term="programming" /><category term="terraform" /><category term="AWS" /><category term="infrastructure-as-code" /><category term="encryption" /><category term="configuration-management" /><summary type="html">In this post we’ll explore Mozilla SOPS to manage configuration secrets.</summary></entry><entry><title type="html">How to use keepass programmatically with typescript</title><link href="https://calzone.proofofpizza.com/tech/tutorial/using-keepass-programatically-with-typescript/" rel="alternate" type="text/html" title="How to use keepass programmatically with typescript" /><published>2022-01-24T00:00:00+00:00</published><updated>2022-01-24T00:00:00+00:00</updated><id>https://calzone.proofofpizza.com/tech/tutorial/using-keepass-programatically-with-typescript</id><content type="html" xml:base="https://calzone.proofofpizza.com/tech/tutorial/using-keepass-programatically-with-typescript/">&lt;p&gt;Even though we all know keeping user credentials in our code is bad (right ?) … sometimes it still happens. For instance when we write our automated tests, and then it “does not really matter that much because it is just the test environment”. Well it might not, if everyone else (sysadmins etc) are doing their work correctly. But why gamble? It’s 2022 folks, security is a shared responsability these days, and there are tons of tools to help us at any level.&lt;/p&gt;

&lt;p&gt;So today let’s look at a great example of these tools: &lt;a href=&quot;https://keepass.info/&quot;&gt;keepass&lt;/a&gt;. It is a tool that allows you to generate strong passwords, store them securely in an encrypted database (a .kdbx file), and retrieve them quickly and easily. You only need to remember one master password. There are many apps out there for mac, windows, linux and mobile devices that can offer even more features such as autofill passwords in your browser etc. And then sharing your passwords is easy with something like &lt;a href=&quot;https://nextcloud.com&quot;&gt;nextcloud&lt;/a&gt; or even just plain old emails (but this gets you in a hell of versions obviously). For teams working with or on the same app you can store them in AWS, GCS, or Azure, and then set IAM permissions so you have fine grained control over them.&lt;/p&gt;

&lt;p&gt;All of that is great but it gets better: We can also use keepass programmatically. This way we can have our code open up the database, and extract the users credentials and run our tests for example. This means that we can store the database with test users in git (because it is safely encrypted), and all we need to do is pass a master password. The easiest way to do that is through an environment variable (or shell variable). That way if we run it locally we can just pass it in from the command line (or possible our IDE). When we run it for instance in a pipeline, we can tell the pipeline to fetch the master password from a secure place (for instance: the pipeline will have an IAM role with a policy that allows it to retrieve secrets from &lt;a href=&quot;https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html&quot;&gt;AWS secrets manager&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Here is an example for typescript. You can find it on &lt;a href=&quot;https://github.com/ProofOfPizza/example-keepass-and-typescript&quot;&gt;github&lt;/a&gt; We can use a &lt;a href=&quot;https://github.com/SnapServ/keepass.io&quot;&gt;keepass.io&lt;/a&gt; library for this. In this small example I have an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.ts&lt;/code&gt; file which imports a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keepass.ts&lt;/code&gt; file where we do all the magic with our database. Imagine the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.ts&lt;/code&gt; file could be anything like a module handles logging in to our app for instance.&lt;/p&gt;

&lt;p&gt;To get started we need to make an encrypted database. For this we install our favourite keepass app, and open it. Choose new to create a new database file, and choose your database name. I chose &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;users&lt;/code&gt;. Then we can fill in a few users. I filled in:&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;admin_user@my-app.com
normal_user1@my-app.com
normal_user2@my-app.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We’ll let keepass generate some nice strong passwords for us and save.&lt;/p&gt;

&lt;p&gt;Then we install keepass.io with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm install --save keepass.io&lt;/code&gt;. Now this might give you a screen full of errors about python missing… but then they lovingly reassure us:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;DO NOT WORRY ABOUT THESE MESSAGES. KEEPASS.IO WILL FALLBACK TO SLOWER NODE.JS METHODS, SO THERE ARE NO LIMITATIONS EXCEPT SLOWER PERFORMANCE.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If we worry about performance, we should make sure to have a working installation of python available. But then again, in most real world scenarios this is not the place where your performance bottleneck is at.
Ok, then to start using we import it at the top &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const kpio = require('keepass.io')&lt;/code&gt; and we’re ready to go!&lt;/p&gt;

&lt;p&gt;We define a class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Keepass&lt;/code&gt; and in it one method called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getPassword&lt;/code&gt;. Obviously you can add other methods as well if needed: keepass.io also supports writing to the database etc. But for our simple purposes we just want to retrieve a password to then use it for other great things.&lt;/p&gt;

&lt;p&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getPassword&lt;/code&gt; we first open the database with the master password and retrieve it’s contents with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getRawApi&lt;/code&gt;.&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;      let db = new kpio.Database();
      db.addCredential(new kpio.Credentials.Password(masterpass));
      db.loadFile(dbPath, function (err: any) {
        if (err) reject(err);
        const rawDb = db.getRawApi().get();
        [...]
      })
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getRawApi.get()&lt;/code&gt; return the contents of the database as javascript Object. If we add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console.log(rawDb)&lt;/code&gt; we see something like:&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;{
  KeePassFile: {
    Meta: {
      Generator: 'KeePass',
      HeaderHash: '9ky87SMwixNRtGVl9TvG0VSzyawLmT9hpAbl8Z2ev1o=',
      DatabaseName: 'users',
      DatabaseNameChanged: '2022-01-24T06:31:05Z',
      DatabaseDescription: '',
      DatabaseDescriptionChanged: '2022-01-24T06:30:53Z',
      DefaultUserName: '',
      DefaultUserNameChanged: '2022-01-24T06:30:53Z',
      MaintenanceHistoryDays: '365',
      Color: '',
      MasterKeyChanged: '2022-01-24T06:30:53Z',
      MasterKeyChangeRec: '-1',
      MasterKeyChangeForce: '-1',
      MemoryProtection: [Object],
      RecycleBinEnabled: 'True',
      RecycleBinUUID: 'uWPZ1nyGcEyuvZyvRSLLnA==',
      RecycleBinChanged: '2022-01-24T06:30:53Z',
      EntryTemplatesGroup: 'AAAAAAAAAAAAAAAAAAAAAA==',
      EntryTemplatesGroupChanged: '2022-01-24T06:30:53Z',
      HistoryMaxItems: '10',
      HistoryMaxSize: '6291456',
      LastSelectedGroup: 'YWA8EJj7aUaHuop6TkTKGQ==',
      LastTopVisibleGroup: 'YWA8EJj7aUaHuop6TkTKGQ==',
      Binaries: '',
      CustomData: ''
    },
    Root: { Group: [Object], DeletedObjects: '' }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As we see there is a lot of meta data about our database here, but the real magic sits inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Root&lt;/code&gt; node. From there we can drill down, and we see that inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rawDb[&quot;KeePassFile&quot;][&quot;Root&quot;][&quot;Group&quot;][&quot;Entry&quot;]&lt;/code&gt; we have an array with a bunch of things amongst which a node &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;String&lt;/code&gt; that contains an array of objects. Inspecting one of those by logging &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rawDb[&quot;KeePassFile&quot;][&quot;Root&quot;][&quot;Group&quot;][&quot;Entry&quot;][0][&quot;String&quot;]&lt;/code&gt;:&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;[
  { Key: 'Notes', Value: '' },
  {
    Key: 'Password',
    Value: { _: 'AO01ylqPyTycqOCOREBz', '$': [Object] }
  },
  { Key: 'Title', Value: 'admin' },
  { Key: 'URL', Value: '' },
  { Key: 'UserName', Value: 'admin_user@my-app.com' }
]
The credentials for adminUser are: admin_user@my-app.com / AO01ylqPyTycqOCOREBz
[
  { Key: 'Notes', Value: '' },
  {
    Key: 'Password',
    Value: { _: 'AO01ylqPyTycqOCOREBz', '$': [Object] }
  },
  { Key: 'Title', Value: 'admin' },
  { Key: 'URL', Value: '' },
  { Key: 'UserName', Value: 'admin_user@my-app.com' }
]
The credentials for normalUser1 are: normal_user1@my-app.com / Mv2j8EHTbSnSTphBWHey
[
  { Key: 'Notes', Value: '' },
  {
    Key: 'Password',
    Value: { _: 'AO01ylqPyTycqOCOREBz', '$': [Object] }
  },
  { Key: 'Title', Value: 'admin' },
  { Key: 'URL', Value: '' },
  { Key: 'UserName', Value: 'admin_user@my-app.com' }
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This has all the info we actually need. If you added a title you will find it here, as well as notes or any other fields you used. So looping through this array, and matching it against the requested userName, will gives us what we need.&lt;/p&gt;

&lt;p&gt;The total then looks something like this:&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;const kpio = require(&quot;keepass.io&quot;);

const dbPath = &quot;./users.kdbx&quot;;
const masterpass = process.env[&quot;KEEPASS_PW&quot;];

class Keepass {
  getPassword = async (userName: string): Promise&amp;lt;string&amp;gt; =&amp;gt; {
    return new Promise((resolve, reject) =&amp;gt; {
      let db = new kpio.Database();
      db.addCredential(new kpio.Credentials.Password(masterpass));
      db.loadFile(dbPath, function (err: any) {
        if (err) reject(err);
        const rawDb = db.getRawApi().get();
        const entries = rawDb[&quot;KeePassFile&quot;][&quot;Root&quot;][&quot;Group&quot;][&quot;Entry&quot;].map(
          (x: any) =&amp;gt; x[&quot;String&quot;]
        );
        const secrets = entries.map((entry: any) =&amp;gt; {
          const userName = entry.find((x: any) =&amp;gt; x[&quot;Key&quot;] === &quot;UserName&quot;)[
            &quot;Value&quot;
          ];
          const password = entry.find((x: any) =&amp;gt; x[&quot;Key&quot;] === &quot;Password&quot;)[
            &quot;Value&quot;
          ][&quot;_&quot;];
          return {
            userName,
            password,
          };
        });
        const returnValue = secrets.find((s: any) =&amp;gt; s.userName === userName)
          ?.password;
        resolve(returnValue);
      });
    });
  };
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you want to see it work then run:&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;git clone git@github.com:ProofOfPizza/example-keepass-and-typescript.git
npm install
tsc
KEEPASS_PW=super-secret12#$ npm start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you see, it is quite easy to use keepass in your code and increase security when it comes to user credentials.
Happy coding and let me know what you think!&lt;/p&gt;</content><author><name>{&quot;avatar&quot;=&gt;&quot;/assets/images/bio-photo.jpg&quot;, &quot;bio&quot;=&gt;&quot;Curiosity killed the cat but at least she learned something!&quot;, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Buy me a coffee&quot;, &quot;icon&quot;=&gt;&quot;fas fa-coffee&quot;, &quot;url&quot;=&gt;&quot;https://www.buymeacoffee.com/ProofOfPizza?ref=footer&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/ProofOfPizza/&quot;}, {&quot;label&quot;=&gt;&quot;GitLab&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-gitlab&quot;, &quot;url&quot;=&gt;&quot;https://gitlab.com/ProofOfPizza&quot;}]}</name></author><category term="tech" /><category term="tutorial" /><category term="coding" /><category term="devops" /><category term="programming" /><category term="typescript" /><category term="node" /><category term="javascript" /><category term="keepass" /><category term="testing" /><summary type="html">Even though we all know keeping user credentials in our code is bad (right ?) … sometimes it still happens. For instance when we write our automated tests, and then it “does not really matter that much because it is just the test environment”. Well it might not, if everyone else (sysadmins etc) are doing their work correctly. But why gamble? It’s 2022 folks, security is a shared responsability these days, and there are tons of tools to help us at any level.</summary></entry><entry><title type="html">Is there creativity in coding?</title><link href="https://calzone.proofofpizza.com/tech/opinion/is-there-creativity-in-programming/" rel="alternate" type="text/html" title="Is there creativity in coding?" /><published>2022-01-10T00:00:00+00:00</published><updated>2022-01-10T00:00:00+00:00</updated><id>https://calzone.proofofpizza.com/tech/opinion/is-there-creativity-in-programming</id><content type="html" xml:base="https://calzone.proofofpizza.com/tech/opinion/is-there-creativity-in-programming/">&lt;p&gt;“But you are such a creative person, why?”, is a comment I get often (in variations of course) when I say I work in software development. I am a trained music professional, and apparently that means I am super creative, and writing code means I am stuck with ones and zeros, and surely there is nothing creative about that. I figured it might be nice to write this piece about exactly how creative musicians and coders are, and what role creativity really plays in our work.&lt;/p&gt;

&lt;p&gt;I will first start with some general, slightly abstract or philosophical if you will, ideas about creativity. I always feel that taking a shit might be one of the best examples of creativity. Sure, it lacks some aspects of it, but it has the most important ones and it quickly breaks some of the most prevalent myths as well. For instance this idea that creativity is “creating something out of nothing”. Right? “There was no song, now there is one. There was an empty canvas now there is a painting” Something created out of nothing. Well… no. Not really, and when I say not really I mean absolutely not. Sure the toilet was empty, and now your work of art lays there smiling and smelling at you, but it was not created out of nothing. In fact: what does it contain that you did not first eat ?&lt;/p&gt;

&lt;p&gt;Creativity is like this, a process which has inputs (whatever you eat and drink) and restructures those, breaking it up in small parts, combining things from different sources etc., and molds it into some fantastic “new” thing as output. (Does it not just smell new?). A nice byproduct of course can be that we feel good, satisfied, energized and so on. Great stuff all along! But not as magical as we sometimes deem it to be. This is true for any form of creativity, whether physical or abstract. Music, forms, colors, ideas all these are composed and recomposed by similar creative processes.&lt;/p&gt;

&lt;p&gt;Something that I have found to be an essential ingredient in creativity is the “why?”. Creativity is (almost?) without exception a process that is applied to solve a problem. Now I use problem here as a broad concept in the sense of obstacle, constraint, etc. When we are confronted with a problem, it is almost inevitable to start thinking about solutions. (So much in fact that it can be a problem in itself! Remember those refinement meetings where we get a solution to refine instead of a problem?)
Sometimes we have one, sometimes we feel immediately that we will be unable to solve it and then stop thinking about it, and sometimes we find ourselves digging into it trying to crack it.&lt;/p&gt;

&lt;p&gt;Then now, coming back to the main subject here: Does coding involve creativity? Does making music involve it ?
The answer of course is: yes, coding is first and foremost a creative process. Firstly analysing the problems at hand, destructuring and restructuring them, then when we have thought of a solution we need to code it. Again, writing code, restructuring it, adapting other people’s solutions to similar but not equal problems. Then beautifying the code. All again and again a process of creativity, different partial solutions to partial problems that together form the solution to the great problem we were confronted with.&lt;/p&gt;

&lt;p&gt;What about musicians then ? Well yes, surely creativity is involved. But sometimes less that you would think! In my experience people miss all types of distinctions in this field, like musicians vs composers vs songwriters vs instrumentalists. In some parts, like songwriting and composition there is a lot of creativity. The problems here could be, for example: “Can I play a melody so that people recognize the original theme in it, while we are moving over a bunch of very different chords?” Or: “Can we compose something that is undoubtedly reggae but uses &lt;a href=&quot;https://en.wikipedia.org/wiki/Polytonality&quot;&gt;polytonality&lt;/a&gt; in the way for instance some composers from the last century did?” When I want to compose music, I generally start by setting up a few constraints/ problems like these. It triggers my brain and ideas will come. After generating a bunch I will then take those musical ideas and craft them into compositions that I like or at least make sense to me.&lt;/p&gt;

&lt;p&gt;Instrumentalists sometimes have to be very creative, more so when they improvise (which is another thing that we could slice up into smaller things to really see what is what). And sometimes it is just repeatedly banging out the same notes that apparently make up the song, and not so much else. Nothing against Robby Williams, but when you’ve played “let me entertain you” at a few hundred parties there is not that much creativity left there.&lt;/p&gt;

&lt;p&gt;Of course, in coding we could also add some more context and precision. In the end it seems to come down to this: Do we get real problems to solve? The fist time we get asked to implement a fuzzy search over a few thousand articles in a DB we have to put everything at work and all creative juices flowing to get it done. The third time we get asked the same it is a repetition of moves, a thing we know how to do, but not necessarily a creative challenge.&lt;/p&gt;

&lt;p&gt;Most coders I know, don’t want to keep repeating the same tricks. We sometimes even omit certain experiences and skills from our resumes so that we do not get asked to do those things again. This is precisely because we are creative at heart, and love to stay creative, stay challenged. If you want boredom? Well try playing in an uninspired orchestra, where you know what to do, you do not really need to study, and actually no one really cares if it gets to sound better as long as everyone gets paid in time.&lt;/p&gt;

&lt;p&gt;There is not just black and white, in fact not even just many shades of grey. There are colours everywhere, and we can choose to have our lives full of colour and glitters! And I for one, definitely intend to keep doing just that!&lt;/p&gt;</content><author><name>{&quot;avatar&quot;=&gt;&quot;/assets/images/bio-photo.jpg&quot;, &quot;bio&quot;=&gt;&quot;Curiosity killed the cat but at least she learned something!&quot;, &quot;links&quot;=&gt;[{&quot;label&quot;=&gt;&quot;Buy me a coffee&quot;, &quot;icon&quot;=&gt;&quot;fas fa-coffee&quot;, &quot;url&quot;=&gt;&quot;https://www.buymeacoffee.com/ProofOfPizza?ref=footer&quot;}, {&quot;label&quot;=&gt;&quot;GitHub&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-github&quot;, &quot;url&quot;=&gt;&quot;https://github.com/ProofOfPizza/&quot;}, {&quot;label&quot;=&gt;&quot;GitLab&quot;, &quot;icon&quot;=&gt;&quot;fab fa-fw fa-gitlab&quot;, &quot;url&quot;=&gt;&quot;https://gitlab.com/ProofOfPizza&quot;}]}</name></author><category term="tech" /><category term="opinion" /><category term="coding" /><category term="programming" /><category term="creativity" /><category term="music" /><category term="problem-solving" /><summary type="html">“But you are such a creative person, why?”, is a comment I get often (in variations of course) when I say I work in software development. I am a trained music professional, and apparently that means I am super creative, and writing code means I am stuck with ones and zeros, and surely there is nothing creative about that. I figured it might be nice to write this piece about exactly how creative musicians and coders are, and what role creativity really plays in our work.</summary></entry></feed>