<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Deepu K Sasidharan</title>
    <description>The latest articles on Forem by Deepu K Sasidharan (@deepu105).</description>
    <link>https://forem.com/deepu105</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F178939%2F38a82d99-3a0c-4a47-84fc-76fff3144cda.png</url>
      <title>Forem: Deepu K Sasidharan</title>
      <link>https://forem.com/deepu105</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/deepu105"/>
    <language>en</language>
    <item>
      <title>A Passwordless Future: Passkeys for Developers</title>
      <dc:creator>Deepu K Sasidharan</dc:creator>
      <pubDate>Fri, 16 Feb 2024 15:53:42 +0000</pubDate>
      <link>https://forem.com/oktadev/a-passwordless-future-passkeys-for-java-developers-1f54</link>
      <guid>https://forem.com/oktadev/a-passwordless-future-passkeys-for-java-developers-1f54</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://auth0.com/blog/webauthn-and-passkeys-for-developers/" rel="noopener noreferrer"&gt;auth0.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So, first things first, why do we even need to go passwordless? After all, passwords have been around for over 1000 years, right? And we were all happily sharing our Netflix passwords.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Password Problem
&lt;/h2&gt;

&lt;p&gt;The most important reason to go passwordless would be the password problem. According to &lt;a href="https://www.verizon.com/business/resources/reports/dbir/2023/summary-of-findings/" rel="noopener noreferrer"&gt;Verizon's 2023 Data Breach Investigations Report&lt;/a&gt;, stolen credentials and phishing account for over 65% of all data breaches.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F4kBHMXHpPNBBHiuwsJCMGK%2F4141a7ac4f7a7b66a3bbf07812c968c2%2Fhacker_wearing_a_hoodie_anime.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F4kBHMXHpPNBBHiuwsJCMGK%2F4141a7ac4f7a7b66a3bbf07812c968c2%2Fhacker_wearing_a_hoodie_anime.jpg" alt="Anime character depicting a hacker"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most of the password problems are human problems in my opinion. Because passwords rely on us humans to remember them and to not share them and do a bunch of other stuff. History has repeatedly, a quadrillion times, shown us that we are not good at doing these things. So it is an &lt;strong&gt;us&lt;/strong&gt; problem rather than the technology itself.&lt;/p&gt;

&lt;p&gt;These are the main problems with passwords:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge-based&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;People can be socially engineered, quite easily, to divulge passwords, or other information that can be used to get to the password.&lt;/li&gt;
&lt;li&gt;In this day and age there are just too many passwords to remember. If passwords are easy to remember they are also easy to guess. Complex passwords are not easy to remember, so we end up reusing passwords.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Phishing&lt;/strong&gt;: Phishing websites can easily harvest passwords from even the most tech-savvy.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Remote Replay&lt;/strong&gt;: Accounts can be accessed remotely using harvested passwords.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Data Breach&lt;/strong&gt;: Applications become a target for data breaches when they store passwords.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Share and Reuse&lt;/strong&gt;: Sharing and reusing passwords makes them even more vulnerable.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Password management&lt;/strong&gt;: Passwords are not just a hassle for the end users, they are a hassle on the server side as well. Because

&lt;ul&gt;
&lt;li&gt;We need to build password recovery and reset flows.&lt;/li&gt;
&lt;li&gt;We need multi-factor authentication flows to secure them further.&lt;/li&gt;
&lt;li&gt;They need to be reset regularly in some use cases.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F2aFBrgOCSmtq4yWQa958jh%2F99c50506bf6eeaad148afee6eb5f9a0a%2Fpassword-problems.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F2aFBrgOCSmtq4yWQa958jh%2F99c50506bf6eeaad148afee6eb5f9a0a%2Fpassword-problems.jpg" alt="Problems with passwords"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.forbes.com/sites/forbestechcouncil/2023/03/23/embracing-the-end-of-the-password-here-and-now" rel="noopener noreferrer"&gt;Did you know it could cost around 70$ to reset a password?&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Of course, password managers help with some aspects of this and everyone should use one. But they are still an overhead and not very convenient for everyone, especially non-tech folks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Passwordless?
&lt;/h2&gt;

&lt;p&gt;The obvious solution for the password problem is to go passwordless. So what exactly is passwordless?&lt;/p&gt;

&lt;p&gt;If you can verify a user's identity with something other than a password as the first factor of authentication, it is passwordless. We are doing this every day to unlock our phones and laptops using our fingerprints, faces, and so on.&lt;/p&gt;

&lt;p&gt;There are a few &lt;a href="https://auth0.com/blog/what-is-passwordless-authentication/" rel="noopener noreferrer"&gt;passwordless methods&lt;/a&gt; that you might have seen here and there. Like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Biometric authentication&lt;/li&gt;
&lt;li&gt;Magic links&lt;/li&gt;
&lt;li&gt;SMS/Email One-Time Password (OTP)&lt;/li&gt;
&lt;li&gt;Push notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But most of these methods are not secure enough to replace a password + Multi-Factor Authentication (MFA) combination.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passkeys
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Passkeys are a password replacement that provide faster, easier, and more secure sign-ins to websites and apps across a user’s devices. Unlike passwords, passkeys are resistant to phishing, are always strong, and are designed so that there are no shared secrets.&lt;/p&gt;

&lt;p&gt;— FIDO Alliance&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is where passkeys come into the picture. A secure passwordless future is the one offered by passkeys in my opinion. You probably already encountered passkeys since Google and GitHub have been rolling it out to all users recently. If you haven't set them up yet, you should!&lt;/p&gt;

&lt;p&gt;A passkey is a unique cryptographic key pair that allows you to access online services without using passwords. It is based on &lt;a href="https://en.wikipedia.org/wiki/Public-key_cryptography" rel="noopener noreferrer"&gt;asymmetric public-key cryptography&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;Before we dive deep into passkeys let's look at some of the underlying technologies that make passkeys possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Public-key cryptography
&lt;/h3&gt;

&lt;p&gt;Asymmetric public key cryptography involves a pair of mathematically linked keys: a public key, which is shared openly, and a private key, kept secret by the owner.&lt;/p&gt;

&lt;p&gt;This key pair can be used for encryption. When a message is encrypted with the public key, only the corresponding private key can decrypt it, ensuring confidentiality.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F21EnBU43HXxjOo1V96LfIh%2F4fc796fcd69bacb82b6d0f3c1f9f1b96%2Fpub-key-encrypt.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F21EnBU43HXxjOo1V96LfIh%2F4fc796fcd69bacb82b6d0f3c1f9f1b96%2Fpub-key-encrypt.jpeg" alt="Encryption using Public-key cryptography"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The same key pair can also be used for digital signatures. A message signed with a private key can be verified with the public key, authenticating the sender's identity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F2SROWLHSH9If5eZ0fZ5dbj%2F89633fe6d9afe573c407fcc9ff8303c0%2Fpub-key-sign.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F2SROWLHSH9If5eZ0fZ5dbj%2F89633fe6d9afe573c407fcc9ff8303c0%2Fpub-key-sign.jpg" alt="Signature verification using Public-key cryptography"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Passkeys use the signature verification mechanism. These keys are generated using a cryptographic algorithm, such as RSA or ECC.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authenticator
&lt;/h3&gt;

&lt;p&gt;An authenticator is a hardware or software entity that can create and store public-private key pairs which can be used for user registration and authentication. There are two types of authenticators:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform authenticators&lt;/strong&gt;: An authenticator built into a user's device. For example, TouchID and FaceID from Apple, smartphone authenticators, Windows Hello, and so on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Roaming authenticators&lt;/strong&gt;: A removable authenticator usable with any device the user is trying to sign in from. They are attached using USB, NFC, and/or Bluetooth. For example, security keys like &lt;a href="https://www.yubico.com/products/how-the-yubikey-works/" rel="noopener noreferrer"&gt;YubiKey&lt;/a&gt;, &lt;a href="https://cloud.google.com/titan-security-key" rel="noopener noreferrer"&gt;Google Titan&lt;/a&gt; and smartphones.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  FIDO
&lt;/h3&gt;

&lt;p&gt;FIDO stands for Fast IDentity Online. FIDO is a global authentication standard based on public key cryptography developed by the &lt;a href="https://fidoalliance.org/" rel="noopener noreferrer"&gt;FIDO Alliance&lt;/a&gt;. It aims to solve all our password problems. FIDO Credentials are cryptographic key pairs that can be used for authentication.&lt;/p&gt;

&lt;p&gt;Passkeys are made possible by the &lt;a href="https://fidoalliance.org/fido2/" rel="noopener noreferrer"&gt;FIDO2&lt;/a&gt; standard which is made up of Web Authentication (WebAuthn) and Client to Authenticator Protocol (CTAP).&lt;/p&gt;

&lt;h3&gt;
  
  
  Web Authentication
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://webauthn.me/introduction" rel="noopener noreferrer"&gt;Web Authentication&lt;/a&gt; is a &lt;a href="https://www.w3.org/TR/webauthn/" rel="noopener noreferrer"&gt;W3C recommendation&lt;/a&gt; that lets a webpage use a set of JavaScript APIs to talk to authenticators.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F362EsMw63XVyLPh1jvJuZL%2F098194b80c218131f37f0af6435483f4%2Fwebauthn-arch-passkeys.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F362EsMw63XVyLPh1jvJuZL%2F098194b80c218131f37f0af6435483f4%2Fwebauthn-arch-passkeys.jpg" alt="WebAuthn architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The WebAuthn architecture consists of three main entities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authenticator&lt;/strong&gt;: Platform or roaming authenticators that let a user authenticate by confirming their presence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relying Party&lt;/strong&gt;: A server (custom implementation or an Identity Provider like Auth0) that requires authentication. It issues challenges and stores public keys.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client&lt;/strong&gt;: A client consists of the user's browser. The client relays information between an authenticator and a relying party.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Client to Authenticator Protocol
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html" rel="noopener noreferrer"&gt;FIDO Client to Authenticator Protocol&lt;/a&gt; is used for communications with authenticators over a variety of transports like USB, NFC, and Bluetooth. It is used to send requests from WebAuthn to authenticators.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Passkeys are passwordless FIDO credentials implemented using WebAuthn.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Passkeys were originally called FIDO Multi-Device Credentials implemented with the WebAuthn. But recently that definition has evolved to mean any passwordless FIDO credentials that are discoverable by the browser. Passkeys are still evolving and hence this could change as well. But for simplicity let's stick to passkeys as that is the term used by the FIDO Alliance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Types of passkeys
&lt;/h3&gt;

&lt;p&gt;Passkeys have two variants. Synced passkeys (sometimes referred to as multi-device passkeys) and device-bound passkeys (sometimes referred to as hardware-bound passkeys).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F3I3nxEpG2HYNBnQi1BZx3O%2Fb10ff4f77c0d385b9b99ba8bf05cc0b6%2Fpasskey-types.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F3I3nxEpG2HYNBnQi1BZx3O%2Fb10ff4f77c0d385b9b99ba8bf05cc0b6%2Fpasskey-types.jpg" alt="Passkey types"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Synced passkeys
&lt;/h4&gt;

&lt;p&gt;Synced passkeys have a better user experience since the private keys are end-to-end encrypted and synced to the cloud. For example, on the Apple ecosystem, the private key is synced on your iCloud Keychain and you can register on one device and log in to any synced Apple device. The same goes for the Google ecosystem using the Chrome browser and Google Password Manager. Or you can use a password manager like BitWarden or 1Password to store your passkeys.&lt;br&gt;
This kind of passkeys can be restored on new devices. But they are less secure than single device-bound passkeys since your private key is on the cloud and theoretically can be breached.&lt;/p&gt;
&lt;h4&gt;
  
  
  Device-bound passkeys
&lt;/h4&gt;

&lt;p&gt;In the device-bound passkeys, the private key stays on the device itself and you need to authenticate using the same authenticator used for registration. It is slightly less convenient but more secure than synced passkeys. The relying party must support registering multiple credentials for a user so that backup keys can be registered, which is a best practice for device-bound passkeys.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For example, I use a YubiKey with a fingerprint reader since I'm on Linux, and my passkeys are device-bound to that YubiKey. I don't get any roaming or backup benefits like in the Apple or Google ecosystem. But it's not a big deal, in my opinion, since I can register multiple YubiKeys as backup and use them on any device, and it's more secure and way more convenient than password + MFA.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  How does it work?
&lt;/h3&gt;

&lt;p&gt;Let's see how user registration and authentication work with passkeys.&lt;/p&gt;
&lt;h4&gt;
  
  
  User registration
&lt;/h4&gt;

&lt;p&gt;First, let's see how the registration flow works.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczqp7jczw2b16kij43m6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczqp7jczw2b16kij43m6.jpg" alt="Passkey registration flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user begins the registration flow. The relying party provides a randomly generated challenge string.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;navigator.credentials.create()&lt;/code&gt; method of the WebAuthn API is invoked and the user provides approval using their authenticator.&lt;/li&gt;
&lt;li&gt;The authenticator creates a private-public key pair which is unique for the relying party's domain and the user. The private key is used to sign the challenge.

&lt;ul&gt;
&lt;li&gt;The private key is stored on the authenticator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For synced passkeys&lt;/strong&gt;, the private key is also synced to a cloud service for backup and roaming (This is the only place where synced passkeys differ from device-bound passkeys).&lt;/li&gt;
&lt;li&gt;An attestation object is created which contains the public key, signed challenge, credential ID, and certificate.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The attestation object and other metadata are then passed to the relying party by the client-side implementation. The relying party verifies the signed challenge using the public key and registers the user by storing the public key and credential ID along with the user details.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  User authentication
&lt;/h4&gt;

&lt;p&gt;Now, let's see how the login flow works, which is quite similar except for the third step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbue2699a1zczijdhuvy3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbue2699a1zczijdhuvy3.jpg" alt="Passkey login flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user begins the login flow. The relying party provides a randomly generated challenge string.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;navigator.credentials.get()&lt;/code&gt; method of the WebAuthn API is invoked and the user provides approval using their authenticator.&lt;/li&gt;
&lt;li&gt;The authenticator retrieves the private keys for the relying party's domain name.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For synced passkeys&lt;/strong&gt;, if the device is new, the private key is synced from a cloud service if available (This is the only place where synced passkeys differ from device-bound passkeys).&lt;/li&gt;
&lt;li&gt;The user selects the private key for their username. The private key is used to sign the challenge.&lt;/li&gt;
&lt;li&gt;An assertion object is created which contains the signed challenge and credential ID.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The assertion object and other metadata are then passed to the relying party by the client-side implementation. The relying party verifies the signed challenge using the public key stored for the user and authenticates the user.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Why Passkeys?
&lt;/h2&gt;

&lt;p&gt;Let's see why we need passkeys to replace passwords.&lt;/p&gt;

&lt;p&gt;Passkeys are superior to password + traditional OTP MFA in terms of security and usability and they are as secure and more convenient than password + FIDO MFA. Most importantly, you don’t have to remember anything (unless you are like me and forget your YubiKeys all the time).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F6VeM8DTr7thzYZYucQk53A%2Fb16e52d6e9b404cd5fc5d576ba972688%2Fpassword-vs-passkey.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F6VeM8DTr7thzYZYucQk53A%2Fb16e52d6e9b404cd5fc5d576ba972688%2Fpassword-vs-passkey.jpg" alt="password vs passkeys"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Discoverable&lt;/strong&gt;: Passwords are knowledge-based but passkeys are discoverable credentials and the browser can autofill them for a service making it unnecessary for you to remember even usernames. It doesn’t rely on something you know, instead, it relies on something you have or something you are which is more secure from hacking and social engineering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phishing resistant&lt;/strong&gt;: Passkeys cannot be phished as they rely on public key cryptography and are bound to the domain name of the website, making it impossible to work on a spoofed website.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remote attack resistant&lt;/strong&gt;: Passkeys rely on physical keys, like biometric sensors of platform authenticators or roaming authenticators like YubiKey, hence cannot be remotely breached.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breach resistant&lt;/strong&gt;: The website only stores the public key of a user which is useless to an attacker on a data breach on the server side. This makes the server less attractive to hackers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not reusable and shareable*&lt;/strong&gt;: They cannot be reused as they are unique per service and user combination and cannot be shared.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;*&lt;/strong&gt;except for Apple which lets you share a passkey by air-dropping it 🤷🏽.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easier management&lt;/strong&gt;: Passkeys are scalable. Synced passkeys are backed up and replicated across your devices by services like iCloud Keychain, Google Password Manager, Bitwarden, and so on. This makes recovery part of the platform rather than the application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-device authentication&lt;/strong&gt;: Passkeys can also perform cross-device authentication regardless of ecosystem or platform. For example, you can simply use your Android phone as an authenticator for your Apple laptop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F42XVjCL09xrYbViYJEBJKz%2Fe18d7b66fdd0973c45ab58b4c880ee8c%2Fusability.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F42XVjCL09xrYbViYJEBJKz%2Fe18d7b66fdd0973c45ab58b4c880ee8c%2Fusability.jpg" alt="Security and usability spectrum of passkeys"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The security of passkeys is way better than most other combinations. When it comes to user experience, though it is subjective, I think it outperforms all other combinations.&lt;/p&gt;
&lt;h2&gt;
  
  
  How Does Passkeys Differ from WebAuthn Multi-Factor Authentication?
&lt;/h2&gt;

&lt;p&gt;Technically they are very similar since both are implemented with WebAuthn and in the case of device-bound passkeys they are even more similar. However, some differences set them apart.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Passkeys are &lt;strong&gt;&lt;a href="https://www.w3.org/TR/webauthn-2/#discoverable-credential" rel="noopener noreferrer"&gt;Discoverable Credentials&lt;/a&gt;&lt;/strong&gt; and are entirely stored on the authenticator. This means for hardware keys like YubiKey, the private key is stored on the key itself and hence can only hold what its memory allows. They are client-side discoverable during authentication ceremonies and can be used in the &lt;a href="https://passkeys.dev/docs/reference/terms/#autofill-ui" rel="noopener noreferrer"&gt;autofill UI&lt;/a&gt; of the browser. WebAuthn/FIDO-based MFA implementations are &lt;strong&gt;Non-Discoverable Credentials&lt;/strong&gt; or &lt;strong&gt;&lt;a href="https://www.w3.org/TR/webauthn-2/#server-side-credential" rel="noopener noreferrer"&gt;Server-side Credentials&lt;/a&gt;&lt;/strong&gt; and hence are not client-side discoverable. They are stored server-side and the private key is encrypted and sent to the relying party, hence there is no storage limitation on the hardware key.&lt;/li&gt;
&lt;li&gt;WebAuthn MFA does not have a synced option, passkeys do.&lt;/li&gt;
&lt;li&gt;In the case of synced passkeys there are even more differences in terms of usability and security. For example, enrollment needs to be done only once and the private keys are synced to a cloud.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most important difference is that passkeys can be used as first-factor authentication whereas WebAuthn MFA can only be used as a second-factor after user registration with a password.&lt;/p&gt;
&lt;h2&gt;
  
  
  Challenges
&lt;/h2&gt;

&lt;p&gt;There are still some challenges when it comes to passkeys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OS/Browser support&lt;/strong&gt;: It is dependent on the OS and Browser to implement the specs, and we all know how that turns out right? This means the support may not be uniform and can become fragmented and we might never have the same experience everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud vendor reliance&lt;/strong&gt;: It relies on companies like Apple, Google, and Microsoft to save the private keys in their cloud securely and protect them from breaches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise use cases&lt;/strong&gt;: Enterprise users might want more control and flexibility which could be a problem. For example, if an enterprise doesn’t allow iCloud or Google Chrome on their computer, synced passkeys will not work there, only device-bound passkeys will work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reset &amp;amp; Recovery&lt;/strong&gt;: There are no default recovery flows for device-bound passkeys, and applications might still need to implement recovery &amp;amp; reset flows to accommodate all use cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://passkeys.dev/device-support/" rel="noopener noreferrer"&gt;Browser and OS compatibility&lt;/a&gt; is still catching up. As of writing, Chrome has the best support and the Apple ecosystem has the most seamless experience, especially for platform authenticators. Linux does not have any support for platform authenticators. Roaming authenticators have great support on all platforms.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Passkeys are the future of secure authentication. They are more secure and more convenient than passwords and traditional MFA. They are also more secure and more convenient than other passwordless methods like magic links, SMS/Email OTP, and push notifications. The wider adoption of passkeys could finally solve the password problem and make the internet a safer place.&lt;/p&gt;

&lt;p&gt;Passkeys are not just for the future, they are here now. You can start using them today. If you are a developer, you can start implementing them in your applications &lt;a href="https://developer.auth0.com/resources/labs/authentication/passkeys#go-beyond-passwords-with-passkeys" rel="noopener noreferrer"&gt;using Auth0&lt;/a&gt;. If you are a user, you can start using them on &lt;a href="https://passkeys.directory/" rel="noopener noreferrer"&gt;services that support them&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;Check out this follow-up blog post.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/oktadev" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F129%2F9284e7ae-5b8a-49ab-89de-0e5ebe85847b.jpg" alt="Okta"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F178939%2F38a82d99-3a0c-4a47-84fc-76fff3144cda.png" alt=""&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/oktadev/a-passwordless-future-passkeys-for-java-developers-3f0c" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;A Passwordless Future: Passkeys for Java Developers&lt;/h2&gt;
      &lt;h3&gt;Deepu K Sasidharan for Okta ・ Jan 2&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#java&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#spring&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#passkeys&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webauthn&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;I hope that you found this article helpful. Here are some additional resources to learn more about WebAuthn and passkeys.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learnpasskeys.io" rel="noopener noreferrer"&gt;learnpasskeys.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webauthn.me" rel="noopener noreferrer"&gt;webauthn.me&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://passkeys.dev" rel="noopener noreferrer"&gt;passkeys.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fidoalliance.org/passkeys/" rel="noopener noreferrer"&gt;fidoalliance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://auth0.com/blog/our-take-on-passkeys/" rel="noopener noreferrer"&gt;Our Take On Passkeys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://auth0.com/blog/activate-passkeys-let-users-log-in-without-password/" rel="noopener noreferrer"&gt;Activate Passkeys and Let Your Users Log in without a Password&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://auth0.com/blog/how-to-explain-public-key-cryptography-digital-signatures-to-anyone/" rel="noopener noreferrer"&gt;How to Explain Public-Key Cryptography and Digital Signatures to Anyone&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you like this article, please leave a like or a comment.&lt;/p&gt;

&lt;p&gt;You can follow me on &lt;a href="https://mastodon.social/@deepu105" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/deepu05/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>passkeys</category>
      <category>webauthn</category>
    </item>
    <item>
      <title>A Passwordless Future: Passkeys for Java Developers</title>
      <dc:creator>Deepu K Sasidharan</dc:creator>
      <pubDate>Tue, 02 Jan 2024 15:39:21 +0000</pubDate>
      <link>https://forem.com/oktadev/a-passwordless-future-passkeys-for-java-developers-3f0c</link>
      <guid>https://forem.com/oktadev/a-passwordless-future-passkeys-for-java-developers-3f0c</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://auth0.com/blog/webauthn-and-passkeys-for-java-developers/" rel="noopener noreferrer"&gt;auth0.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This blog post is a continuation of my previous &lt;a href="https://auth0.com/blog/webauthn-and-passkeys-for-developers/" rel="noopener noreferrer"&gt;blog post on passkeys&lt;/a&gt;. In this post, you will learn how to implement passkeys using Auth0 and the WebAuthn4j library on your Java applications.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/oktadev" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F129%2F9284e7ae-5b8a-49ab-89de-0e5ebe85847b.jpg" alt="Okta"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F178939%2F38a82d99-3a0c-4a47-84fc-76fff3144cda.png" alt=""&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/oktadev/a-passwordless-future-passkeys-for-java-developers-1f54" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;A Passwordless Future: Passkeys for Developers&lt;/h2&gt;
      &lt;h3&gt;Deepu K Sasidharan for Okta ・ Feb 16&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#security&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#passkeys&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webauthn&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;If you have not read the previous blog post, I would highly recommend you read it first to understand the basics of passkeys and WebAuthn.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passkeys
&lt;/h2&gt;

&lt;p&gt;A passkey is a unique cryptographic key pair that allows you to access online services without using passwords. It is based on &lt;a href="https://en.wikipedia.org/wiki/Public-key_cryptography" rel="noopener noreferrer"&gt;asymmetric public-key cryptography&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Passkeys are passwordless FIDO credentials implemented using WebAuthn.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why Passkeys?
&lt;/h2&gt;

&lt;p&gt;Passkeys are superior to password + traditional OTP MFA in terms of security and usability and they are as secure and more convenient than password + FIDO MFA. Most importantly, you don’t have to remember anything.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F6VeM8DTr7thzYZYucQk53A%2Fb16e52d6e9b404cd5fc5d576ba972688%2Fpassword-vs-passkey.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F6VeM8DTr7thzYZYucQk53A%2Fb16e52d6e9b404cd5fc5d576ba972688%2Fpassword-vs-passkey.jpg" alt="password vs passkeys"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's See Passkeys in Action with Auth0
&lt;/h2&gt;

&lt;p&gt;Let's build a Spring Boot web app and secure it using passkeys with the help of Auth0 by Okta. You can find a sample app &lt;a href="https://a0.to/jfall-passkey" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt; if you just want to try passkeys.&lt;/p&gt;

&lt;p&gt;Before you get started, you will need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Java 17 or higher. You can use &lt;a href="https://sdkman.io/" rel="noopener noreferrer"&gt;SDKMAN!&lt;/a&gt; to install Java if you don't have it already.&lt;/li&gt;
&lt;li&gt;  A free Auth0 account. &lt;a href="https://a0.to/blog_signup" rel="noopener noreferrer"&gt;Sign up&lt;/a&gt; if you don't have one already.&lt;/li&gt;
&lt;li&gt;  The Auth0 CLI. &lt;a href="https://github.com/auth0/auth0-cli#installation" rel="noopener noreferrer"&gt;Install&lt;/a&gt; the CLI if you don't have it and log in to your Auth0 account using the &lt;code&gt;auth0 login&lt;/code&gt; command.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Create a Spring Boot application
&lt;/h3&gt;

&lt;p&gt;Create a new Spring Boot application using the &lt;a href="https://start.spring.io/" rel="noopener noreferrer"&gt;Spring Initializr&lt;/a&gt;. You can use the web version or the curl command below. Use the default for most of the options. For the dependencies, select &lt;code&gt;web&lt;/code&gt;, and &lt;code&gt;okta&lt;/code&gt;. For the build tool, select &lt;code&gt;Gradle&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-G&lt;/span&gt; https://start.spring.io/starter.tgz &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;web,okta &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;baseDir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;passkey-demo &lt;span class="se"&gt;\&lt;/span&gt;
 | &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xzvf&lt;/span&gt; -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  The &lt;code&gt;web&lt;/code&gt; dependency provides Spring Web MVC with basic HTTP REST functionality.&lt;/li&gt;
&lt;li&gt;  The &lt;code&gt;okta&lt;/code&gt; dependency provides the Okta Spring Boot Starter, which provides the required dependencies and configuration to add OIDC authentication to your application.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Add a web controller
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Imports are omitted for brevity so make sure to import them using your IDE.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Open the created starter application in your favorite IDE. Add a simple web controller to the application. Create a new file &lt;code&gt;src/main/java/com/example/demo/HomeController.java&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomeController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@AuthenticationPrincipal&lt;/span&gt; &lt;span class="nc"&gt;OidcUser&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Hello, "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getFullName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"!"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This controller will handle requests to the &lt;code&gt;/&lt;/code&gt; path.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you run the application using &lt;code&gt;./gradlew bootRun&lt;/code&gt;, you will see a login page from the Okta Spring Boot starter instead of your home screen. This is OK, and you will be able to configure this soon. You can comment out the &lt;code&gt;okta-spring-boot-starter&lt;/code&gt; dependency in the &lt;code&gt;build.gradle&lt;/code&gt; file if you want to run the application at this point.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Enable passkeys on your Auth0 tenant
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Log in to your &lt;a href="https://manage.auth0.com" rel="noopener noreferrer"&gt;Auth0 Dashboard&lt;/a&gt; and navigate to &lt;strong&gt;Authentication&lt;/strong&gt; &amp;gt; &lt;strong&gt;Database&lt;/strong&gt; &amp;gt; &lt;strong&gt;Username-Password-Authentication&lt;/strong&gt;.

&lt;ol&gt;
&lt;li&gt;If the second tab says &lt;strong&gt;Authentication Methods&lt;/strong&gt;, your tenant supports passkeys, proceed to the next step.&lt;/li&gt;
&lt;li&gt;If the second tab says &lt;strong&gt;Password Policy&lt;/strong&gt;, your tenant doesn't support passkeys, &lt;a href="https://auth0.com/docs/get-started/auth0-overview/create-tenants" rel="noopener noreferrer"&gt;Create a new tenant&lt;/a&gt; and proceed to the next step.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Navigate to &lt;strong&gt;Authentication&lt;/strong&gt; &amp;gt; &lt;strong&gt;Authentication Profile&lt;/strong&gt; and select &lt;strong&gt;Identifier First&lt;/strong&gt;. &lt;strong&gt;Save&lt;/strong&gt; your changes.&lt;/li&gt;

&lt;li&gt;Navigate to &lt;strong&gt;Authentication&lt;/strong&gt; &amp;gt; &lt;strong&gt;Database&lt;/strong&gt; &amp;gt; &lt;strong&gt;Username-Password-Authentication&lt;/strong&gt; and select the &lt;strong&gt;Authentication Methods&lt;/strong&gt; tab and enable &lt;strong&gt;Passkey&lt;/strong&gt;.&lt;/li&gt;

&lt;/ol&gt;

&lt;h3&gt;
  
  
  Configure OIDC Authentication with Auth0
&lt;/h3&gt;

&lt;p&gt;Configure the application to use Auth0 as the Identity Provider (IdP). You can use the Auth0 CLI to create a new authorization server application. Run the following command to create a new application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;auth0 apps create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"Spring Boot Passkeys"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"Spring Boot Example"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--type&lt;/span&gt; regular &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--callbacks&lt;/span&gt; http://localhost:8080/login/oauth2/code/okta &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--logout-urls&lt;/span&gt; http://localhost:8080 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--reveal-secrets&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  The &lt;code&gt;--type&lt;/code&gt; option specifies that you use a regular web application.&lt;/li&gt;
&lt;li&gt;  The &lt;code&gt;--callbacks&lt;/code&gt; option specifies the callback URL for the application.&lt;/li&gt;
&lt;li&gt;  The &lt;code&gt;--logout-urls&lt;/code&gt; option specifies the logout URL for the application.&lt;/li&gt;
&lt;li&gt;  The &lt;code&gt;--reveal-secrets&lt;/code&gt; option will display the client secret in the output.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also use the &lt;code&gt;auth0 apps update&lt;/code&gt; command to update the application with the callback and logout URLs.&lt;/p&gt;

&lt;p&gt;Note down the Auth0 issuer (for example, &lt;code&gt;https://dev-12345678.us.auth0.com/&lt;/code&gt;), &lt;code&gt;CLIENT ID&lt;/code&gt;, and &lt;code&gt;CLIENT SECRET&lt;/code&gt; from the output. You will use these values in the next step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure the Spring Boot application
&lt;/h3&gt;

&lt;p&gt;Configure the application by creating an &lt;code&gt;application.properties&lt;/code&gt; file in the applications root folder with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# trailing `/` is important for issuer URI&lt;/span&gt;
&lt;span class="s"&gt;okta.oauth2.issuer=https://&amp;lt;AUTH0_domain&amp;gt;/&lt;/span&gt;
&lt;span class="s"&gt;okta.oauth2.client-id=&amp;lt;AUTH0_clientId&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;okta.oauth2.client-secret=&amp;lt;AUTH0_clientSecret&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the &lt;code&gt;application.properties&lt;/code&gt; file to the &lt;code&gt;.gitignore&lt;/code&gt; file to avoid committing the secrets to the repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run the application
&lt;/h3&gt;

&lt;p&gt;To run the application, execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew bootRun
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application should start successfully. Navigate to &lt;a href="http://localhost:8080/" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt; in your browser. You will be redirected to the Auth0 universal login page for authentication.&lt;/p&gt;

&lt;p&gt;Click on the &lt;strong&gt;Sign up&lt;/strong&gt; link to register a new user. Enter any email address and click &lt;strong&gt;Continue&lt;/strong&gt;. You will now be prompted to register a passkey.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F6HjspO1xZy3n4wOCalfmpQ%2Fa6b88eb00dab38ee7c724536b69ee4c8%2Fregister-screen.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F6HjspO1xZy3n4wOCalfmpQ%2Fa6b88eb00dab38ee7c724536b69ee4c8%2Fregister-screen.jpg" alt="Registration Screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a passkey using your platform authenticator or roaming authenticator like YubiKey. Once you have registered a passkey, you should be redirected back to the application and see the welcome message.&lt;/p&gt;

&lt;p&gt;Open a new incognito window and navigate to &lt;a href="http://localhost:8080/" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;. You will be prompted to sign in using your passkey. Once you have signed in, you will see the welcome message.&lt;/p&gt;

&lt;p&gt;Isn't that cool? You just implemented passkeys in your Spring Boot application with so little effort thanks to Auth0.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebAuthn for Java
&lt;/h2&gt;

&lt;p&gt;Though Web Authentication’s user experience is a client-side implementation using JavaScript, the backend or Relying party can be a Java server. Ideally using an IdP like Auth0 would be the best option since it takes care of all the heavy lifting for you. But if you want to implement it yourself and walk the harder path, you can use one of the below libraries.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://github.com/webauthn4j/webauthn4j" rel="noopener noreferrer"&gt;WebAuthn4j&lt;/a&gt;: A 100% FIDO2 conformant library with support for all attestation formats and validation. It is used by Keycloak and Spring Security.&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://github.com/Yubico/java-webauthn-server" rel="noopener noreferrer"&gt;java-webauthn-server&lt;/a&gt;: A library from Yubico that supports many attestation format. But it is not 100% FIDO2 conformant.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  WebAuthn4j with Spring Security in Action
&lt;/h2&gt;

&lt;p&gt;Let's look at a simple Spring Boot application that uses passkeys for authentication without using an IdP. You can find the sample app &lt;a href="https://a0.to/jfall-webauthn" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clone and run the application
&lt;/h3&gt;

&lt;p&gt;Start by cloning the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/deepu105/webauthn4j-spring-boot-passkeys-demo.git

&lt;span class="nb"&gt;cd &lt;/span&gt;webauthn4j-spring-boot-passkeys-demo
./gradlew bootRun
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit &lt;a href="http://localhost:8080/" rel="noopener noreferrer"&gt;http://localhost:8080/&lt;/a&gt;. You should see the below screen. Try registering a new user with passkeys and log in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F6AMgTTV5zvVh1kqb3Y3MjQ%2Fad84606693d09195dc43a4aa1c5ea8a5%2Fregister-screen-webauthn4j.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F6AMgTTV5zvVh1kqb3Y3MjQ%2Fad84606693d09195dc43a4aa1c5ea8a5%2Fregister-screen-webauthn4j.jpg" alt="Sign up Screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  WebAuthn4j configuration
&lt;/h3&gt;

&lt;p&gt;Let's look at some of the important parts of the application.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The &lt;code&gt;webauthn4j-spring-security-core&lt;/code&gt; dependency, in &lt;code&gt;build.gradle&lt;/code&gt; file, provides the Spring Security integration for WebAuthn4j.&lt;/li&gt;
&lt;li&gt;  The required beans for WebAuthn4j are configured in &lt;code&gt;src/main/java/com/example/demo/config/WebSecurityBeanConfig.java&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  The &lt;code&gt;InMemoryWebAuthnAuthenticatorManager&lt;/code&gt; is used to keep things simple but it means authenticator data is lost on application restart. For production use, it is better to implement the &lt;code&gt;WebAuthnAuthenticatorManager&lt;/code&gt; interface and persist credential IDs for users.&lt;/li&gt;
&lt;li&gt;  WebAuthn4j is configured using the standard Spring Security filter chain in &lt;code&gt;src/main/java/com/example/demo/config/WebSecurityConfig.java&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="nd"&gt;@EnableWebSecurity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WebSecurityConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;SecurityFilterChain&lt;/span&gt; &lt;span class="nf"&gt;filterChain&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpSecurity&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;AuthenticationManager&lt;/span&gt; &lt;span class="n"&gt;authenticationManager&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// WebAuthn Login&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apply&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WebAuthnLoginConfigurer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;webAuthnLogin&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;defaultSuccessUrl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;failureHandler&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Login error"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendRedirect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/login?error=Login failed: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="o"&gt;})&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;attestationOptionsEndpoint&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rp&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"WebAuthn4J Passkeys Demo"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pubKeyCredParams&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="c1"&gt;// supported algorithms for cryptography&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PublicKeyCredentialParameters&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PublicKeyCredentialType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PUBLIC_KEY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;COSEAlgorithmIdentifier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ES256&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PublicKeyCredentialParameters&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PublicKeyCredentialType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PUBLIC_KEY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;COSEAlgorithmIdentifier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RS256&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;attestation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AttestationConveyancePreference&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DIRECT&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;uvm&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;credProps&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;extensionProviders&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertionOptionsEndpoint&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;extensionProviders&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// 'publickey-credentials-get *' allows getting WebAuthn credentials to all nested browsing contexts (iframes) regardless of their origin.&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;permissionsPolicy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"publickey-credentials-get *"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
            &lt;span class="c1"&gt;// Disable "X-Frame-Options" to allow cross-origin iframe access&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;frameOptions&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Customizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withDefaults&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;disable&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;});&lt;/span&gt;

        &lt;span class="c1"&gt;// Authorization&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authorizeHttpRequests&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authz&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;authz&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestMatchers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpMethod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;GET&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/login"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;permitAll&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestMatchers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpMethod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;POST&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/signup"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;permitAll&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;anyRequest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;access&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getWebExpressionAuthorizationManager&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@webAuthnSecurityExpression.isWebAuthnAuthenticated(authentication)"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exceptionHandling&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eh&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;eh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accessDeniedHandler&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accessDeniedException&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access denied"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accessDeniedException&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendRedirect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/login"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}));&lt;/span&gt;

        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authenticationManager&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authenticationManager&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// As WebAuthn has its own CSRF protection mechanism (challenge), CSRF token is disabled here&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;csrf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csrf&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;csrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;csrfTokenRepository&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CookieCsrfTokenRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withHttpOnlyFalse&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="n"&gt;csrf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ignoringRequestMatchers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/webauthn/**"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;});&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The endpoints are configured in &lt;code&gt;src/main/java/com/example/demo/web/WebAuthnSampleController.java&lt;/code&gt;. The &lt;code&gt;/&lt;/code&gt; and &lt;code&gt;/login&lt;/code&gt; endpoints are quite simple and self-explanatory. The &lt;code&gt;/signup&lt;/code&gt; endpoint handles the WebAuthn registration request using WebAuthn4j. The request is first validated using &lt;code&gt;WebAuthnRegistrationRequestValidator&lt;/code&gt; and then the authenticator is created using &lt;code&gt;WebAuthnAuthenticatorManager&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Controller&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WebAuthnSampleController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/signup"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpServletRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@Valid&lt;/span&gt; &lt;span class="nd"&gt;@ModelAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"userForm"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;UserCreateForm&lt;/span&gt; &lt;span class="n"&gt;userCreateForm&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BindingResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RedirectAttributes&lt;/span&gt; &lt;span class="n"&gt;redirectAttributes&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasErrors&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"errorMessage"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Your input needs correction."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User input validation failed."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;VIEW_LOGIN&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="nc"&gt;WebAuthnRegistrationRequestValidationResponse&lt;/span&gt; &lt;span class="n"&gt;registrationRequestValidationResponse&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;registrationRequestValidationResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registrationRequestValidator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;userCreateForm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getClientDataJSON&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;userCreateForm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAttestationObject&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;userCreateForm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTransports&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;userCreateForm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getClientExtensions&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WebAuthnException&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;WebAuthnAuthenticationException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"errorMessage"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Authenticator registration request validation failed. Please try again."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"WebAuthn registration request validation failed."&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;VIEW_LOGIN&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userCreateForm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUsername&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;authenticator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebAuthnAuthenticatorImpl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"authenticator"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;registrationRequestValidationResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAttestationObject&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getAuthenticatorData&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getAttestedCredentialData&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;registrationRequestValidationResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAttestationObject&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getAttestationStatement&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;registrationRequestValidationResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAttestationObject&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getAuthenticatorData&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSignCount&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;registrationRequestValidationResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTransports&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;registrationRequestValidationResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRegistrationExtensionsClientOutputs&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;registrationRequestValidationResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAttestationObject&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getAuthenticatorData&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getExtensions&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;webAuthnAuthenticatorManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createAuthenticator&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authenticator&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;IllegalArgumentException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"errorMessage"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Registration failed. The user may already be registered."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Registration failed."&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;VIEW_LOGIN&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RuntimeException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"errorMessage"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Registration failed by unexpected error."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Registration failed."&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;VIEW_LOGIN&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"successMessage"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"User registration successful. Please login."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;VIEW_LOGIN&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Client-side configuration
&lt;/h3&gt;

&lt;p&gt;The file &lt;code&gt;src/main/resources/templates/login.html&lt;/code&gt; handles login and sign-up. The login button will invoke the &lt;code&gt;navigator.credentials.get()&lt;/code&gt; API and the register button will invoke the &lt;code&gt;navigator.credentials.create()&lt;/code&gt; API. The buttons submit the corresponding forms with the input data in them. All inputs except the &lt;code&gt;username&lt;/code&gt; field are hidden as their data will be set using JavaScript.&lt;/p&gt;

&lt;p&gt;WebAuthn4j exposes &lt;code&gt;/webauthn/attestation/options&lt;/code&gt; endpoint in the application to fetch the registration options. Some of the option parameters need to be decoded from base64URL. The &lt;a href="https://github.com/deepu105/base64url-arraybuffer" rel="noopener noreferrer"&gt;base64url-arraybuffer&lt;/a&gt; library is used for this. The options are then passed to the &lt;code&gt;navigator.credentials.create()&lt;/code&gt; API. The response from the API is then updated to the form fields and submitted to the &lt;code&gt;/signup&lt;/code&gt; endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;signup-form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userHandle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userHandle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;optionsRes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/webauthn/attestation/options&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;optionsRes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;publicKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;base64url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;base64url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;excludeCredentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;excludeCredentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;base64url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;})),&lt;/span&gt;
            &lt;span class="na"&gt;authenticatorSelection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;requireResidentKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;userVerification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;discouraged&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;credential&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;publicKey&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clientDataJSON&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientDataJSON&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attestationObject&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attestationObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clientExtensions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getClientExtensionResults&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;signup-form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error:%s, Message:%s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WebAuthn4j exposes &lt;code&gt;/webauthn/assertion/options&lt;/code&gt; endpoint in the application to fetch the authentication options. Some of the option parameters need to be decoded from base64URL. The options are then passed to the &lt;code&gt;navigator.credentials.get()&lt;/code&gt; API. The response from the API is then updated to the form fields and submitted to the &lt;code&gt;/login&lt;/code&gt; endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;login-form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;optionsRes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/webauthn/assertion/options&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;optionsRes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;publicKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;base64url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;userVerification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;preferred&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;credential&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;publicKey&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;credentialId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loginClientDataJSON&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientDataJSON&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;authenticatorData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authenticatorData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;signature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loginClientExtensions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getClientExtensionResults&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;login-form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error:%s, Message:%s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;You have now learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  How to implement passkeys using an IdP like Auth0.&lt;/li&gt;
&lt;li&gt;  You also learned how to configure the application to use Auth0 as the Identity Provider and how to configure Auth0 for passkey support.&lt;/li&gt;
&lt;li&gt;  Roll your own passkey solution using WebAuthn4j and Spring Security.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Passkeys are the future of authentication. They are more secure and convenient than traditional passwords and OTPs. Though you could roll your own solution using WebAuthn4j, it is always better to use an IdP like Auth0 to handle the heavy lifting for you and take care of all the security best practices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;I hope that you found this article helpful. Here are some additional resources to learn more about WebAuthn and passkeys.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://learnpasskeys.io" rel="noopener noreferrer"&gt;learnpasskeys.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://webauthn.me" rel="noopener noreferrer"&gt;webauthn.me&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://passkeys.dev" rel="noopener noreferrer"&gt;passkeys.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://fidoalliance.org/passkeys/" rel="noopener noreferrer"&gt;fidoalliance&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you like this article, please leave a like or a comment.&lt;/p&gt;

&lt;p&gt;You can follow me on &lt;a href="https://mastodon.social/@deepu105" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/deepu05/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>spring</category>
      <category>passkeys</category>
      <category>webauthn</category>
    </item>
    <item>
      <title>Deploy Secure Spring Boot Microservices on Amazon EKS Using Terraform and Kubernetes</title>
      <dc:creator>Deepu K Sasidharan</dc:creator>
      <pubDate>Thu, 23 Nov 2023 14:34:44 +0000</pubDate>
      <link>https://forem.com/oktadev/deploy-secure-spring-boot-microservices-on-amazon-eks-using-terraform-and-kubernetes-3f0d</link>
      <guid>https://forem.com/oktadev/deploy-secure-spring-boot-microservices-on-amazon-eks-using-terraform-and-kubernetes-3f0d</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://auth0.com/blog/terraform-eks-java-microservices/" rel="noopener noreferrer"&gt;auth0.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When it comes to infrastructure, public clouds are the most popular choice these days, and Amazon Web Services (AWS) is the go-to option. If you are working with microservices, orchestrating their deployments becomes essential. Kubernetes is the de-facto choice for orchestrating microservices, and most public cloud providers offer managed Kubernetes services. For AWS, the managed Kubernetes service is Amazon Elastic Kubernetes Service (EKS).&lt;/p&gt;

&lt;p&gt;Deploying and managing microservices on the public cloud is not without its challenges. Each cloud service has its own complexities. Among them, Amazon EKS is one of the most flexible but also one of the most difficult Kubernetes services to use. EKS utilizes clever orchestrations on top of other AWS services like EC2, EBS, and more.&lt;/p&gt;

&lt;p&gt;To run a microservice stack on EKS, you will need to spend extra time and effort setting it up and managing it. This is where infrastructure as code (IaC) tools like &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; come in handy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;So here is what you will learn to do today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scaffold a Java microservice stack using JHipster, Spring Boot, and Spring Cloud&lt;/li&gt;
&lt;li&gt;Create an EKS cluster, Virtual Private Cloud (VPC), subnets, and required Kubernetes add-ons using Terraform on AWS&lt;/li&gt;
&lt;li&gt;Set up Terraform scripts for OpenID Connect (OIDC) authentication for the microservice stack using Auth0 by Okta&lt;/li&gt;
&lt;li&gt;Build and deploy the microservice stack to the cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://portal.aws.amazon.com/billing/signup" rel="noopener noreferrer"&gt;AWS account&lt;/a&gt; with the &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/security_iam_id-based-policy-examples.html" rel="noopener noreferrer"&gt;IAM permissions to create EKS clusters&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AWS CLI &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;installed&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html" rel="noopener noreferrer"&gt;configured&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/tools/" rel="noopener noreferrer"&gt;kubectl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; installed and configured&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.terraform.io/downloads" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; CLI&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sdkman.io/usage" rel="noopener noreferrer"&gt;Java 11+&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://auth0.github.io/auth0-cli/" rel="noopener noreferrer"&gt;Auth0 CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;[Optional] &lt;a href="https://www.jhipster.tech/installation/" rel="noopener noreferrer"&gt;JHipster&lt;/a&gt; CLI&lt;/li&gt;
&lt;li&gt;[Optional] &lt;a href="https://github.com/kdash-rs/kdash" rel="noopener noreferrer"&gt;KDash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Terraform, Why Not Cloud Formation?
&lt;/h2&gt;

&lt;p&gt;At this point, the first question that might pop up in your mind would be, "Why not use &lt;a href="https://aws.amazon.com/cloudformation/" rel="noopener noreferrer"&gt;CloudFormation&lt;/a&gt;?". It's a good question; after all, CloudFormation is built by AWS and hence sounds like an excellent solution to manage AWS resources. But anyone who has tried both CloudFormation and Terraform will probably tell you to forget that CloudFormation even exists. I think CloudFormation is far more complex and less developer-friendly than Terraform. You also need to write a lot more boilerplate with CloudFormation in YAML or JSON. Yikes! In contrast, Terraform is elegant and concise, and the syntax is easier to read and write. It's cross-platform, developer-friendly, and does not require a lot of ramp-up time.&lt;/p&gt;

&lt;p&gt;Okay, now that we have that sorted, let's dive into the steps to deploy our microservice stack on EKS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaffold a Java Microservice Stack Using JHipster
&lt;/h2&gt;

&lt;p&gt;To start, you will scaffold a Java microservice stack using &lt;a href="https://www.jhipster.tech" rel="noopener noreferrer"&gt;JHipster&lt;/a&gt;, Spring Boot, and Consul. JHipster is an excellent tool to generate a microservice stack with Spring Boot, Angular/React/Vue.js, and other modern frameworks. You can use another microservice stack if you want. If you prefer using the same application as in this demo, then you can either scaffold it using JHipster &lt;a href="https://www.jhipster.tech/jdl/intro" rel="noopener noreferrer"&gt;JDL&lt;/a&gt; or clone the sample repository from &lt;a href="https://github.com/oktadev/auth0-jhipster-k8s-eks-microservices-example" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Here is how you can scaffold your microservice stack using JHipster:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F4yatkCE8FKwJaySwnBRNeG%2Fee5ecddec53a48ffc712b0aaa8a1e70e%2Fjh-microservice-auth0-consul.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F4yatkCE8FKwJaySwnBRNeG%2Fee5ecddec53a48ffc712b0aaa8a1e70e%2Fjh-microservice-auth0-consul.jpg" alt="JHipster microservice architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1: Scaffold the microservice stack using JHipster&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use JHipster version &lt;strong&gt;7.9.3&lt;/strong&gt; or &lt;strong&gt;8.0.0&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;jhipster-microservice-stack
&lt;span class="nb"&gt;cd &lt;/span&gt;jhipster-microservice-stack
&lt;span class="c"&gt;# download the JDL file.&lt;/span&gt;
jhipster download https://raw.githubusercontent.com/oktadev/auth0-jhipster-k8s-eks-microservices-example/main/apps.jdl
&lt;span class="c"&gt;# Update the `dockerRepositoryName` property to use your Docker Repository URI/Name.&lt;/span&gt;
&lt;span class="c"&gt;# scaffold the apps.&lt;/span&gt;
jhipster jdl apps.jdl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 2: Clone the sample repository&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/oktadev/auth0-jhipster-k8s-eks-microservices-example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In either case, remember to change the Docker repository name in the JDL file and Kubernetes manifests to match your Docker repository.&lt;/p&gt;

&lt;p&gt;The JHipster scaffolded sample application has a gateway application and two microservices. It uses &lt;a href="https://www.consul.io/" rel="noopener noreferrer"&gt;Consul&lt;/a&gt; for service discovery and centralized configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an EKS Cluster Using Terraform
&lt;/h2&gt;

&lt;p&gt;Now let us move on to the important part of the tutorial. Creating an EKS cluster in AWS is not as straightforward as in other cloud platforms. You need to also create a lot more resources for everything to work correctly without surprises. You will be using a bunch of Terraform providers to help with this, and you will also use some prebuilt Terraform modules like &lt;a href="https://github.com/terraform-aws-modules/terraform-aws-vpc" rel="noopener noreferrer"&gt;AWS VPC Terraform module&lt;/a&gt; and &lt;a href="https://github.com/aws-ia/terraform-aws-eks-blueprints" rel="noopener noreferrer"&gt;Amazon EKS Blueprints for Terraform&lt;/a&gt; to reduce the amount of boilerplate you need to write.&lt;/p&gt;

&lt;p&gt;These are the AWS resources and VPC architecture you will create:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F3Mp7zJ1fmWkxng0Pqrxnft%2F905300cce62bf12b986a2177f5b7d11c%2Ftf-eks-arch.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F3Mp7zJ1fmWkxng0Pqrxnft%2F905300cce62bf12b986a2177f5b7d11c%2Ftf-eks-arch.jpg" alt="AWS EKS and VPC architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Build the Terraform configuration
&lt;/h3&gt;

&lt;p&gt;First, make sure you use a specific version of the providers as different versions might use different attributes and features. Create a &lt;code&gt;versions.tf&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;terraform
&lt;span class="nb"&gt;cd &lt;/span&gt;terraform
&lt;span class="nb"&gt;touch &lt;/span&gt;versions.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following to the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.0.0"&lt;/span&gt;

  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 4.47"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;kubernetes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/kubernetes"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 2.20"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;helm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/helm"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 2.9"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you need to define variables and configure the providers. Create a &lt;code&gt;config.tf&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;config.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following to the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ##  To save state in s3. Update to suit your needs&lt;/span&gt;
&lt;span class="c1"&gt;# backend "s3" {&lt;/span&gt;
&lt;span class="c1"&gt;#   bucket = "create-an-s3-bucket-and-provide-name-here"&lt;/span&gt;
&lt;span class="c1"&gt;#   region = local.region&lt;/span&gt;
&lt;span class="c1"&gt;#   key    = "eks-cluster-with-new-vpc/terraform.tfstate"&lt;/span&gt;
&lt;span class="c1"&gt;# }&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eu-west-1"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS region"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_availability_zones"&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"okta-auth0-jhipster-eks"&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.27"&lt;/span&gt;
  &lt;span class="nx"&gt;instance_types&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"t2.large"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# can be multiple, comma separated&lt;/span&gt;

  &lt;span class="nx"&gt;vpc_cidr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
  &lt;span class="nx"&gt;azs&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Blueprint&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="nx"&gt;GitHubRepo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github.com/aws-ia/terraform-aws-eks-blueprints"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Kubernetes provider&lt;/span&gt;
&lt;span class="c1"&gt;# You should **not** schedule deployments and services in this workspace.&lt;/span&gt;
&lt;span class="c1"&gt;# This keeps workspaces modular (one for provision EKS, another for scheduling&lt;/span&gt;
&lt;span class="c1"&gt;# Kubernetes resources) as per best practices.&lt;/span&gt;
&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;host&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_endpoint&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_ca_certificate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_certificate_authority_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;exec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;api_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"client.authentication.k8s.io/v1beta1"&lt;/span&gt;
    &lt;span class="nx"&gt;command&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt;
    &lt;span class="c1"&gt;# This requires the awscli to be installed locally where Terraform is executed&lt;/span&gt;
    &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"eks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"get-token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--cluster-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"helm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;kubernetes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;host&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_endpoint&lt;/span&gt;
    &lt;span class="nx"&gt;cluster_ca_certificate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_certificate_authority_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;exec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;api_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"client.authentication.k8s.io/v1beta1"&lt;/span&gt;
      &lt;span class="nx"&gt;command&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt;
      &lt;span class="c1"&gt;# This requires the awscli to be installed locally where Terraform is executed&lt;/span&gt;
      &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"eks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"get-token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--cluster-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can uncomment the &lt;code&gt;backend&lt;/code&gt; section above to save the state in AWS S3 instead of your local filesystem. This is recommended for production setup so that everyone in a team has the same state. This file defines configurable and local variables used across the workspace and configures some of the providers used. The Kubernetes provider is included in this file so the EKS module can complete successfully. Otherwise, it throws an error when creating &lt;code&gt;kubernetes_config_map.aws_auth&lt;/code&gt;. The Helm provider is used to install Kubernetes add-ons to the cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build the VPC
&lt;/h3&gt;

&lt;p&gt;Next up, you need a VPC, subnets, route tables, and other networking bits. You will use the &lt;code&gt;vpc&lt;/code&gt; module from the &lt;a href="https://github.com/terraform-aws-modules" rel="noopener noreferrer"&gt;&lt;code&gt;terraform-aws-modules&lt;/code&gt;&lt;/a&gt; repository. This module is a wrapper around the AWS VPC module. It makes it easier to configure VPCs and all the other required networking resources. Create a &lt;code&gt;vpc.tf&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;vpc.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following to the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;#---------------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;# VPC, Subnets, Internet gateway, Route tables, etc.&lt;/span&gt;
&lt;span class="c1"&gt;#---------------------------------------------------------------&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-aws-modules/vpc/aws"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.0"&lt;/span&gt;

  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;cidr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt;

  &lt;span class="nx"&gt;azs&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;azs&lt;/span&gt;
  &lt;span class="nx"&gt;public_subnets&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;azs&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cidrsubnet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
  &lt;span class="nx"&gt;private_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;azs&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cidrsubnet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

  &lt;span class="nx"&gt;enable_nat_gateway&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;single_nat_gateway&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_dns_hostnames&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# Manage so we can name them&lt;/span&gt;
  &lt;span class="nx"&gt;manage_default_network_acl&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;default_network_acl_tags&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${local.name}-default"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;manage_default_route_table&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;default_route_table_tags&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${local.name}-default"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;manage_default_security_group&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;default_security_group_tags&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${local.name}-default"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;public_subnet_tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"kubernetes.io/cluster/${local.name}"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"shared"&lt;/span&gt;
    &lt;span class="s2"&gt;"kubernetes.io/role/elb"&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;private_subnet_tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"kubernetes.io/cluster/${local.name}"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"shared"&lt;/span&gt;
    &lt;span class="s2"&gt;"kubernetes.io/role/internal-elb"&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new VPC, three private subnets, and three public subnets,&lt;/li&gt;
&lt;li&gt;An Internet gateway and NAT gateway for the public subnets,&lt;/li&gt;
&lt;li&gt;and AWS routes for the gateways, public/private route tables, and route table associations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Build the EKS cluster
&lt;/h3&gt;

&lt;p&gt;Now that you have the networking part done, you can build configurations for the EKS cluster and its add-ons. You will use the &lt;a href="https://github.com/terraform-aws-modules" rel="noopener noreferrer"&gt;&lt;code&gt;terraform-aws-modules&lt;/code&gt;&lt;/a&gt; to create the EKS cluster and &lt;code&gt;eks_blueprints&lt;/code&gt; module from &lt;a href="https://aws-ia.github.io/terraform-aws-eks-blueprints/" rel="noopener noreferrer"&gt;&lt;code&gt;terraform-aws-eks-blueprints&lt;/code&gt;&lt;/a&gt;to configure EKS add-ons.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;eks-cluster.tf&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;eks-cluster.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following to the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;#---------------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;# EKS cluster, worker nodes, security groups, IAM roles, K8s add-ons, etc.&lt;/span&gt;
&lt;span class="c1"&gt;#---------------------------------------------------------------&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"eks"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-aws-modules/eks/aws"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 19.15"&lt;/span&gt;

  &lt;span class="nx"&gt;cluster_name&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_version&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_version&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_endpoint_public_access&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;                         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_id&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_ids&lt;/span&gt;                     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets&lt;/span&gt;

  &lt;span class="c1"&gt;# EKS Addons&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_addons&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws-ebs-csi-driver&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;most_recent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;coredns&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="nx"&gt;kube-proxy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="nx"&gt;vpc-cni&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;eks_managed_node_group_defaults&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# Needed by the aws-ebs-csi-driver&lt;/span&gt;
    &lt;span class="nx"&gt;iam_role_additional_policies&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;AmazonEBSCSIDriverPolicy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;eks_managed_node_groups&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;initial&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;instance_types&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_types&lt;/span&gt;
      &lt;span class="nx"&gt;min_size&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
      &lt;span class="nx"&gt;max_size&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
      &lt;span class="nx"&gt;desired_size&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
      &lt;span class="nx"&gt;subnet_ids&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"eks_blueprints_addons"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github.com/aws-ia/terraform-aws-eks-blueprints//modules/kubernetes-addons?ref=v4.32.1"&lt;/span&gt;

  &lt;span class="nx"&gt;eks_cluster_id&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_name&lt;/span&gt;
  &lt;span class="nx"&gt;eks_cluster_endpoint&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_endpoint&lt;/span&gt;
  &lt;span class="nx"&gt;eks_cluster_version&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_version&lt;/span&gt;
  &lt;span class="nx"&gt;eks_oidc_provider&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oidc_provider&lt;/span&gt;
  &lt;span class="nx"&gt;eks_oidc_provider_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oidc_provider_arn&lt;/span&gt;

  &lt;span class="c1"&gt;# K8S Add-ons&lt;/span&gt;
  &lt;span class="nx"&gt;enable_aws_load_balancer_controller&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_metrics_server&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_aws_cloudwatch_metrics&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# To update local kubeconfig with new cluster details&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"null_resource"&lt;/span&gt; &lt;span class="s2"&gt;"kubeconfig"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints_addons&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"local-exec"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws eks --region ${local.region}  update-kubeconfig --name $AWS_CLUSTER_NAME"&lt;/span&gt;
    &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;AWS_CLUSTER_NAME&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;eks&lt;/code&gt; module definition creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EKS Cluster Control plane with one &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/managed-node-groups.html" rel="noopener noreferrer"&gt;managed node group&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;Cluster and node security groups and rules, IAM roles and policies required,&lt;/li&gt;
&lt;li&gt;Amazon EKS add-ons ebs-csi-driver, vpc-cni, CoreDNS, and kube-proxy,&lt;/li&gt;
&lt;li&gt;and AWS Key Management Service (KMS) configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;eks_blueprints_addons&lt;/code&gt; module definition creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Load Balancer Controller that provisions an AWS Network Load Balancer for distributing traffic,&lt;/li&gt;
&lt;li&gt;and the &lt;a href="https://github.com/kubernetes-sigs/metrics-server" rel="noopener noreferrer"&gt;Metrics Server&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;null_resource&lt;/code&gt; configuration updates your local &lt;code&gt;kubeconfig&lt;/code&gt; file with the new cluster details. It's not a required step for provisioning but just a handy hack.&lt;/p&gt;

&lt;p&gt;Finally, you can also define some outputs to be captured. Create a &lt;code&gt;outputs.tf&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;outputs.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following to the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"vpc_private_subnet_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"VPC private subnet CIDR"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets_cidr_blocks&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"vpc_public_subnet_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"VPC public subnet CIDR"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_subnets_cidr_blocks&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"vpc_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"VPC CIDR"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr_block&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"eks_cluster_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EKS cluster ID"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"eks_cluster_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EKS cluster Name"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"eks_managed_nodegroups"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EKS managed node groups"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_managed_node_groups&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"configure_kubectl"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Configure kubectl: make sure you're logged in with the correct AWS profile and run the following command to update your kubeconfig"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws eks update-kubeconfig --name ${module.eks.cluster_name} --alias ${module.eks.cluster_name}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Provision the cluster
&lt;/h3&gt;

&lt;p&gt;Our Terraform definitions are ready. Now you can provision the cluster.&lt;/p&gt;

&lt;p&gt;First, ensure you have configured your AWS CLI to use the correct AWS account and a region that supports EKS. If not, run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Visit https://console.aws.amazon.com/iam/home?#/security_credentials for creating access keys&lt;/span&gt;
aws configure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, initialize Terraform workspace and plan the changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Download modules and providers. Initialize state.&lt;/span&gt;
terraform init
&lt;span class="c"&gt;# see a preview of what will be done&lt;/span&gt;
terraform plan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Review the plan and make sure everything is correct.&lt;/p&gt;

&lt;p&gt;Now you can apply the changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm by typing &lt;code&gt;yes&lt;/code&gt; when prompted. This will take a while (15-20 minutes), so sit back and have a coffee or contemplate what led you to this point in life. 😉&lt;/p&gt;

&lt;p&gt;Once the EKS cluster is ready, you will see the output variables printed to the console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;configure_kubectl&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws eks update-kubeconfig --name okta-auth0-jhipster-eks --alias okta-auth0-jhipster-eks"&lt;/span&gt;
&lt;span class="nx"&gt;eks_cluster_name&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"okta-auth0-jhipster-eks"&lt;/span&gt;
&lt;span class="nx"&gt;eks_managed_nodegroups&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"initial"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"autoscaling_group_schedule_arns"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="s2"&gt;"iam_role_arn"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::216713166862:role/initial-eks-node-group-20230628170956397500000007"&lt;/span&gt;
    &lt;span class="s2"&gt;"iam_role_name"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"initial-eks-node-group-20230628170956397500000007"&lt;/span&gt;
    &lt;span class="s2"&gt;"iam_role_unique_id"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AROATE5I4QAHNFCURF5WI"&lt;/span&gt;
    &lt;span class="s2"&gt;"launch_template_arn"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:ec2:eu-west-1:216713166862:launch-template/lt-028654c46a256c879"&lt;/span&gt;
    &lt;span class="s2"&gt;"launch_template_id"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lt-028654c46a256c879"&lt;/span&gt;
    &lt;span class="s2"&gt;"launch_template_latest_version"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="s2"&gt;"launch_template_name"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"initial-2023062817211846290000000e"&lt;/span&gt;
    &lt;span class="s2"&gt;"node_group_arn"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:eks:eu-west-1:216713166862:nodegroup/okta-auth0-jhipster-eks/initial-20230628172118695900000010/f2c48183-0cd0-f970-d405-0869ccddad37"&lt;/span&gt;
    &lt;span class="s2"&gt;"node_group_autoscaling_group_names"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"eks-initial-20230628172118695900000010-f2c48183-0cd0-f970-d405-0869ccddad37"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="s2"&gt;"node_group_id"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"okta-auth0-jhipster-eks:initial-20230628172118695900000010"&lt;/span&gt;
    &lt;span class="s2"&gt;"node_group_labels"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tomap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cm"&gt;/* of string */&lt;/span&gt;
    &lt;span class="s2"&gt;"node_group_resources"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"autoscaling_groups"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eks-initial-20230628172118695900000010-f2c48183-0cd0-f970-d405-0869ccddad37"&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="s2"&gt;"remote_access_security_group_id"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="s2"&gt;"node_group_status"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ACTIVE"&lt;/span&gt;
    &lt;span class="s2"&gt;"node_group_taints"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;toset&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
&lt;span class="nx"&gt;vpc_private_subnet_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tolist&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.10.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.11.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.12.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;vpc_public_subnet_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tolist&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.0.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.1.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.2.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the cluster details if you run &lt;code&gt;kdash&lt;/code&gt; or &lt;code&gt;kubectl get nodes&lt;/code&gt; commands.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F7jsmSHoLHEGh9HMb1lWNqj%2Fcbac14bd55c5b4a31865cd683feb4b53%2Feks-cluster.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F7jsmSHoLHEGh9HMb1lWNqj%2Fcbac14bd55c5b4a31865cd683feb4b53%2Feks-cluster.png" alt="EKS cluster in KDash"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The EKS cluster defined here will not come under AWS free tier; hence, running this will cost money, so delete the cluster as soon as you finish the tutorial to keep the cost within a few dollars.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Set Up OIDC Authentication Using Auth0 by Okta
&lt;/h2&gt;

&lt;p&gt;You can proceed to deploy the sample application. You could skip this step if you used a sample that does not use Auth0 or OIDC for authentication.&lt;/p&gt;

&lt;p&gt;Since you are using Terraform, you can set up the Auth0 application using the &lt;a href="https://registry.terraform.io/providers/auth0/auth0/latest/docs" rel="noopener noreferrer"&gt;Auth0 Terraform provider&lt;/a&gt;. This will allow you to automate the setup of the Auth0 application and manage the addition of users, customizations, and such.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create an Auth0 application
&lt;/h3&gt;

&lt;p&gt;Open the &lt;code&gt;versions.tf&lt;/code&gt; file in the &lt;code&gt;terraform&lt;/code&gt; folder and add the following content to the &lt;code&gt;required_providers&lt;/code&gt; section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;    &lt;span class="nx"&gt;auth0&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"auth0/auth0"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.49.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's create a Terraform script that creates an Auth0 web application and required customizations. Create a file named &lt;code&gt;auth0.tf&lt;/code&gt; in the &lt;code&gt;terraform&lt;/code&gt; folder and add the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"auth0"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;domain&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://&amp;lt;your_auth0_domain_uri&amp;gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;debug&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Create a new Auth0 application for the JHipster app&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"auth0_client"&lt;/span&gt; &lt;span class="s2"&gt;"java_ms_client"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"JavaMicroservices"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Java Microservices Client Created Through Terraform"&lt;/span&gt;
  &lt;span class="nx"&gt;app_type&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"regular_web"&lt;/span&gt;
  &lt;span class="nx"&gt;callbacks&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:8080/login/oauth2/code/oidc"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;allowed_logout_urls&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:8080"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;oidc_conformant&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="nx"&gt;jwt_configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;alg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"RS256"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Configuring client_secret_post as an authentication method.&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"auth0_client_credentials"&lt;/span&gt; &lt;span class="s2"&gt;"java_ms_client_creds"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;client_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;auth0_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;java_ms_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;authentication_method&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"client_secret_post"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Create roles for the JHipster app&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"auth0_role"&lt;/span&gt; &lt;span class="s2"&gt;"admin"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ROLE_ADMIN"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Administrator"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"auth0_role"&lt;/span&gt; &lt;span class="s2"&gt;"user"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ROLE_USER"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"User"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Create an action to customize the authentication flow to add the roles and the username to the access token claims expected by JHipster applications.&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"auth0_action"&lt;/span&gt; &lt;span class="s2"&gt;"jhipster_action"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"jhipster_roles_claim"&lt;/span&gt;
  &lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"node18"&lt;/span&gt;
  &lt;span class="nx"&gt;deploy&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;code&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOT&lt;/span&gt;&lt;span class="sh"&gt;
  /**
   * Handler that will be called during the execution of a PostLogin flow.
   *
   * @param {Event} event - Details about the user and the context in which they are logging in.
   * @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
   */
   exports.onExecutePostLogin = async (event, api) =&amp;gt; {
     const namespace = 'https://www.jhipster.tech';
     if (event.authorization) {
         api.idToken.setCustomClaim(namespace + '/roles', event.authorization.roles);
         api.accessToken.setCustomClaim(namespace + '/roles', event.authorization.roles);
     }
   };
&lt;/span&gt;&lt;span class="no"&gt;  EOT

&lt;/span&gt;  &lt;span class="nx"&gt;supported_triggers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"post-login"&lt;/span&gt;
    &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"v3"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Attach the action to the login flow&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"auth0_trigger_actions"&lt;/span&gt; &lt;span class="s2"&gt;"login_flow"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;trigger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"post-login"&lt;/span&gt;

  &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;id&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;auth0_action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jhipster_action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="nx"&gt;display_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;auth0_action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jhipster_action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Create a test user. You can create more users here if needed&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"auth0_user"&lt;/span&gt; &lt;span class="s2"&gt;"test_user"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;connection_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Username-Password-Authentication"&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Jane Doe"&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"jhipster@test.com"&lt;/span&gt;
  &lt;span class="nx"&gt;email_verified&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;password&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"passpass$12$12"&lt;/span&gt; &lt;span class="c1"&gt;# Don't set passwords like this in production! Use env variables instead.&lt;/span&gt;
  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ignore_changes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"auth0_user_roles"&lt;/span&gt; &lt;span class="s2"&gt;"test_user_roles"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;user_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;auth0_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;roles&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;auth0_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;auth0_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"auth0_webapp_client_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Auth0 JavaMicroservices Client ID"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;auth0_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;java_ms_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"auth0_webapp_client_secret"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Auth0 JavaMicroservices Client Secret"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;auth0_client_credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;java_ms_client_creds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client_secret&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find your Auth0 domain in the &lt;a href="https://manage.auth0.com/" rel="noopener noreferrer"&gt;Auth0 dashboard&lt;/a&gt; or by running the &lt;code&gt;auth0 tenants list&lt;/code&gt; command. The script above does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;auth0_client&lt;/code&gt; resource definition creates an Auth0 Web application client conforming to the OIDC standard.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;auth0_client_credentials&lt;/code&gt; resource definition configures the client to use the &lt;code&gt;client_secret_post&lt;/code&gt; authentication method.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;auth0_role&lt;/code&gt; resource definition creates two roles for the JHipster application.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;auth0_action&lt;/code&gt; resource definition creates an action that will be executed during the Auth0 post-login flow. This action will add the roles and the username to the ID and access token claims as expected by JHipster applications.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;auth0_user&lt;/code&gt; resource definition creates a test user.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Provision the Auth0 application
&lt;/h3&gt;

&lt;p&gt;Now before you can run this script you need to create a machine-to-machine application in Auth0 so that Terraform can communicate with the Auth0 management API. This can be done using the Auth0 CLI. Please note that you also need to have &lt;a href="https://jqlang.github.io/jq/" rel="noopener noreferrer"&gt;jq&lt;/a&gt; installed to run the below commands. Run the following commands to create an application after logging into the CLI with the &lt;code&gt;auth0 login&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a machine-to-machine application on Auth0&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AUTH0_M2M_APP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;auth0 apps create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"Auth0 Terraform Provider"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"Auth0 Terraform Provider M2M"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--type&lt;/span&gt; m2m &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--reveal-secrets&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--json&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'. | {client_id: .client_id, client_secret: .client_secret}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Extract the client ID and client secret from the output.&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AUTH0_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$AUTH0_M2M_APP&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.client_id'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AUTH0_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$AUTH0_M2M_APP&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.client_secret'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create the application and set environment variables for the client ID and secret. This application needs to be authorized to use the Auth0 management API. This can be done using the commands below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get the ID and IDENTIFIER fields of the Auth0 Management API&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AUTH0_MANAGEMENT_API_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;auth0 apis list &lt;span class="nt"&gt;--json&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'map(select(.name == "Auth0 Management API"))[0].id'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AUTH0_MANAGEMENT_API_IDENTIFIER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;auth0 apis list &lt;span class="nt"&gt;--json&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'map(select(.name == "Auth0 Management API"))[0].identifier'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Get the SCOPES to be authorized&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AUTH0_MANAGEMENT_API_SCOPES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;auth0 apis scopes list &lt;span class="nv"&gt;$AUTH0_MANAGEMENT_API_ID&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.[].value'&lt;/span&gt; | jq &lt;span class="nt"&gt;-ncR&lt;/span&gt; &lt;span class="s1"&gt;'[inputs]'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Authorize the Auth0 Terraform Provider application to use the Auth0 Management API&lt;/span&gt;
auth0 api post &lt;span class="s2"&gt;"client-grants"&lt;/span&gt; &lt;span class="nt"&gt;--data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{"client_id": "'&lt;/span&gt;&lt;span class="nv"&gt;$AUTH0_CLIENT_ID&lt;/span&gt;&lt;span class="s1"&gt;'", "audience": "'&lt;/span&gt;&lt;span class="nv"&gt;$AUTH0_MANAGEMENT_API_IDENTIFIER&lt;/span&gt;&lt;span class="s1"&gt;'", "scope":'&lt;/span&gt;&lt;span class="nv"&gt;$AUTH0_MANAGEMENT_API_SCOPES&lt;/span&gt;&lt;span class="s1"&gt;'}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can run the Terraform script to create the Auth0 application. Run the following commands to initialize the script and apply it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Upgrade the terraform script&lt;/span&gt;
terraform init &lt;span class="nt"&gt;-upgrade&lt;/span&gt;
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create the Auth0 application and the test user. You can find the client ID and secret in the output of the &lt;code&gt;terraform output&lt;/code&gt; command. You can also find the client ID and secret in the Auth0 dashboard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Client ID&lt;/span&gt;
terraform output &lt;span class="nt"&gt;--json&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.auth0_webapp_client_id.value'&lt;/span&gt;
&lt;span class="c"&gt;# Client Secret&lt;/span&gt;
terraform output &lt;span class="nt"&gt;--json&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.auth0_webapp_client_secret.value'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the client ID and client secret from the output. You will need these values in the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure the JHipster application to use Auth0
&lt;/h3&gt;

&lt;p&gt;Update &lt;code&gt;kubernetes/registry-k8s/application-configmap.yml&lt;/code&gt; with the Spring Security OIDC configuration using values from the previous step. This configuration is loaded into Consul, and it shares the values with the gateway and microservices.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application-config&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jhipster&lt;/span&gt;
&lt;span class="c1"&gt;#common configuration shared between all applications&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;application.yml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|-&lt;/span&gt;
    &lt;span class="s"&gt;configserver:&lt;/span&gt;
      &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;jhipster:&lt;/span&gt;
      &lt;span class="s"&gt;security:&lt;/span&gt;
        &lt;span class="s"&gt;...&lt;/span&gt;
        &lt;span class="s"&gt;oauth2:&lt;/span&gt;
          &lt;span class="s"&gt;audience:&lt;/span&gt;
            &lt;span class="s"&gt;- https://&amp;lt;your-auth0-domain&amp;gt;/api/v2/&lt;/span&gt;
    &lt;span class="s"&gt;spring:&lt;/span&gt;
      &lt;span class="s"&gt;security:&lt;/span&gt;
        &lt;span class="s"&gt;oauth2:&lt;/span&gt;
          &lt;span class="s"&gt;client:&lt;/span&gt;
            &lt;span class="s"&gt;provider:&lt;/span&gt;
              &lt;span class="s"&gt;oidc:&lt;/span&gt;
                &lt;span class="s"&gt;# make sure to include the trailing slash&lt;/span&gt;
                &lt;span class="s"&gt;issuer-uri: https://&amp;lt;your-auth0-domain&amp;gt;/&lt;/span&gt;
            &lt;span class="s"&gt;registration:&lt;/span&gt;
              &lt;span class="s"&gt;oidc:&lt;/span&gt;
                &lt;span class="s"&gt;client-id: &amp;lt;client-id&amp;gt;&lt;/span&gt;
                &lt;span class="s"&gt;client-secret: &amp;lt;client-secret&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;# app specific configuration&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The microservice applications are now ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secure secrets
&lt;/h3&gt;

&lt;p&gt;If you have noticed, you are setting secrets in plain text on the &lt;code&gt;application-configmap.yml&lt;/code&gt; file, which is not ideal and is not a best practice for security. The best way to do this securely would be to use &lt;a href="https://aws.amazon.com/secrets-manager/" rel="noopener noreferrer"&gt;AWS Secrets Manager&lt;/a&gt;, an external service like &lt;a href="https://www.hashicorp.com/products/vault" rel="noopener noreferrer"&gt;HashiCorp Vault&lt;/a&gt;, or &lt;a href="https://github.com/bitnami-labs/sealed-secrets" rel="noopener noreferrer"&gt;Sealed Secrets&lt;/a&gt;. To learn more about these methods see the blog post &lt;a href="https://auth0.com/blog/kubernetes-secrets-management/" rel="noopener noreferrer"&gt;Shhhh... Kubernetes Secrets Are Not Really Secret!&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy the Microservice Stack
&lt;/h2&gt;

&lt;p&gt;You are ready to deploy to our shiny new EKS cluster, but first, you need to build and push the Docker images to a container registry. You can use &lt;a href="https://aws.amazon.com/ecr/" rel="noopener noreferrer"&gt;Amazon Elastic Container Registry (ECR)&lt;/a&gt; or any other container registry.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build the Docker images
&lt;/h3&gt;

&lt;p&gt;You need to build Docker images for each app. This is specific to the JHipster application used in this tutorial which uses &lt;a href="https://github.com/GoogleContainerTools/jib" rel="noopener noreferrer"&gt;Jib&lt;/a&gt; to build the images. Make sure you are logged into Docker using &lt;code&gt;docker login&lt;/code&gt;. Navigate to each app folder (&lt;strong&gt;store&lt;/strong&gt;, &lt;strong&gt;invoice&lt;/strong&gt;, &lt;strong&gt;product&lt;/strong&gt;) and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;docker-repo-uri-or-name&amp;gt;/&amp;lt;image-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Image names should be &lt;code&gt;store&lt;/code&gt;, &lt;code&gt;invoice&lt;/code&gt;, and &lt;code&gt;product&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy the applications to EKS
&lt;/h3&gt;

&lt;p&gt;Start the deployment using the handy script provided by JHipster. You could also manually apply deployments using &lt;code&gt;kubectl apply -f &amp;lt;file&amp;gt;&lt;/code&gt; commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;kubernetes
./kubectl-apply.sh &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F3vo9lWwxkHWghbvcnSt9wg%2Fb5158d9a4b0ab89e8a73d255acb206ff%2Feks-pods.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F3vo9lWwxkHWghbvcnSt9wg%2Fb5158d9a4b0ab89e8a73d255acb206ff%2Feks-pods.png" alt="JHipster pods in KDash"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also run the following command to see the status of the deployments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; jhipster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;View the Consul registry using port-forwarding as follows, and you will be able to access the application at &lt;code&gt;http://localhost:8500&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/consul-ui &lt;span class="nt"&gt;-n&lt;/span&gt; jhipster 8500
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can access the gateway application using port-forwarding as follows, and you will be able to access the application at &lt;code&gt;http://localhost:8080&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/store &lt;span class="nt"&gt;-n&lt;/span&gt; jhipster 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, you can access the application via the load balancer exposed. Find the external IP of the &lt;code&gt;store&lt;/code&gt; service by navigating to the service tab in KDash or by running the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get svc store &lt;span class="nt"&gt;-n&lt;/span&gt; jhipster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need to add the external IP of the store to allowed callback URLs and allowed logout URLs in the Auth0 web application. You can do this by adding the values to the &lt;code&gt;callbacks&lt;/code&gt; and &lt;code&gt;allowed_logout_urls&lt;/code&gt; array in the &lt;code&gt;java_ms_client&lt;/code&gt; resource in &lt;code&gt;auth0.tf&lt;/code&gt; file like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"auth0_client"&lt;/span&gt; &lt;span class="s2"&gt;"java_ms_client"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"JavaMicroservices"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Java Microservices Client Created Through Terraform"&lt;/span&gt;
  &lt;span class="nx"&gt;app_type&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"regular_web"&lt;/span&gt;
  &lt;span class="nx"&gt;callbacks&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:8080/login/oauth2/code/oidc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"http://aws-elb-id.region.elb.amazonaws.com:8080/login/oauth2/code/oidc"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;allowed_logout_urls&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"http://aws-elb-id.region.elb.amazonaws.com:8080"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;oidc_conformant&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="nx"&gt;jwt_configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;alg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"RS256"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;terraform apply -target="auth0_client.java_ms_client"&lt;/code&gt; to update the configuration in your Auth0 tenant.&lt;/p&gt;

&lt;p&gt;Now you should be able to visit the external IP of the &lt;code&gt;store&lt;/code&gt; service on port 8080 and see the application, and you should be able to log in using your Auth0 test user credentials.&lt;/p&gt;

&lt;p&gt;If you encounter an issue where the Consul pods do not start, you might have issues with the AWS EBS addon for EKS. Run &lt;code&gt;kubectl describe pvc -n jhipster&lt;/code&gt; to see if there are any errors. If you see &lt;code&gt;could not create volume in EC2: UnauthorizedOperation&lt;/code&gt; in errors, then you need to troubleshoot by following the &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/managing-ebs-csi.html" rel="noopener noreferrer"&gt;Managing the Amazon EBS CSI driver as an Amazon EKS add-on&lt;/a&gt; guide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tear Down the Cluster with Terraform
&lt;/h2&gt;

&lt;p&gt;Once you are done with the tutorial, you can delete the cluster and all the resources created using Terraform by running the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;terraform
&lt;span class="c"&gt;# The commands below might take a while to finish.&lt;/span&gt;
terraform destroy &lt;span class="nt"&gt;-target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"module.eks_blueprints_addons"&lt;/span&gt; &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;
&lt;span class="c"&gt;# If deleting the VPC fails, then manually delete the load balancers and the security groups&lt;/span&gt;
&lt;span class="c"&gt;# for the load balancer associated with the VPC from the AWS EC2 console and try again.&lt;/span&gt;
&lt;span class="c"&gt;# This is due to the fact that EKS creates a load balancer for Kubernetes service and it is not known to Terraform.&lt;/span&gt;
terraform destroy &lt;span class="nt"&gt;-target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"module.eks"&lt;/span&gt; &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;
terraform destroy &lt;span class="nt"&gt;-target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"module.vpc"&lt;/span&gt; &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;
&lt;span class="c"&gt;# Clean up Auth0 apps and anything left over.&lt;/span&gt;
terraform destroy &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find all the code from this example on &lt;a href="https://github.com/oktadev/auth0-jhipster-k8s-eks-microservices-example" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;If you like this article, please leave a like or a comment.&lt;/p&gt;

&lt;p&gt;You can follow me on &lt;a href="https://mastodon.social/@deepu105" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/deepu05/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>terraform</category>
      <category>kubernetes</category>
      <category>aws</category>
    </item>
    <item>
      <title>Mastodon for Developers: Everything You Need to Know</title>
      <dc:creator>Deepu K Sasidharan</dc:creator>
      <pubDate>Mon, 06 Feb 2023 13:34:14 +0000</pubDate>
      <link>https://forem.com/oktadev/mastodon-for-developers-everything-you-need-to-know-2hhf</link>
      <guid>https://forem.com/oktadev/mastodon-for-developers-everything-you-need-to-know-2hhf</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://auth0.com/blog/mastdon-for-developers/" rel="noopener noreferrer"&gt;auth0.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Mastodon?
&lt;/h2&gt;

&lt;p&gt;Mastodon is an open-source, distributed micro-blogging platform that can host social networking sites. It was created by &lt;a href="https://en.wikipedia.org/wiki/Eugen_Rochko" rel="noopener noreferrer"&gt;Eugen Rochko&lt;/a&gt; and was first released in 2016. It is similar to Twitter regarding features, target audience, and user experience. But unlike Twitter, it is run on a decentralized network of servers, each of which is called an &lt;strong&gt;instance&lt;/strong&gt;. This does not mean that you have fragmented silos; instances are federated, which means you can talk to other instances, follow people, and see content from other instances, making Mastodon the ideal decentralized social network.&lt;/p&gt;

&lt;p&gt;Any single company does not own the Mastodon network, and users can join any instance they wish. The federation allows people to connect and interact with each other across different instances, making Mastodon more open, secure, and free than something like Twitter. This means no single entity has absolute control over Mastodon, which is a big advantage over traditional social networks like Twitter, which is owned by a single company and is not open source.&lt;/p&gt;

&lt;p&gt;After the recent &lt;a href="https://twitterisgoinggreat.com" rel="noopener noreferrer"&gt;Twitter saga&lt;/a&gt;, it's clear why this is a major benefit. However, this does not mean there is no moderation. Quite the contrary, actually. Each instance can have its own rules and can moderate content as they see fit. This means you can join an instance that is more aligned with your values and beliefs or even hosts your own if you have the necessary resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Should You Care about Mastodon?
&lt;/h2&gt;

&lt;p&gt;Platforms like Twitter, Facebook, Instagram, and TikTok make most of their revenue from advertisements. This means they are selling our attention to advertisers.&lt;/p&gt;

&lt;p&gt;How do you keep someones attention? Our brains are naturally wired to seek dopamine reward pathways and &lt;a href="https://www.iomcworld.org/open-access/neurotransmitter-dopamine-da-and-its-role-in-the-development-of-social-media-addiction-59222.html" rel="noopener noreferrer"&gt;algorithms&lt;/a&gt; used by these social media often target dopamine pathways to create a feeling of reward and reinforcement that encourages people to keep using their services. This is done by providing users with a steady stream of new content and positive feedback, such as likes and comments, which stimulates dopamine production in the brain. This can lead to people becoming addicted to social media, as they constantly seek out the reward of dopamine that comes with using the platform.&lt;/p&gt;

&lt;p&gt;In addition, many platforms use algorithms to personalize content and increase the chance of a positive outcome, further encouraging people to use the service. But that's not all; these algorithms also use tactics like amplifying content that is reactionary, sensational, or &lt;a href="https://en.wikipedia.org/wiki/Mean_world_syndrome" rel="noopener noreferrer"&gt;frightening&lt;/a&gt;, which grabs our attention.&lt;/p&gt;

&lt;p&gt;Platforms like Mastodon, which &lt;a href="https://arstechnica.com/tech-policy/2022/12/twitter-rival-mastodon-rejects-funding-to-preserve-nonprofit-status/" rel="noopener noreferrer"&gt;does not operate for profit&lt;/a&gt;, do not have any incentives to follow similar tactics. This does not mean that it is not possible as a Mastodon instance can choose to serve advertisements. But since you have control as a user, you can choose to join an instance that does not serve advertisements. This means that algorithms are not manipulating you to keep you hooked on the platform. This is a big advantage over platforms like Twitter which are designed to keep you hooked on the platform, making it easier to sow division and hatred by nefarious actors.&lt;/p&gt;

&lt;p&gt;Mastodon is an important platform for anyone who values their freedom, security, mental health, and privacy online. It is a great alternative to Twitter for those who are looking for a more open and secure platform to connect with others without having to worry about being harassed or bullied. It is quite customizable and provides greater control over your data. With its growing user base, Mastodon is quickly becoming the go-to platform for many people looking for a better way to connect with others and get out of the grasp of BigTech.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Fediverse, and How Does It Work?
&lt;/h2&gt;

&lt;p&gt;Fediverse is the term used to describe a network of interconnected servers that can communicate with each other using decentralized networking protocols. Fediverse is bigger than Mastodon and can include, among others:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mastodon servers (social networking and microblogging)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://friendi.ca/" rel="noopener noreferrer"&gt;Friendica&lt;/a&gt; servers (social networking and microblogging)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://joinpeertube.org/" rel="noopener noreferrer"&gt;PeerTube&lt;/a&gt; servers (video hosting)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pleroma.social/" rel="noopener noreferrer"&gt;Pleroma&lt;/a&gt; servers (social networking and microblogging)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fediverse networks can be used for social networks, file hosting services, and so on.&lt;/p&gt;

&lt;p&gt;Fediverse works using several different communication protocols. The most important ones are &lt;a href="https://activitypub.rocks/" rel="noopener noreferrer"&gt;ActivityPub&lt;/a&gt;, &lt;a href="https://www.w3.org/community/ostatus/wiki/Main_Page" rel="noopener noreferrer"&gt;OStatus&lt;/a&gt;, and &lt;a href="https://diaspora.github.io/diaspora_federation/" rel="noopener noreferrer"&gt;diaspora&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;ActivityPub is a protocol that allows servers to communicate with each other. It is a decentralized protocol based on the &lt;a href="https://www.w3.org/TR/activitystreams-core/" rel="noopener noreferrer"&gt;ActivityStreams&lt;/a&gt; standard. Mastodon, PeerTube, and Pleroma use ActivityPub.&lt;/p&gt;

&lt;p&gt;OStatus is a decentralized protocol based on the &lt;a href="https://www.rfc-editor.org/rfc/rfc4287" rel="noopener noreferrer"&gt;Atom Syndication Format&lt;/a&gt;. OStatus is a predecessor to ActivityPub and is used by older instances of Mastodon and Pleroma.&lt;/p&gt;

&lt;p&gt;Diaspora is a decentralized protocol. Mastodon, Friendica, and Pleroma use it.&lt;/p&gt;

&lt;p&gt;Any server that supports one of these protocols can communicate with other servers that support the same protocol.&lt;/p&gt;

&lt;p&gt;It is difficult to estimate the number of users in Fediverse due to the distributed nature, but &lt;a href="https://fediverse.observer/stats" rel="noopener noreferrer"&gt;rough third-party estimates&lt;/a&gt; put it at around 8 million users. This is a small fraction of the number of users on Twitter, but it is growing rapidly, as well as the number of instances.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing Servers
&lt;/h2&gt;

&lt;p&gt;One of the biggest strengths of Mastodon, decentralization, is also its biggest hurdle when it comes to adoption. This means that there is no single place to sign up for Mastodon. Instead, you have to choose an instance to join. Choosing a server could be a daunting task, especially if you are new to Mastodon. There are some factors to consider when choosing a server. These include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quality and reliability of the server&lt;/li&gt;
&lt;li&gt;Community and moderation&lt;/li&gt;
&lt;li&gt;Rules and Policies&lt;/li&gt;
&lt;li&gt;Non-profit status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choosing a server based on this is important, but it's not as critical as it seems. This is because you can follow people from other servers and see their content in your timeline. This means you can join a server that is more aligned with your values and beliefs and still follow people from other servers. This is one of the biggest advantages of Mastodon over Twitter, where you are forced to follow people from the same server. Not just that, Mastodon lets you move from one server to another in case you choose a server that turns out to be unreliable, or you realize that you disagree with the server's policies. You can also easily import your data, like your followers and people you follow, to the new server. So choose a server you feel comfortable with and stick around to see how it goes. You can always move to another server if you don't like it.&lt;/p&gt;

&lt;p&gt;As of this writing, there are over 17,000 instances of Mastodon, which is growing daily. This means that there is a Mastodon instance for everyone. You can find a list of Mastodon instances on &lt;a href="https://instances.social/" rel="noopener noreferrer"&gt;instances.social&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recommended servers
&lt;/h3&gt;

&lt;p&gt;For technical folks like developers, you could consider joining one of these servers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://fosstodon.org/" rel="noopener noreferrer"&gt;fosstodon.org&lt;/a&gt;: Ideal for developers, especially if you are an open-source enthusiast&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mstdn.social/" rel="noopener noreferrer"&gt;mstdn.social&lt;/a&gt;: It's a general-purpose server with a good community and is quite reliable&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mastodon.social/" rel="noopener noreferrer"&gt;mastodon.social&lt;/a&gt;: The official Mastodon server with a good community and is quite reliable&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hachyderm.io/" rel="noopener noreferrer"&gt;hachyderm.io&lt;/a&gt;: A server for tech industry professionals&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://techhub.social/" rel="noopener noreferrer"&gt;techhub.social&lt;/a&gt;: A server for technology enthusiasts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building a Timeline Based on Hashtags and Follows
&lt;/h2&gt;

&lt;p&gt;Unlike Twitter, when you join a Mastodon server, you will not be greeted by a timeline with interesting posts and recommendations for people to follow. Instead, you are going to be greeted by an empty timeline. This is by design, as Mastodon does not have any algorithm or recommendation system, and you will not be following people from the same server. This means that you have to build your timeline by following people and hashtags. This is a good thing as it gives you more control over your timeline, and you will not be bombarded with content you are not interested in. You can find and follow people from other instances by searching for their usernames. For example, if you want to follow me, you can search for my username &lt;code&gt;@deepu105&lt;/code&gt; or &lt;code&gt;@deepu105@mastodon.social&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Similarly, you can also follow hashtags; look for the &lt;strong&gt;+&lt;/strong&gt; button on the top right corner of the screen when you are on the hashtag page. This works on the web version and some mobile clients.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx1jiyxru13e2wk75yxry.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx1jiyxru13e2wk75yxry.png" alt="Follow hashtags" width="800" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is important to follow people and hashtags you are interested in to have interesting content on your timeline. You can mute people you are not interested in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkl7d0yrs3jshaxgp5tkf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkl7d0yrs3jshaxgp5tkf.png" alt="Mute accounts" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also filter hashtags or words you don't want to see on your timeline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38tfpw5szflkrzpr89ty.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38tfpw5szflkrzpr89ty.png" alt="Filter hashtags" width="800" height="583"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a great way to keep your timeline clean and free of unwanted content.&lt;/p&gt;

&lt;p&gt;If you are migrating from Twitter, &lt;a href="https://www.movetodon.org/" rel="noopener noreferrer"&gt;Movetodon.org&lt;/a&gt; is a great tool to help you find and follow people from Twitter on Mastodon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cross-Posting
&lt;/h2&gt;

&lt;p&gt;If you prefer to keep your Twitter account and cross-post to Mastodon and vice versa, you can use some tools to do so, including writing your own scripts using Twitter and Mastodon APIs. My personal favorite is &lt;a href="https://moa.party/" rel="noopener noreferrer"&gt;moa.party&lt;/a&gt;. It supports cross-posting to and from Twitter and Mastodon and is simple to setup and use. It can also post from Instagram to Mastodon. It is &lt;a href="https://gitlab.com/fedstoa/moa" rel="noopener noreferrer"&gt;open-source&lt;/a&gt;, and you can host it yourself if you don't want to give the service access to your Twitter/Mastodon accounts &lt;a href="https://moaparty.com/oauth/" rel="noopener noreferrer"&gt;using OAuth&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Securing the Account
&lt;/h2&gt;

&lt;p&gt;As usual, it is important to keep your Mastodon account secure. This includes using a strong password, using a password manager, and enabling two-factor authentication (2FA). Mastodon supports 2FA using TOTP (Time-based One-time Password Algorithm) authenticator apps like Google Authenticator and FIDO security keys like Yubikey.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frbiqqn07wqb7fwf7aumd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frbiqqn07wqb7fwf7aumd.png" alt="Adding 2FA" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also verify your Mastodon account by linking your official website, GitHub profile, and so on. This is a great way to prove that you are the owner of the account and prevent impersonation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcq6dnj627slu5xbtcc1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcq6dnj627slu5xbtcc1.png" alt="Verify your Mastodon account" width="800" height="635"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To do this, go to &lt;strong&gt;Preferences&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Appearance&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Profile metadata&lt;/strong&gt; and copy the verification URL and add it to your website as instructed. For the GitHub profile, add your Mastodon profile URL in the GitHub profile's website field. For example, my Mastodon profile URL is &lt;code&gt;https://mastodon.social/@deepu105&lt;/code&gt;. Now add your website or GitHub profile URL to your Mastodon profile metadata and save.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz4jwost56dotdc3waj4o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz4jwost56dotdc3waj4o.png" alt="Verified links on Mastodon account" width="800" height="639"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving between Servers
&lt;/h2&gt;

&lt;p&gt;If you decide to move from one Mastodon server to another, here are a few tips to make the process smooth.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, create a profile on the new server you want to use. Note that this will be a new username, as Mastodon usernames include the server name, and you need to choose a username that is available on the new server.&lt;/li&gt;
&lt;li&gt;Export your data from the old server by going to &lt;strong&gt;Preferences&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Import and export&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Data export&lt;/strong&gt;. You will get CSV files for each item you export, like Follows, mutes, and so on.&lt;/li&gt;
&lt;li&gt;Create an account alias in your new account by going to &lt;strong&gt;Preferences&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Account&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Account settings&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Moving from a different account&lt;/strong&gt;. This will allow you to redirect your old account and move followers from your old account to the new account.&lt;/li&gt;
&lt;li&gt;Now, from your old account, redirect to the new account by going to &lt;strong&gt;Preferences&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Account&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Account settings&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Moving to a different account&lt;/strong&gt;. This will redirect your old account to the new account, and all your followers will be moved to the new account.&lt;/li&gt;
&lt;li&gt;Now, from your new account, go to &lt;strong&gt;Preferences&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Import and export&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Import&lt;/strong&gt; and import the CSV files that you exported from your old account. This will import all your follows, mutes, lists, and so on to the new account.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Mastodon is a great alternative to Twitter and is a great way to connect with people from around the world without worrying about a single entity dictating what you can and cannot do with your social media. It is a great way to build a community around your projects and share your thoughts and ideas. It is also a great way to connect with people from the open-source community. I hope this guide will help you get started with Mastodon.&lt;/p&gt;




&lt;p&gt;If you like this article, please leave a like or a comment.&lt;/p&gt;

&lt;p&gt;You can follow me on &lt;a href="https://mastodon.social/@deepu105" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/deepu05/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>social</category>
      <category>mastodon</category>
    </item>
    <item>
      <title>Shhhh... Kubernetes Secrets Are Not Really Secret!</title>
      <dc:creator>Deepu K Sasidharan</dc:creator>
      <pubDate>Thu, 15 Dec 2022 11:46:30 +0000</pubDate>
      <link>https://forem.com/oktadev/shhhh-kubernetes-secrets-are-not-really-secret-3h38</link>
      <guid>https://forem.com/oktadev/shhhh-kubernetes-secrets-are-not-really-secret-3h38</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://auth0.com/blog/kubernetes-secrets-management/" rel="noopener noreferrer"&gt;auth0.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Kubernetes has become an inevitable part of the modern software infrastructure. Hence managing sensitive data on Kubernetes is also an essential aspect of modern software engineering so that you can put the security back into DevSecOps. Kubernetes offers a way to store sensitive data using the &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/" rel="noopener noreferrer"&gt;Secret&lt;/a&gt; object. While it's better than nothing, it is not really a secret, as it is just &lt;a href="https://en.wikipedia.org/wiki/Base64" rel="noopener noreferrer"&gt;base64&lt;/a&gt; encoded strings that anyone with access to the cluster or the code can decode.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Caution:&lt;/strong&gt;&lt;br&gt;
Kubernetes Secrets are, by default, stored unencrypted in the API server's underlying data store (etcd). Anyone with API access can retrieve or modify a Secret, and so can anyone with access to etcd. Additionally, anyone authorized to create a Pod in a namespace can use that access to read any Secret in that namespace; this includes indirect access, such as the ability to create a Deployment.&lt;br&gt;
— &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/" rel="noopener noreferrer"&gt;Kubernetes docs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problem of reading secrets from the cluster can be fixed using proper &lt;a href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/" rel="noopener noreferrer"&gt;RBAC&lt;/a&gt; configuration and by securing the API server, check out &lt;a href="https://developer.okta.com/blog/2021/12/02/k8s-security-best-practices" rel="noopener noreferrer"&gt;How to Secure Your Kubernetes Clusters With Best Practices&lt;/a&gt; to learn more about RBAC and cluster API security. Securing secrets on the source code is the bigger problem. Everyone who has access to the repositories containing those secret definitions can also decode them. This makes it quite tricky to manage Kubernetes secrets in Git, like every other resource.&lt;/p&gt;

&lt;p&gt;Let's see how to setup more secure secrets using the;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sealed Secrets,&lt;/li&gt;
&lt;li&gt;External Secrets Operator,&lt;/li&gt;
&lt;li&gt;Secrets Store CSI driver.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You would need a Kubernetes cluster to run the samples. I used &lt;a href="https://k3d.io/" rel="noopener noreferrer"&gt;k3d&lt;/a&gt; to create a local cluster. You can also use &lt;a href="https://kind.sigs.k8s.io/" rel="noopener noreferrer"&gt;kind&lt;/a&gt; or &lt;a href="https://minikube.sigs.k8s.io/docs/" rel="noopener noreferrer"&gt;minikube&lt;/a&gt; for this purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sealed Secrets
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/bitnami-labs/sealed-secrets" rel="noopener noreferrer"&gt;Sealed Secrets&lt;/a&gt; is an open-source Kubernetes controller and a client-side CLI tool from Bitnami that aims to solve the "&lt;strong&gt;storing secrets in Git&lt;/strong&gt;" part of the problem, using asymmetric crypto encryption. Sealed Secrets with an RBAC configuration preventing non-admins from reading secrets is an excellent solution for the entire problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2wsii5h6myb5t2tfenj0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2wsii5h6myb5t2tfenj0.jpg" alt="Sealed Secrets Architecture" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It works as below;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Encrypt the secret on the developer machine using a public key and the &lt;code&gt;kubeseal&lt;/code&gt; CLI. This encodes the encrypted secret into a Kubernetes Custom Resource Definition (CRD)&lt;/li&gt;
&lt;li&gt;Deploy the CRD to the target cluster&lt;/li&gt;
&lt;li&gt;The Sealed Secret controller decrypts the secret using a private key on the target cluster to produce a standard Kubernetes secret.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The private key is only available to the Sealed Secrets controller on the cluster, and the public key is available to the developers. This way, &lt;strong&gt;only the cluster can decrypt the secrets, and the developers can only encrypt them&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Supports template definition so that metadata can be added to the unsealed secrets. For example, you can add labels and annotations to the unsealed secrets using the template definition.&lt;/li&gt;
&lt;li&gt;The unsealed secrets will be owned by the sealed secret CRD and updated when the sealed secrets are updated.&lt;/li&gt;
&lt;li&gt;Certificates are rotated every 30 days by default, and this can be customized.&lt;/li&gt;
&lt;li&gt;Secrets are encrypted using unique keys for each cluster, namespace, and secret combination (private key + namespace name + secret name), preventing any loopholes in decryption. This behavior is configurable using scopes &lt;code&gt;strict&lt;/code&gt;, &lt;code&gt;namespace-wide&lt;/code&gt;, and &lt;code&gt;cluster-wide&lt;/code&gt; during the sealing process.&lt;/li&gt;
&lt;li&gt;Can be used to manage existing secrets in the cluster.&lt;/li&gt;
&lt;li&gt;Has a &lt;a href="https://marketplace.visualstudio.com/items?itemName=codecontemplator.kubeseal" rel="noopener noreferrer"&gt;VSCode extension&lt;/a&gt; to make it easier to use.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Disadvantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Since it unseals the sealed secrets into regular secrets, you can still decode them if you have access to the cluster and namespace.&lt;/li&gt;
&lt;li&gt;Requires resealing for each cluster environment, as the key pair will be unique for each cluster.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;Install the controller on the cluster and the CLI on the local machine.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download the &lt;code&gt;controller.yaml&lt;/code&gt; manifest file from the &lt;a href="https://github.com/bitnami-labs/sealed-secrets/releases" rel="noopener noreferrer"&gt;release&lt;/a&gt; page.&lt;/li&gt;
&lt;li&gt;Deploy the controller using &lt;code&gt;kubectl apply -f controller.yaml&lt;/code&gt; to your cluster. The controller will be installed on the &lt;code&gt;kube-system&lt;/code&gt; namespace. The controller will start and be ready in a few moments.&lt;/li&gt;
&lt;li&gt;Install the CLI on your local machine using &lt;code&gt;brew install kubeseal&lt;/code&gt; (Linux &amp;amp; macOS) or using the pre-built binaries on the &lt;a href="https://github.com/bitnami-labs/sealed-secrets/releases" rel="noopener noreferrer"&gt;release&lt;/a&gt; page.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Usage
&lt;/h3&gt;

&lt;p&gt;Let's create a sealed secret.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a secret using the &lt;code&gt;kubectl create secret&lt;/code&gt; command or by hand coding a YAML file as follows:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; secretvalue | kubectl create secret generic mysecret &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dry-run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;client &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/stdin &lt;span class="nt"&gt;-o&lt;/span&gt; yaml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; my-secret.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will produce a secret definition like the one below;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# my-secret.yaml&lt;/span&gt;

&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;c2VjcmV0dmFsdWU=&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Secret&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;creationTimestamp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysecret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Seal the secret using the &lt;code&gt;kubeseal&lt;/code&gt; CLI. This will encrypt the secret using the public key fetched from the server and produce a sealed secret definition. The &lt;code&gt;my-secret.yaml&lt;/code&gt; file can be discarded now. You can also download the public key and use it locally in offline mode.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubeseal &lt;span class="nt"&gt;--format&lt;/span&gt; yaml &amp;lt; my-secret.yaml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; my-sealed-secret.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will produce a sealed secret definition, &lt;code&gt;my-sealed-secret.yaml&lt;/code&gt;, like the one below;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# my-sealed-secret.yaml&lt;/span&gt;

&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bitnami.com/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SealedSecret&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;creationTimestamp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysecret&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;encryptedData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AgA6a4AGzd7qzR8mTPqTPFNor8tTtT5...==&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;creationTimestamp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysecret&lt;/span&gt;
      &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file is safe to commit to Git or to share with other developers.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Finally, you can deploy this to the cluster to be unsealed.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; my-sealed-secret.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Now, you can see the unsealed secret in the cluster.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl describe secret mysecret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use this secret in deployments like any other Kubernetes secret.&lt;/p&gt;

&lt;h2&gt;
  
  
  External Secrets Operator
&lt;/h2&gt;

&lt;p&gt;Sealed Secrets are a great starting point for securing secrets, but there is an even better way. Using the &lt;a href="https://external-secrets.io/" rel="noopener noreferrer"&gt;External Secrets Operator (ESO)&lt;/a&gt; and an external secret management system like &lt;a href="https://www.vaultproject.io/" rel="noopener noreferrer"&gt;HashiCorp Vault&lt;/a&gt;, &lt;a href="https://aws.amazon.com/secrets-manager/" rel="noopener noreferrer"&gt;AWS Secrets Manager&lt;/a&gt;, &lt;a href="https://cloud.google.com/secret-manager" rel="noopener noreferrer"&gt;Google Secrets Manager&lt;/a&gt;, or &lt;a href="https://azure.microsoft.com/en-us/services/key-vault/" rel="noopener noreferrer"&gt;Azure Key Vault&lt;/a&gt;. While this is a bit more involved to set up, it is a better approach if you use a cloud provider to host your Kubernetes cluster. ESO supports many such secret managers and watches for changes to external secret stores, and keeps Kubernetes secrets in sync.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11upqnxxvmyrdca9kjzj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11upqnxxvmyrdca9kjzj.jpg" alt="External Secrets Operator Architecture" width="800" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ESO provides four CRDs to manage secrets. The &lt;code&gt;ExternalSecret&lt;/code&gt; and &lt;code&gt;ClusterExternalSecret&lt;/code&gt; CRD define what data needs to be fetched and how it should be transformed. The &lt;code&gt;SecretStore&lt;/code&gt; and &lt;code&gt;ClusterSecretStore&lt;/code&gt; CRD define the connection details to the external secret stores. The &lt;code&gt;Cluster&lt;/code&gt; variations can be used cluster-wide.&lt;/p&gt;

&lt;p&gt;It works as below;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;SecretStore&lt;/code&gt; CRD to define the connection details to the external secret store.&lt;/li&gt;
&lt;li&gt;Create secrets in the external secret store.&lt;/li&gt;
&lt;li&gt;Create an &lt;code&gt;ExternalSecret&lt;/code&gt; CRD to define what data needs to be fetched from the external secret store.&lt;/li&gt;
&lt;li&gt;Deploy the CRDs to the target cluster.&lt;/li&gt;
&lt;li&gt;The ESO controller will fetch the data from the external secret store and create a Kubernetes secret.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Secrets are stored in a secure external secret manager, not the code repository.&lt;/li&gt;
&lt;li&gt;Keeps secrets in sync with the external secret manager.&lt;/li&gt;
&lt;li&gt;Works with many external secret managers.&lt;/li&gt;
&lt;li&gt;Can use multiple secret stores in the same cluster.&lt;/li&gt;
&lt;li&gt;Provides &lt;a href="https://prometheus.io/" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt; metrics for monitoring.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Disadvantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Needs an elaborate setup to use.&lt;/li&gt;
&lt;li&gt;Creates a Kubernetes secret object which can be decoded if you have access to the cluster and namespace.&lt;/li&gt;
&lt;li&gt;Relies on the external secret manager and its access policies to be secure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;ESO can be installed via &lt;a href="https://helm.sh/" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; using the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add external-secrets https://charts.external-secrets.io

helm &lt;span class="nb"&gt;install &lt;/span&gt;external-secrets &lt;span class="se"&gt;\&lt;/span&gt;
  external-secrets/external-secrets &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; external-secrets &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--create-namespace&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to include ESO in your Helm releases, add the &lt;code&gt;--set installCRDs=true&lt;/code&gt; flag to the above command.&lt;/p&gt;

&lt;p&gt;Let's see how you can use ESO with different secret managers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using HashiCorp Vault
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.vaultproject.io/" rel="noopener noreferrer"&gt;HashiCorp Vault&lt;/a&gt; is a popular secret manager providing different secret engines. ESO can only be used with the &lt;a href="https://www.vaultproject.io/docs/secrets/kv" rel="noopener noreferrer"&gt;KV Secrets Engine&lt;/a&gt; offered by Vault. Vault provides a free and open-source version that you can self-manage and a managed version with a free tier on the HashiCorp Cloud Platform (HCP).&lt;/p&gt;

&lt;p&gt;Make sure you have a Key-value secret store setup in &lt;a href="https://developer.hashicorp.com/vault/tutorials/getting-started" rel="noopener noreferrer"&gt;your local Vault instance&lt;/a&gt; or on the &lt;a href="https://developer.hashicorp.com/vault/tutorials/cloud" rel="noopener noreferrer"&gt;HCP cloud&lt;/a&gt;. You can also &lt;a href="https://blog.container-solutions.com/tutorialexternal-secrets-with-hashicorp-vault" rel="noopener noreferrer"&gt;deploy Vault to your Kubernetes cluster&lt;/a&gt; using the &lt;a href="https://www.vaultproject.io/docs/platform/k8s/helm" rel="noopener noreferrer"&gt;Vault Helm chart&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new &lt;code&gt;SecretStore&lt;/code&gt; CRD, &lt;code&gt;vault-backend.yaml&lt;/code&gt;, to define the connection details to Vault.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# vault-backend.yaml&lt;/span&gt;

&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;external-secrets.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SecretStore&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vault-backend&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;vault&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_VAULT_ADDRESS"&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;secret"&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v2"&lt;/span&gt;
      &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin"&lt;/span&gt; &lt;span class="c1"&gt;# required for HCP Vault&lt;/span&gt;
      &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# points to a secret that contains a vault token&lt;/span&gt;
        &lt;span class="c1"&gt;# https://www.vaultproject.io/docs/auth/token&lt;/span&gt;
        &lt;span class="na"&gt;tokenSecretRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vault-token"&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a secret resource to hold the Vault token. Use a token that has &lt;a href="https://www.vaultproject.io/docs/concepts/policies" rel="noopener noreferrer"&gt;policies&lt;/a&gt; with read access to the &lt;code&gt;secret/&lt;/code&gt; path in the Vault KV store.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create secret generic vault-token &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dry-run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;client &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR_VAULT_TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a secret in Vault. If you are using the Vault CLI, you can use the below command to create a secret. Make sure you are logged in to the vault instance from the CLI with appropriate policies.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vault kv put secret/mysecret my-value&lt;span class="o"&gt;=&lt;/span&gt;supersecret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create an &lt;code&gt;ExternalSecret&lt;/code&gt; CRD to define what data needs to be fetched from Vault.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# vault-secret.yaml&lt;/span&gt;

&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;external-secrets.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ExternalSecret&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vault-example&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;refreshInterval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;15s"&lt;/span&gt;
  &lt;span class="na"&gt;secretStoreRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vault-backend&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SecretStore&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vault-example-sync&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secretKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;secret-from-vault&lt;/span&gt;
      &lt;span class="na"&gt;remoteRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;secret/mysecret&lt;/span&gt;
        &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Apply the above CRDs to the cluster, and it should create a Kubernetes secret named &lt;code&gt;vault-example-sync&lt;/code&gt; with the data fetched from Vault.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; vault-backend.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; vault-secret.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the secret in the cluster using the &lt;code&gt;kubectl describe&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl describe secret vault-example-sync

&lt;span class="c"&gt;# output should have the below data&lt;/span&gt;
Name:         vault-example-sync
Namespace:    default
Labels:       &amp;lt;none&amp;gt;
Annotations:  reconcile.external-secrets.io/data-hash: ...

Type:  Opaque

Data
&lt;span class="o"&gt;====&lt;/span&gt;
secret-from-vault:  16 bytes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;If you have issues creating the secret&lt;/strong&gt;, check the events section of the describe output of the &lt;code&gt;ExternalSecret&lt;/code&gt; resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl describe externalsecret vault-example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see permission errors, make sure you use tokens with the right policies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other secret managers
&lt;/h3&gt;

&lt;p&gt;Setting up other secret managers is similar to the above steps. The only difference would be the &lt;code&gt;SecretStore&lt;/code&gt; CRD and the &lt;code&gt;remoteRef&lt;/code&gt; section in the &lt;code&gt;ExternalSecret&lt;/code&gt; CRD. You can find official guides for different providers in the &lt;a href="https://external-secrets.io/" rel="noopener noreferrer"&gt;ESO documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Secrets Store CSI Driver
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://secrets-store-csi-driver.sigs.k8s.io/" rel="noopener noreferrer"&gt;Secrets Store CSI Driver&lt;/a&gt; is a native upstream Kubernetes driver that can be used to abstract where the secret is stored from the workload. If you want to use a cloud provider's secret manager without exposing the secrets as Kubernetes Secret objects, you can use the CSI Driver to mount secrets as volumes in your pods. This is a great option if you use a cloud provider to host your Kubernetes cluster. The driver supports many cloud providers and can be used with different secret managers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsj73oax5x6x4ww75pd7s.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsj73oax5x6x4ww75pd7s.jpg" alt="Secrets Store CSI Driver Architecture" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Secrets Store CSI Driver is a daemonset that communicates with the secret provider to retrieve secrets specified in a &lt;code&gt;SecretProviderClass&lt;/code&gt; custom resource.&lt;/p&gt;

&lt;p&gt;It works as below;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;SecretProviderClass&lt;/code&gt; CRD to define the details of the secret to be fetched from the secret provider.&lt;/li&gt;
&lt;li&gt;Create deployments and reference the &lt;code&gt;SecretProviderClass&lt;/code&gt; in the pod's volume spec.&lt;/li&gt;
&lt;li&gt;The driver will fetch the secret from the secret provider and mount it as a &lt;code&gt;tmpfs&lt;/code&gt; volume in the pod during pod startup. This volume will be removed during pod deletion.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The driver can also sync changes to secrets. The driver currently supports &lt;a href="https://github.com/hashicorp/secrets-store-csi-driver-provider-vault" rel="noopener noreferrer"&gt;Vault&lt;/a&gt;, &lt;a href="https://github.com/aws/secrets-store-csi-driver-provider-aws" rel="noopener noreferrer"&gt;AWS&lt;/a&gt;, &lt;a href="https://github.com/Azure/secrets-store-csi-driver-provider-azure" rel="noopener noreferrer"&gt;Azure&lt;/a&gt;, and &lt;a href="https://github.com/GoogleCloudPlatform/secrets-store-csi-driver-provider-gcp" rel="noopener noreferrer"&gt;GCP&lt;/a&gt; providers. Secrets Store CSI Driver can also sync provider secrets as Kubernetes secrets; if required, this behavior needs to be explicitly enabled during installation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Secrets are stored in a secure external secret manager, not the code repository.&lt;/li&gt;
&lt;li&gt;Keeps secrets in sync with the external secret manager. It also supports the rotation of secrets.&lt;/li&gt;
&lt;li&gt;Works with all major external secret managers.&lt;/li&gt;
&lt;li&gt;Mounts secrets as volumes in the pod so they are not exposed as Kubernetes secrets. It can be configured to create Kubernetes secrets as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Disadvantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Needs an elaborate setup to use and is more complex than ESO.&lt;/li&gt;
&lt;li&gt;Uses more resources than ESO as this needs to run in every node.&lt;/li&gt;
&lt;li&gt;Relies on the external secret store and its access policies to be secure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Using Google Secret Manager provider
&lt;/h3&gt;

&lt;p&gt;Let us see how to configure the driver to use Google Secret Manager (GSM) as the secret provider.&lt;/p&gt;

&lt;p&gt;Make sure you are using a Google Kubernetes Engine (GKE) cluster with the &lt;a href="https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity" rel="noopener noreferrer"&gt;Workload Identity&lt;/a&gt; feature enabled. Workload Identity allows workloads in your GKE clusters to impersonate Identity and Access Management (IAM) service accounts to access Google Cloud services. You would also need to enable Kubernetes Engine API, Secret Manager API, and Billing for the project. The &lt;code&gt;gcloud&lt;/code&gt; CLI should prompt you to enable these APIs if they are not enabled.&lt;/p&gt;

&lt;p&gt;The below command can be used to create a new cluster with Workload Identity enabled using the &lt;code&gt;gcloud&lt;/code&gt; CLI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your gcp project&amp;gt;
gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;project &lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;

gcloud container clusters create hello-hipster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--workload-pool&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;.svc.id.goog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Install the Secrets Store CSI Driver
&lt;/h4&gt;

&lt;p&gt;Secrets Store CSI Driver can be installed on the cluster with Helm using the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts

helm &lt;span class="nb"&gt;install &lt;/span&gt;csi-secrets-store &lt;span class="se"&gt;\&lt;/span&gt;
    secrets-store-csi-driver/secrets-store-csi-driver &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--namespace&lt;/span&gt; kube-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will install the driver and CRDs on the &lt;code&gt;kube-system&lt;/code&gt; namespace. You also need to install the provider required into the cluster.&lt;/p&gt;

&lt;h4&gt;
  
  
  Install the GSM provider
&lt;/h4&gt;

&lt;p&gt;Let us install the GSM provider into the cluster. The provider can be installed using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/GoogleCloudPlatform/secrets-store-csi-driver-provider-gcp/main/deploy/provider-gcp-plugin.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Create a Secret
&lt;/h4&gt;

&lt;p&gt;First, you need to setup a workload identity service account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a service account for workload identity&lt;/span&gt;
gcloud iam service-accounts create gke-workload

&lt;span class="c"&gt;# Allow "default/mypod" to act as the new service account&lt;/span&gt;
gcloud iam service-accounts add-iam-policy-binding &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role&lt;/span&gt; roles/iam.workloadIdentityUser &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--member&lt;/span&gt; &lt;span class="s2"&gt;"serviceAccount:&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;.svc.id.goog[default/mypodserviceaccount]"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    gke-workload@&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;.iam.gserviceaccount.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's create a secret that this service account can access.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a secret with 1 active version&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"mysupersecret"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; secret.data
gcloud secrets create testsecret &lt;span class="nt"&gt;--replication-policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;automatic &lt;span class="nt"&gt;--data-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret.data
&lt;span class="nb"&gt;rm &lt;/span&gt;secret.data

&lt;span class="c"&gt;# grant the new service account permission to access the secret&lt;/span&gt;
gcloud secrets add-iam-policy-binding testsecret &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--member&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;serviceAccount:gke-workload@&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;.iam.gserviceaccount.com &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;roles/secretmanager.secretAccessor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can create a &lt;code&gt;SecretProviderClass&lt;/code&gt; resource that will be used to fetch the secret from GSM. Remember to replace &lt;code&gt;$PROJECT_ID&lt;/code&gt; with your GCP project ID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# secret-provider-class.yaml&lt;/span&gt;

&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;secrets-store.csi.x-k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SecretProviderClass&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-secrets&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcp&lt;/span&gt;
  &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;- resourceName: "projects/$PROJECT_ID/secrets/testsecret/versions/latest"&lt;/span&gt;
        &lt;span class="s"&gt;path: "good1.txt"&lt;/span&gt;
      &lt;span class="s"&gt;- resourceName: "projects/$PROJECT_ID/secrets/testsecret/versions/latest"&lt;/span&gt;
        &lt;span class="s"&gt;path: "good2.txt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Create a Pod
&lt;/h4&gt;

&lt;p&gt;Now you can create a pod that will use the &lt;code&gt;SecretProviderClass&lt;/code&gt; resource to fetch the secret from GSM. Remember to replace &lt;code&gt;$PROJECT_ID&lt;/code&gt; with your GCP project ID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# my-pod.yaml&lt;/span&gt;

&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mypodserviceaccount&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;iam.gke.io/gcp-service-account&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gke-workload@$PROJECT_ID.iam.gserviceaccount.com&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mypod&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;serviceAccountName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mypodserviceaccount&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/google.com/cloudsdktool/cloud-sdk:slim&lt;/span&gt;
      &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mypod&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;100m&lt;/span&gt;
      &lt;span class="na"&gt;stdin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;stdinOnce&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;terminationMessagePath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/dev/termination-log&lt;/span&gt;
      &lt;span class="na"&gt;terminationMessagePolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;File&lt;/span&gt;
      &lt;span class="na"&gt;tty&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/var/secrets"&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysecret&lt;/span&gt;
  &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysecret&lt;/span&gt;
      &lt;span class="na"&gt;csi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;secrets-store.csi.k8s.io&lt;/span&gt;
        &lt;span class="na"&gt;readOnly&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;volumeAttributes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;secretProviderClass&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app-secrets"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply the above resources to the cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; secret-provider-class.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; my-pod.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for the pod to start, and then &lt;code&gt;exec&lt;/code&gt; into the pod and check the contents of the mounted files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mypod /bin/bash
&lt;span class="c"&gt;# execute the below command in the pod to see the contents of the mounted secret file&lt;/span&gt;
root@mypod:/# &lt;span class="nb"&gt;cat&lt;/span&gt; /var/secrets/good1.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Other secret managers
&lt;/h3&gt;

&lt;p&gt;You can find similar guides for the &lt;a href="https://github.com/aws/secrets-store-csi-driver-provider-aws" rel="noopener noreferrer"&gt;AWS CSI provider&lt;/a&gt;, &lt;a href="https://azure.github.io/secrets-store-csi-driver-provider-azure/docs/getting-started/usage/" rel="noopener noreferrer"&gt;Azure CSI provider&lt;/a&gt; and &lt;a href="https://developer.hashicorp.com/vault/tutorials/kubernetes/kubernetes-secret-store-driver" rel="noopener noreferrer"&gt;Vault CSI provider&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Sealed Secrets are a great solution for small teams and projects to secure secrets in Git. For larger teams and projects, the External Secrets Operator or the Secrets Store CSI Driver is a better solution to manage secrets securely. The External Secrets Operator can be used with many secret management systems and is not limited to the ones mentioned above. Of course, this should be used with RBAC to prevent non-admins from reading secrets in the cluster. The Secrets Store CSI Driver might be more involved than ESO, but it is a more native solution.&lt;/p&gt;

&lt;p&gt;Cover image created using &lt;a href="https://midjourney.gitbook.io/" rel="noopener noreferrer"&gt;Midjourney&lt;/a&gt; under &lt;a href="https://creativecommons.org/licenses/by-nc/4.0/" rel="noopener noreferrer"&gt;CC BY-NC 4.0&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;If you like this article, please leave a like or a comment.&lt;/p&gt;

&lt;p&gt;You can follow me on &lt;a href="https://mastodon.social/@deepu105" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/deepu05/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>devsecops</category>
    </item>
    <item>
      <title>Rust Easy! Modern Cross-platform Command Line Tools to Supercharge Your Terminal</title>
      <dc:creator>Deepu K Sasidharan</dc:creator>
      <pubDate>Mon, 07 Nov 2022 09:36:44 +0000</pubDate>
      <link>https://forem.com/deepu105/rust-easy-modern-cross-platform-command-line-tools-to-supercharge-your-terminal-4dd3</link>
      <guid>https://forem.com/deepu105/rust-easy-modern-cross-platform-command-line-tools-to-supercharge-your-terminal-4dd3</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://deepu.tech/rust-terminal-tools-linux-mac-windows-fish-zsh/" rel="noopener noreferrer"&gt;deepu.tech&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Rust is taking over the terminal. Rust is a general-purpose programming language that is blazing fast and memory safe. It is the fastest-growing and most loved programming language in the world. It is used to build everything from operating systems to web servers to command-line tools. Recently there has been a surge of command line tools and utilities written in Rust, and many of them are intended to replace standard Unix commands. They are faster, more user-friendly, and have more features than their standard Unix counterparts. In this post, I will cover some of the best Rust command line tools I have used for a while. You can also use these to supercharge your terminal.&lt;/p&gt;

&lt;p&gt;These tools are available for both GNU/Linux and macOS. I have not tested them on Windows, but most should also work on Windows. I recommend aliasing the commands to replace the standard commands based on your preferences. If you have &lt;a href="https://doc.rust-lang.org/cargo/" rel="noopener noreferrer"&gt;Cargo&lt;/a&gt;, the rust package manager, you can install all these using Cargo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alacritty
&lt;/h2&gt;

&lt;p&gt;Let us start with the terminal itself. &lt;a href="https://github.com/alacritty/alacritty" rel="noopener noreferrer"&gt;Alacritty&lt;/a&gt; is a cross-platform modern terminal emulator with sensible defaults. It is &lt;strong&gt;GPU accelerated&lt;/strong&gt;, super fast, and highly configurable. You can use it on Linux, macOS, and Windows. It doesn't have much in terms of a UI, and hence all &lt;a href="https://github.com/alacritty/alacritty/releases/download/v0.11.0/alacritty.yml" rel="noopener noreferrer"&gt;configurations&lt;/a&gt; are done through YAML files. I don't use it as my primary terminal as I love &lt;a href="https://invent.kde.org/utilities/yakuake" rel="noopener noreferrer"&gt;Yakuake&lt;/a&gt; too much for all its cool features. We can get most of those features (tabs, split panes, dropdown mode) using &lt;a href=""&gt;tmux&lt;/a&gt; and &lt;a href="https://github.com/noctuid/tdrop" rel="noopener noreferrer"&gt;tdrop&lt;/a&gt; if really needed. I use Alacrity when I need speed and GPU acceleration. There is an excellent tutorial on &lt;a href="https://arslan.io/2018/02/05/gpu-accelerated-terminal-alacritty/" rel="noopener noreferrer"&gt;using Alacritty with tmux&lt;/a&gt;. You could also use &lt;a href="https://github.com/zellij-org/zellij" rel="noopener noreferrer"&gt;Zellij&lt;/a&gt;, a modern terminal multiplexer written in Rust, with Alacritty.&lt;/p&gt;

&lt;p&gt;There is also the &lt;a href="https://www.warp.dev/" rel="noopener noreferrer"&gt;Warp&lt;/a&gt; terminal, but it is not open source. It is a great terminal, but I prefer open source software. Thanks to &lt;a href="https://dev.to/francisc"&gt;Fran Sancisco&lt;/a&gt; for the suggestion.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FXPYyJof.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FXPYyJof.png" alt="Alacritty"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

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

&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; alacritty
&lt;span class="c"&gt;# Fedora/CentOS&lt;/span&gt;
dnf copr &lt;span class="nb"&gt;enable &lt;/span&gt;atim/alacritty
dnf &lt;span class="nb"&gt;install &lt;/span&gt;alacritty
&lt;span class="c"&gt;# Debian/Ubuntu&lt;/span&gt;
add-apt-repository ppa:aslatter/ppa
apt &lt;span class="nb"&gt;install &lt;/span&gt;alacritty
&lt;span class="c"&gt;# macOS Homebrew&lt;/span&gt;
brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; alacritty
&lt;span class="c"&gt;# Windows Scoop&lt;/span&gt;
scoop bucket add extras
scoop &lt;span class="nb"&gt;install &lt;/span&gt;alacritty
&lt;span class="c"&gt;# Cargo on any&lt;/span&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;alacritty


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Starship
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://starship.rs/" rel="noopener noreferrer"&gt;Starship&lt;/a&gt; is the best terminal prompt I have ever used. Forget &lt;a href="https://ohmyz.sh/" rel="noopener noreferrer"&gt;Oh My Zsh&lt;/a&gt; and stuff like that. Starship is fast, highly customizable, and has a great default theme and settings. I didn't even change most of the default settings, as things were perfect as it is. Starship works on shells like zsh, fish, and bash and can also work alongside other prompts like Oh My Zsh, in case you still want to use Oh My Zsh for other plugins like autosuggestions and so on. Starship works best with a &lt;a href="https://www.nerdfonts.com/" rel="noopener noreferrer"&gt;Nerd Font&lt;/a&gt; as it can show icons and ligatures based on context. I used Oh My Zsh for many years with the &lt;a href="https://github.com/romkatv/powerlevel10k" rel="noopener noreferrer"&gt;powerlevel10k&lt;/a&gt; theme, but the prompt was a bit slow. Starship is blazing fast with more features and an excellent UX.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FbPmhPKH.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FbPmhPKH.gif" alt="starship"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; starship
&lt;span class="c"&gt;# Fedora/CentOS&lt;/span&gt;
dnf &lt;span class="nb"&gt;install &lt;/span&gt;starship
&lt;span class="c"&gt;# Debian/Ubuntu&lt;/span&gt;
curl &lt;span class="nt"&gt;-sS&lt;/span&gt; https://starship.rs/install.sh | sh
&lt;span class="c"&gt;# macOS/Linux Homebrew&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;starship
&lt;span class="c"&gt;# macOS MacPorts&lt;/span&gt;
port &lt;span class="nb"&gt;install &lt;/span&gt;starship
&lt;span class="c"&gt;# Windows Scoop&lt;/span&gt;
scoop &lt;span class="nb"&gt;install &lt;/span&gt;starship
&lt;span class="c"&gt;# Cargo&lt;/span&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;starship &lt;span class="nt"&gt;--locked&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  bat
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/sharkdp/bat" rel="noopener noreferrer"&gt;bat&lt;/a&gt; is one of my favorite tools from this list. It's a replacement for &lt;code&gt;cat&lt;/code&gt;, and once you have used &lt;code&gt;bat&lt;/code&gt;, you will never go back. It provides features like syntax highlight, line numbers, Git change highlight, shows special chars, paging, and so on. It is super fast and looks beautiful. I have aliased &lt;code&gt;cat&lt;/code&gt; to &lt;code&gt;bat&lt;/code&gt; immediately after trying it for the first time. By default, bat behaves similarly to &lt;code&gt;less&lt;/code&gt; by paging large output, but that can be disabled to make it work precisely like &lt;code&gt;cat&lt;/code&gt;. It can be used as a drop-in replacement for &lt;code&gt;cat&lt;/code&gt; even in scripts. &lt;code&gt;bat&lt;/code&gt; can also be used as a previewer for &lt;a href="https://github.com/junegunn/fzf" rel="noopener noreferrer"&gt;fzf&lt;/a&gt;. It can also be combined with many other commands and tools like &lt;code&gt;tail&lt;/code&gt;, &lt;code&gt;man&lt;/code&gt;, and &lt;code&gt;git&lt;/code&gt;, among others, to add syntax highlighting to outputs. Syntax highlighting themes are configurable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2F46r4uom.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2F46r4uom.png" alt="bat"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; bat
&lt;span class="c"&gt;# Fedora/CentOS&lt;/span&gt;
dnf &lt;span class="nb"&gt;install &lt;/span&gt;bat
&lt;span class="c"&gt;# Debian/Ubuntu&lt;/span&gt;
apt &lt;span class="nb"&gt;install &lt;/span&gt;bat
&lt;span class="c"&gt;# macOS/Linux Homebrew&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;bat
&lt;span class="c"&gt;# macOS MacPorts&lt;/span&gt;
port &lt;span class="nb"&gt;install &lt;/span&gt;bat
&lt;span class="c"&gt;# Windows Scoop&lt;/span&gt;
scoop &lt;span class="nb"&gt;install &lt;/span&gt;bat
&lt;span class="c"&gt;# Cargo&lt;/span&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;bat &lt;span class="nt"&gt;--locked&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  LSD and exa
&lt;/h2&gt;

&lt;p&gt;Both &lt;a href="https://github.com/Peltoche/lsd" rel="noopener noreferrer"&gt;LSD&lt;/a&gt; and &lt;a href="https://github.com/ogham/exa" rel="noopener noreferrer"&gt;exa&lt;/a&gt; are replacements for the &lt;code&gt;ls&lt;/code&gt; command. They both look gorgeous with nice colors and icons and have features like headers, sorting, tree views, and so on. Exa is a bit faster than LSD for tree views and can show the Git status of files and folders. I prefer exa due to the Git support and faster tree views. I have set up my &lt;code&gt;ls&lt;/code&gt; alias to use exa by default. Both can be configured to show custom columns and sorting behaviors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FKsMv5xG.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FKsMv5xG.png" alt="lsd-exa"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  exa Installation
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; exa
&lt;span class="c"&gt;# Fedora/CentOS&lt;/span&gt;
dnf &lt;span class="nb"&gt;install &lt;/span&gt;exa
&lt;span class="c"&gt;# Debian/Ubuntu&lt;/span&gt;
apt &lt;span class="nb"&gt;install &lt;/span&gt;exa
&lt;span class="c"&gt;# macOS Homebrew&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;exa
&lt;span class="c"&gt;# Cargo&lt;/span&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;exa

&lt;span class="c"&gt;# Alias ls to exa&lt;/span&gt;
&lt;span class="nb"&gt;alias ls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'exa --git --icons --color=always --group-directories-first'&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  LSD Installation
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; lsd
&lt;span class="c"&gt;# Fedora/CentOS&lt;/span&gt;
dnf &lt;span class="nb"&gt;install &lt;/span&gt;lsd
&lt;span class="c"&gt;# Debian/Ubuntu&lt;/span&gt;
dpkg &lt;span class="nt"&gt;-i&lt;/span&gt; lsd_0.23.1_amd64.deb &lt;span class="c"&gt;# get .deb file from https://github.com/Peltoche/lsd/releases&lt;/span&gt;
&lt;span class="c"&gt;# macOS Homebrew&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;lsd
&lt;span class="c"&gt;# macOS MacPorts&lt;/span&gt;
port &lt;span class="nb"&gt;install &lt;/span&gt;lsd
&lt;span class="c"&gt;# Windows Scoop&lt;/span&gt;
scop &lt;span class="nb"&gt;install &lt;/span&gt;lsd
&lt;span class="c"&gt;# Cargo&lt;/span&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;lsd

&lt;span class="c"&gt;# Alias ls to lsd&lt;/span&gt;
&lt;span class="nb"&gt;alias ls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'lsd --header --color=always --group-directories-first'&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  rip
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/nivekuil/rip" rel="noopener noreferrer"&gt;rip&lt;/a&gt; is an improved version of the &lt;code&gt;rm&lt;/code&gt; command. It is faster, safer, and user-friendly. rip sends deleted files to a temp location so they can be recovered using &lt;code&gt;rip -u&lt;/code&gt;. I really like the simplicity and the revert feature, as I don't have to worry about accidentally deleting something using &lt;code&gt;rm&lt;/code&gt;. While rip can be aliased to replace &lt;code&gt;rm&lt;/code&gt;, the creators advise not doing that as you might get used to it and do &lt;code&gt;rm&lt;/code&gt; on other systems where you cannot revert the delete.&lt;/p&gt;
&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; rm-improved
&lt;span class="c"&gt;# Fedora/CentOS/Debian/Ubuntu&lt;/span&gt;
&lt;span class="c"&gt;# Install from binary or build locally using Cargo&lt;/span&gt;
&lt;span class="c"&gt;# macOS Homebrew&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;rm-improved
&lt;span class="c"&gt;# Cargo&lt;/span&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;rm-improved


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  xcp
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/tarka/xcp" rel="noopener noreferrer"&gt;xcp&lt;/a&gt; is a partial clone of the &lt;code&gt;cp&lt;/code&gt; command. It is faster and more user-friendly with progress bars, parallel copying, &lt;code&gt;.gitignore&lt;/code&gt; support, and so on. I like its simplicity and developer experience, especially the progress bars. I have aliased &lt;code&gt;cp&lt;/code&gt; to &lt;code&gt;xcp&lt;/code&gt; so I can use it everywhere.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FNOnkDyx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FNOnkDyx.png" alt="xcp"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; xcp
&lt;span class="c"&gt;# Fedora/CentOS/Debian/Ubuntu/macOS&lt;/span&gt;
&lt;span class="c"&gt;# Install from binary or build locally using Cargo&lt;/span&gt;
&lt;span class="c"&gt;# Cargo&lt;/span&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;xcp

&lt;span class="c"&gt;# Alias cp to xcp&lt;/span&gt;
&lt;span class="nb"&gt;alias cp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'xcp'&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  zoxide
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ajeetdsouza/zoxide" rel="noopener noreferrer"&gt;zoxide&lt;/a&gt; is a smarter &lt;code&gt;cd&lt;/code&gt; replacement. It remembers the directories you visit, and you can jump to them without providing a full path. You can provide partial paths or even a word from the path. When there are similar paths, zoxide offers an interactive selection using &lt;a href="https://github.com/junegunn/fzf" rel="noopener noreferrer"&gt;fzf&lt;/a&gt;. It is super fast and works with all major shells. I like how it works, and I have aliased &lt;code&gt;cd&lt;/code&gt; to &lt;code&gt;z&lt;/code&gt; so I can use it everywhere.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FOTZS3yu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FOTZS3yu.png" alt="zoxide"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; zoxide
&lt;span class="c"&gt;# Fedora/CentOS&lt;/span&gt;
dnf &lt;span class="nb"&gt;install &lt;/span&gt;zoxide
&lt;span class="c"&gt;# Debian/Ubuntu&lt;/span&gt;
apt &lt;span class="nb"&gt;install &lt;/span&gt;zoxide
&lt;span class="c"&gt;# macOS/Linux Homebrew&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;zoxide
&lt;span class="c"&gt;# macOS MacPorts&lt;/span&gt;
port &lt;span class="nb"&gt;install &lt;/span&gt;zoxide
&lt;span class="c"&gt;# Windows Scoop&lt;/span&gt;
scoop &lt;span class="nb"&gt;install &lt;/span&gt;zoxide
&lt;span class="c"&gt;# Cargo&lt;/span&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;zoxide &lt;span class="nt"&gt;--locked&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Once installed, you must add the following to your shell config file. For other shells, refer the &lt;a href="https://github.com/ajeetdsouza/zoxide#step-2-add-zoxide-to-your-shell" rel="noopener noreferrer"&gt;docs&lt;/a&gt;&lt;/p&gt;


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

&lt;p&gt;&lt;span class="c"&gt;# bash (~/.bashrc)&lt;/span&gt;&lt;br&gt;
&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;zoxide init bash&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# zsh (~/.zshrc)&lt;/span&gt;&lt;br&gt;
&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;zoxide init zsh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# fish (~/.config/fish/config.fish)&lt;/span&gt;&lt;br&gt;
zoxide init fish | &lt;span class="nb"&gt;source&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="c"&gt;# Alias cd to z&lt;/span&gt;&lt;br&gt;
&lt;span class="nb"&gt;alias cd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'z'&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  dust&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/bootandy/dust" rel="noopener noreferrer"&gt;Dust&lt;/a&gt; is an alternative for the &lt;code&gt;du&lt;/code&gt; command. It is fast and has a better UX with nice visualization for disk usage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FwfYJPqn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FwfYJPqn.png" alt="dust"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;


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

&lt;p&gt;&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;&lt;br&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; dust&lt;br&gt;
&lt;span class="c"&gt;# Fedora/CentOS&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# Install binary from &lt;a href="https://github.com/bootandy/dust/releases" rel="noopener noreferrer"&gt;https://github.com/bootandy/dust/releases&lt;/a&gt;&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# Debian/Ubuntu&lt;/span&gt;&lt;br&gt;
deb-get &lt;span class="nb"&gt;install &lt;/span&gt;du-dust&lt;br&gt;
&lt;span class="c"&gt;# macOS Homebrew&lt;/span&gt;&lt;br&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;dust&lt;br&gt;
&lt;span class="c"&gt;# macOS MacPorts&lt;/span&gt;&lt;br&gt;
port &lt;span class="nb"&gt;install &lt;/span&gt;dust&lt;br&gt;
&lt;span class="c"&gt;# Windows Scoop&lt;/span&gt;&lt;br&gt;
scoop &lt;span class="nb"&gt;install &lt;/span&gt;dust&lt;br&gt;
&lt;span class="c"&gt;# Cargo&lt;/span&gt;&lt;br&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;du-dust&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  ripgrep&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/BurntSushi/ripgrep" rel="noopener noreferrer"&gt;ripgrep (rg)&lt;/a&gt; is a line-oriented search tool that recursively searches your current directory for a regex pattern. It is faster than &lt;code&gt;grep&lt;/code&gt; and has many features like compressed files search, colorized output, smart case, file type filtering, multi-threading, and so on. It understands &lt;code&gt;.gitignore&lt;/code&gt; files and skips hidden and ignored files. &lt;a href="https://beyondgrep.com/feature-comparison/" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is a feature comparison with other similar tools, and yes, it is faster than all the other tools in the list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fbi8838T.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fbi8838T.png" alt="ripgrep"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;


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

&lt;p&gt;&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;&lt;br&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; ripgrep&lt;br&gt;
&lt;span class="c"&gt;# Fedora/CentOS&lt;/span&gt;&lt;br&gt;
dnf &lt;span class="nb"&gt;install &lt;/span&gt;ripgrep&lt;br&gt;
&lt;span class="c"&gt;# Debian/Ubuntu&lt;/span&gt;&lt;br&gt;
apt-get &lt;span class="nb"&gt;install &lt;/span&gt;ripgrep&lt;br&gt;
&lt;span class="c"&gt;# macOS/Linux Homebrew&lt;/span&gt;&lt;br&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;ripgrep&lt;br&gt;
&lt;span class="c"&gt;# macOS MacPorts&lt;/span&gt;&lt;br&gt;
port &lt;span class="nb"&gt;install &lt;/span&gt;ripgrep&lt;br&gt;
&lt;span class="c"&gt;# Windows Scoop&lt;/span&gt;&lt;br&gt;
scoop &lt;span class="nb"&gt;install &lt;/span&gt;ripgrep&lt;br&gt;
&lt;span class="c"&gt;# Cargo&lt;/span&gt;&lt;br&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;ripgrep&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  fd&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/sharkdp/fd" rel="noopener noreferrer"&gt;fd&lt;/a&gt; is a simpler alternative to &lt;code&gt;find&lt;/code&gt;. It is more intuitive to use and comes with sensible defaults. It is extremely fast due to parallel traversing and shows a modern colorized output and supports patterns and regex, parallel commands, smart case, understands &lt;code&gt;.gitignore&lt;/code&gt; files, and so on. I have aliased &lt;code&gt;find&lt;/code&gt; to &lt;code&gt;fd&lt;/code&gt; as I could never remember what options to pass to get a basic find command working.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FtcSzQ4S.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FtcSzQ4S.png" alt="fd"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;


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

&lt;p&gt;&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;&lt;br&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; fd&lt;br&gt;
&lt;span class="c"&gt;# Fedora/CentOS&lt;/span&gt;&lt;br&gt;
dnf &lt;span class="nb"&gt;install &lt;/span&gt;fd-find&lt;br&gt;
&lt;span class="c"&gt;# Debian/Ubuntu&lt;/span&gt;&lt;br&gt;
apt &lt;span class="nb"&gt;install &lt;/span&gt;fd-find&lt;br&gt;
&lt;span class="c"&gt;# macOS Homebrew&lt;/span&gt;&lt;br&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;fd&lt;br&gt;
&lt;span class="c"&gt;# macOS MacPorts&lt;/span&gt;&lt;br&gt;
port &lt;span class="nb"&gt;install &lt;/span&gt;fd&lt;br&gt;
&lt;span class="c"&gt;# Windows Scoop&lt;/span&gt;&lt;br&gt;
scoop &lt;span class="nb"&gt;install &lt;/span&gt;fd&lt;br&gt;
&lt;span class="c"&gt;# Cargo&lt;/span&gt;&lt;br&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;fd-find&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  sd&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/chmln/sd" rel="noopener noreferrer"&gt;sd&lt;/a&gt; is a find-and-replace CLI, and you can use it as a replacement for &lt;code&gt;sed&lt;/code&gt; and &lt;code&gt;awk&lt;/code&gt;. It is way more user-friendly and modern. It is also magnitudes faster than &lt;code&gt;sed&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;


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

&lt;p&gt;&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;&lt;br&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; sd&lt;br&gt;
&lt;span class="c"&gt;# Fedora/CentOS&lt;/span&gt;&lt;br&gt;
dnf &lt;span class="nb"&gt;install &lt;/span&gt;sd&lt;br&gt;
&lt;span class="c"&gt;# Debian/Ubuntu&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# Install binary from the release page&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# macOS Homebrew&lt;/span&gt;&lt;br&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;sd&lt;br&gt;
&lt;span class="c"&gt;# Windows Scoop&lt;/span&gt;&lt;br&gt;
choco &lt;span class="nb"&gt;install &lt;/span&gt;sd-cli&lt;br&gt;
&lt;span class="c"&gt;# Cargo&lt;/span&gt;&lt;br&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;sd&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  procs&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/dalance/procs" rel="noopener noreferrer"&gt;procs&lt;/a&gt; is a &lt;code&gt;ps&lt;/code&gt; replacement. It provides colorized human-readable output, multi-column search, more information than &lt;code&gt;ps&lt;/code&gt;, docker support, paging, watch mode, and tree view. It is a much more user-friendly and modern alternative to &lt;code&gt;ps&lt;/code&gt;. You can filter by name and PID and use logical and/or operators to combine multiple filters. It also has a tree view which is very useful for seeing the process hierarchy. It can also show docker container names for the process running docker containers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fnvho7hM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fnvho7hM.png" alt="procs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;


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

&lt;p&gt;&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;&lt;br&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; procs&lt;br&gt;
&lt;span class="c"&gt;# Fedora/CentOS&lt;/span&gt;&lt;br&gt;
dnf &lt;span class="nb"&gt;install &lt;/span&gt;procs&lt;br&gt;
&lt;span class="c"&gt;# Debian/Ubuntu&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# Install binary from the release page&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# macOS Homebrew&lt;/span&gt;&lt;br&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;procs&lt;br&gt;
&lt;span class="c"&gt;# macOS MacPorts&lt;/span&gt;&lt;br&gt;
port &lt;span class="nb"&gt;install &lt;/span&gt;procs&lt;br&gt;
&lt;span class="c"&gt;# Windows Scoop&lt;/span&gt;&lt;br&gt;
scoop &lt;span class="nb"&gt;install &lt;/span&gt;procs&lt;br&gt;
&lt;span class="c"&gt;# Cargo&lt;/span&gt;&lt;br&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;procs&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  bottom&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ClementTsang/bottom" rel="noopener noreferrer"&gt;bottom&lt;/a&gt; is a &lt;code&gt;top&lt;/code&gt; replacement with a nice terminal UI. It's quite feature-rich and customizable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FnbL8gBi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FnbL8gBi.png" alt="bottom"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;


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

&lt;p&gt;&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;&lt;br&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; bottom&lt;br&gt;
&lt;span class="c"&gt;# Fedora/CentOS&lt;/span&gt;&lt;br&gt;
dnf copr &lt;span class="nb"&gt;enable &lt;/span&gt;atim/bottom &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;br&gt;
dnf &lt;span class="nb"&gt;install &lt;/span&gt;bottom&lt;br&gt;
&lt;span class="c"&gt;# Debian/Ubuntu&lt;/span&gt;&lt;br&gt;
dpkg &lt;span class="nt"&gt;-i&lt;/span&gt; bottom_0.6.8_amd64.deb&lt;br&gt;
&lt;span class="c"&gt;# macOS Homebrew&lt;/span&gt;&lt;br&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;bottom&lt;br&gt;
&lt;span class="c"&gt;# macOS MacPorts&lt;/span&gt;&lt;br&gt;
port &lt;span class="nb"&gt;install &lt;/span&gt;bottom&lt;br&gt;
&lt;span class="c"&gt;# Windows Scoop&lt;/span&gt;&lt;br&gt;
scoop &lt;span class="nb"&gt;install &lt;/span&gt;bottom&lt;br&gt;
&lt;span class="c"&gt;# Cargo&lt;/span&gt;&lt;br&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;bottom &lt;span class="nt"&gt;--locked&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Topgrade&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/topgrade-rs/topgrade" rel="noopener noreferrer"&gt;Topgrade&lt;/a&gt; is a fantastic utility if you prefer to keep your system up-to-date, like me. It detects most of the package managers on your system and triggers updates. It is configurable, so you can configure it to ignore certain package managers. On my system, it detected pacman, SDKMAN, Flatpak, snap, Homebrew, rustup, Linux firmware, Pip, and so on. Topgrade is cross-platform; you can use it on Windows, macOS, and Linux.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2F7PXryFh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2F7PXryFh.png" alt="topgrade"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;


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

&lt;p&gt;&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;&lt;br&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; topgrade&lt;br&gt;
&lt;span class="c"&gt;# Fedora/CentOS/Debian/Ubuntu/Windows&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# Install binary from the release page&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# macOS Homebrew&lt;/span&gt;&lt;br&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;topgrade&lt;br&gt;
&lt;span class="c"&gt;# macOS MacPorts&lt;/span&gt;&lt;br&gt;
port &lt;span class="nb"&gt;install &lt;/span&gt;topgrade&lt;br&gt;
&lt;span class="c"&gt;# Cargo&lt;/span&gt;&lt;br&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;topgrade &lt;span class="nt"&gt;--locked&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Broot&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Canop/broot" rel="noopener noreferrer"&gt;Broot&lt;/a&gt; is a &lt;code&gt;tree&lt;/code&gt; alternative with a better user experience, and you can use it to navigate a file structure. It's fast and respects &lt;code&gt;.gitignore&lt;/code&gt;. You can cd into a directory from the tree view, open sub-directories in a panel, and even preview files. It has excellent keyboard navigation as well. It has many more features.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FkAuL7oJ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FkAuL7oJ.png" alt="broot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;


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

&lt;p&gt;&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;&lt;br&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; broot&lt;br&gt;
&lt;span class="c"&gt;# Fedora/CentOS/Debian/Ubuntu/Windows&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# Install binary from release page &lt;a href="https://dystroy.org/broot/install/" rel="noopener noreferrer"&gt;https://dystroy.org/broot/install/&lt;/a&gt;&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# macOS Homebrew&lt;/span&gt;&lt;br&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;broot&lt;br&gt;
&lt;span class="c"&gt;# macOS MacPorts&lt;/span&gt;&lt;br&gt;
port &lt;span class="nb"&gt;install &lt;/span&gt;broot&lt;br&gt;
&lt;span class="c"&gt;# Cargo&lt;/span&gt;&lt;br&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;broot &lt;span class="nt"&gt;--locked&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Tokei&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/XAMPPRocky/tokei" rel="noopener noreferrer"&gt;Tokei&lt;/a&gt; is a nice utility to count lines and stats of code. It is very fast, accurate, and has a nice output. It supports over 150 languages and can output in JSON, YAML, CBOR, and human-readable tables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FDGFI13M.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FDGFI13M.png" alt="tokei"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;


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

&lt;p&gt;&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;&lt;br&gt;
yay &lt;span class="nt"&gt;-S&lt;/span&gt; tokei&lt;br&gt;
&lt;span class="c"&gt;# Fedora/CentOS&lt;/span&gt;&lt;br&gt;
dnf &lt;span class="nb"&gt;install &lt;/span&gt;tokei&lt;br&gt;
&lt;span class="c"&gt;# Debian/Ubuntu&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# Install binary from the release page&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# macOS Homebrew&lt;/span&gt;&lt;br&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;tokei&lt;br&gt;
&lt;span class="c"&gt;# macOS MacPorts&lt;/span&gt;&lt;br&gt;
port &lt;span class="nb"&gt;install &lt;/span&gt;tokei&lt;br&gt;
&lt;span class="c"&gt;# Windows Scoop&lt;/span&gt;&lt;br&gt;
scoop &lt;span class="nb"&gt;install &lt;/span&gt;tokei&lt;br&gt;
&lt;span class="c"&gt;# Cargo&lt;/span&gt;&lt;br&gt;
cargo &lt;span class="nb"&gt;install &lt;/span&gt;tokei&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Other notable tools&lt;br&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/kdash-rs/kdash/" rel="noopener noreferrer"&gt;kdash&lt;/a&gt;: A fast and simple dashboard for Kubernetes. Its created by me :)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/zellij-org/zellij" rel="noopener noreferrer"&gt;Zellij&lt;/a&gt;: A feature rich modern terminal multiplexer with batteries included.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nushell/nushell" rel="noopener noreferrer"&gt;Nushell&lt;/a&gt;: A modern shell written in Rust. Looks quite promising.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ducaale/xh" rel="noopener noreferrer"&gt;xh&lt;/a&gt;: A HTTPie alternative with better performance.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/y2z/monolith" rel="noopener noreferrer"&gt;monolith&lt;/a&gt;: Convert any webpage into a single HTML file with all assets inlined.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/dandavison/delta" rel="noopener noreferrer"&gt;delta&lt;/a&gt;: A syntax-highlighting pager for git, diff, and grep output.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/sirwart/ripsecrets" rel="noopener noreferrer"&gt;ripsecrets&lt;/a&gt;: Find secret keys in your code before committing them to git.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nerdypepper/eva" rel="noopener noreferrer"&gt;eva&lt;/a&gt;: A CLI REPL calculator.&lt;/li&gt;
&lt;li&gt;You can find a list of other Rust CLI tools &lt;a href="https://gist.github.com/sts10/daadbc2f403bdffad1b6d33aff016c0a" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you like this article, please leave a like or a comment.&lt;/p&gt;

&lt;p&gt;You can follow me on &lt;a href="https://twitter.com/deepu105" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/deepu05/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>terminal</category>
      <category>linux</category>
      <category>macos</category>
    </item>
    <item>
      <title>What the Heck Is Project Loom for Java?</title>
      <dc:creator>Deepu K Sasidharan</dc:creator>
      <pubDate>Sat, 27 Aug 2022 14:29:25 +0000</pubDate>
      <link>https://forem.com/oktadev/what-the-heck-is-project-loom-for-java-21hh</link>
      <guid>https://forem.com/oktadev/what-the-heck-is-project-loom-for-java-21hh</guid>
      <description>&lt;p&gt;Java has had good multi-threading and concurrency capabilities from early on in its evolution and can effectively utilize multi-threaded and multi-core CPUs. Java Development Kit (JDK) 1.1 had basic support for platform threads (or Operating System (OS) threads), and JDK 1.5 had more utilities and updates to improve concurrency and multi-threading. JDK 8 brought asynchronous programming support and more concurrency improvements. While things have continued to improve over multiple versions, there has been nothing groundbreaking in Java for the last three decades, apart from support for concurrency and multi-threading using OS threads.&lt;/p&gt;

&lt;p&gt;Though the concurrency model in Java is powerful and flexible as a feature, it was not the easiest to use, and the developer experience hasn't been great. This is primarily due to the shared state concurrency model used by default. One has to resort to synchronizing threads to avoid issues like data races and thread blocking. I wrote more about Java concurrency in my &lt;a href="https://deepu.tech/concurrency-in-modern-languages-java/"&gt;Concurrency in modern programming languages: Java&lt;/a&gt; post.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Project Loom?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Project Loom aims to drastically reduce the effort of writing, maintaining, and observing high-throughput concurrent applications that make the best use of available hardware.&lt;/p&gt;

&lt;p&gt;— Ron Pressler (Tech lead, Project Loom)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;OS threads are at the core of Java's concurrency model and have a very mature ecosystem around them, but they also come with some drawbacks and are expensive computationally. Let's look at the two most common use cases for concurrency and the drawbacks of the current Java concurrency model in these cases.&lt;/p&gt;

&lt;p&gt;One of the most common concurrency use cases is serving requests over the wire using a server. For this, the preferred approach is the thread-per-request model, where a separate thread handles each request. Throughput of such systems can be explained using &lt;a href="https://en.wikipedia.org/wiki/Little%27s_law"&gt;Little's law&lt;/a&gt;, which states that in a &lt;strong&gt;stable system&lt;/strong&gt;, the average concurrency (number of requests concurrently processed by the server), &lt;strong&gt;L&lt;/strong&gt;, is equal to the throughput (average rate of requests), &lt;strong&gt;λ&lt;/strong&gt;, times the latency (average duration of processing each request), &lt;strong&gt;W&lt;/strong&gt;. With this, you can derive that throughput equals average concurrency divided by latency (&lt;strong&gt;λ = L/W&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;So in a thread-per-request model, the throughput will be limited by the number of OS threads available, which depends on the number of physical cores/threads available on the hardware. To work around this, you have to use shared thread pools or asynchronous concurrency, both of which have their drawbacks. Thread pools have many limitations, like thread leaking, deadlocks, resource thrashing, etc. Asynchronous concurrency means you must adapt to a more complex programming style and handle data races carefully. There are also chances for memory leaks, thread locking, etc.&lt;/p&gt;

&lt;p&gt;Another common use case is parallel processing or multi-threading, where you might split a task into subtasks across multiple threads. Here you have to write solutions to avoid data corruption and data races. In some cases, you must also ensure thread synchronization when executing a parallel task distributed over multiple threads. The implementation becomes even more fragile and puts a lot more responsibility on the developer to ensure there are no issues like thread leaks and cancellation delays.&lt;/p&gt;

&lt;p&gt;Project Loom aims to fix these issues in the current concurrency model by introducing two new features: &lt;em&gt;virtual threads&lt;/em&gt; and &lt;em&gt;structured concurrency&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Virtual threads
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Java 19 is scheduled to be released in September 2022, and Virtual threads will be a preview feature. Yayyy!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://openjdk.org/jeps/425"&gt;Virtual threads&lt;/a&gt; are lightweight threads that are not tied to OS threads but are managed by the JVM. They are suitable for thread-per-request programming styles without having the limitations of OS threads. You can create millions of virtual threads without affecting throughput. This is quite similar to coroutines, like &lt;a href="https://go.dev/tour/concurrency/1"&gt;goroutines&lt;/a&gt;, made famous by the Go programming language (Golang).&lt;/p&gt;

&lt;p&gt;The new virtual threads in Java 19 will be pretty easy to use. Compare the below with Golang's goroutines or Kotlin's coroutines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Virtual thread&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startVirtualThread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, Project Loom!"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Goroutine&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, Goroutines!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Kotlin coroutine&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;runBlocking&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, Kotlin coroutines!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fun fact: before JDK 1.1, Java had support for green threads (aka virtual threads), but the feature was removed in JDK 1.1 as that implementation was not any better than platform threads.&lt;/p&gt;

&lt;p&gt;The new implementation of virtual threads is done in the JVM, where it maps multiple virtual threads to one or more OS threads, and the developer can use virtual threads or platform threads as per their needs. A few other important aspects of this implementation of virtual threads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is a &lt;code&gt;Thread&lt;/code&gt; in code, runtime, debugger, and profiler&lt;/li&gt;
&lt;li&gt;It's a Java entity and not a wrapper around a native thread&lt;/li&gt;
&lt;li&gt;Creating and blocking them are cheap operations&lt;/li&gt;
&lt;li&gt;They should not be pooled&lt;/li&gt;
&lt;li&gt;Virtual threads use a work-stealing &lt;code&gt;ForkJoinPool&lt;/code&gt; scheduler&lt;/li&gt;
&lt;li&gt;Pluggable schedulers can be used for asynchronous programming&lt;/li&gt;
&lt;li&gt;A virtual thread will have its own stack memory&lt;/li&gt;
&lt;li&gt;The virtual threads API is very similar to platform threads and hence easier to adopt/migrate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at some examples that show the power of virtual threads.&lt;/p&gt;

&lt;h4&gt;
  
  
  Total number of threads
&lt;/h4&gt;

&lt;p&gt;First, let's see how many platform threads vs. virtual threads we can create on a machine. My machine is Intel Core i9-11900H with 8 cores, 16 threads, and 64GB RAM running Fedora 36.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Platform threads&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AtomicInteger&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;incrementAndGet&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Thread count = "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;LockSupport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;park&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}).&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On my machine, the code crashed after &lt;strong&gt;32_539&lt;/strong&gt; platform threads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Virtual threads&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AtomicInteger&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startVirtualThread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;incrementAndGet&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Thread count = "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;LockSupport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;park&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On my machine, the process hung after &lt;strong&gt;14_625_956&lt;/strong&gt; virtual threads but didn't crash, and as memory became available, it kept going slowly. You may be wondering why this behavior! It's due to the parked virtual threads being garbage collected, and the JVM is able to create more virtual threads and assign them to the underlying platform thread.&lt;/p&gt;

&lt;h4&gt;
  
  
  Task throughput
&lt;/h4&gt;

&lt;p&gt;Let's try to run 100,000 tasks using platform threads.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;defaultThreadFactory&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;IntStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This uses the &lt;code&gt;newThreadPerTaskExecutor&lt;/code&gt; with the default thread factory and thus uses a thread group. When I ran this code and timed it, I got the numbers shown here. I get better performance when I use a thread pool with &lt;code&gt;Executors.newCachedThreadPool()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 'newThreadPerTaskExecutor' with 'defaultThreadFactory'&lt;/span&gt;
0:18.77 real,   18.15 s user,   7.19 s sys,     135% 3891pu,    0 amem,         743584 mmem
&lt;span class="c"&gt;# 'newCachedThreadPool' with 'defaultThreadFactory'&lt;/span&gt;
0:11.52 real,   13.21 s user,   4.91 s sys,     157% 6019pu,    0 amem,         2215972 mmem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not so bad. Now, let's do the same using virtual threads.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;IntStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I run and time it, I get the following numbers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;0:02.62 real,   6.83 s user,    1.46 s sys,     316% 14840pu,   0 amem,         350268 mmem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is far more performant than using platform threads with thread pools. Of course, these are simple use cases; both thread pools and virtual thread implementations can be further optimized for better performance, but that's not the point of this post.&lt;/p&gt;

&lt;p&gt;Running Java Microbenchmark Harness (JMH) with the same code gives the following results, and you can see that virtual threads outperform platform threads by a huge margin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Throughput&lt;/span&gt;
Benchmark                             Mode  Cnt  Score   Error  Units
LoomBenchmark.platformThreadPerTask  thrpt    5  0.362 ± 0.079  ops/s
LoomBenchmark.platformThreadPool     thrpt    5  0.528 ± 0.067  ops/s
LoomBenchmark.virtualThreadPerTask   thrpt    5  1.843 ± 0.093  ops/s

&lt;span class="c"&gt;# Average time&lt;/span&gt;
Benchmark                             Mode  Cnt  Score   Error  Units
LoomBenchmark.platformThreadPerTask   avgt    5  5.600 ± 0.768   s/op
LoomBenchmark.platformThreadPool      avgt    5  3.887 ± 0.717   s/op
LoomBenchmark.virtualThreadPerTask    avgt    5  1.098 ± 0.020   s/op
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find the benchmark &lt;a href="https://github.com/deepu105/java-loom-benchmarks"&gt;source code on GitHub&lt;/a&gt;. Here are some other meaningful benchmarks for virtual threads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An interesting benchmark using ApacheBench &lt;a href="https://github.com/ebarlas/project-loom-comparison"&gt;on GitHub&lt;/a&gt; by &lt;a href="https://twitter.com/ElliotBarlas"&gt;Elliot Barlas&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A benchmark using Akka actors &lt;a href="https://medium.com/@zakgof/a-simple-benchmark-for-jdk-project-looms-virtual-threads-4f43ef8aeb1"&gt;on Medium&lt;/a&gt; by &lt;a href="https://medium.com/@zakgof"&gt;Alexander Zakusylo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JMH benchmarks for I/O and non-I/O tasks &lt;a href="https://github.com/colincachia/loom-benchmark"&gt;on GitHub&lt;/a&gt; by &lt;a href="https://twitter.com/colincachia"&gt;Colin Cachia&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Structured concurrency
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Structured concurrency will be an incubator feature in Java 19.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://openjdk.org/jeps/428"&gt;Structured concurrency&lt;/a&gt; aims to simplify multi-threaded and parallel programming. It treats multiple tasks running in different threads as a single unit of work, streamlining error handling and cancellation while improving reliability and observability. This helps to avoid issues like thread leaking and cancellation delays. Being an incubator feature, this might go through further changes during stabilization.&lt;/p&gt;

&lt;p&gt;Consider the following example using &lt;code&gt;java.util.concurrent.ExecutorService&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;handleOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;ExecutionException&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;esvc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ScheduledThreadPoolExecutor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;esvc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;updateInventory&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="nc"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;esvc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;updateOrder&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;theInventory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// Join updateInventory&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;theOrder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;           &lt;span class="c1"&gt;// Join updateOrder&lt;/span&gt;

        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Inventory "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;theInventory&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" updated for order "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;theOrder&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We want &lt;code&gt;updateInventory()&lt;/code&gt; and &lt;code&gt;updateOrder()&lt;/code&gt; subtasks to be executed concurrently. Each of those can succeed or fail independently. Ideally, the &lt;code&gt;handleOrder()&lt;/code&gt; method should fail if any subtask fails. However, if a failure occurs in one subtask, things get messy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Imagine that &lt;code&gt;updateInventory()&lt;/code&gt; fails and throws an exception. Then, the &lt;code&gt;handleOrder()&lt;/code&gt; method throws an exception when calling &lt;code&gt;inventory.get()&lt;/code&gt;. So far this is fine, but what about &lt;code&gt;updateOrder()&lt;/code&gt;? Since it runs on its own thread, it can complete successfully. But now we have an issue with a mismatch in inventory and order. Suppose the &lt;code&gt;updateOrder()&lt;/code&gt; is an expensive operation. In that case, we are just wasting the resources for nothing, and we will have to write some sort of guard logic to revert the updates done to order as our overall operation has failed.&lt;/li&gt;
&lt;li&gt;Imagine that &lt;code&gt;updateInventory()&lt;/code&gt; is an expensive long-running operation and &lt;code&gt;updateOrder()&lt;/code&gt; throws an error. The &lt;code&gt;handleOrder()&lt;/code&gt; task will be blocked on &lt;code&gt;inventory.get()&lt;/code&gt; even though &lt;code&gt;updateOrder()&lt;/code&gt; threw an error. Ideally, we would like the &lt;code&gt;handleOrder()&lt;/code&gt; task to cancel &lt;code&gt;updateInventory()&lt;/code&gt; when a failure occurs in &lt;code&gt;updateOrder()&lt;/code&gt; so that we are not wasting time.&lt;/li&gt;
&lt;li&gt;If the thread executing &lt;code&gt;handleOrder()&lt;/code&gt; is interrupted, the interruption is not propagated to the subtasks. In this case &lt;code&gt;updateInventory()&lt;/code&gt; and &lt;code&gt;updateOrder()&lt;/code&gt; will leak and continue to run in the background.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For these situations, we would have to carefully write workarounds and failsafe, putting all the burden on the developer.&lt;/p&gt;

&lt;p&gt;We can achieve the same functionality with structured concurrency using the code below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;handleOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;ExecutionException&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StructuredTaskScope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ShutdownOnFailure&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;updateInventory&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="nc"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;updateOrder&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;           &lt;span class="c1"&gt;// Join both forks&lt;/span&gt;
        &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;throwIfFailed&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// ... and propagate errors&lt;/span&gt;

        &lt;span class="c1"&gt;// Here, both forks have succeeded, so compose their results&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Inventory "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;resultNow&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" updated for order "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;resultNow&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike the previous sample using &lt;code&gt;ExecutorService&lt;/code&gt;, we can now use &lt;code&gt;StructuredTaskScope&lt;/code&gt; to achieve the same result while confining the lifetimes of the subtasks to the lexical scope, in this case, the body of the &lt;em&gt;try-with-resources&lt;/em&gt; statement. The code is much more readable, and the intent is also clear. &lt;code&gt;StructuredTaskScope&lt;/code&gt; also ensures the following behavior automatically.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error handling with short-circuiting&lt;/strong&gt; — If either the &lt;code&gt;updateInventory()&lt;/code&gt; or &lt;code&gt;updateOrder()&lt;/code&gt; fails, the other is canceled unless its already completed. This is managed by the cancellation policy implemented by &lt;code&gt;ShutdownOnFailure()&lt;/code&gt;; other policies are possible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cancellation propagation&lt;/strong&gt; — If the thread running &lt;code&gt;handleOrder()&lt;/code&gt; is interrupted before or during the call to &lt;code&gt;join()&lt;/code&gt;, both forks are canceled automatically when the thread exits the scope.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Observability&lt;/strong&gt; — A thread dump would clearly display the task hierarchy, with the threads running &lt;code&gt;updateInventory()&lt;/code&gt; and &lt;code&gt;updateOrder()&lt;/code&gt; shown as children of the scope.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  State of Project Loom
&lt;/h2&gt;

&lt;p&gt;The Loom project started in 2017 and has undergone many changes and proposals. Virtual threads were initially called fibers, but later on they were renamed to avoid confusion. Today with Java 19 getting closer to release, the project has delivered the two features discussed above. One as a preview and another as an incubator. Hence the path to stabilization of the features should be more precise.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does this mean to regular Java developers?
&lt;/h2&gt;

&lt;p&gt;When these features are production ready, it should not affect regular Java developers much, as these developers may be using libraries for concurrency use cases. But it can be a big deal in those rare scenarios where you are doing a lot of multi-threading without using libraries. Virtual threads could be a no-brainer replacement for all use cases where you use thread pools today. This will increase performance and scalability in most cases based on the benchmarks out there. Structured concurrency can help simplify the multi-threading or parallel processing use cases and make them less fragile and more maintainable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does this mean to Java library developers?
&lt;/h2&gt;

&lt;p&gt;When these features are production ready, it will be a big deal for libraries and frameworks that use threads or parallelism. Library authors will see huge performance and scalability improvements while simplifying the codebase and making it more maintainable. Most Java projects using thread pools and platform threads will benefit from switching to virtual threads. Candidates include Java server software like Tomcat, Undertow, and Netty; and web frameworks like Spring and Micronaut. I expect most Java web technologies to migrate to virtual threads from thread pools. Java web technologies and trendy reactive programming libraries like RxJava and Akka could also use structured concurrency effectively. This doesn't mean that virtual threads will be the one solution for all; there will still be use cases and benefits for asynchronous and reactive programming.&lt;/p&gt;




&lt;p&gt;If you like this article, please leave a like or a comment.&lt;/p&gt;

&lt;p&gt;You can follow me on &lt;a href="https://twitter.com/deepu105"&gt;Twitter&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/deepu05/"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The cover image was created using a photo by &lt;a href="https://unsplash.com/@tama66"&gt;Peter Herrmann&lt;/a&gt; on &lt;a href="https://unsplash.com"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post was originally published on the &lt;a href="https://developer.okta.com/blog/2022/08/26/state-of-java-project-loom"&gt;Okta Developer Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>concurrency</category>
      <category>loom</category>
      <category>openjdk</category>
    </item>
    <item>
      <title>How to Deploy JHipster Microservices on Amazon EKS Using Terraform and Kubernetes</title>
      <dc:creator>Deepu K Sasidharan</dc:creator>
      <pubDate>Tue, 05 Jul 2022 13:50:34 +0000</pubDate>
      <link>https://forem.com/jhipster/how-to-deploy-jhipster-microservices-on-amazon-eks-using-terraform-and-kubernetes-49a5</link>
      <guid>https://forem.com/jhipster/how-to-deploy-jhipster-microservices-on-amazon-eks-using-terraform-and-kubernetes-49a5</guid>
      <description>&lt;p&gt;When it comes to infrastructure, public clouds are the most popular choice these days, especially Amazon Web Services (AWS). If you are in one of those lucky or unlucky (depending on how you see it) teams running microservices, then you need a way to orchestrate their deployments. When it comes to orchestrating microservices, Kubernetes is the de-facto choice. Most public cloud providers also provide managed Kubernetes as a service; for example, Google provides Google Kubernetes Engine (GKE), Microsoft provides Azure Kubernetes Service (AKS), and Amazon provides Amazon Elastic Kubernetes Service (EKS).&lt;/p&gt;

&lt;p&gt;This doesn't mean that deploying and managing microservices on the public cloud is easy; each cloud service comes with its own challenges and pain. This is especially true for Amazon EKS, which, in my opinion, is the most difficult Kubernetes service to use, but also one of the most flexible. This is because EKS consists of some clever orchestrations doing a complex dance on top of other AWS services like EC2, EBS, etc.&lt;/p&gt;

&lt;p&gt;If you want to run a microservice stack on EKS, you will need to spend some extra time and effort setting it up and managing it. This is where infrastructure as code (IaC) tools like &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; come in handy.&lt;/p&gt;

&lt;p&gt;So here is what you will learn to do today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scaffold a Java microservice stack using JHipster, Spring Boot, and Spring Cloud&lt;/li&gt;
&lt;li&gt;Create an EKS cluster, Virtual Private Cloud (VPC), subnets, and required Kubernetes add-ons using Terraform on AWS&lt;/li&gt;
&lt;li&gt;Set up OIDC authentication for the microservice stack using Okta&lt;/li&gt;
&lt;li&gt;Build and deploy the microservice stack to the cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://portal.aws.amazon.com/billing/signup" rel="noopener noreferrer"&gt;AWS account&lt;/a&gt; with the &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/security_iam_id-based-policy-examples.html" rel="noopener noreferrer"&gt;IAM permissions to create EKS clusters&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AWS CLI &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;installed&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html" rel="noopener noreferrer"&gt;configured&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/install-aws-iam-authenticator.html" rel="noopener noreferrer"&gt;AWS IAM Authenticator&lt;/a&gt; installed&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/tasks/tools/" rel="noopener noreferrer"&gt;kubectl&lt;/a&gt; installed&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; installed and configured&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.terraform.io/downloads" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; installed&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sdkman.io/usage" rel="noopener noreferrer"&gt;Java 11+&lt;/a&gt; installed&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cli.okta.com/" rel="noopener noreferrer"&gt;Okta CLI&lt;/a&gt; installed&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.jhipster.tech/installation/" rel="noopener noreferrer"&gt;JHipster&lt;/a&gt; CLI installed&lt;/li&gt;
&lt;li&gt;[Optional] &lt;a href="https://github.com/kdash-rs/kdash" rel="noopener noreferrer"&gt;KDash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Terraform, why not CloudFormation?
&lt;/h2&gt;

&lt;p&gt;At this point, the first question that might pop up in your mind would be, "Why not use &lt;a href="https://aws.amazon.com/cloudformation/" rel="noopener noreferrer"&gt;CloudFormation&lt;/a&gt;?". It's a good question; after all, CloudFormation is built by AWS and hence sounds like an excellent solution to manage AWS resources. But anyone who has tried both CloudFormation and Terraform will probably tell you to forget that CloudFormation even exists. I think CloudFormation is far more complex and less developer-friendly than Terraform. You also need to write a lot more boilerplate with CloudFormation in YAML or JSON. Yikes! And most importantly, Terraform is far more powerful and flexible than CloudFormation. It's cross-platform, which means you can take care of all your infrastructure management needs on any platform with one tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaffold a Java microservice stack using JHipster
&lt;/h2&gt;

&lt;p&gt;You need a microservice stack to deploy to the cluster. I'm using a microservice stack scaffolded for demo purposes using &lt;a href="https://www.jhipster.tech" rel="noopener noreferrer"&gt;JHipster&lt;/a&gt;. You can use another microservice stack if you want. If you prefer using the same application as in this demo, then you can either scaffold it using JHipster &lt;a href="https://www.jhipster.tech/jdl/intro" rel="noopener noreferrer"&gt;JDL&lt;/a&gt; or clone the sample repository from &lt;a href="https://github.com/oktadev/okta-jhipster-k8s-eks-microservices-example" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1lbqsamb41i5wpmuht5c.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1lbqsamb41i5wpmuht5c.jpg" alt="JHipster microservice architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1: Scaffold the microservice stack using JHipster&lt;/strong&gt;&lt;/p&gt;

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

&lt;span class="nb"&gt;mkdir &lt;/span&gt;jhipster-microservice-stack
&lt;span class="nb"&gt;cd &lt;/span&gt;jhipster-microservice-stack
&lt;span class="c"&gt;# download the JDL file.&lt;/span&gt;
jhipster download https://raw.githubusercontent.com/oktadev/okta-jhipster-k8s-eks-microservices-example/main/apps.jdl
&lt;span class="c"&gt;# Update the `dockerRepositoryName` property to use your Docker Repository URI/Name.&lt;/span&gt;
&lt;span class="c"&gt;# scaffold the apps.&lt;/span&gt;
jhipster jdl apps.jdl


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Option 2: Clone the sample repository&lt;/strong&gt;&lt;/p&gt;

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

git clone https://github.com/oktadev/okta-jhipster-k8s-eks-microservices-example


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

&lt;/div&gt;

&lt;p&gt;In either case, remember to change the Docker repository name to match your Docker repository.&lt;/p&gt;

&lt;p&gt;The JHipster scaffolded sample application has a gateway application, two microservices, and uses &lt;a href="https://www.jhipster.tech/jhipster-registry/" rel="noopener noreferrer"&gt;JHipster Registry&lt;/a&gt; for service discovery and centralized configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an EKS cluster using Terraform
&lt;/h2&gt;

&lt;p&gt;Now let us move on to the important part of the tutorial. Creating an EKS cluster in AWS is not as straightforward as in Google Cloud Platform (GCP). You need to also create a lot more resources for everything to work correctly without surprises. You will be using a bunch of Terraform providers to help with this, and you will also use some prebuilt Terraform modules like &lt;a href="https://github.com/terraform-aws-modules/terraform-aws-vpc" rel="noopener noreferrer"&gt;AWS VPC Terraform module&lt;/a&gt; and &lt;a href="https://github.com/aws-ia/terraform-aws-eks-blueprints" rel="noopener noreferrer"&gt;Amazon EKS Blueprints for Terraform&lt;/a&gt; to reduce the amount of boilerplate you need to write.&lt;/p&gt;

&lt;p&gt;These are the AWS resources and VPC architecture you will create:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8wk8rdzsjdbub2um2s76.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8wk8rdzsjdbub2um2s76.jpg" alt="AWS EKS and VPC architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Build the Terraform configuration
&lt;/h3&gt;

&lt;p&gt;First, make sure you use a specific version of the providers as different versions might use different attributes and features. Create a &lt;code&gt;versions.tf&lt;/code&gt; file:&lt;/p&gt;

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

&lt;span class="nb"&gt;mkdir &lt;/span&gt;terraform
&lt;span class="nb"&gt;cd &lt;/span&gt;terraform
&lt;span class="nb"&gt;touch &lt;/span&gt;versions.tf


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

&lt;/div&gt;

&lt;p&gt;Add the following to the file:&lt;/p&gt;

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

&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.0.0"&lt;/span&gt;

  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 3.72"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;kubernetes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/kubernetes"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 2.10"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;helm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/helm"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 2.4.1"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;random&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/random"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 3.2.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;nullres&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/null"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 3.1"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Next, you need to define variables and configure the providers. Create a &lt;code&gt;config.tf&lt;/code&gt; file:&lt;/p&gt;

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

&lt;span class="nb"&gt;touch &lt;/span&gt;config.tf


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

&lt;/div&gt;

&lt;p&gt;Add the following to the file:&lt;/p&gt;

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

&lt;span class="c1"&gt;# ##  To save state in s3. Update to suit your needs&lt;/span&gt;
&lt;span class="c1"&gt;# backend "s3" {&lt;/span&gt;
&lt;span class="c1"&gt;#   bucket = "create-an-s3-bucket-and-provide-name-here"&lt;/span&gt;
&lt;span class="c1"&gt;#   region = local.region&lt;/span&gt;
&lt;span class="c1"&gt;#   key    = "eks-cluster-with-new-vpc/terraform.tfstate"&lt;/span&gt;
&lt;span class="c1"&gt;# }&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eu-west-1"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS region"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"random_string"&lt;/span&gt; &lt;span class="s2"&gt;"suffix"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;length&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
  &lt;span class="nx"&gt;special&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_availability_zones"&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"okta-jhipster-eks-${random_string.suffix.result}"&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.22"&lt;/span&gt;
  &lt;span class="nx"&gt;instance_types&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"t2.large"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# can be multiple, comma separated&lt;/span&gt;

  &lt;span class="nx"&gt;vpc_cidr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
  &lt;span class="nx"&gt;azs&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Blueprint&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="nx"&gt;GitHubRepo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github.com/aws-ia/terraform-aws-eks-blueprints"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Kubernetes provider&lt;/span&gt;
&lt;span class="c1"&gt;# You should **not** schedule deployments and services in this workspace.&lt;/span&gt;
&lt;span class="c1"&gt;# This keeps workspaces modular (one for provision EKS, another for scheduling&lt;/span&gt;
&lt;span class="c1"&gt;# Kubernetes resources) as per best practices.&lt;/span&gt;
&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;host&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_endpoint&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_ca_certificate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_certificate_authority_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;exec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;api_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"client.authentication.k8s.io/v1alpha1"&lt;/span&gt;
    &lt;span class="nx"&gt;command&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt;
    &lt;span class="c1"&gt;# This requires the awscli to be installed locally where Terraform is executed&lt;/span&gt;
    &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"eks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"get-token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--cluster-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"helm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;kubernetes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;host&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_endpoint&lt;/span&gt;
    &lt;span class="nx"&gt;cluster_ca_certificate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_certificate_authority_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;exec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;api_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"client.authentication.k8s.io/v1alpha1"&lt;/span&gt;
      &lt;span class="nx"&gt;command&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt;
      &lt;span class="c1"&gt;# This requires the awscli to be installed locally where Terraform is executed&lt;/span&gt;
      &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"eks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"get-token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--cluster-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;You can uncomment the &lt;code&gt;backend&lt;/code&gt; section above to save state in S3 instead of your local filesystem. This is recommended for production setup so that everyone in a team has the same state. This file defines configurable and local variables used across the workspace and configures some of the providers used. The Kubernetes provider is included in this file so the EKS module can complete successfully. Otherwise, it throws an error when creating &lt;code&gt;kubernetes_config_map.aws_auth&lt;/code&gt;. The helm provider is used to install Kubernetes add-ons to the cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build the VPC
&lt;/h3&gt;

&lt;p&gt;Next up, you need a VPC, subnets, route tables, and other networking bits. You will use the &lt;code&gt;vpc&lt;/code&gt; module from the &lt;a href="https://github.com/terraform-aws-modules" rel="noopener noreferrer"&gt;&lt;code&gt;terraform-aws-modules&lt;/code&gt;&lt;/a&gt; repository. This module is a wrapper around the AWS VPC module. It makes it easier to configure VPCs and all the other required networking resources. Create a &lt;code&gt;vpc.tf&lt;/code&gt; file:&lt;/p&gt;

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

&lt;span class="nb"&gt;touch &lt;/span&gt;vpc.tf


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

&lt;/div&gt;

&lt;p&gt;Add the following to the file:&lt;/p&gt;

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

&lt;span class="c1"&gt;#---------------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;# VPC, Subnets, Internet gateway, Route tables, etc.&lt;/span&gt;
&lt;span class="c1"&gt;#---------------------------------------------------------------&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-aws-modules/vpc/aws"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 3.0"&lt;/span&gt;

  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;cidr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt;

  &lt;span class="nx"&gt;azs&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;azs&lt;/span&gt;
  &lt;span class="nx"&gt;public_subnets&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;azs&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cidrsubnet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
  &lt;span class="nx"&gt;private_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;azs&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cidrsubnet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

  &lt;span class="nx"&gt;enable_nat_gateway&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;single_nat_gateway&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_dns_hostnames&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# Manage so we can name&lt;/span&gt;
  &lt;span class="nx"&gt;manage_default_network_acl&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;default_network_acl_tags&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${local.name}-default"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;manage_default_route_table&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;default_route_table_tags&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${local.name}-default"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;manage_default_security_group&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;default_security_group_tags&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${local.name}-default"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;public_subnet_tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"kubernetes.io/cluster/${local.name}"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"shared"&lt;/span&gt;
    &lt;span class="s2"&gt;"kubernetes.io/role/elb"&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;private_subnet_tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"kubernetes.io/cluster/${local.name}"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"shared"&lt;/span&gt;
    &lt;span class="s2"&gt;"kubernetes.io/role/internal-elb"&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This will create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new VPC, three private subnets, and three public subnets,&lt;/li&gt;
&lt;li&gt;Internet gateway and NAT gateway for the public subnets,&lt;/li&gt;
&lt;li&gt;and AWS routes for the gateways, public/private route tables, and route table associations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Build the EKS cluster
&lt;/h3&gt;

&lt;p&gt;Now that you have the networking part done, you can build configurations for the EKS cluster and its add-ons. You will use the &lt;code&gt;eks_blueprints&lt;/code&gt; module from &lt;a href="https://aws-ia.github.io/terraform-aws-eks-blueprints/v4.0.9/" rel="noopener noreferrer"&gt;&lt;code&gt;terraform-aws-eks-blueprints&lt;/code&gt;&lt;/a&gt;, which is a wrapper around the &lt;a href="https://github.com/terraform-aws-modules" rel="noopener noreferrer"&gt;&lt;code&gt;terraform-aws-modules&lt;/code&gt;&lt;/a&gt; and provides additional modules to configure EKS add-ons.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;eks-cluster.tf&lt;/code&gt; file:&lt;/p&gt;

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

&lt;span class="nb"&gt;touch &lt;/span&gt;eks-cluster.tf


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

&lt;/div&gt;

&lt;p&gt;Add the following to the file:&lt;/p&gt;

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

&lt;span class="c1"&gt;#---------------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;# EKS cluster, worker nodes, security groups, IAM roles, K8s add-ons, etc.&lt;/span&gt;
&lt;span class="c1"&gt;#---------------------------------------------------------------&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"eks_blueprints"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github.com/aws-ia/terraform-aws-eks-blueprints?ref=v4.0.9"&lt;/span&gt;

  &lt;span class="nx"&gt;cluster_name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_version&lt;/span&gt;

  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_id&lt;/span&gt;
  &lt;span class="nx"&gt;private_subnet_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets&lt;/span&gt;

  &lt;span class="nx"&gt;managed_node_groups&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;node_group_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"managed-ondemand"&lt;/span&gt;
      &lt;span class="nx"&gt;instance_types&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_types&lt;/span&gt;
      &lt;span class="nx"&gt;min_size&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
      &lt;span class="nx"&gt;subnet_ids&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"eks_blueprints_kubernetes_addons"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github.com/aws-ia/terraform-aws-eks-blueprints//modules/kubernetes-addons?ref=v4.0.9"&lt;/span&gt;

  &lt;span class="nx"&gt;eks_cluster_id&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_id&lt;/span&gt;
  &lt;span class="nx"&gt;eks_cluster_endpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_endpoint&lt;/span&gt;
  &lt;span class="nx"&gt;eks_oidc_provider&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oidc_provider&lt;/span&gt;
  &lt;span class="nx"&gt;eks_cluster_version&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_version&lt;/span&gt;

  &lt;span class="c1"&gt;# EKS Managed Add-ons&lt;/span&gt;
  &lt;span class="nx"&gt;enable_amazon_eks_vpc_cni&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_amazon_eks_coredns&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_amazon_eks_kube_proxy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# K8S Add-ons&lt;/span&gt;
  &lt;span class="nx"&gt;enable_aws_load_balancer_controller&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_metrics_server&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_cluster_autoscaler&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_aws_cloudwatch_metrics&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;

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

&lt;span class="c1"&gt;# To update local kubeconfig with new cluster details&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"null_resource"&lt;/span&gt; &lt;span class="s2"&gt;"kubeconfig"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints_kubernetes_addons&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"local-exec"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws eks --region ${local.region}  update-kubeconfig --name $AWS_CLUSTER_NAME"&lt;/span&gt;
    &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;AWS_CLUSTER_NAME&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;eks_blueprints&lt;/code&gt; module definition creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EKS Cluster Control plane with one &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/managed-node-groups.html" rel="noopener noreferrer"&gt;managed node group&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/fargate-profile.html" rel="noopener noreferrer"&gt;fargate profile&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;Cluster and node security groups and rules, IAM roles and policies required,&lt;/li&gt;
&lt;li&gt;and AWS Key Management Service (KMS) configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;eks_blueprints_kubernetes_addons&lt;/code&gt; module definition creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon EKS add-ons vpc-cni, CoreDNS, and kube-proxy,&lt;/li&gt;
&lt;li&gt;AWS Load Balancer Controller that provisions an AWS Network Load Balancer for distributing traffic,&lt;/li&gt;
&lt;li&gt;and &lt;a href="https://github.com/kubernetes-sigs/metrics-server" rel="noopener noreferrer"&gt;Metrics Server&lt;/a&gt;, and Cluster Autoscaler for scaling your workloads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;null_resource&lt;/code&gt; configuration updates your local kubeconfig with the new cluster details. It's not a required step for provisioning but just a handy hack.&lt;/p&gt;

&lt;p&gt;Finally, you can also define some outputs to be captured. Create a &lt;code&gt;outputs.tf&lt;/code&gt; file:&lt;/p&gt;

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

&lt;span class="nb"&gt;touch &lt;/span&gt;outputs.tf


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

&lt;/div&gt;

&lt;p&gt;Add the following to the file:&lt;/p&gt;

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

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"vpc_private_subnet_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"VPC private subnet CIDR"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets_cidr_blocks&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"vpc_public_subnet_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"VPC public subnet CIDR"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_subnets_cidr_blocks&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"vpc_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"VPC CIDR"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr_block&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"eks_cluster_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EKS cluster ID"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"eks_managed_nodegroups"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EKS managed node groups"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;managed_node_groups&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"eks_managed_nodegroup_ids"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EKS managed node group ids"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;managed_node_groups_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"eks_managed_nodegroup_arns"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EKS managed node group arns"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;managed_node_group_arn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"eks_managed_nodegroup_role_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EKS managed node group role name"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;managed_node_group_iam_role_names&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"eks_managed_nodegroup_status"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EKS managed node group status"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;managed_node_groups_status&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"configure_kubectl"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Configure kubectl: make sure you're logged in with the correct AWS profile and run the following command to update your kubeconfig"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configure_kubectl&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Provision the cluster
&lt;/h3&gt;

&lt;p&gt;Our Terraform definitions are ready. Now you can provision the cluster. First, initialize Terraform workspace and plan the changes:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# download modules and providers. Initialize state.&lt;/span&gt;
terraform init
&lt;span class="c"&gt;# see a preview of what will be done&lt;/span&gt;
terraform plan


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

&lt;/div&gt;

&lt;p&gt;Review the plan and make sure everything is correct. Ensure you have configured your AWS CLI and IAM Authenticator to use the correct AWS account. If not, run the following:&lt;/p&gt;

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

&lt;span class="c"&gt;# Visit https://console.aws.amazon.com/iam/home?#/security_credentials for creating access keys&lt;/span&gt;
aws configure


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

&lt;/div&gt;

&lt;p&gt;Now you can apply the changes:&lt;/p&gt;

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

terraform apply


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

&lt;/div&gt;

&lt;p&gt;Confirm by typing &lt;code&gt;yes&lt;/code&gt; when prompted. This will take a while (15-20 minutes), so sit back and have a coffee or contemplate what led you to this point in life. 😉&lt;/p&gt;

&lt;p&gt;Once the EKS cluster is ready, you will see the output variables printed to the console.&lt;/p&gt;

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

&lt;span class="nx"&gt;configure_kubectl&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws eks --region eu-west-1 update-kubeconfig --name okta-tf-demo"&lt;/span&gt;
&lt;span class="nx"&gt;eks_cluster_id&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"okta-tf-demo"&lt;/span&gt;
&lt;span class="nx"&gt;eks_managed_nodegroup_arns&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tolist&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"arn:aws:eks:eu-west-1:216713166862:nodegroup/okta-tf-demo/managed-ondemand-20220610125341399700000010/f0c0a6d6-b8e1-cf91-3d21-522552d6bc2e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;eks_managed_nodegroup_ids&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tolist&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"okta-tf-demo:managed-ondemand-20220610125341399700000010"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;eks_managed_nodegroup_role_name&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tolist&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"okta-tf-demo-managed-ondemand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;eks_managed_nodegroup_status&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tolist&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"ACTIVE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;eks_managed_nodegroups&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tolist&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"node"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_arn"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"arn:aws:eks:eu-west-1:216713166862:nodegroup/okta-tf-demo/managed-ondemand-20220610125341399700000010/f0c0a6d6-b8e1-cf91-3d21-522552d6bc2e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_iam_instance_profile_arn"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"arn:aws:iam::216713166862:instance-profile/okta-tf-demo-managed-ondemand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_iam_instance_profile_id"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"okta-tf-demo-managed-ondemand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_iam_role_arn"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"arn:aws:iam::216713166862:role/okta-tf-demo-managed-ondemand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_iam_role_name"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"okta-tf-demo-managed-ondemand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_id"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"okta-tf-demo:managed-ondemand-20220610125341399700000010"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_launch_template_arn"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_launch_template_id"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_launch_template_latest_version"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_status"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"ACTIVE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eu-west-1"&lt;/span&gt;
&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
&lt;span class="nx"&gt;vpc_private_subnet_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.10.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.11.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.12.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nx"&gt;vpc_public_subnet_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.0.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.1.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.2.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;You should see the cluster details if you run &lt;code&gt;kdash&lt;/code&gt; or &lt;code&gt;kubectl get nodes&lt;/code&gt; commands.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4ukr6v224k21n7whazi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4ukr6v224k21n7whazi.png" alt="EKS cluster in KDash"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The EKS cluster defined here will not come under AWS free tier; hence, running this will cost money, so delete the cluster as soon as you finish the tutorial to keep the cost within a few dollars.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Set up OIDC authentication using Okta
&lt;/h2&gt;

&lt;p&gt;You can proceed to deploy the sample application. You could skip this step if you used a sample that does not use Okta or OIDC for authentication.&lt;/p&gt;

&lt;p&gt;First, navigate to the &lt;strong&gt;store&lt;/strong&gt; application folder.&lt;/p&gt;

&lt;p&gt;Before you begin, you’ll need a free Okta developer account. Install the &lt;a href="https://cli.okta.com/" rel="noopener noreferrer"&gt;Okta CLI&lt;/a&gt; and run &lt;code&gt;okta register&lt;/code&gt; to sign up for a new account. If you already have an account, run &lt;code&gt;okta login&lt;/code&gt;. Then, run &lt;code&gt;okta apps create jhipster&lt;/code&gt;. Select the default app name, or change it as you see fit. Accept the default Redirect URI values provided for you.&lt;/p&gt;

&lt;p&gt;The Okta CLI streamlines configuring a JHipster app and does several things for you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creates an OIDC app with the correct redirect URIs:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;login: &lt;code&gt;http://localhost:8080/login/oauth2/code/oidc&lt;/code&gt; and &lt;code&gt;http://localhost:8761/login/oauth2/code/oidc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;logout: &lt;code&gt;http://localhost:8080&lt;/code&gt; and &lt;code&gt;http://localhost:8761&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Creates &lt;code&gt;ROLE_ADMIN&lt;/code&gt; and &lt;code&gt;ROLE_USER&lt;/code&gt; groups that JHipster expects&lt;/li&gt;
&lt;li&gt;Adds your current user to the &lt;code&gt;ROLE_ADMIN&lt;/code&gt; and &lt;code&gt;ROLE_USER&lt;/code&gt; groups&lt;/li&gt;
&lt;li&gt;Creates a groups claim in your default authorization server and adds the user’s groups to it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The &lt;a href="http://localhost:8761%5C*" rel="noopener noreferrer"&gt;http://localhost:8761\*&lt;/a&gt; redirect URIs are for the JHipster Registry, which is often used when creating microservices with JHipster. The Okta CLI adds these by default.&lt;/p&gt;

&lt;p&gt;The OIDC app configuration will be written to &lt;code&gt;.okta.env&lt;/code&gt; file in the folder where you ran the command.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Make sure to add the newly created &lt;code&gt;.okta.env&lt;/code&gt; file to your &lt;code&gt;.gitignore&lt;/code&gt; file so that you don't accidentally expose your credentials to the public.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Update &lt;code&gt;kubernetes/registry-k8s/application-configmap.yml&lt;/code&gt; with the OIDC configuration from the &lt;code&gt;.okta.env&lt;/code&gt; file. The Spring Cloud Config server reads from this file and shares the values with the gateway and microservices.&lt;/p&gt;

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

&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;application.yml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|-&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;spring:&lt;/span&gt;
      &lt;span class="s"&gt;security:&lt;/span&gt;
        &lt;span class="s"&gt;oauth2:&lt;/span&gt;
          &lt;span class="s"&gt;client:&lt;/span&gt;
            &lt;span class="s"&gt;provider:&lt;/span&gt;
              &lt;span class="s"&gt;oidc:&lt;/span&gt;
                &lt;span class="s"&gt;issuer-uri: https://&amp;lt;your-okta-domain&amp;gt;/oauth2/default&lt;/span&gt;
            &lt;span class="s"&gt;registration:&lt;/span&gt;
              &lt;span class="s"&gt;oidc:&lt;/span&gt;
                &lt;span class="s"&gt;client-id: &amp;lt;client-id&amp;gt;&lt;/span&gt;
                &lt;span class="s"&gt;client-secret: &amp;lt;client-secret&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Next, configure the JHipster Registry to use OIDC for authentication. Modify &lt;code&gt;kubernetes/registry-k8s/jhipster-registry.yml&lt;/code&gt; to enable the &lt;code&gt;oauth2&lt;/code&gt; profile, which is preconfigured in the JHipster Registry application.&lt;/p&gt;

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

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_PROFILES_ACTIVE&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod,k8s,oauth2&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The application is now ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secure secrets
&lt;/h3&gt;

&lt;p&gt;If you have noticed, you are setting secrets in plain text on the &lt;code&gt;application-configmap.yml&lt;/code&gt; file, which is not ideal and is not a best practice for security. For the specific JHipster application, you can use the encrypt functionality provided by the JHipster Registry to encrypt the secrets. See &lt;a href="https://developer.okta.com/blog/2021/06/01/kubernetes-spring-boot-jhipster#encrypt-your-secrets-with-spring-cloud-config" rel="noopener noreferrer"&gt;Encrypt Your Secrets with Spring Cloud Config&lt;/a&gt; to learn how to do this. But that would also rely on a base64 encoded encryption key added as a Kubernetes Secret, which still can be decoded. The best way to do this would be to use &lt;a href="https://aws.amazon.com/secrets-manager/" rel="noopener noreferrer"&gt;AWS Secrets Manager&lt;/a&gt;, an external service like &lt;a href="https://www.hashicorp.com/products/vault" rel="noopener noreferrer"&gt;HashiCorp Vault&lt;/a&gt;, or &lt;a href="https://github.com/bitnami-labs/sealed-secrets" rel="noopener noreferrer"&gt;Sealed Secrets&lt;/a&gt;. To learn more about these methods see &lt;a href="https://developer.okta.com/blog/2021/06/01/kubernetes-spring-boot-jhipster#encrypt-your-kubernetes-secrets" rel="noopener noreferrer"&gt;Encrypt Your Kubernetes Secrets&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy the microservice stack
&lt;/h2&gt;

&lt;p&gt;You are ready to deploy to our shiny new EKS cluster, but first, you need to build and push the Docker images to a container registry. You can use &lt;a href="https://aws.amazon.com/ecr/" rel="noopener noreferrer"&gt;Amazon Elastic Container Registry (ECR)&lt;/a&gt; or any other container registry.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build the Docker images
&lt;/h3&gt;

&lt;p&gt;You need to build Docker images for each app. This is specific to the JHipster application used in this tutorial which uses &lt;a href="https://github.com/GoogleContainerTools/jib" rel="noopener noreferrer"&gt;Jib&lt;/a&gt; to build the images. Make sure you are logged into Docker using &lt;code&gt;docker login&lt;/code&gt;. Navigate to each app folder (&lt;strong&gt;store&lt;/strong&gt;, &lt;strong&gt;invoice&lt;/strong&gt;, &lt;strong&gt;product&lt;/strong&gt;) and run the following command:&lt;/p&gt;

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

./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;docker-repo-uri-or-name&amp;gt;/&amp;lt;image-name&amp;gt;


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

&lt;/div&gt;

&lt;p&gt;Image names should be &lt;code&gt;store&lt;/code&gt;, &lt;code&gt;invoice&lt;/code&gt;, and &lt;code&gt;product&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy the applications to EKS
&lt;/h3&gt;

&lt;p&gt;Start the deployment using the handy script provided by JHipster. You could also manually apply deployments using &lt;code&gt;kubectl apply -f &amp;lt;file&amp;gt;&lt;/code&gt; commands.&lt;/p&gt;

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

&lt;span class="nb"&gt;cd &lt;/span&gt;kubernetes
./kubectl-apply.sh &lt;span class="nt"&gt;-f&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzjl4sbkienmdyngbc7t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzjl4sbkienmdyngbc7t.png" alt="EKS pods in KDash"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also run the following command to see the status of the deployments:&lt;/p&gt;

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

kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; jhipster


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

&lt;/div&gt;

&lt;p&gt;View the registry using port-forwarding as follows, and you will be able to access the application at &lt;code&gt;http://localhost:8761&lt;/code&gt;.&lt;/p&gt;

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

kubectl port-forward svc/jhipster-registry &lt;span class="nt"&gt;-n&lt;/span&gt; jhipster 8761


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

&lt;/div&gt;

&lt;p&gt;You can access the gateway application using port-forwarding as follows, and you will be able to access the application at &lt;code&gt;http://localhost:8080&lt;/code&gt;.&lt;/p&gt;

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

kubectl port-forward svc/store &lt;span class="nt"&gt;-n&lt;/span&gt; jhipster 8080


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

&lt;/div&gt;

&lt;p&gt;Or, you can access the application via the load balancer exposed. Find the external IP of the &lt;code&gt;store&lt;/code&gt; service by navigating to the service tab in KDash or by running the following:&lt;/p&gt;

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

kubectl get svc store &lt;span class="nt"&gt;-n&lt;/span&gt; jhipster


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

&lt;/div&gt;

&lt;p&gt;Navigate to the Okta Admin Console and go to &lt;strong&gt;Applications&lt;/strong&gt; &amp;gt; &lt;strong&gt;Applications&lt;/strong&gt; from left-hand navigation. Find the application you created earlier with &lt;code&gt;okta apps create jhipster&lt;/code&gt; and add the external IP from &lt;code&gt;kubectl get svc&lt;/code&gt; command to the &lt;strong&gt;Sign-in redirect URIs&lt;/strong&gt; and &lt;strong&gt;Sign-out redirect URIs&lt;/strong&gt;. Make sure to use the same paths as the current &lt;code&gt;localhost&lt;/code&gt; entries.&lt;/p&gt;

&lt;p&gt;Now you should be able to visit the external IP of the &lt;code&gt;store&lt;/code&gt; service on port 8080 and see the application, and you should be able to log in using your Okta credentials.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tear down the cluster with Terraform
&lt;/h2&gt;

&lt;p&gt;Once you are done with the tutorial, you can delete the cluster and all the resources created using Terraform by running the following commands:&lt;/p&gt;


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

&lt;p&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;terraform&lt;br&gt;
&lt;span class="c"&gt;# The commands below might take a while to finish.&lt;/span&gt;&lt;br&gt;
terraform destroy &lt;span class="nt"&gt;-target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"module.eks_blueprints_kubernetes_addons"&lt;/span&gt; &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# If deleting VPC fails, then manually delete the load balancers and security groups&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# for the load balancer associated with the VPC from AWS EC2 console and try again.&lt;/span&gt;&lt;br&gt;
terraform destroy &lt;span class="nt"&gt;-target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"module.eks_blueprints"&lt;/span&gt; &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;&lt;br&gt;
terraform destroy &lt;span class="nt"&gt;-target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"module.vpc"&lt;/span&gt; &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# cleanup anything left over.&lt;/span&gt;&lt;br&gt;
terraform destroy &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Learn more about Java Microservices, EKS, Kubernetes, and JHipster&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;If you want to learn more about Kubernetes, OIDC, or using OIDC with Kubernetes, and security in general, check out these additional resources.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/06/09/cloud-native-java-microservices-with-istio" rel="noopener noreferrer"&gt;Cloud Native Java Microservices with JHipster and Istio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/06/01/kubernetes-spring-boot-jhipster#encrypt-your-kubernetes-secrets" rel="noopener noreferrer"&gt;Kubernetes to the Cloud with Spring Boot and JHipster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/11/08/k8s-api-server-oidc" rel="noopener noreferrer"&gt;How to Secure Your Kubernetes Cluster with OpenID Connect and RBAC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/12/02/k8s-security-best-practices" rel="noopener noreferrer"&gt;How to Secure Your Kubernetes Clusters With Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/10/08/secure-access-to-aws-eks" rel="noopener noreferrer"&gt;Secure Access to AWS EKS Clusters for Admins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/06/06/microservices-digitalocean-kubernetes" rel="noopener noreferrer"&gt;Run Microservices on DigitalOcean with Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/05/05/kubernetes-microservices-azure" rel="noopener noreferrer"&gt;Kubernetes Microservices on Azure with Cosmos DB&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find all the code from this example on &lt;a href="https://github.com/oktadev/okta-jhipster-k8s-eks-microservices-example" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you liked this tutorial, chances are you'll enjoy the others we publish. Please follow &lt;a href="https://twitter.com/oktadev" rel="noopener noreferrer"&gt;@oktadev on Twitter&lt;/a&gt; and &lt;a href="https://youtube.com/oktadev" rel="noopener noreferrer"&gt;subscribe to our YouTube channel&lt;/a&gt; to get notified when we publish new developer tutorials.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>jhipster</category>
      <category>eks</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Cloud Native Java Microservices with JHipster and Istio</title>
      <dc:creator>Deepu K Sasidharan</dc:creator>
      <pubDate>Tue, 28 Jun 2022 10:24:09 +0000</pubDate>
      <link>https://forem.com/jhipster/cloud-native-java-microservices-with-jhipster-and-istio-1emb</link>
      <guid>https://forem.com/jhipster/cloud-native-java-microservices-with-jhipster-and-istio-1emb</guid>
      <description>&lt;p&gt;Microservices are not everyone's cup of tea, and they shouldn't be. Not every problem can or should be solved by microservices. Sometimes building a simple monolith is a far better option. Microservices are solutions for use cases where scale and scalability are important. A few years ago, microservices were all the rage, made popular, especially by companies like Netflix, Spotify, Google, etc. While the hype has died down a bit, genuine use cases still exist. With ongoing advances in cloud computing technologies, building microservices as cloud-native services is the way to go due to many benefits.&lt;/p&gt;

&lt;p&gt;Today we will look at building a cloud-native Java microservice stack that utilizes a service mesh to provide most of the distributed system needs and we'll deploy it to the cloud using Kubernetes.&lt;/p&gt;

&lt;p&gt;So here is what we will do today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build a Java microservice stack using JHipster, Spring Boot, and Spring Cloud&lt;/li&gt;
&lt;li&gt;Create a Google Kubernetes Engine (GKE) cluster&lt;/li&gt;
&lt;li&gt;Deploy Istio service mesh to the cluster&lt;/li&gt;
&lt;li&gt;Set up monitoring and observability&lt;/li&gt;
&lt;li&gt;Deploy and monitor the microservices to the cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud Platform&lt;/a&gt; account&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.docker.com/get-started" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; installed on your machine&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed on your machine&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.jhipster.tech/installation/" rel="noopener noreferrer"&gt;JHipster&lt;/a&gt; installed on your machine&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cloud.google.com/sdk/docs/install" rel="noopener noreferrer"&gt;Google Cloud SDK&lt;/a&gt; installed and configured on your machine&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/tasks/tools/" rel="noopener noreferrer"&gt;kubectl&lt;/a&gt; or &lt;a href="https://github.com/kdash-rs/kdash" rel="noopener noreferrer"&gt;KDash&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Basic understanding of Java, Spring, Containers, and Kubernetes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you prefer to follow along by watching a video, see [How to Build Low-Code Microservices on the Cloud Using Istio, JHipster, and Kubernetes] on the &lt;a href="https://youtube.com/oktadev" rel="noopener noreferrer"&gt;OktaDev YouTube channel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/zGpnIhRgMaM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Why build cloud-native microservices using a service mesh?
&lt;/h2&gt;

&lt;p&gt;Before we dive into building a cloud-native microservice stack, let's look at what a service mesh is and the benefits of using one.&lt;/p&gt;

&lt;p&gt;A service mesh provides features to help with common distributed microservice challenges. Like service discovery, routing, load balancing, and so on. Today we will be using &lt;a href="https://istio.io/" rel="noopener noreferrer"&gt;Istio&lt;/a&gt;, one of the most popular service mesh solutions available. Istio is tailored for distributed application architectures, especially those you might run in Kubernetes. Istio plays nicely with Kubernetes, so nicely that you might think that it's part of the Kubernetes platform itself. Istio isn't the only service mesh around; we also have platforms like &lt;a href="https://linkerd.io/" rel="noopener noreferrer"&gt;Linkerd&lt;/a&gt; and &lt;a href="https://www.consul.io/" rel="noopener noreferrer"&gt;Consul&lt;/a&gt;, which are also quite popular.&lt;/p&gt;

&lt;p&gt;Istio specifically provides the following features.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secure service-to-service communication over TLS. Of course, with support for identity-based authentication and authorization.&lt;/li&gt;
&lt;li&gt;Service discovery, so that your microservices can discover each other.&lt;/li&gt;
&lt;li&gt;Automatic load balancing for the services&lt;/li&gt;
&lt;li&gt;Traffic control features like routing, circuit breaking, retries, fail-overs, and fault injection.&lt;/li&gt;
&lt;li&gt;A pluggable policy layer that can enforce stuff like access control, rate limiting, A/B testing, traffic splits, quotas, etc.&lt;/li&gt;
&lt;li&gt;It also provides automatic metrics, logs, and traces for all traffic within the cluster from Ingress to Egress and between pods.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is Istio Service Mesh?
&lt;/h3&gt;

&lt;p&gt;Let's take a quick look at Istio internals. The Istio architecture can be classified into two distinct planes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qhlxbs4n5145a97m231.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qhlxbs4n5145a97m231.png" alt="Istio Service Mesh Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Control plane&lt;/strong&gt;: It consists of the istiod demon, and it manages and configures the envoy proxies to route traffic. The control plane also enforces policies and collects telemetry, and includes components like Pilot for traffic management, Citadel to manage security, and Galley to manage configurations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data plane&lt;/strong&gt;: It's made of &lt;a href="https://www.envoyproxy.io/" rel="noopener noreferrer"&gt;Envoy&lt;/a&gt; proxies deployed as sidecars to our application containers. Envoy is a high-performance, lightweight distributed proxy. It controls all the incoming and outgoing traffic to the container it is attached to.&lt;/p&gt;

&lt;p&gt;We can use tools like &lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt;, &lt;a href="https://prometheus.io/" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt;, &lt;a href="https://www.kiali.io/" rel="noopener noreferrer"&gt;Kiali&lt;/a&gt; and &lt;a href="https://zipkin.io/" rel="noopener noreferrer"&gt;Zipkin&lt;/a&gt; for monitoring and observability as they work well with the telemetry provided by Istio. You can use these or use your existing monitoring stack as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a Java Microservices Stack using JHipster
&lt;/h2&gt;

&lt;p&gt;Before you proceed, ensure you have installed JHipster. If not, install it using the command &lt;code&gt;npm -g install generator-jhipster&lt;/code&gt;. At the moment of writing, I'm using JHipster version &lt;strong&gt;7.8.1&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We will be using the &lt;a href="https://www.jhipster.tech/jdl/intro" rel="noopener noreferrer"&gt;JHipster Domain Language (JDL)&lt;/a&gt; to define our microservices, entities, and deployment options. But first, let's take a look at the architecture we will be building today.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nidl5ydet0tam4b61n6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nidl5ydet0tam4b61n6.png" alt="Istio Microservice Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have the Istio control plane taking care of policy, load balancing, etc. We also have the Istio Ingress gateway to route all external traffic to our applications. We have four microservices. First is a gateway application created by JHipster that acts as our React GUI and authentication layer. The remaining are services that provide APIs. Each of our containers will have an envoy proxy as an auto-injected sidecar. We hook up Grafana, Prometheus, Zipkin, and Kiali to the telemetry provided by Istio so that we have monitoring and observability for our cluster. Each microservice also has its own database.&lt;/p&gt;

&lt;p&gt;If you would prefer not to build the application yourself, clone the example from &lt;a href="https://github.com/oktadev/okta-java-spring-k8s-istio-microservices-example" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/oktadev/okta-java-spring-k8s-istio-microservices-example.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's not an overly complex architecture, but it's also not that simple. First, let us define our microservice using JDL. Create a file called &lt;code&gt;app.jdl&lt;/code&gt; and paste the following content into it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;application&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;baseName&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;
    &lt;span class="n"&gt;applicationType&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt;
    &lt;span class="n"&gt;packageName&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;okta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;developer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;
    &lt;span class="n"&gt;serviceDiscoveryType&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt;
    &lt;span class="n"&gt;authenticationType&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;
    &lt;span class="n"&gt;prodDatabaseType&lt;/span&gt; &lt;span class="n"&gt;postgresql&lt;/span&gt;
    &lt;span class="n"&gt;cacheProvider&lt;/span&gt; &lt;span class="n"&gt;hazelcast&lt;/span&gt;
    &lt;span class="n"&gt;buildTool&lt;/span&gt; &lt;span class="n"&gt;gradle&lt;/span&gt;
    &lt;span class="n"&gt;clientFramework&lt;/span&gt; &lt;span class="n"&gt;react&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;entities&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;application&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;baseName&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;
    &lt;span class="n"&gt;applicationType&lt;/span&gt; &lt;span class="n"&gt;microservice&lt;/span&gt;
    &lt;span class="n"&gt;packageName&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;okta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;developer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;
    &lt;span class="n"&gt;serviceDiscoveryType&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt;
    &lt;span class="n"&gt;authenticationType&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;
    &lt;span class="n"&gt;prodDatabaseType&lt;/span&gt; &lt;span class="n"&gt;postgresql&lt;/span&gt;
    &lt;span class="n"&gt;cacheProvider&lt;/span&gt; &lt;span class="n"&gt;hazelcast&lt;/span&gt;
    &lt;span class="n"&gt;buildTool&lt;/span&gt; &lt;span class="n"&gt;gradle&lt;/span&gt;
    &lt;span class="n"&gt;serverPort&lt;/span&gt; &lt;span class="mi"&gt;8081&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;entities&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProductCategory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProductOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OrderItem&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;application&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;baseName&lt;/span&gt; &lt;span class="n"&gt;invoice&lt;/span&gt;
    &lt;span class="n"&gt;applicationType&lt;/span&gt; &lt;span class="n"&gt;microservice&lt;/span&gt;
    &lt;span class="n"&gt;packageName&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;okta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;developer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;
    &lt;span class="n"&gt;serviceDiscoveryType&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt;
    &lt;span class="n"&gt;authenticationType&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;
    &lt;span class="n"&gt;prodDatabaseType&lt;/span&gt; &lt;span class="n"&gt;postgresql&lt;/span&gt;
    &lt;span class="n"&gt;buildTool&lt;/span&gt; &lt;span class="n"&gt;gradle&lt;/span&gt;
    &lt;span class="n"&gt;serverPort&lt;/span&gt; &lt;span class="mi"&gt;8082&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;entities&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Shipment&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;application&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;baseName&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;
    &lt;span class="n"&gt;applicationType&lt;/span&gt; &lt;span class="n"&gt;microservice&lt;/span&gt;
    &lt;span class="n"&gt;packageName&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;okta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;developer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;
    &lt;span class="n"&gt;serviceDiscoveryType&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt;
    &lt;span class="n"&gt;authenticationType&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;
    &lt;span class="n"&gt;databaseType&lt;/span&gt; &lt;span class="n"&gt;mongodb&lt;/span&gt;
    &lt;span class="n"&gt;cacheProvider&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt;
    &lt;span class="n"&gt;enableHibernateCache&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="n"&gt;buildTool&lt;/span&gt; &lt;span class="n"&gt;gradle&lt;/span&gt;
    &lt;span class="n"&gt;serverPort&lt;/span&gt; &lt;span class="mi"&gt;8083&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;entities&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each application defines its name, package name, authentication type, database, etc. For all supported options and configurations, please refer to the &lt;a href="https://www.jhipster.tech/jdl/applications" rel="noopener noreferrer"&gt;JDL applications documentation&lt;/a&gt;. Each application also defines the &lt;code&gt;applicationType&lt;/code&gt; and the entities it serves. Next, add the entity definitions to the &lt;code&gt;app.jdl&lt;/code&gt; you just created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Entities for Store Gateway
 */&lt;/span&gt;
&lt;span class="c1"&gt;// Customer for the store&lt;/span&gt;
&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;lastName&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;gender&lt;/span&gt; &lt;span class="nc"&gt;Gender&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="nf"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;(/^[^&lt;/span&gt;&lt;span class="err"&gt;@\&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]+&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="p"&gt;[^&lt;/span&gt;&lt;span class="err"&gt;@\&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]+&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;.[^&lt;/span&gt;&lt;span class="err"&gt;@\&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]+&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;/)&lt;/span&gt;
  &lt;span class="n"&gt;phone&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;addressLine1&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;addressLine2&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;country&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;Gender&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;MALE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;FEMALE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OTHER&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;relationship&lt;/span&gt; &lt;span class="nc"&gt;OneToOne&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;serviceClass&lt;/span&gt;
&lt;span class="n"&gt;paginate&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pagination&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Entities for product microservice
 */&lt;/span&gt;
&lt;span class="c1"&gt;// Product sold by the Online store&lt;/span&gt;
&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;itemSize&lt;/span&gt; &lt;span class="nc"&gt;Size&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="nc"&gt;ImageBlob&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;M&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;XL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;XXL&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="nc"&gt;ProductCategory&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="nc"&gt;ProductOrder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;placedDate&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;invoiceId&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;
  &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;COMPLETED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PENDING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;CANCELLED&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="nc"&gt;OrderItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;totalPrice&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nc"&gt;OrderItemStatus&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;OrderItemStatus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;AVAILABLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OUT_OF_STOCK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BACK_ORDER&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;relationship&lt;/span&gt; &lt;span class="nc"&gt;ManyToOne&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;OrderItem&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;relationship&lt;/span&gt; &lt;span class="nc"&gt;OneToMany&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;ProductOrder&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;orderItem&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nc"&gt;OrderItem&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;ProductCategory&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;productCategory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProductCategory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProductOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OrderItem&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;serviceClass&lt;/span&gt;
&lt;span class="n"&gt;paginate&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProductOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OrderItem&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pagination&lt;/span&gt;
&lt;span class="n"&gt;microservice&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProductOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProductCategory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OrderItem&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Entities for Invoice microservice
 */&lt;/span&gt;
&lt;span class="c1"&gt;// Invoice for sales&lt;/span&gt;
&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nc"&gt;InvoiceStatus&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;paymentMethod&lt;/span&gt; &lt;span class="nc"&gt;PaymentMethod&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;paymentDate&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;paymentAmount&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;InvoiceStatus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;PAID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ISSUED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;CANCELLED&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="nc"&gt;Shipment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;trackingCode&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;PaymentMethod&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;CREDIT_CARD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;CASH_ON_DELIVERY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PAYPAL&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;relationship&lt;/span&gt; &lt;span class="nc"&gt;OneToMany&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;shipment&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nc"&gt;Shipment&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Shipment&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;serviceClass&lt;/span&gt;
&lt;span class="n"&gt;paginate&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Shipment&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pagination&lt;/span&gt;
&lt;span class="n"&gt;microservice&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Shipment&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;invoice&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Entities for notification microservice
 */&lt;/span&gt;
&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;sentDate&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;format&lt;/span&gt; &lt;span class="nc"&gt;NotificationType&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;productId&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;NotificationType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;EMAIL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SMS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PARCEL&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;microservice&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We define entities for each service and mark the entities as microservice entities. We also define relationships between entities, enums, and other options like pagination, service layer, etc. Please refer to JDL &lt;a href="https://www.jhipster.tech/jdl/entities-fields" rel="noopener noreferrer"&gt;Entities&lt;/a&gt; and &lt;a href="https://www.jhipster.tech/jdl/relationships" rel="noopener noreferrer"&gt;relationships&lt;/a&gt; documentation for more possibilities.&lt;/p&gt;

&lt;p&gt;Now, we are ready to run JHipster. Open a terminal window on the folder where you saved the JDL and run the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;jhipster-istio
&lt;span class="nb"&gt;cd &lt;/span&gt;jhipster-istio

jhipster jdl app.jdl &lt;span class="nt"&gt;--fork&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create the applications with all their entities and specified configurations. You should be able to see the gateway application in action by running the following command on the &lt;strong&gt;store&lt;/strong&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew &lt;span class="c"&gt;# starts the Spring Boot application&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see everything that's generated in the &lt;a href="https://github.com/oktadev/okta-java-spring-k8s-istio-microservices-example" rel="noopener noreferrer"&gt;example application on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a GKE cluster and install Istio
&lt;/h2&gt;

&lt;p&gt;To deploy the stack to Google Kubernetes Engine, we need to create a cluster and install Istio. So let's begin by creating a cluster using Google Cloud SDK.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a cluster
&lt;/h3&gt;

&lt;p&gt;Ensure you are logged into the &lt;a href="https://cloud.google.com/sdk/gcloud" rel="noopener noreferrer"&gt;gcloud CLI&lt;/a&gt; from the command-line and run the following command to create a GKE cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# set region and zone&lt;/span&gt;
gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;compute/region europe-west1
gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;compute/zone europe-west1-b
&lt;span class="c"&gt;# Create a project and enable container APIs&lt;/span&gt;
gcloud projects create jhipster-demo-okta &lt;span class="c"&gt;# You need to also enable billing via GUI&lt;/span&gt;
gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;project jhipster-demo-okta
gcloud services &lt;span class="nb"&gt;enable &lt;/span&gt;container.googleapis.com

&lt;span class="c"&gt;# Create GKE Cluster&lt;/span&gt;
gcloud container clusters create hello-hipster &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--num-nodes&lt;/span&gt; 4 &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--machine-type&lt;/span&gt; n1-standard-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This could take anywhere from 5 to 15 minutes. &lt;code&gt;--machine-type&lt;/code&gt; is important as we need more CPU than available in the default setup. Once the cluster is created, it should be set automatically as the current Kubernetes context. You can verify that by running &lt;code&gt;kubectl config current-context&lt;/code&gt;. If the new cluster is not set as the current context, you can set it by running &lt;code&gt;gcloud container clusters get-credentials hello-hipster&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flrpwoguep2b4twclgq2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flrpwoguep2b4twclgq2y.png" alt="GKE Cluster nodes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: I'm using &lt;a href="https://kdash.cli.rs/" rel="noopener noreferrer"&gt;KDash&lt;/a&gt; to monitor the cluster; you can try it or use kubectl, &lt;a href="https://github.com/derailed/k9s" rel="noopener noreferrer"&gt;k9s&lt;/a&gt;, and so on as you prefer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Install Istio to cluster
&lt;/h3&gt;

&lt;p&gt;As of writing this, I'm using Istio version 1.13.4. You can install &lt;strong&gt;istioctl&lt;/strong&gt; by running the following command, preferably from your home directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ISTIO_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.13.4
curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://istio.io/downloadIstio | sh -
&lt;span class="nb"&gt;cd &lt;/span&gt;istio-&lt;span class="nv"&gt;$ISTIO_VERSION&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;/bin:&lt;span class="nv"&gt;$PATH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now be able to run &lt;strong&gt;istioctl&lt;/strong&gt; from the command line. Now, we can use the CLI to Install Istio to the GKE cluster. Istio provides a few &lt;a href="https://helm.sh/" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; profiles out of the box. We will use the demo profile for demo purposes. You can choose the production or dev profile as well. The command should install Istio and set up everything required on our cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;istioctl &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--set&lt;/span&gt; &lt;span class="nv"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;demo &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: If you run into any trouble with firewall or user privilege issues, please refer to &lt;a href="https://istio.io/latest/docs/setup/platform-setup/gke/" rel="noopener noreferrer"&gt;GKE setup guide from Istio&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the installation is complete, we need to fetch the External IP of the Istio Ingress Gateway. If you are using KDash, you can see it on the services tab, or you can run the following command to get it using kubectl.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get svc istio-ingressgateway &lt;span class="nt"&gt;-n&lt;/span&gt; istio-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install observability tools
&lt;/h3&gt;

&lt;p&gt;Istio also provides addons for most of the popular monitoring and observability tools. Let's install Grafana, Prometheus, Kiali and Zipkin on our cluster. These are preconfigured to work with the telemetry data provided by Istio. Ensure you are in the folder where you installed Istio, like &lt;strong&gt;istio-1.13.4&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;istio-&lt;span class="nv"&gt;$ISTIO_VERSION&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; samples/addons/grafana.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; samples/addons/prometheus.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; samples/addons/kiali.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; samples/addons/extras/zipkin.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4x0lyz0wlq5xnqduajm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4x0lyz0wlq5xnqduajm.png" alt="GKE Cluster with Istio pods"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we look at the istio-system namespace, we can see all the Istio components along with Grafana, Prometheus, Kiali, and Zipkin running. You can also see this by running the following command .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; istio-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy the microservice stack to GKE
&lt;/h2&gt;

&lt;p&gt;Our cluster is ready, and we have Istio installed. Now, we can deploy our microservice stack to the cluster. First, we need to create Kubernetes manifests for our deployments and services and configurations for Istio. And once again, JHipster comes to the rescue. We can use the &lt;a href="https://www.jhipster.tech/jdl/deployments" rel="noopener noreferrer"&gt;JDL deployment&lt;/a&gt; configurations to generate Kubernetes setup for our stack with one command easily.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Kubernetes manifests
&lt;/h3&gt;

&lt;p&gt;Create a new JDL file, say &lt;code&gt;deployment.jdl&lt;/code&gt;, and add the following content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// will be created under 'kubernetes' folder&lt;/span&gt;
&lt;span class="nf"&gt;deployment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;deploymentType&lt;/span&gt; &lt;span class="n"&gt;kubernetes&lt;/span&gt;
  &lt;span class="n"&gt;appsFolders&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;dockerRepositoryName&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;your-docker-repository-name&amp;gt;"&lt;/span&gt;
  &lt;span class="n"&gt;serviceDiscoveryType&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt;
  &lt;span class="n"&gt;istio&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;kubernetesServiceType&lt;/span&gt; &lt;span class="nc"&gt;Ingress&lt;/span&gt;
  &lt;span class="n"&gt;kubernetesNamespace&lt;/span&gt; &lt;span class="n"&gt;jhipster&lt;/span&gt;
  &lt;span class="n"&gt;ingressDomain&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;istio-ingress-gateway-external-ip&amp;gt;.nip.io"&lt;/span&gt;
  &lt;span class="n"&gt;ingressType&lt;/span&gt; &lt;span class="n"&gt;gke&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope it's self-explanatory. You can refer to the JDL deployment documentation for all the available options. We have enabled Istio and set the ingress domain to the Istio Ingress Gateway's external IP that we noted earlier. Now we need a DNS for our IP. For real use-cases, you should map a DNS for the IP using a service provided by your cloud vendor like Google Cloud DNS, but for testing and demo purposes, we can use a wildcard DNS service like &lt;a href="http://nip.io" rel="noopener noreferrer"&gt;&lt;strong&gt;nip.io&lt;/strong&gt;&lt;/a&gt; to resolve our IP. Just append &lt;code&gt;nip.io&lt;/code&gt; to our IP and use that as the &lt;code&gt;ingressDomain&lt;/code&gt;. Make sure to use a docker repo where you have push rights.&lt;/p&gt;

&lt;p&gt;Now run the following command from the root folder where you ran the previous &lt;code&gt;jhipster jdl&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jhipster jdl deployment.jdl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new folder, &lt;strong&gt;kubernetes&lt;/strong&gt;, with all the required Kubernetes manifests like deployments, services, Istio virtual services, gateways, and so on, for all the applications, databases, and monitoring.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4k0y303lyerwr2orp008.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4k0y303lyerwr2orp008.png" alt="JHipster JDL deployment"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each of the services will also have an Istio &lt;a href="https://istio.io/latest/docs/reference/config/networking/virtual-service/" rel="noopener noreferrer"&gt;virtual service&lt;/a&gt; and &lt;a href="https://istio.io/latest/docs/reference/config/networking/destination-rule/" rel="noopener noreferrer"&gt;destination rule&lt;/a&gt;. For example, the invoice service will have the following destination rule defining traffic policies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.istio.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DestinationRule&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;invoice-destinationrule&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jhipster&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;invoice&lt;/span&gt;
  &lt;span class="na"&gt;trafficPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;loadBalancer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;simple&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RANDOM&lt;/span&gt;
    &lt;span class="na"&gt;connectionPool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;tcp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;maxConnections&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
        &lt;span class="na"&gt;connectTimeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;100ms&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;http1MaxPendingRequests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
        &lt;span class="na"&gt;http2MaxRequests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
        &lt;span class="na"&gt;maxRequestsPerConnection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
        &lt;span class="na"&gt;maxRetries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
    &lt;span class="na"&gt;outlierDetection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;consecutive5xxErrors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;baseEjectionTime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;60s&lt;/span&gt;
  &lt;span class="na"&gt;subsets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also includes the following virtual service that defines the route. You could also use virtual services to do traffic split between two versions of the same app, among other things.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.istio.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VirtualService&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;invoice-virtualservice&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jhipster&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;invoice&lt;/span&gt;
  &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;invoice&lt;/span&gt;
            &lt;span class="na"&gt;subset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v1"&lt;/span&gt;
          &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;attempts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
        &lt;span class="na"&gt;perTryTimeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gateway is defined for the store application as it is our GUI as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.istio.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Gateway&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;store-gateway&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jhipster&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;gateway&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;store-gateway&lt;/span&gt;
    &lt;span class="na"&gt;istio&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingressgateway&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;istio&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingressgateway&lt;/span&gt;
  &lt;span class="na"&gt;servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
        &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTP&lt;/span&gt;
      &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;store.jhipster.34.76.233.160.nip.io&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http2&lt;/span&gt;
        &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTP2&lt;/span&gt;
      &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;store.jhipster.34.76.233.160.nip.io&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.istio.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VirtualService&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;store-gw-virtualservice&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jhipster&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;store-gw-virtualservice&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;store.jhipster.34.76.233.160.nip.io&lt;/span&gt;
  &lt;span class="na"&gt;gateways&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;store-gateway&lt;/span&gt;
  &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/services/invoice/&lt;/span&gt;
      &lt;span class="na"&gt;rewrite&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
      &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;invoice&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/services/notification/&lt;/span&gt;
      &lt;span class="na"&gt;rewrite&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
      &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;notification&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/services/product/&lt;/span&gt;
      &lt;span class="na"&gt;rewrite&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
      &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;product&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;store&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, there are also many useful commands printed on the console that you can use to do the deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy to GKE
&lt;/h3&gt;

&lt;p&gt;We are ready to deploy now. First, we need to build and push the images to the registry. We can use the handy &lt;a href="https://github.com/GoogleContainerTools/jib" rel="noopener noreferrer"&gt;Jib&lt;/a&gt; commands provided by JHipster. Navigate to each of the microservice folders and run the commands below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;store &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;yourDockerRepository/store
&lt;span class="nb"&gt;cd &lt;/span&gt;invoice &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;yourDockerRepository/invoice
&lt;span class="nb"&gt;cd &lt;/span&gt;notification &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;yourDockerRepository/notification
&lt;span class="nb"&gt;cd &lt;/span&gt;product &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;yourDockerRepository/product
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the images are pushed to the Docker registry, we can deploy the stack using the handy script provided by JHipster. Navigate to the &lt;code&gt;kubernetes&lt;/code&gt; folder created by JHipster and run the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;kubernetes
./kubectl-apply.sh &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the deployments are done, we must wait for the pods to be in &lt;strong&gt;RUNNING&lt;/strong&gt; status. Useful links will be printed on the terminal; make a note of them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkj4qoisgz0u5r1w1x3p4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkj4qoisgz0u5r1w1x3p4.png" alt="GKE cluster with application pods"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now access the application at the given &lt;code&gt;http://store.jhipster.&amp;lt;istio-ingress-gateway-external-ip&amp;gt;.nip.io&lt;/code&gt; URI and log in with the default credentials.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhhbdlkd5kubdfdr7vhp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhhbdlkd5kubdfdr7vhp.png" alt="Store gateway application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Currently, the JHipster OIDC setup does not work with Istio and there is an &lt;a href="https://github.com/jhipster/generator-jhipster/issues/17384" rel="noopener noreferrer"&gt;open issue&lt;/a&gt; in JHipster issue tracker for this. Alternative solutions would be to use an &lt;a href="https://istio.io/latest/blog/2021/better-external-authz/" rel="noopener noreferrer"&gt;external authorization server&lt;/a&gt; with something like &lt;a href="https://www.openpolicyagent.org/" rel="noopener noreferrer"&gt;Open Policy Agent&lt;/a&gt;. We will cover this in a later blog post.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Monitoring and observability
&lt;/h3&gt;

&lt;p&gt;Since we deployed tools for observability, let's see what we have.&lt;/p&gt;

&lt;h4&gt;
  
  
  Grafana
&lt;/h4&gt;

&lt;p&gt;First up are Grafana and Prometheus for metrics and dashboards. Click the URI for Grafana from the previous deployment step. Click &lt;strong&gt;General&lt;/strong&gt; at the top left corner and click the &lt;strong&gt;istio&lt;/strong&gt; folder. You should see multiple preconfigured dashboards. You can monitor the performance of the workloads and the istio system itself here. You can also create your own dashboards if you like. Prometheus provides the data visualized on Grafana.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frf9aod84wpoinggq9rwx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frf9aod84wpoinggq9rwx.png" alt="Grafana dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Kiali
&lt;/h4&gt;

&lt;p&gt;Kiali is a management console for Istio service mesh, and it provides a web interface for visualizing the network topology of your service mesh. You can use it to explore the network topology of your cluster and see the network traffic flowing through it. Click &lt;strong&gt;Graph&lt;/strong&gt; on the left side menu to see the network topology.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxsfjkx9zr1t2sef5xnrv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxsfjkx9zr1t2sef5xnrv.png" alt="Kiali dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Zipkin
&lt;/h4&gt;

&lt;p&gt;Zipkin is a distributed tracing solution for distributed systems. It is a tool for capturing distributed traces and providing a centralized view of the traces. This is essential for a microservice setup where a request could span multiple services, and debugging would require tracing them. Click &lt;strong&gt;RUN QUERY&lt;/strong&gt; on the home screen to fetch recent traces, and click &lt;strong&gt;SHOW&lt;/strong&gt; on one of them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjwjiamrxz9xdotr3ps5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjwjiamrxz9xdotr3ps5.png" alt="Zipkin dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Cleanup the GCP cluster
&lt;/h3&gt;

&lt;p&gt;Once you are done with experiments, make sure to delete the cluster you created so that you don't end up with a big bill from Google. You can delete the cluster from the Google Cloud Console GUI or via the command line using the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters delete hello-hipster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Learn more about Java Microservices, Istio, Kubernetes, and JHipster
&lt;/h2&gt;

&lt;p&gt;If you want to learn more about Kubernetes, OIDC, or using OIDC with Kubernetes, and security in general, check out these additional resources.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/06/22/terraform-eks-microservices" rel="noopener noreferrer"&gt;How to Deploy Java Microservices on Amazon EKS Using Terraform and Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/11/08/k8s-api-server-oidc" rel="noopener noreferrer"&gt;How to Secure Your Kubernetes Cluster with OpenID Connect and RBAC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/05/05/kubernetes-microservices-azure" rel="noopener noreferrer"&gt;Kubernetes Microservices on Azure with Cosmos DB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/12/02/k8s-security-best-practices" rel="noopener noreferrer"&gt;How to Secure Your Kubernetes Clusters With Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/docs/concepts/oauth-openid/" rel="noopener noreferrer"&gt;OAuth 2.0 and OpenID Connect Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/10/08/secure-access-to-aws-eks" rel="noopener noreferrer"&gt;Secure Access to AWS EKS Clusters for Admins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find all the code from this example on &lt;a href="https://github.com/oktadev/okta-java-spring-k8s-istio-microservices-example" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you liked this tutorial, chances are you'll enjoy the others we publish. Please follow &lt;a href="https://twitter.com/oktadev" rel="noopener noreferrer"&gt;@oktadev on Twitter&lt;/a&gt; and &lt;a href="https://youtube.com/oktadev" rel="noopener noreferrer"&gt;subscribe to our YouTube channel&lt;/a&gt; to get notified when we publish new developer tutorials.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>jhipster</category>
      <category>java</category>
      <category>istio</category>
    </item>
    <item>
      <title>Does Java 18 finally have a better alternative to JNI?</title>
      <dc:creator>Deepu K Sasidharan</dc:creator>
      <pubDate>Mon, 11 Apr 2022 07:21:26 +0000</pubDate>
      <link>https://forem.com/oktadev/does-java-18-finally-have-a-better-alternative-to-jni-1ka9</link>
      <guid>https://forem.com/oktadev/does-java-18-finally-have-a-better-alternative-to-jni-1ka9</guid>
      <description>&lt;p&gt;Java 18 was released last month (March 2022), and with it comes the second incubator of the Foreign Function &amp;amp; Memory API, so let us look at the state of Foreign Function Interface (FFI) in Java.&lt;/p&gt;

&lt;p&gt;If you would prefer to follow along by watching a video, here's the recording of my FOSDEM'22 talk on this topic, from the &lt;a href="https://youtu.be/lW69_AtAXzE" rel="noopener noreferrer"&gt;the OktaDev YouTube channel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/lW69_AtAXzE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Foreign Function Interface?
&lt;/h2&gt;

&lt;p&gt;A foreign function interface is the ability to call functions or routines written in one programming language from another programming language. This is generally used to access native functions or programs on the host OS written in low-level languages like C. Most languages provide some form of this feature out of the box. The term originated from common LISP, but it's known by different names in different languages. Most languages use the C/C++ calling conventions for FFI and natively support calling C/C++ functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is Foreign Function Interface needed?
&lt;/h2&gt;

&lt;p&gt;Most of the use cases for FFI are around interacting with legacy apps and accessing host OS features or native libraries. But the recent surge in machine learning and advanced arithmetics make FFI even more necessary. These days we use foreign functions for an array of use cases, some of which are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interacting with legacy apps&lt;/li&gt;
&lt;li&gt;Accessing features not available in the language&lt;/li&gt;
&lt;li&gt;Using native libraries&lt;/li&gt;
&lt;li&gt;Accessing functions or programs on the host OS&lt;/li&gt;
&lt;li&gt;Multi-precision arithmetic, Matrix multiplications&lt;/li&gt;
&lt;li&gt;GPU and CPU offloading (Cuda, OpenCL, OpenGL, Vulcan, DirectX, and so on)&lt;/li&gt;
&lt;li&gt;Deep learning (Tensorflow, cuDNN, Blas, and so on)&lt;/li&gt;
&lt;li&gt;OpenSSL, V8, and many more&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A brief history of FFI in Java
&lt;/h2&gt;

&lt;p&gt;Before we dwell on the current state of FFI in Java, let's look at a brief history of FFI in Java.&lt;/p&gt;

&lt;h3&gt;
  
  
  Java Native Interface (JNI)
&lt;/h3&gt;

&lt;p&gt;For a long time, the standard for FFI in Java has been Java Native Interface (JNI), and it is notorious for being slow and insecure. If you are used to other languages like Rust, Go, or Python, you probably know how easy and intuitive it is to use FFI in them and that leaves something to be desired in Java. Even to do a small native call using JNI, you have to do a considerable amount of work, and it could still go wrong and end up being a security issue for the app.&lt;/p&gt;

&lt;p&gt;The main issues with JNI are its complexity to use and the need to write C bridge code manually. These issues can lead to unsafe code and security risks. This can also cause performance overhead in some situations.&lt;br&gt;
The performance and memory safety of the JNI code depends on the developer, and hence reliability will vary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Native interface access for C/C++/Assembly&lt;/li&gt;
&lt;li&gt;Fastest solution in Java&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complicated to use and brittle&lt;/li&gt;
&lt;li&gt;Not very secure and could cause memory safety issues&lt;/li&gt;
&lt;li&gt;Overhead and performance loss is possible&lt;/li&gt;
&lt;li&gt;Difficult to debug&lt;/li&gt;
&lt;li&gt;Depends on Java developers to write safe C binding code manually&lt;/li&gt;
&lt;li&gt;You need to compile and ship the C code for each target platform&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Java Native Access (JNA)
&lt;/h3&gt;

&lt;p&gt;The complexity of JNI has given rise to some community-driven libraries that make it simpler to do FFI in Java. &lt;a href="https://github.com/java-native-access/jna" rel="noopener noreferrer"&gt;Java Native Access (JNA)&lt;/a&gt; is one of them. It's built on top of JNI and at least makes FFI easier to use, especially as it removes the need to write any C binding code manually and reduces the chances of memory safety issues. Still, it has some of the disadvantages of being JNI-based and is slightly slower than JNI in many cases. However, JNA is widely used and battle-tested, so definitely a better option than using JNI directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Native interface access for C/C++/Assembly&lt;/li&gt;
&lt;li&gt;Simpler to use compared to JNI&lt;/li&gt;
&lt;li&gt;Dynamic binding, no need to write any C binding code manually&lt;/li&gt;
&lt;li&gt;Widely used and mature library&lt;/li&gt;
&lt;li&gt;Better cross-platform support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses reflection&lt;/li&gt;
&lt;li&gt;Built on top of JNI&lt;/li&gt;
&lt;li&gt;Has performance overhead and can be slower than JNI&lt;/li&gt;
&lt;li&gt;Difficult to debug&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Java Native Runtime (JNR)
&lt;/h3&gt;

&lt;p&gt;Another popular option is &lt;a href="https://github.com/jnr/jnr-ffi" rel="noopener noreferrer"&gt;Java Native Runtime (JNR)&lt;/a&gt;. Though not as widely used or mature as JNA, it's much more modern and has better performance than JNA for most use cases. However, there are some cases where JNA might perform better.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Native interface access for C/C++/Assembly&lt;/li&gt;
&lt;li&gt;Easy to use&lt;/li&gt;
&lt;li&gt;Dynamic binding, no need to write any C binding code manually&lt;/li&gt;
&lt;li&gt;Modern API&lt;/li&gt;
&lt;li&gt;Comparable performance to JNI&lt;/li&gt;
&lt;li&gt;Better cross-platform support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built on top of JNI&lt;/li&gt;
&lt;li&gt;Difficult to debug&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Enter Project Panama
&lt;/h2&gt;

&lt;p&gt;Project Panama is the latest Java project aiming to simplify and improve FFI in Java, and as part of this, many proposals are currently being incubated. Let's take a look at some of the active proposals and how they will work, and let's see if we finally get proper native FFI in Java.&lt;/p&gt;
&lt;h3&gt;
  
  
  Foreign-Memory Access API
&lt;/h3&gt;

&lt;p&gt;The first piece of the puzzle is the foreign-memory access API. It was first incubated in JDK 14, and after three incubations, a new &lt;a href="https://openjdk.java.net/jeps/412" rel="noopener noreferrer"&gt;JEP&lt;/a&gt; combined it into the Foreign Function &amp;amp; Memory API.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API to safely and efficiently access foreign memory outside of the Java heap

&lt;ul&gt;
&lt;li&gt;Consistent API for different types of memory&lt;/li&gt;
&lt;li&gt;Does not compromise JVM memory safety&lt;/li&gt;
&lt;li&gt;Explicit memory deallocation&lt;/li&gt;
&lt;li&gt;Interacts with different memory resources, including off-heap or native memory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openjdk.java.net/jeps/370" rel="noopener noreferrer"&gt;JEP-370&lt;/a&gt; - First incubator in JDK 14&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openjdk.java.net/jeps/383" rel="noopener noreferrer"&gt;JEP-383&lt;/a&gt; - Second incubator in JDK 15&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openjdk.java.net/jeps/393" rel="noopener noreferrer"&gt;JEP-393&lt;/a&gt; - Third incubator in JDK 16&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Foreign Linker API
&lt;/h3&gt;

&lt;p&gt;Another essential part that makes FFI possible is Foreign Linker API. This was first incubated in JDK 16 and was combined into Foreign Function &amp;amp; Memory API in the next revision.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API for statically-typed, pure-Java access to native code

&lt;ul&gt;
&lt;li&gt;Focuses on ease of use, flexibility, and performance&lt;/li&gt;
&lt;li&gt;Initial support for C interop&lt;/li&gt;
&lt;li&gt;Calls native code in a &lt;code&gt;.dll&lt;/code&gt;, &lt;code&gt;.so&lt;/code&gt;, or &lt;code&gt;.dylib&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Creates a native function pointer to a Java method that can be passed to code in a native library&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openjdk.java.net/jeps/389" rel="noopener noreferrer"&gt;JEP-389&lt;/a&gt; - First incubator in JDK 16&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Vector API
&lt;/h3&gt;

&lt;p&gt;Next is the vector API, which is crucial for FFI, especially in machine learning and advanced computations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API for reliable and performant vector computations

&lt;ul&gt;
&lt;li&gt;Platform agnostic&lt;/li&gt;
&lt;li&gt;Clear and concise API&lt;/li&gt;
&lt;li&gt;Reliable runtime compilation and performance&lt;/li&gt;
&lt;li&gt;Graceful degradations&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openjdk.java.net/jeps/338" rel="noopener noreferrer"&gt;JEP-338&lt;/a&gt; - First incubator in JDK 16&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openjdk.java.net/jeps/414" rel="noopener noreferrer"&gt;JEP-414&lt;/a&gt; - Second incubator in JDK 17&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openjdk.java.net/jeps/417" rel="noopener noreferrer"&gt;JEP-417&lt;/a&gt; - Third incubator in JDK 18&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Foreign Function &amp;amp; Memory API
&lt;/h3&gt;

&lt;p&gt;Finally, the Foreign Linker API &amp;amp; Foreign-Memory Access API has evolved together to become the Foreign Function &amp;amp; Memory API. It was first incubated in JDK 17.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Evolution of the Foreign-Memory Access API and the Foreign Linker API

&lt;ul&gt;
&lt;li&gt;Same goals and features as the original two (ease of use, safety, performance, generality)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openjdk.java.net/jeps/412" rel="noopener noreferrer"&gt;JEP-412&lt;/a&gt; - First incubator in JDK 17&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openjdk.java.net/jeps/419" rel="noopener noreferrer"&gt;JEP-419&lt;/a&gt; - Second incubator in JDK 18&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openjdk.java.net/jeps/424" rel="noopener noreferrer"&gt;JEP-424&lt;/a&gt; - First preview expected in JDK 19&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  jextract
&lt;/h3&gt;

&lt;p&gt;And finally, there is the fantastic jextract tool. While it's not an API or part of the JDK itself, it is an essential tool for Project Panama.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A simple command-line tool&lt;/li&gt;
&lt;li&gt;Generates a Java API from one or more native C headers&lt;/li&gt;
&lt;li&gt;Ships with OpenJDK Panama builds at the moment and will be part of the JDK in the future&lt;/li&gt;
&lt;li&gt;Makes working with large C headers a cakewalk&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, to generate a Java API for OpenGL with jextract, you could simply run the following on a Unix like OS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jextract &lt;span class="nt"&gt;--source&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; org.opengl &lt;span class="nt"&gt;-I&lt;/span&gt; /usr/include /usr/include/GL/glut.h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  JNI vs. Panama
&lt;/h2&gt;

&lt;p&gt;Since JNI is the current standard and Panama aims to replace that, it makes sense to compare the two. Let's take a simple example of calling the &lt;code&gt;getpid&lt;/code&gt; function from the standard C &lt;code&gt;unistd&lt;/code&gt; header.&lt;/p&gt;

&lt;h3&gt;
  
  
  JNI
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jdqdni0gvv6zubwzq8f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jdqdni0gvv6zubwzq8f.png" alt="getpid with JNI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see here, there are precisely six steps to make this simple native call using JNI. You start by writing a Java class that declares the native method. Then you use &lt;code&gt;javac&lt;/code&gt; to generate a header file and a C class for this. These are the native bindings. Next, you will implement the C class. Remember, these are Java developers writing C code. This means you must write memory-safe C code, which has access to the entire JVM via the &lt;code&gt;JNIEnv&lt;/code&gt; variable passed to the C class.  In many scenarios, the developer may not have much experience in C. So that will be fun... or more like a security nightmare. Next, you will compile the C code into a platform-specific dynamic library and determine where to place that binary. Pray, if you must, that all this works without exposing the app to a security vulnerability. Then, you will load this into the Java class and compile and run the class, and hopefully, it works.&lt;/p&gt;

&lt;p&gt;Ooof! This was just a simple &lt;code&gt;getpid&lt;/code&gt; call; imagine writing something like an OpenGL interface or GPU offloading program using JNI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Panama
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hj2rlctq3rnhi1o0mp4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hj2rlctq3rnhi1o0mp4.png" alt="getpid with panama"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using the new Panama APIs, you can do the same thing in two different ways, either by manually looking up and loading the native function or using the jextract tool.&lt;/p&gt;

&lt;p&gt;In the first case, you just write some Java code using CLinker API. You look up the native method and invoke it; it's as simple as that. You can also do more complex stuff like working with native memory, etc. With this approach, you are using the Foreign Linker API and Foreign Memory API directly to do native calls and manage native memory. This is not the most efficient way, as this requires you to write a lot of boilerplate code and is not very scalable when working with large C headers.&lt;/p&gt;

&lt;p&gt;The second option is to use jextract. With jextract, the entire process above can be turned into one line of code. With jextract you get a pure Java API for the native program, and you won't have to write any native code or touch any header files. jextract generates everything using the Foreign Linker and Foreign Memory API. Isn't that awesome! This is the kind of FFI experience you get in languages like Go and Rust.&lt;/p&gt;

&lt;p&gt;For simple native calls, you can use the first approach, but for complex ones, the second approach is much better and scalable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benchmark
&lt;/h3&gt;

&lt;p&gt;Let's run some &lt;a href="https://github.com/openjdk/jmh" rel="noopener noreferrer"&gt;Java Microbenchmark Harness (JMH)&lt;/a&gt; benchmarks to compare the performance of JNI and Panama API. We will use the &lt;code&gt;getpid&lt;/code&gt; function from the standard C &lt;code&gt;unistd&lt;/code&gt; header for the comparison. We will call the API using JNI and Panama APIs and compare the performance. I'm running the benchmark on Linux with OpenJDK 19 early access build for Panama (&lt;code&gt;openjdk 19-panama 2022-09-20&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Here is the code for JNI, which uses the prebuilt &lt;a href="https://github.com/bytedeco/javacpp" rel="noopener noreferrer"&gt;JavaCPP&lt;/a&gt; library to call the &lt;code&gt;getpid&lt;/code&gt; function. We don't have to write all the manual C binding code and rituals as the JavaCPP library already does it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Benchmark&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;JNILinux&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bytedeco&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;javacpp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;linux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getpid&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;span class="nd"&gt;@Benchmark&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;JNIMac&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bytedeco&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;javacpp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;macosx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getpid&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the code for Panama, which uses the Foreign Linker API to call the &lt;code&gt;getpid&lt;/code&gt; function manually.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// get System linker&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;CLinker&lt;/span&gt; &lt;span class="n"&gt;linker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CLinker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;systemCLinker&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// predefine symbols and method handle info&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;NativeSymbol&lt;/span&gt; &lt;span class="n"&gt;nativeSymbol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;linker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lookup&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"getpid"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;MethodHandle&lt;/span&gt; &lt;span class="n"&gt;getPidMH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;linker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;downcallHandle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;nativeSymbol&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;FunctionDescriptor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ValueLayout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;OfInt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;JAVA_INT&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="nd"&gt;@Benchmark&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;panamaDowncall&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;getPidMH&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invokeExact&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With jextract, you can further simplify it by generating a Java API for the header file with the below command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Linux&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;C_INCLUDE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/include
&lt;span class="c"&gt;# macOS&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;C_INCLUDE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include

jextract &lt;span class="nt"&gt;--source&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; generated/src/main/java &lt;span class="nt"&gt;-t&lt;/span&gt; org.unix &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="nv"&gt;$C_INCLUDE&lt;/span&gt; &lt;span class="nv"&gt;$C_INCLUDE&lt;/span&gt;/unistd.h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is the code for calling the API generated by jextract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Benchmark&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;panamaJExtract&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unistd_h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getpid&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a sample result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Benchmark                    Mode  Cnt   Score   Error  Units
FFIBenchmark.JNI             avgt   40  50.221 ± 0.512  ns/op
FFIBenchmark.panamaDowncall  avgt   40  49.382 ± 0.701  ns/op
FFIBenchmark.panamaJExtract  avgt   40  49.946 ± 0.721  ns/op
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It seems like using Panama API is slightly faster than JNI, which is at the incubator stage, so I'm expecting this to become better when stable.&lt;/p&gt;

&lt;p&gt;If you would like to run the benchmarks yourself, follow the instructions in the readme file on &lt;a href="https://github.com/deepu105/Java-FFI-benchmarks" rel="noopener noreferrer"&gt;the source repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So, are we there yet?
&lt;/h2&gt;

&lt;p&gt;The current state of Project Panama, as of JDK 18, is as follows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Still incubating&lt;/li&gt;
&lt;li&gt;Can already work with languages that have C interop like C/C++, Fortran, Rust, etc.&lt;/li&gt;
&lt;li&gt;Performance on par or better than JNI. Hopefully, this will be improved further.&lt;/li&gt;
&lt;li&gt;jextract makes it really easy to use native libs.&lt;/li&gt;
&lt;li&gt;Memory safe and less brittle than JNI&lt;/li&gt;
&lt;li&gt;Native/off-heap memory access&lt;/li&gt;
&lt;li&gt;Documentation needs huge improvement. It's an incubator feature, so expect this to improve.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Learn more about Java and FFI
&lt;/h2&gt;

&lt;p&gt;If you want to learn more about Java and FFI in general, check out these additional resources.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/01/31/local-https-java/" rel="noopener noreferrer"&gt;Three Ways to Run Your Java Locally with HTTPS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/12/14/antipatterns-secrets-java/" rel="noopener noreferrer"&gt;Five Anti-Patterns with Secrets in Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/03/03/spring-native-jhipster/" rel="noopener noreferrer"&gt;Introducing Spring Native for JHipster: Serverless Full-Stack Made Easy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/11/05/java-records/" rel="noopener noreferrer"&gt;Java Records: A WebFlux and Spring Data Example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://foojay.io/today/project-panama-for-newbies-part-1/" rel="noopener noreferrer"&gt;Project Panama for Newbies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you liked this tutorial, chances are you'll enjoy the others we publish. Please follow &lt;a href="https://twitter.com/oktadev" rel="noopener noreferrer"&gt;@oktadev on Twitter&lt;/a&gt; and &lt;a href="https://youtube.com/oktadev" rel="noopener noreferrer"&gt;subscribe to our YouTube channel&lt;/a&gt; to get notified when we publish new developer tutorials.&lt;/p&gt;

</description>
      <category>java</category>
      <category>security</category>
    </item>
    <item>
      <title>Golang Finally Gets Generics! Does It Make Go a Better Language?</title>
      <dc:creator>Deepu K Sasidharan</dc:creator>
      <pubDate>Mon, 04 Apr 2022 09:48:03 +0000</pubDate>
      <link>https://forem.com/deepu105/golang-finally-gets-generics-does-it-make-go-a-better-language-3dai</link>
      <guid>https://forem.com/deepu105/golang-finally-gets-generics-does-it-make-go-a-better-language-3dai</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://deepu.tech/go-impressions-part-2/"&gt;deepu.tech&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So Golang finally made the sane decision to add support for generics, in version 1.18, after dragging it out for years and after multiple proposals. Well, simplicity is nice when you are a newbie in the language but becomes annoying really fast, in my personal experience, especially in large codebases.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1505784416471158788-249" src="https://platform.twitter.com/embed/Tweet.html?id=1505784416471158788"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1505784416471158788-249');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1505784416471158788&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;As a polyglot developer who has worked extensively in Golang for a few years, I have been quite critical of it &lt;a href="https://deepu.tech/reflection-on-golang/"&gt;in a previous blog post titled "My reflections on Golang"&lt;/a&gt;, written in 2019. If you are ready to be triggered, go ahead and read that post and come back here. One of my biggest gripes was that Go didn't have generics. So now that it has generics, I decided to re-evaluate my opinions about Go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generics in Go
&lt;/h2&gt;

&lt;p&gt;First, let's see how generics work in Go. Fortunately, generics in Go work pretty much the same way as in other languages like C++, Java, C#, and Rust. Of course, there will be some minor differences and some missing features since the Go version is, as you guessed, simpler. The design document for generics has some &lt;a href="https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#comparison-with-java"&gt;comparisons with Java, C++, and Rust&lt;/a&gt;.&lt;br&gt;
Though I would have preferred to see the &lt;code&gt;&amp;lt;&amp;gt;&lt;/code&gt; syntax to keep in line with most other popular languages, there seem to be &lt;a href="https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#why-not-use-the-syntax-like-c_and-java"&gt;pretty good reasons&lt;/a&gt; to use &lt;code&gt;[]&lt;/code&gt; instead, like avoiding ambiguity when mixed with channels and multiple variables assignments.&lt;/p&gt;

&lt;p&gt;Let's look at some examples. One common use case where Go developers usually write duplicated code is array and map operations like find, filter, map, and reduce. I can remember being annoyed about this and for the lack of a library like &lt;a href="https://lodash.com"&gt;Lodash&lt;/a&gt; for the same reason. With generics, you can finally write reusable utility code once and use it for all data types.&lt;/p&gt;

&lt;p&gt;Let's write a findIndex method for arrays with and without generics.&lt;/p&gt;
&lt;h3&gt;
  
  
  Without generics
&lt;/h3&gt;

&lt;p&gt;Without generics, you would write a function for each type of data you want to use, and as you start using structs, you would need a function for every unique struct. In large codebases, it's common to find thousands and thousands of lines of code just covering utilities where the only difference would be the type of argument in the method signature. This is the complete opposite of the DRY (Don't repeat yourself) principle. Every time you have to use a new data type, you must write a new function or a new block of code. Yikes!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;FindIndexFromStringArray&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;FindIndexFromIntArray&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;FindIndexFromFloat64Array&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3.3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="m"&gt;3.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;FindIndexFromStringArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;FindIndexFromIntArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;FindIndexFromFloat64Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// and more&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  With generics
&lt;/h3&gt;

&lt;p&gt;You can simplify the above code to one function with generics, and it will work for all possible data types you pass.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Foo&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;FindIndexFromArray&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;FindIndexFromArray&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;FindIndexFromArray&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3.3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="m"&gt;3.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;FindIndexFromArray&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;FindIndexFromArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;comparable&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generics would significantly reduce duplicated code in your codebase. You can also write other useful generic functions like map, reduce, filter, and so on for arrays and maps. Here are some &lt;a href="https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#examples"&gt;examples from the official design document&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Map over any slice using the given mapping function.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T2&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;T1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;T2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;T2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;T2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;floats&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c"&gt;// Reduce any slice using the given reduction function.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Reduce&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T2&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;T1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initializer&lt;/span&gt; &lt;span class="n"&gt;T2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;T2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;T2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;initializer&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Reduce&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c"&gt;// Filter any slice using the given predicate function.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;evens&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c"&gt;// Keys returns a slice of keys from a map.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt; &lt;span class="n"&gt;comparable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When declaring generic types, the type can be specific (&lt;code&gt;T comparable&lt;/code&gt;), any (&lt;code&gt;T any&lt;/code&gt;), approximate (&lt;code&gt;T ~string&lt;/code&gt;), or a union (&lt;code&gt;T int64 | float64 | int&lt;/code&gt;). Type constraints can be defined as type aliases as well.&lt;/p&gt;

&lt;p&gt;Like in Java or Rust, you can use generics in Go for functions, struct containers, interface implementations, etc. This can help to reduce boilerplate code and make writing Go code much more enjoyable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does this make Go better?
&lt;/h2&gt;

&lt;p&gt;Yes! Without a doubt, generics make Go much more enjoyable to write. While generics adds a bit of complexity, IMO the amount of boilerplate you can get rid of is worth that added complexity. For polyglot developers, who are used to languages like Java, TypeScript, Rust, C#, or C++, this is an excellent feature that might sway them to try Go. I talked about the things I liked in Go in my &lt;a href="https://deepu.tech/reflection-on-golang/"&gt;previous post&lt;/a&gt;, so I'm not going to reiterate that here. Let's see if what I didn't like remains the same and if generics help alleviate those pain points. Please note that this is very opinionated base on my tastes and experiences, especially as a polyglot developer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Generics&lt;/strong&gt;: ✅ Finally here and works great&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error handling&lt;/strong&gt;: ❌ It is still tedious and needs boilerplate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default values&lt;/strong&gt;: ❌ There is still no default values for methods&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Too much boilerplate&lt;/strong&gt;: ✅ Introduction of generics will remove a lot of it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency management&lt;/strong&gt;: ❌ Still not a fan of Go's dependency management and especially the way breaking versions are handled&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source code in GOPATH&lt;/strong&gt;: ✅ Not a problem anymore with Go modules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confusing pointer behaviors&lt;/strong&gt;: ❌ Pointers are still confusing and need to be used with care&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Struct hell&lt;/strong&gt;: ✅ Generics should help to make this pain point much less painful&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weird interface construct&lt;/strong&gt;: ❌ I'm still not a fan of this, and IMO Rust has a much better design here&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single GC algorithm&lt;/strong&gt;: ❌ Maybe I'm just spoiled by Java. Many testimonials detail how &lt;a href="https://deepu.tech/memory-management-in-golang/"&gt;the Go GC algorithm&lt;/a&gt; doesn't work in some use cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer experience&lt;/strong&gt;: ✅ I would say this has improved over the years. Still not as good as Rust, but better than many other languages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where does that leave Go?
&lt;/h2&gt;

&lt;p&gt;Recently I was pretty interested in systems programming and CLIs. I would have used Go for those if I hadn't known Rust. Once I started using Rust, I gave up on Go, as &lt;a href="https://deepu.tech/my-second-impression-of-rust/"&gt;I didn't see much reason to use it over Rust&lt;/a&gt; for the use cases that I was interested in. Honestly, the annoyance with Go was one reason that prompted me to learn Rust. I did use Go for some simple stuff like building a &lt;a href="https://github.com/deepu105/keylight"&gt;CLI for the Elgato Keylights&lt;/a&gt;, and probably if I had a use case of building microservices, then I might have given Go a thought along with Java. With generics making Go much better, IMO, I might give Go more weightage for microservices and simple CLIs.&lt;/p&gt;




&lt;p&gt;If you like this article, please leave a like or a comment.&lt;/p&gt;

&lt;p&gt;You can follow me on &lt;a href="https://twitter.com/deepu105"&gt;Twitter&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/deepu05/"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Cover image credit: Image derived from work in &lt;a href="https://github.com/egonelbre/gophers"&gt;egonelbre/gophers&lt;/a&gt; created by &lt;a href="https://twitter.com/egonelbre"&gt;@egonelbre&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>go</category>
      <category>programming</category>
      <category>languages</category>
      <category>thepragmaticprogrammer</category>
    </item>
    <item>
      <title>Why Safe Programming Matters and Why a Language Like Rust Matters</title>
      <dc:creator>Deepu K Sasidharan</dc:creator>
      <pubDate>Mon, 21 Mar 2022 07:30:12 +0000</pubDate>
      <link>https://forem.com/oktadev/why-safe-programming-matters-and-why-a-language-like-rust-matters-3m45</link>
      <guid>https://forem.com/oktadev/why-safe-programming-matters-and-why-a-language-like-rust-matters-3m45</guid>
      <description>&lt;p&gt;As programmers, how many of you have a good understanding of programming safety or secure programming? It's not the same as application security or cyber security. I have to confess; I didn't know a lot about these in the early years of my career, especially since I didn't come from a computer science background. But looking back, I think programming security is something every programmer should be aware of and should be taught at a junior level.&lt;/p&gt;

&lt;p&gt;What is safe programming, or to be more precise, what does being safe mean for a programming language? Or rather, what does unsafe mean? Let's set the context first.&lt;/p&gt;

&lt;p&gt;If you would rather follow along by watching a video, check out the video of the talk I made on the same topic, at FOSDEM'22, below from &lt;a href="https://youtu.be/8jtPUOytWyU" rel="noopener noreferrer"&gt;the OktaDev YouTube channel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/8jtPUOytWyU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Programming safety
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Programming safety = Memory safety + Type safety + Thread safety&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When we talk about "safety" in programming, we mean some combination of three distinct things: memory safety, type safety, and thread safety. There are four if you count null safety as distinct from memory safety, but we'll group those two together today.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory safety
&lt;/h3&gt;

&lt;p&gt;In a memory-safe language, when you access a variable or an item in an array, you can be sure that you are indeed accessing what you meant to or are allowed to access. In other words, you will not be reading or writing into the memory of another variable or pointer by mistake, regardless of what you do in your program.&lt;/p&gt;

&lt;p&gt;So, why is this a big deal? Don't all major programming languages ensure this?&lt;/p&gt;

&lt;p&gt;Yes, to varying extents. But some languages are unsafe by default—for example, C and C++. In C or C++, you can access the memory of another variable by mistake, or you can free a pointer twice; that's called double-free error. Sometimes a program continues to use a pointer after it has been freed, and that's called a use-after-free (UAF) error or a dangling pointer error. Such behaviors are categorized as undefined; they are unpredictable and cause security vulnerabilities rather than just crashing the program. In these scenarios, a crashing program is a good thing as it won't cause a security vulnerability.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I call it my billion-dollar mistake. It was the invention of the null reference in 1965&lt;/p&gt;

&lt;p&gt;– Tony Hoare&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then there is also null safety which is kind of related to memory safety. I come from a Java/JavaScript background, and we are used to the concept of null. Null is infamous for being the worst invention in programming. Garbage collected languages need a concept of nothing so that a pointer can be freed when unused. But the concept also leads to issues and pain, like the null pointer exceptions. Technically this relates to memory safety, but most memory-safe languages still let you use null as a value leading to null pointer errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Type safety
&lt;/h3&gt;

&lt;p&gt;In a type-safe language, when you access a variable, you access it as the correct type of data according to how it is stored. This gives us the confidence to work on data without manually checking for the data type during runtime. Memory safety is required for a language to be type-safe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thread safety
&lt;/h3&gt;

&lt;p&gt;In a thread-safe language, you can access or modify the same memory from multiple threads simultaneously without worrying about data races. This is generally achieved using message passing techniques, mutual exclusion locks (mutexes), and thread synchronization. Thread safety is required for optimal memory and type safety, so generally, memory and type-safe languages tend to be thread-safe as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does it matter?
&lt;/h2&gt;

&lt;p&gt;Ok! Why does this matter, and why should we care? Let's take a look at some stats to get an idea first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory safety issues
&lt;/h3&gt;

&lt;p&gt;Memory safety issues are the cause of most security CVEs (Common Vulnerabilities and Exposures) we encounter. Undefined behavior can be abused by a hacker to take control of the program or to leak privileged information. If you try to access an out of bounds array element in a memory-safe language, you will just crash the program with panic or error, which is predictable behavior.&lt;/p&gt;

&lt;p&gt;This is why memory-related bugs in C/C++ systems often result in CVEs and emergency patches. There are other memory-unsafe behaviors in C/C++, like accessing pointers from stack frames that have been popped, a memory that has been de-allocated, iterator invalidation, and so on. Memory safe languages, even ones that are not the safest, still protect against such security issues.&lt;/p&gt;

&lt;p&gt;If we take a look at stats, we can see that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;About 70% of all &lt;a href="https://msrc-blog.microsoft.com/2019/07/18/we-need-a-safer-systems-programming-language/" rel="noopener noreferrer"&gt;CVEs at Microsoft&lt;/a&gt; are memory safety issues.&lt;/li&gt;
&lt;li&gt;Two-thirds of &lt;a href="https://static.sched.com/hosted_files/lssna19/d6/kernel-modules-in-rust-lssna2019.pdf" rel="noopener noreferrer"&gt;Linux kernel vulnerabilities&lt;/a&gt; come from memory safety issues.&lt;/li&gt;
&lt;li&gt;An &lt;a href="https://langui.sh/2019/07/23/apple-memory-safety/" rel="noopener noreferrer"&gt;Apple study&lt;/a&gt; found that 60-70% of vulnerabilities in iOS and macOS are memory safety vulnerabilities.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://security.googleblog.com/2019/05/queue-hardening-enhancements.html" rel="noopener noreferrer"&gt;Google estimated&lt;/a&gt; that 90% of Android vulnerabilities are memory safety issues.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.zdnet.com/article/chrome-70-of-all-security-bugs-are-memory-safety-issues/" rel="noopener noreferrer"&gt;70% of all Chrome&lt;/a&gt; security bugs are memory safety issues.&lt;/li&gt;
&lt;li&gt;An &lt;a href="https://twitter.com/LazyFishBarrel/status/1129000965741404160" rel="noopener noreferrer"&gt;analysis of 0-days&lt;/a&gt; that were discovered being exploited in the wild found that more than 80% of the exploited - vulnerabilities were memory safety issues.&lt;/li&gt;
&lt;li&gt;Some of the most popular security issues of all time are memory safety issues:

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/SQL_Slammer" rel="noopener noreferrer"&gt;Slammer worm&lt;/a&gt;, &lt;a href="https://www.abetterinternet.org/docs/memory-safety/out-of-bounds%20write" rel="noopener noreferrer"&gt;WannaCry&lt;/a&gt;, &lt;a href="https://blog.lookout.com/trident-pegasus-technical-details" rel="noopener noreferrer"&gt;Trident exploit&lt;/a&gt;, &lt;a href="https://tonyarcieri.com/would-rust-have-prevented-heartbleed-another-look" rel="noopener noreferrer"&gt;HeartBleed&lt;/a&gt;, &lt;a href="https://googleprojectzero.blogspot.com/2015/09/stagefrightened.html" rel="noopener noreferrer"&gt;Stagefright&lt;/a&gt;, &lt;a href="https://blog.qualys.com/laws-of-vulnerabilities/2015/01/27/the-ghost-vulnerability" rel="noopener noreferrer"&gt;Ghost&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;That's a huge chunk of CVEs, and of course, it's no surprise that most of it is from C/C++ systems 🤷&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1501086412296228866-774" src="https://platform.twitter.com/embed/Tweet.html?id=1501086412296228866"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1501086412296228866-774');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1501086412296228866&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Imagine a world without memory safety issues. Imagine the amount of developer time saved, amount of money saved, amount of resources saved. Sometimes I wonder why we still use C/C++. Why do we trust humans, against all available evidence, to handle memory manually? And this is without considering other non-CVE memory issues like memory leaks, memory efficiency, and so on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thread safety issues
&lt;/h3&gt;

&lt;p&gt;Though not as notorious as memory safety, thread safety is also a cause of major headaches for developers and can result in security issues.&lt;/p&gt;

&lt;p&gt;Thread safety issues can cause two types of vulnerabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Information loss caused by one thread overwriting information from another

&lt;ul&gt;
&lt;li&gt;Pointer corruption that allows privilege escalation or remote execution&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Integrity loss due to information from multiple threads being interlaced

&lt;ul&gt;
&lt;li&gt;The best-known attack of this type is called a &lt;a href="https://en.wikipedia.org/wiki/Time_of_check_to_time_of_use" rel="noopener noreferrer"&gt;TOCTOU&lt;/a&gt; (time of check to time of use) attack, which is a race condition between checking a condition (like a security credential) and using the results.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Both information loss and integrity loss can be exploited and lead to security issues. While thread safety-related exploits are harder and less common than memory safety ones, they are still possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Type safety issues
&lt;/h3&gt;

&lt;p&gt;While not as critical as memory and thread safety, lack of type safety can also lead to security issues, and type safety is important for ensuring memory safety.&lt;/p&gt;

&lt;p&gt;Low-level exploits are possible in languages that are not type-safe, as an attacker can manipulate the data structure and change the data type to gain access to privileged information. Although this type of exploit is pretty rare, it's not unheard of.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Rust?
&lt;/h2&gt;

&lt;p&gt;Now that we understand how important programming safety is, let's see why Rust is one of the safest languages and how it avoids most of the security issues we normally encounter with languages like C/C++.&lt;/p&gt;

&lt;p&gt;For those not familiar, Rust is a high-level multi-paradigm language. It's ideal for functional and imperative programming. It has very modern and, in my opinion, the best tooling for a programming language. Though it was originally designed as a systems programming language, its advantages and flexibility have made it suitable for all sorts of use cases as a general-purpose language.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Rust throws around some buzzwords in its docs, but they are not just marketing buzz; they actually mean it with full sincerity, and they matter a lot."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Rust's safety guarantee
&lt;/h3&gt;

&lt;p&gt;The safety guarantee is one of the most important aspects of Rust; Rust is memory-safe, null-safe, type-safe, and thread-safe by design.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu236hj0qn0d31ym1k53u.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu236hj0qn0d31ym1k53u.jpg" alt="memory safety meme"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the compiler detects unsafe code, it will refuse to compile that code by default. You would have to go out of your way to break those guarantees using the &lt;code&gt;unsafe&lt;/code&gt; keyword. So even in cases where you would have to write unsafe code, you are making it explicit and hence issues can easily be traced down to specific code blocks.&lt;/p&gt;

&lt;h4&gt;
  
  
  Memory safety in Rust
&lt;/h4&gt;

&lt;p&gt;Rust ensures memory safety at compile time using its innovative ownership mechanism and the borrow checker built into the compiler. The compiler does not allow memory unsafe code unless it's explicitly marked as unsafe in an unsafe block or function. This static compile-time analysis eliminates many types of memory bugs, and with some additional runtime checks, Rust guarantees memory safety.&lt;br&gt;
There is no concept of null at the language level. Instead, Rust provides the &lt;code&gt;Option&lt;/code&gt; &lt;a href="http://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/first-edition/enums.html" rel="noopener noreferrer"&gt;enum&lt;/a&gt;, which can be used to mark the presence or absence of a value. This makes the resulting code null safe and much easier to deal with, and you will never encounter null pointer exceptions in Rust.&lt;/p&gt;

&lt;p&gt;The ownership and borrowing mechanisms make Rust one of the most memory-efficient languages while avoiding pitfalls with manual memory management and garbage collection. It has memory efficiency and speeds comparable to C/C++, and memory safety that's better than garbage-collected languages like Java and Go.&lt;/p&gt;

&lt;p&gt;I've written detailed articles about &lt;a href="https://deepu.tech/memory-management-in-programming/" rel="noopener noreferrer"&gt;memory management in different languages&lt;/a&gt; in my personal blog, so check them out if you are interested in learning more about memory management in Java, Rust, JavaScript, and Go.&lt;/p&gt;

&lt;h4&gt;
  
  
  Type safety in Rust
&lt;/h4&gt;

&lt;p&gt;Rust is statically typed, and it guarantees type safety by strict compile-time type checks and by guaranteeing memory safety. This is not special, as most modern languages are statically typed. Rust also allows some level of dynamic typing with the &lt;code&gt;dyn&lt;/code&gt; keyword and &lt;code&gt;Any&lt;/code&gt; type when required. But the powerful type inference and the compiler ensure type safety even in those cases.&lt;/p&gt;

&lt;h4&gt;
  
  
  Thread safety in Rust
&lt;/h4&gt;

&lt;p&gt;Rust guarantees thread safety using similar concepts for memory safety and provides standard library features like channels, mutex, and ARC (Atomically Reference Counted) pointers. In safe Rust, you can have either one mutable reference to a value or unlimited read-only references to it at any given time. The ownership mechanism makes it impossible to cause accidental data race from a shared state. This makes us confident to focus on code and let the compiler worry about shared data between threads.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Rust features
&lt;/h3&gt;

&lt;p&gt;I wrote about &lt;a href="https://deepu.tech/my-second-impression-of-rust/" rel="noopener noreferrer"&gt;my impressions of Rust&lt;/a&gt; in a detailed post on my blog where I explain Rust's excellent features that make it unique. Here is a short summary of those features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero cost abstractions&lt;/strong&gt;: Rust offers true zero-cost abstractions, which means that you can write code in any style with any number of abstractions without paying any performance penalty. Very few languages offer this, which is why Rust is so fast. Rust compiler will always generate the best byte code regardless of the style of code you write. This means you can write functional-style code and get the same performance as its imperative counterpart.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immutable by default&lt;/strong&gt;: Values in Rust are immutable, or read-only, by default. Mutability has to be declared explicitly. This, along with the ability to pass by value or reference, makes it super easy to write functional code without side effects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pattern matching&lt;/strong&gt;: Rust has excellent support for advanced pattern matching. Pattern matching is used extensively for error handling and control flows in Rust.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced generics, traits, and types&lt;/strong&gt;: Rust has advanced &lt;a href="https://doc.rust-lang.org/book/ch10-01-syntax.html" rel="noopener noreferrer"&gt;generics&lt;/a&gt; and &lt;a href="https://doc.rust-lang.org/book/ch10-02-traits.html" rel="noopener noreferrer"&gt;traits&lt;/a&gt; with &lt;a href="https://doc.rust-lang.org/reference/items/type-aliases.html" rel="noopener noreferrer"&gt;type aliasing&lt;/a&gt; and &lt;a href="https://doc.rust-lang.org/rust-by-example/types/inference.html" rel="noopener noreferrer"&gt;type inference&lt;/a&gt; support. Though generics could easily become complex when combined with lifetimes, it's one of the most powerful features of Rust.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Macros&lt;/strong&gt;: There is also support for metaprogramming using &lt;a href="https://doc.rust-lang.org/book/ch19-06-macros.html" rel="noopener noreferrer"&gt;macros&lt;/a&gt;. Rust supports both declarative macros and procedural macros. Macros can be used like annotations, attributes, and functions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Great tooling and one of the best compilers&lt;/strong&gt;: Rust has one of the best compilers and the best tooling I have seen and experienced (compared to JS world, JVM languages, Go, Python, Ruby, CSharp, PHP, C/C++). It also has excellent documentation, which is shipped with the tooling for offline use. How awesome is that!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Excellent community and ecosystem&lt;/strong&gt;: Rust has one of the most vibrant and friendly communities. The ecosystem is quite young but is one of the fastest-growing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Usually, a programming language would offer a choice between safety, speed, and high-level abstractions. At the very best, you can pick two of those. For example, with Java/C#/Go, you get safety and high-level abstractions at the cost of runtime overhead, whereas C++ gives you speed and abstractions at the cost of safety. But Rust offers all three and a good developer experience as a bonus. I don't think many other mainstream languages can claim that.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Rust, not Firefox, is Mozilla's greatest industry contribution."&lt;/p&gt;

&lt;p&gt;– TechRepublic&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This doesn't mean there are no downsides, and Rust is definitely not a silver bullet. There are issues like the steep learning curve and complexity of the language. But it's the closest thing to a silver bullet, in my opinion. That doesn't mean you should just start using Rust for everything. If a use case requires speed, concurrency, building system tools, or building CLIs, then Rust is an ideal choice. Personally, I would recommend Rust over C/C++ for any use case unless you are building a tool for a legacy platform that Rust does not support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more about Rust and security
&lt;/h2&gt;

&lt;p&gt;If you want to learn more about Rust and security in general, check out these additional resources.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/01/28/webassembly-on-kubernetes-with-rust" rel="noopener noreferrer"&gt;Containerless! How to Run WebAssembly Workloads on Kubernetes with Rust&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://deepu.tech/memory-management-in-rust/" rel="noopener noreferrer"&gt;Visualizing memory management in Rust&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.abetterinternet.org/docs/memory-safety/" rel="noopener noreferrer"&gt;What is memory safety and why does it matter?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/02/08/cookies-vs-tokens" rel="noopener noreferrer"&gt;A Comparison of Cookies and Tokens for Secure Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/10/29/things-to-keep-in-mind-about-auth" rel="noopener noreferrer"&gt;The Things to Keep in Mind about Auth&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you liked this tutorial, chances are you'll enjoy the others we publish. Please follow &lt;a href="https://twitter.com/oktadev" rel="noopener noreferrer"&gt;@oktadev on Twitter&lt;/a&gt; and &lt;a href="https://youtube.com/oktadev" rel="noopener noreferrer"&gt;subscribe to our YouTube channel&lt;/a&gt; to get notified when we publish new developer tutorials.&lt;/p&gt;

</description>
      <category>security</category>
      <category>rust</category>
      <category>cpp</category>
      <category>c</category>
    </item>
  </channel>
</rss>
