<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Rahul Lokurte Blog]]></title><description><![CDATA[Cloud Enthusiast - Love writing about AWS, Terraform and Devops]]></description><link>https://rahullokurte.com</link><generator>RSS for Node</generator><lastBuildDate>Sat, 18 Apr 2026 20:29:37 GMT</lastBuildDate><atom:link href="https://rahullokurte.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Understanding Token and Positional Embeddings in Transformers]]></title><description><![CDATA[Transformers, the backbone of many state-of-the-art NLP models such as BERT, GPT has revolutionized the way we approach natural language understanding tasks. One key innovation in transformers is their ability to handle entire sequences of tokens sim...]]></description><link>https://rahullokurte.com/understanding-token-and-positional-embeddings-in-transformers</link><guid isPermaLink="true">https://rahullokurte.com/understanding-token-and-positional-embeddings-in-transformers</guid><category><![CDATA[nlp]]></category><category><![CDATA[nlp transformers]]></category><category><![CDATA[transformers]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Mon, 18 Nov 2024 17:02:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731949154082/ff92914b-80b4-40e6-93c6-24b5deb11d04.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Transformers, the backbone of many state-of-the-art NLP models such as BERT, GPT has revolutionized the way we approach natural language understanding tasks. One key innovation in transformers is their ability to handle entire sequences of tokens simultaneously, without relying on <a target="_blank" href="https://en.wikipedia.org/wiki/Recurrent_neural_network">recurrence</a> or <a target="_blank" href="https://en.wikipedia.org/wiki/Convolutional_neural_network">convolutions</a>. However, to achieve this, they require mechanisms to represent both the semantic meaning of tokens and their positional information in a sequence. These mechanisms are known as token embeddings and positional embeddings.</p>
<p>This blog will take a deep dive into these embeddings, explaining how they are constructed, their mathematical foundations, and how they empower transformer models to achieve incredible performance.</p>
<h2 id="heading-token-embeddings-giving-words-meaning">Token Embeddings: Giving Words Meaning</h2>
<h3 id="heading-what-are-token-embeddings">What Are Token Embeddings?</h3>
<p>Token embeddings represent the semantic meaning of words in a vector space. In simple terms, they map words (or subwords) into numerical vectors that capture their meanings. Words with similar meanings are mapped to nearby points in this vector space.</p>
<p>For example:</p>
<ul>
<li><p>The word "king" might be represented as [0.5,0.2,0.8,...][0.5, 0.2, 0.8, ...][0.5,0.2,0.8,...].</p>
</li>
<li><p>The word "queen" might be represented as [0.6,0.3,0.7,...][0.6, 0.3, 0.7, ...][0.6,0.3,0.7,...].</p>
</li>
<li><p>Their closeness in the vector space reflects their semantic similarity.</p>
</li>
</ul>
<h3 id="heading-tokenization-and-vocabulary">Tokenization and Vocabulary</h3>
<p>Transformers begin by breaking down text into smaller units called <strong>tokens</strong>. These tokens can be words, subwords, or characters, depending on the tokenizer used (e.g., WordPiece, BPE, or SentencePiece). The tokenizer then assigns each token a unique <strong>token ID</strong> based on a pre-defined vocabulary.</p>
<h4 id="heading-example">Example</h4>
<p>Consider the sentence:</p>
<ul>
<li><strong>"I love NLP."</strong></li>
</ul>
<p>The vocabulary might look like this:</p>
<ul>
<li><p><strong>"I" → Token ID 1</strong></p>
</li>
<li><p><strong>"love" → Token ID 2</strong></p>
</li>
<li><p><strong>"NLP" → Token ID 3</strong></p>
</li>
</ul>
<p>The sentence is tokenized as: [1, 2, 3]</p>
<p>Each token ID is mapped to an <strong>embedding vector</strong> using an <strong>embedding matrix</strong>.</p>
<h3 id="heading-mathematical-representation-of-token-embeddings">Mathematical Representation of Token Embeddings</h3>
<p>The embedding matrix <strong>E</strong> is a learnable matrix of size <strong>[V×d]</strong>, where:</p>
<ul>
<li><p><strong>V</strong> is the size of the vocabulary.</p>
</li>
<li><p><strong>d</strong> is the embedding dimension.</p>
</li>
</ul>
<p>When a token ID is encountered, its embedding is obtained by indexing the corresponding row in <strong>E</strong>:</p>
<p>Token Embedding=E[Token ID]</p>
<p>For example:</p>
<ul>
<li><p>For <strong>Token ID 1 (I)</strong>: E[1]=[0.1,0.3,0.4]</p>
</li>
<li><p>For <strong>Token ID 2 (love)</strong>: E[2]=[0.2,0.6,0.5]</p>
</li>
<li><p>For <strong>Token ID 3 (NLP)</strong>: E[3]=[0.7,0.9,0.8]</p>
</li>
</ul>
<h2 id="heading-positional-embeddings-encoding-sequence-order">Positional Embeddings: Encoding Sequence Order</h2>
<h3 id="heading-why-are-positional-embeddings-needed">Why Are Positional Embeddings Needed?</h3>
<p>Transformers process tokens in parallel, unlike RNNs, which process them sequentially. This parallelism means transformers lack an inherent understanding of the order of tokens. For example, the sentences:</p>
<ul>
<li><strong>"I love NLP"</strong> and <strong>"NLP love I"</strong> would appear identical without positional information.</li>
</ul>
<p>To address this, positional embeddings encode the position of each token in the sequence, allowing the model to differentiate between tokens based on their order.</p>
<h3 id="heading-mathematical-construction-of-fixed-positional-embeddings">Mathematical Construction of Fixed Positional Embeddings</h3>
<p>The <strong>fixed sinusoidal positional embeddings</strong>, introduced in the original transformer paper, provide a deterministic way to represent positions. These embeddings are computed using sine and cosine functions, ensuring smooth variations across dimensions and positions.</p>
<h4 id="heading-the-formula">The Formula</h4>
<p>For a sequence length <strong>L</strong> and embedding size <strong>d</strong>, positional embeddings are computed as follows:</p>
<ul>
<li>For even dimensions (2i):</li>
</ul>
<p>PE(pos,2i)=sin⁡(pos/100002id)</p>
<ul>
<li>For odd dimensions (2i+1):</li>
</ul>
<p>PE(pos,2i+1)=cos⁡(pos/100002id)</p>
<p>Where:</p>
<ul>
<li><p><strong>pos</strong> is the position of the token in the sequence.</p>
</li>
<li><p><strong>i</strong> is the dimension index.</p>
</li>
</ul>
<h4 id="heading-example-1">Example</h4>
<p>Consider a sequence of length <strong>L=4</strong> and embedding size <strong>d=6</strong>. For position pos=2:</p>
<ul>
<li>Dimension 0 (2i=0):</li>
</ul>
<p>PE(2,0)=sin⁡(2/10000^0/6)=sin⁡(2)≈0.909</p>
<ul>
<li>Dimension 1 (2i+1=12i+1 = 12i+1=1):</li>
</ul>
<p>PE(2,1)=cos⁡(2/10000^0/6)=cos⁡(2)≈−0.416</p>
<ul>
<li>Higher dimensions follow the same principle, with increasing divisors.</li>
</ul>
<h3 id="heading-key-properties-of-sinusoidal-embeddings">Key Properties of Sinusoidal Embeddings</h3>
<ul>
<li><p><strong>Periodic Nature</strong>: The periodicity of sine and cosine functions allows the model to generalize to sequences longer than those seen during training.</p>
</li>
<li><p><strong>Smooth Variations</strong>: Neighboring positions have embeddings with small, smooth differences.</p>
</li>
<li><p><strong>Unique Representations</strong>: The combination of sine and cosine ensures each position is uniquely encoded.</p>
</li>
</ul>
<h2 id="heading-combining-token-and-positional-embeddings">Combining Token and Positional Embeddings</h2>
<p>The final input to a transformer model is obtained by adding the <strong>token embeddings</strong> and the <strong>positional embeddings</strong> element-wise. This addition provides the model with both:</p>
<ol>
<li><p><strong>What</strong>: The semantic meaning of tokens (via token embeddings).</p>
</li>
<li><p><strong>Where</strong>: The position of tokens in the sequence (via positional embeddings).</p>
</li>
</ol>
<h4 id="heading-example-2">Example</h4>
<p>Consider the sentence <strong>"I love NLP"</strong>:</p>
<ul>
<li><p>Token Embeddings: [[0.1,0.3,0.4],[0.2,0.6,0.5],[0.7,0.9,0.8]]</p>
</li>
<li><p>Positional Embeddings: [[0.01,0.02,0.03],[0.04,0.05,0.06],[0.07,0.08,0.09]]</p>
</li>
</ul>
<p><strong>Final Input Embeddings</strong>:</p>
<p>[[0.11,0.32,0.43],[0.24,0.65,0.56],[0.77,0.98,0.89]]</p>
<h3 id="heading-trainable-vs-fixed-positional-embeddings">Trainable vs. Fixed Positional Embeddings</h3>
<ul>
<li><p><strong>Fixed Positional Embeddings</strong>:</p>
<ul>
<li><p>Sinusoidal embeddings are fixed and not updated during training.</p>
</li>
<li><p>Advantage: Simpler and generalizable.</p>
</li>
<li><p>Example: Used in the original transformer model.</p>
</li>
</ul>
</li>
<li><p><strong>Learnable Positional Embeddings</strong>:</p>
<ul>
<li><p>Each position is assigned a unique embedding, which is learned during training.</p>
</li>
<li><p>Advantage: More flexible for specific tasks.</p>
</li>
<li><p>Example: Used in models like GPT and BERT.</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-libraries-and-implementation"><strong>Libraries and Implementation</strong></h2>
<ul>
<li><p><strong>TensorFlow/Keras</strong>: You can create sinusoidal positional embeddings using <code>tf.keras.layers.Layer</code>. Alternatively, KerasNLP provides high-level APIs for transformer models.</p>
</li>
<li><p><strong>PyTorch</strong>: PyTorch allows you to manually construct embeddings using <code>torch</code> functions or use pre-built models from <code>torch.nn</code>.</p>
</li>
<li><p><strong>Hugging Face Transformers</strong>: Hugging Face provides ready-to-use transformer models, including pre-trained positional embeddings.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Token embeddings and positional embeddings are the foundation of transformer models. Token embeddings capture <strong>meaning</strong>, while positional embeddings encode <strong>order</strong>, together enabling models to process sequences effectively. By understanding their mathematical and practical implementations, we can better appreciate the inner workings of transformers and build more effective NLP solutions. Whether using fixed sinusoidal embeddings or trainable embeddings, these components are indispensable in modern natural language processing.</p>
<p>Reference Research Paper: <a target="_blank" href="https://arxiv.org/abs/1706.03762"><strong>Attention Is All You Need</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Overview of RAG]]></title><description><![CDATA[When an User prompts the questions to the LLM, it produces the response based on the training data. The pre-trained LLM data is its parametric data. It might not have an context specific to our query. LLM still produces an answer confidently. It is t...]]></description><link>https://rahullokurte.com/overview-of-rag</link><guid isPermaLink="true">https://rahullokurte.com/overview-of-rag</guid><category><![CDATA[RAG ]]></category><category><![CDATA[AI]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Wed, 13 Nov 2024 13:25:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731504285299/53f3af31-6962-4728-b47a-75959ff3b930.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When an User prompts the questions to the LLM, it produces the response based on the training data. The pre-trained LLM data is its <code>parametric data</code>. It might not have an context specific to our query. LLM still produces an answer confidently. It is termed as <code>Hallucinations</code>. The high level issues with using the pretrained LLM are:</p>
<ol>
<li><p>It lacks the context of the query.</p>
</li>
<li><p>It hallucinates and confidently produces a response.</p>
</li>
</ol>
<p>To overcome this challenges, we can provide our own data from different sources within the organisations. The own data that we provide to LLMs are termed as <code>non-parametric data</code>. Now we ask LLM to verify our own data and the capabilities of pretrained data together when it produces an response to a query. This is termed as <code>Retrieval Augmented Generation</code>.</p>
<h2 id="heading-different-research-paradigms-of-rag">Different Research Paradigms of RAG</h2>
<p>The research on RAG is continuously evolving. It can be categrized into 3 stages.</p>
<ol>
<li><p>Naive RAG</p>
</li>
<li><p>Advanced RAG</p>
</li>
<li><p>Modular RAG</p>
</li>
</ol>
<h2 id="heading-naive-rag">Naive RAG</h2>
<p>Most of the initial research was done in this <a target="_blank" href="http://space.It">space.It</a> is a traditional process that includes indexing,retrieval and Generation. It is called as <code>Retrieve-Read</code> framework.</p>
<p>Indexing involves cleaning and extracting the raw data from various sources. Due to LLM models context limitations, we need to break the text to smaller chunks. The chunks are encoded into vector representation using the embedding model and stored in a vector database. This step is very important as in retrieval stage, we use it to do similarity searches.</p>
<p>When we receive an input query, we use the same encoding that was used during Indexing to transform the query to vector representation. Now, we compute the similarity scores with the query and stored chunks vectors. We can extract configurable number of chunks that are matched with the query. This context is passed to LLMs as a context information along with the original input query.</p>
<p>In generation phase, the LLM uses the context information and the original query to formulate the response. We can also pass extra conversational history, so the LLM can use it further to enhance its response. The response will depend on the parametric knowledge that the LLM model has.</p>
<h3 id="heading-drawbacks">Drawbacks</h3>
<p>Some of the drawback are</p>
<ol>
<li><p>Retrieval challenge - If we ask same question multiple times, it may respond with different answers everytime. It struggles with accuracy and also it cannot recall what the answer were given previously. So, we may get different chunks in the context and miss crucial informations.</p>
</li>
<li><p>Generation challenges - The model may still hallucinate and it may produce a response that is not provided in the context. It shows bias towards specific outputs. It may not generate quality response and answers are not reliable, if it generates different response every time with the same question.</p>
</li>
<li><p>Data challenges - The context that was provided may not be sufficient for the models to provide a response when we do different tasks with the same data. Specifically, if we have data from different sources, it may provide repeated similar answers for different tasks. Sometimes, the models will just respond to the retrieved content without providing additional content from the parametric data it has been pretrained.</p>
</li>
</ol>
<h2 id="heading-advanced-rag">Advanced RAG</h2>
<p>It is an improvement on the limitations that we have in Naive RAG. For quality of retrieval, it introduces pre-retrieval and post-retrieval steps. To improve the indexing, it uses some of advanced techniques like <code>sliding window</code>, <code>fine-grained segementation</code>, <code>metadata</code>.</p>
<p>During pre-retrieval, its main focus is to optimize the indexing and original input query. The query optimization will help in making the original question more clearer. It helps in retrieval process, if the context is accurate. It involves <code>query rewriting</code>, <code>query-transfomation</code>, <code>query-expansion</code></p>
<p>During post-retrieval, It mainly focuses on reranking the chunks and compressing of context, so that only relevant information are passed to the models. It ensures that LLM does not get the unrelated context that may overweigh the original intentions.</p>
<h2 id="heading-modular-rag">Modular RAG</h2>
<p>As more advanced in RAG space is evolving, we are leaning more towards the Modular approach of tackling RAG systems. The main focus is adaptability. It supports complex and efficient retrival mechanisms. It adds different kinds of the specialized modules to enhance particular components in RAG systems.</p>
<ul>
<li><p>Search Module is added to enhance the similarity searches and also improving the retrieval through fine tuning.The search module helps in efficient searching across different data sources like search engines, databases, knowledge graphs.</p>
</li>
<li><p>RAGFusion adds a capability for expanding user queries and can do parallel vector searches. Thus it will have more insights in the provided data.</p>
</li>
<li><p>Memory module adds a capability to the LLM retrival memory. It can retain most of the informations in memory and it will have relavant information more closer to the data. It adds more depth to the knowledge base.</p>
</li>
<li><p>Routing module helps in getting relavant information by selecting most appropriate data source to a given input query. It can combine from various data sources or access specific database.</p>
</li>
<li><p>Predict module enhances LLM capability to predict accurately, so the models can filter out unnecessary information to the input query.</p>
</li>
</ul>
<p>By using these modules, it makes significant improvements in retrieval quality, thereby advancing the capabilities of RAG systems.</p>
<p><strong>Reference Source from research paper</strong>: <a target="_blank" href="https://arxiv.org/abs/2312.10997">https://arxiv.org/abs/2312.10997</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731504214212/774a1d99-4ee0-4bee-88f1-ba639ae018df.png" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Basics of Retrieval Augmented Generation]]></title><description><![CDATA[In the fast-evolving field of artificial intelligence, Large Language Models (LLMs) have rapidly transformed how we approach language processing. These models, such as ChatGPT, Claude, and Bard, have proven their utility across a range of application...]]></description><link>https://rahullokurte.com/basics-of-retrieval-augmented-generation</link><guid isPermaLink="true">https://rahullokurte.com/basics-of-retrieval-augmented-generation</guid><category><![CDATA[AI]]></category><category><![CDATA[RAG ]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Mon, 11 Nov 2024 17:23:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731345735992/8a1371c5-c357-4fd7-a4a6-57de1d7ff6cf.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the fast-evolving field of artificial intelligence, <strong>Large Language Models (LLMs)</strong> have rapidly transformed how we approach language processing. These models, such as ChatGPT, Claude, and Bard, have proven their utility across a range of applications, from answering complex queries to acting as autonomous AI agents capable of simulating human-like conversations. At the core of these capabilities lies a generative AI technique called <strong>Retrieval Augmented Generation (RAG)</strong>, which enhances the memory and recall abilities of LLMs.</p>
<h2 id="heading-understanding-large-language-models-llms">Understanding Large Language Models (LLMs)</h2>
<p><strong>Large Language Models</strong> are advanced machine-learning systems trained on extensive datasets. These models use techniques in deep learning to understand, generate, and analyze human language, making them invaluable for natural language processing (NLP) tasks. LLMs rely on extensive data and millions (or billions) of parameters to generate responses that mimic human language patterns.</p>
<p>While LLMs have remarkable abilities, they’re not perfect. LLMs have significant limitations in retaining context over extended interactions and recalling information precisely, especially as the dialogue grows. To overcome these limitations, <strong>Retrieval Augmented Generation (RAG)</strong> was developed as a solution to improve the memory, accuracy, and relevance of information provided by LLMs.</p>
<h2 id="heading-what-is-retrieval-augmented-generation-rag">What is Retrieval Augmented Generation (RAG)?</h2>
<p><strong>Retrieval Augmented Generation</strong> combines traditional information retrieval techniques with generative language models. In simpler terms, RAG integrates two key AI processes:</p>
<ul>
<li><p><strong>Retrieval</strong>: This component searches external databases or knowledge bases to fetch relevant information based on the prompt or question.</p>
</li>
<li><p><strong>Generation</strong>: This component generates responses based on the retrieved information, enhancing the response's relevance and accuracy.</p>
</li>
</ul>
<p>By merging these two, RAG allows AI models to pull in highly specific information not contained within the original training dataset, allowing them to answer questions with increased precision and reliability.</p>
<h2 id="heading-why-do-we-need-retrieval-augmented-generation">Why Do We Need Retrieval Augmented Generation?</h2>
<p>While LLMs demonstrate immense potential, they face inherent challenges:</p>
<h3 id="heading-limited-contextual-memory">Limited Contextual Memory</h3>
<p>LLMs have limited capacity for maintaining context, especially in lengthy interactions. They are prone to “forgetting” or distorting details from previous interactions as the conversation grows. RAG addresses this limitation by retrieving contextually relevant information each time a query is made, providing the model with accurate information even in extended interactions.</p>
<h3 id="heading-outdated-or-static-knowledge">Outdated or Static Knowledge</h3>
<p>LLMs are trained on datasets available up to a specific cutoff date, making them unable to account for more recent developments or niche knowledge areas. With RAG, LLMs can retrieve and integrate up-to-date information from external databases, ensuring that responses remain relevant and accurate.</p>
<h3 id="heading-lack-of-precision-in-complex-queries">Lack of Precision in Complex Queries</h3>
<p>LLMs may produce general responses to queries that require domain-specific knowledge. RAG improves accuracy in such cases by fetching precise, domain-specific data relevant to the user's question, ensuring that even complex queries are met with appropriate responses.</p>
<h2 id="heading-how-retrieval-augmented-generation-overcomes-these-challenges">How Retrieval Augmented Generation Overcomes These Challenges</h2>
<p>Incorporating a retrieval mechanism within an LLM system addresses these challenges effectively. RAG enables systems to connect with external resources, making it possible to:</p>
<ul>
<li><p>Recall precise information on demand.</p>
</li>
<li><p>Dynamically update the response with the most relevant content.</p>
</li>
<li><p>Maintain the conversational flow by bridging gaps in the model’s inherent knowledge.</p>
</li>
</ul>
<p>With RAG, models can generate responses that are not only coherent and contextually appropriate but also rich in current and relevant data.</p>
<h2 id="heading-examples-of-rag-enabled-use-cases">Examples of RAG-Enabled Use Cases</h2>
<p>The RAG technique has enabled several advanced applications across industries:</p>
<ul>
<li><p><strong>Customer Support Systems</strong>: Chatbots equipped with RAG can fetch real-time information, making them more effective in handling user-specific queries.</p>
</li>
<li><p><strong>Medical Research</strong>: RAG-powered models can search through vast repositories of medical journals, providing healthcare professionals with accurate and up-to-date information.</p>
</li>
<li><p><strong>Legal Research</strong>: RAG allows legal professionals to quickly retrieve relevant case laws and precedents, making their work more efficient.</p>
</li>
<li><p><strong>Finance and Investment Analysis</strong>: RAG can enhance investment platforms by providing financial analysts with the latest market insights and trends.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Managing Concurrent Writes and Object Locking in Amazon S3: Strategies and Best Practices]]></title><description><![CDATA[Amazon S3 is a powerful, scalable storage service, but when multiple clients or processes attempt to write to the same S3 object simultaneously, things can get tricky. Since S3 doesn’t natively handle concurrent writes or provide object locking, it’s...]]></description><link>https://rahullokurte.com/managing-concurrent-writes-and-object-locking-in-amazon-s3-strategies-and-best-practices</link><guid isPermaLink="true">https://rahullokurte.com/managing-concurrent-writes-and-object-locking-in-amazon-s3-strategies-and-best-practices</guid><category><![CDATA[AWS]]></category><category><![CDATA[S3]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Tue, 03 Sep 2024 14:52:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1725375012157/cf9b73d2-d705-4883-93c3-866331e7a143.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Amazon S3 is a powerful, scalable storage service, but when multiple clients or processes attempt to write to the same S3 object simultaneously, things can get tricky. Since S3 doesn’t natively handle concurrent writes or provide object locking, it’s up to developers to implement strategies that ensure data consistency and avoid race conditions. In this blog, we’ll explore various approaches to managing concurrent writes in Amazon S3, complete with code examples.</p>
<h1 id="heading-understanding-the-challenge-concurrent-writes-in-amazon-s3">Understanding the Challenge: Concurrent Writes in Amazon S3</h1>
<p>When multiple clients attempt to write to the same S3 object (i.e., the same key) at the same time, S3 will accept all requests. However, only the data from the request with the most recent timestamp will be stored. This means that earlier writes can be overwritten by later ones, leading to potential data loss or inconsistency.</p>
<p>Moreover, S3 does not support native object locking. This absence of built-in locking mechanisms means that you need to handle concurrency at the application level. Below, we’ll discuss several strategies you can implement to manage concurrent writes and achieve data consistency.</p>
<ol>
<li><strong>Application-Level Locking</strong></li>
</ol>
<p><strong>Application-level locking</strong> involves creating your own locking mechanism to control access to an S3 object. This ensures that only one process can write to an object at a time, avoiding race conditions.</p>
<p><strong>Implementation Example: Lock Management Using DynamoDB</strong></p>
<p>You can use a DynamoDB table to manage locks. Each S3 object key corresponds to an entry in the table. Before writing to S3, your application checks if the object is locked. If it’s not locked, the application creates a lock entry in DynamoDB and then writes to S3.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> boto3
<span class="hljs-keyword">from</span> botocore.exceptions <span class="hljs-keyword">import</span> ClientError

dynamodb = boto3.resource(<span class="hljs-string">'dynamodb'</span>)
s3 = boto3.client(<span class="hljs-string">'s3'</span>)

lock_table = dynamodb.Table(<span class="hljs-string">'S3ObjectLocks'</span>)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">acquire_lock</span>(<span class="hljs-params">key</span>):</span>
    <span class="hljs-keyword">try</span>:
        lock_table.put_item(
            Item={<span class="hljs-string">'S3Key'</span>: key, <span class="hljs-string">'Lock'</span>: <span class="hljs-literal">True</span>},
            ConditionExpression=<span class="hljs-string">'attribute_not_exists(S3Key)'</span>
        )
        <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
    <span class="hljs-keyword">except</span> ClientError <span class="hljs-keyword">as</span> e:
        print(<span class="hljs-string">f"Lock not acquired: <span class="hljs-subst">{e.response[<span class="hljs-string">'Error'</span>][<span class="hljs-string">'Message'</span>]}</span>"</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">release_lock</span>(<span class="hljs-params">key</span>):</span>
    lock_table.delete_item(Key={<span class="hljs-string">'S3Key'</span>: key})

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">write_to_s3</span>(<span class="hljs-params">bucket, key, data</span>):</span>
    <span class="hljs-keyword">if</span> acquire_lock(key):
        <span class="hljs-keyword">try</span>:
            s3.put_object(Bucket=bucket, Key=key, Body=data)
            print(<span class="hljs-string">f"Successfully wrote to <span class="hljs-subst">{key}</span>"</span>)
        <span class="hljs-keyword">finally</span>:
            release_lock(key)
    <span class="hljs-keyword">else</span>:
        print(<span class="hljs-string">f"Failed to acquire lock for <span class="hljs-subst">{key}</span>"</span>)

<span class="hljs-comment"># Example usage</span>
write_to_s3(<span class="hljs-string">'my-bucket'</span>, <span class="hljs-string">'my-object'</span>, <span class="hljs-string">'Hello World!'</span>)
</code></pre>
<p><strong>Pros:</strong></p>
<ul>
<li><p>Provides fine-grained control over access to S3 objects.</p>
</li>
<li><p>It can be tailored to specific use cases.</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li><p>Adds complexity to the application.</p>
</li>
<li><p>Potential for deadlocks or orphaned locks if not carefully managed.</p>
</li>
</ul>
<ol start="2">
<li><strong>Using S3 Versioning</strong></li>
</ol>
<p><strong>S3 Versioning</strong> allows you to keep multiple versions of an object. When versioning is enabled, each time you write to an object, S3 stores it as a new version instead of overwriting the existing one. This ensures that no data is lost if concurrent writes occur.</p>
<p><strong>Implementation Example: Enabling Versioning and Retrieving Versions</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> boto3

s3 = boto3.client(<span class="hljs-string">'s3'</span>)

<span class="hljs-comment"># Enable versioning on a bucket</span>
s3.put_bucket_versioning(
    Bucket=<span class="hljs-string">'my-bucket'</span>,
    VersioningConfiguration={
        <span class="hljs-string">'Status'</span>: <span class="hljs-string">'Enabled'</span>
    }
)

<span class="hljs-comment"># Upload multiple versions of an object</span>
s3.put_object(Bucket=<span class="hljs-string">'my-bucket'</span>, Key=<span class="hljs-string">'my-object'</span>, Body=<span class="hljs-string">'First version'</span>)
s3.put_object(Bucket=<span class="hljs-string">'my-bucket'</span>, Key=<span class="hljs-string">'my-object'</span>, Body=<span class="hljs-string">'Second version'</span>)

<span class="hljs-comment"># Retrieve all versions of the object</span>
versions = s3.list_object_versions(Bucket=<span class="hljs-string">'my-bucket'</span>, Prefix=<span class="hljs-string">'my-object'</span>)
<span class="hljs-keyword">for</span> version <span class="hljs-keyword">in</span> versions.get(<span class="hljs-string">'Versions'</span>, []):
    print(<span class="hljs-string">f"Version ID: <span class="hljs-subst">{version[<span class="hljs-string">'VersionId'</span>]}</span>, Data: <span class="hljs-subst">{s3.get_object(Bucket=<span class="hljs-string">'my-bucket'</span>, Key=<span class="hljs-string">'my-object'</span>, VersionId=version[<span class="hljs-string">'VersionId'</span>])[<span class="hljs-string">'Body'</span>].read().decode()}</span>"</span>)
</code></pre>
<p><strong>Pros:</strong></p>
<ul>
<li><p>Prevents data loss by storing all versions.</p>
</li>
<li><p>Allows you to revert to previous versions if necessary.</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li><p>Storage costs can increase due to multiple versions.</p>
</li>
<li><p>Additional logic is required to manage and clean up old versions if necessary.</p>
</li>
</ul>
<ol start="3">
<li><strong>Pre-Signed URLs with Conditional Headers</strong></li>
</ol>
<p><strong>Pre-signed URLs</strong> allow you to generate a temporary URL that grants permission to perform specific operations on an S3 object. By using conditional headers, you can ensure that the write operation only proceeds if certain conditions are met, such as the object not having been modified since it was last accessed.</p>
<p><strong>Implementation Example: Conditional PUT Request with Pre-Signed URL</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> boto3
<span class="hljs-keyword">from</span> botocore.client <span class="hljs-keyword">import</span> Config

s3 = boto3.client(<span class="hljs-string">'s3'</span>, config=Config(signature_version=<span class="hljs-string">'s3v4'</span>))

<span class="hljs-comment"># Generate a pre-signed URL that only allows the write if the object's ETag matches</span>
url = s3.generate_presigned_url(
    ClientMethod=<span class="hljs-string">'put_object'</span>,
    Params={
        <span class="hljs-string">'Bucket'</span>: <span class="hljs-string">'my-bucket'</span>,
        <span class="hljs-string">'Key'</span>: <span class="hljs-string">'my-object'</span>,
        <span class="hljs-string">'ContentType'</span>: <span class="hljs-string">'text/plain'</span>
    },
    ExpiresIn=<span class="hljs-number">3600</span>,
    HttpMethod=<span class="hljs-string">'PUT'</span>,
    Conditions=[
        {<span class="hljs-string">"If-Match"</span>: <span class="hljs-string">"d41d8cd98f00b204e9800998ecf8427e"</span>}  <span class="hljs-comment"># Replace with actual ETag of the object</span>
    ]
)

<span class="hljs-comment"># Use the pre-signed URL to upload the object (e.g., with requests or a web client)</span>
print(<span class="hljs-string">f"Pre-signed URL: <span class="hljs-subst">{url}</span>"</span>)
</code></pre>
<p><strong>Pros:</strong></p>
<ul>
<li><p>Provides a lightweight way to conditionally allow operations.</p>
</li>
<li><p>Doesn’t require managing locks or versioning.</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li><p>Requires careful handling of conditions like ETags or timestamps.</p>
</li>
<li><p>Only suitable for scenarios where condition-based writes are sufficient.</p>
</li>
</ul>
<ol start="4">
<li><strong>Using AWS S3 Object Lock for Compliance/Governance</strong></li>
</ol>
<p><strong>AWS S3 Object Lock</strong> is primarily designed to prevent objects from being deleted or overwritten for a fixed amount of time or indefinitely, which is useful for compliance and data retention. While not a direct solution for concurrency, it can ensure data immutability.</p>
<p><strong>Implementation Example: Enabling Object Lock</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> boto3

s3 = boto3.client(<span class="hljs-string">'s3'</span>)

<span class="hljs-comment"># Enable Object Lock on the bucket</span>
s3.put_object_lock_configuration(
    Bucket=<span class="hljs-string">'my-bucket'</span>,
    ObjectLockConfiguration={
        <span class="hljs-string">'ObjectLockEnabled'</span>: <span class="hljs-string">'Enabled'</span>,
        <span class="hljs-string">'Rule'</span>: {
            <span class="hljs-string">'DefaultRetention'</span>: {
                <span class="hljs-string">'Mode'</span>: <span class="hljs-string">'GOVERNANCE'</span>,
                <span class="hljs-string">'Days'</span>: <span class="hljs-number">30</span>
            }
        }
    }
)

<span class="hljs-comment"># Upload an object with Object Lock</span>
s3.put_object(
    Bucket=<span class="hljs-string">'my-bucket'</span>,
    Key=<span class="hljs-string">'my-object'</span>,
    Body=<span class="hljs-string">'Content to protect'</span>,
    ObjectLockMode=<span class="hljs-string">'GOVERNANCE'</span>,
    ObjectLockRetainUntilDate=<span class="hljs-string">'2024-12-31T00:00:00.000Z'</span>
)
</code></pre>
<p><strong>Pros:</strong></p>
<ul>
<li><p>Ensures data immutability, which can prevent accidental overwrites.</p>
</li>
<li><p>Useful for regulatory compliance and data retention.</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li><p>Not specifically designed for managing concurrent writes.</p>
</li>
<li><p>Requires careful management to avoid unintended data retention or access issues.</p>
</li>
</ul>
<h3 id="heading-choosing-the-right-strategy">Choosing the Right Strategy</h3>
<p>Each strategy has its pros and cons, and the right approach depends on your specific use case:</p>
<ul>
<li><p><strong>Application-Level Locking</strong> is ideal when you need fine-grained control over who can write to an object and when.</p>
</li>
<li><p><strong>S3 Versioning</strong> is best when you want to keep all versions of an object and manage them post-facto.</p>
</li>
<li><p><strong>Pre-Signed URLs with Conditional Headers</strong> are effective for lightweight, condition-based write controls.</p>
</li>
<li><p><strong>S3 Object Lock</strong> is more about ensuring data immutability, especially in compliance scenarios.</p>
</li>
</ul>
<p>In some cases, you may need to combine these strategies. For example, you could use S3 Versioning alongside Application-Level Locking to ensure both consistency and auditability of writes to your S3 bucket.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Managing concurrent writes and object locking in Amazon S3 requires thoughtful consideration and careful implementation. While S3 does not provide native support for object locking or handling concurrency, the strategies outlined above can help you achieve the desired consistency and reliability in your application.</p>
<p>By implementing these techniques, you can effectively manage concurrent writes, avoid race conditions, and ensure that your data remains consistent and secure in Amazon S3.</p>
]]></content:encoded></item><item><title><![CDATA[Saga Pattern using AWS Step Functions]]></title><description><![CDATA[When working with distributed systems in a microservice architecture, we must always architect our services with failure in mind. When the transaction happens between a microservice, we have to handle the failures. The Saga pattern can be used in a f...]]></description><link>https://rahullokurte.com/saga-pattern-using-aws-step-functions</link><guid isPermaLink="true">https://rahullokurte.com/saga-pattern-using-aws-step-functions</guid><category><![CDATA[AWS]]></category><category><![CDATA[stepfunction]]></category><category><![CDATA[design patterns]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Sat, 04 May 2024 15:12:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1714835434923/0f348612-fe08-4c4c-b654-01a8ac264f37.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When working with distributed systems in a microservice architecture, we must always architect our services with failure in mind. When the transaction happens between a microservice, we have to handle the failures. The Saga pattern can be used in a failure of distributed transactions. We will look into implementing the Saga pattern using AWS step functions.</p>
<h2 id="heading-introduction">Introduction</h2>
<p>The Saga pattern is a design pattern for managing distributed transactions across multiple microservices or components in a distributed system. Traditional two-phase commit protocols can become complex and brittle in distributed environments, where failures are common, and coordinating transactions across multiple services can lead to increased latency and potential for deadlock.</p>
<p>The Saga pattern addresses these challenges by breaking down a long-lived transaction into a series of smaller, independent transactions, known as saga steps. Each saga step represents a unit of work that can be completed or compensated independently of other steps. If a failure occurs during the execution of a saga, compensating transactions are used to undo the work performed by previously completed steps, ensuring eventual consistency.</p>
<h3 id="heading-implementing-the-saga-pattern-with-aws-step-functions"><strong>Implementing the Saga Pattern with AWS Step Functions</strong></h3>
<p>In AWS Step Functions, you can model a saga as a state machine where each state represents a saga step. The state machine transitions between states based on the success or failure of each step, executing compensating transactions as needed to maintain consistency.</p>
<p>To implement the Saga pattern with AWS Step Functions:</p>
<ol>
<li><p><strong>Define Saga Steps</strong>: Identify the individual steps of the saga and map them to state machine states.</p>
</li>
<li><p><strong>Handle Success and Failure</strong>: Define transitions between states based on the success or failure of each step.</p>
</li>
<li><p><strong>Implement Compensating Transactions</strong>: For each saga step, define compensating transactions to undo the effects of completed steps in case of failure.</p>
</li>
<li><p><strong>Manage State and Data</strong>: Use input and output data to track the state of the saga and pass information between steps.</p>
</li>
</ol>
<h3 id="heading-design-considerations"><strong>Design Considerations</strong></h3>
<p>When designing sagas with AWS Step Functions, consider the following best practices:</p>
<ul>
<li><p><strong>Error Handling</strong>: Handle errors gracefully at each step of the saga and implement retry logic where appropriate.</p>
</li>
<li><p><strong>Timeouts</strong>: Set timeouts for each step to prevent long-running tasks from blocking the execution of the saga.</p>
</li>
<li><p><strong>Idempotency</strong>: Ensure that each step of the saga is idempotent to handle retries and duplicate requests.</p>
</li>
<li><p><strong>Data Consistency</strong>: Use consistent data models and transactional updates to maintain data consistency across saga steps.</p>
</li>
</ul>
<h3 id="heading-walkthrough-example-implementing-a-saga-pattern-for-order-processing"><strong>Walkthrough Example: Implementing a Saga Pattern for Order Processing</strong></h3>
<p>In this example, we'll implement a saga pattern to manage the order processing workflow in an e-commerce system. The order processing workflow consists of three main steps:</p>
<ol>
<li><p><strong>Place Order</strong>: Initiates the order processing and reserves inventory.</p>
</li>
<li><p><strong>Charge Payment</strong>: Charges the customer's payment method.</p>
</li>
<li><p><strong>Fulfill Order</strong>: Ships the ordered items to the customer.</p>
</li>
</ol>
<p>If any step fails, we'll need to compensate by undoing the work of the previously completed steps.</p>
<h4 id="heading-step-1-define-the-state-machine"><strong>Step 1: Define the State Machine</strong></h4>
<p>First, let's define the state machine using AWS Step Functions' JSON-based state language. Each state represents a step in the order processing saga.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"Comment"</span>: <span class="hljs-string">"Order Processing Saga"</span>,
  <span class="hljs-attr">"StartAt"</span>: <span class="hljs-string">"PlaceOrder"</span>,
  <span class="hljs-attr">"States"</span>: {
    <span class="hljs-attr">"PlaceOrder"</span>: {
      <span class="hljs-attr">"Type"</span>: <span class="hljs-string">"Task"</span>,
      <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"arn:aws:lambda:ap-south-1:123456789012:function:PlaceOrderFunction"</span>,
      <span class="hljs-attr">"Next"</span>: <span class="hljs-string">"ChargePayment"</span>,
      <span class="hljs-attr">"Catch"</span>: [{
        <span class="hljs-attr">"ErrorEquals"</span>: [<span class="hljs-string">"States.ALL"</span>],
        <span class="hljs-attr">"Next"</span>: <span class="hljs-string">"CompensatePlaceOrder"</span>
      }]
    },
    <span class="hljs-attr">"ChargePayment"</span>: {
      <span class="hljs-attr">"Type"</span>: <span class="hljs-string">"Task"</span>,
      <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"arn:aws:lambda:ap-south-1:123456789012:function:ChargePaymentFunction"</span>,
      <span class="hljs-attr">"Next"</span>: <span class="hljs-string">"FulfillOrder"</span>,
      <span class="hljs-attr">"Catch"</span>: [{
        <span class="hljs-attr">"ErrorEquals"</span>: [<span class="hljs-string">"States.ALL"</span>],
        <span class="hljs-attr">"Next"</span>: <span class="hljs-string">"CompensateChargePayment"</span>
      }]
    },
    <span class="hljs-attr">"FulfillOrder"</span>: {
      <span class="hljs-attr">"Type"</span>: <span class="hljs-string">"Task"</span>,
      <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"arn:aws:lambda:ap-south-1:123456789012:function:FulfillOrderFunction"</span>,
      <span class="hljs-attr">"End"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"Catch"</span>: [{
        <span class="hljs-attr">"ErrorEquals"</span>: [<span class="hljs-string">"States.ALL"</span>],
        <span class="hljs-attr">"Next"</span>: <span class="hljs-string">"CompensateFulfillOrder"</span>
      }]
    },
    <span class="hljs-attr">"CompensatePlaceOrder"</span>: {
      <span class="hljs-attr">"Type"</span>: <span class="hljs-string">"Task"</span>,
      <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"arn:aws:lambda:ap-south-1:123456789012:function:CompensatePlaceOrderFunction"</span>,
      <span class="hljs-attr">"End"</span>: <span class="hljs-literal">true</span>
    },
    <span class="hljs-attr">"CompensateChargePayment"</span>: {
      <span class="hljs-attr">"Type"</span>: <span class="hljs-string">"Task"</span>,
      <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"arn:aws:lambda:ap-south-1:123456789012:function:CompensateChargePaymentFunction"</span>,
      <span class="hljs-attr">"End"</span>: <span class="hljs-literal">true</span>
    },
    <span class="hljs-attr">"CompensateFulfillOrder"</span>: {
      <span class="hljs-attr">"Type"</span>: <span class="hljs-string">"Task"</span>,
      <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"arn:aws:lambda:ap-south-1:123456789012:function:CompensateFulfillOrderFunction"</span>,
      <span class="hljs-attr">"End"</span>: <span class="hljs-literal">true</span>
    }
  }
}
</code></pre>
<h4 id="heading-step-2-implement-lambda-functions"><strong>Step 2: Implement Lambda Functions</strong></h4>
<p>Next, implement the Lambda functions for each step of the saga:</p>
<ul>
<li><strong>PlaceOrderFunction</strong>: Initiates the order processing and reserves inventory.</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> boto3

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">place_order</span>(<span class="hljs-params">order_details</span>):</span>
    <span class="hljs-comment"># Dummy logic to place order and reserve inventory</span>
    order_id = <span class="hljs-string">'123456'</span>
    <span class="hljs-keyword">return</span> order_id

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lambda_handler</span>(<span class="hljs-params">event, context</span>):</span>
    order_details = event[<span class="hljs-string">'order_details'</span>]
    order_id = place_order(order_details)
    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">'orderId'</span>: order_id
    }
</code></pre>
<ul>
<li><strong>ChargePaymentFunction</strong>: Charges the customer's payment method.</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> boto3

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">charge_payment</span>(<span class="hljs-params">order_id, payment_details</span>):</span>
    <span class="hljs-comment"># Dummy logic to charge payment</span>
    payment_id = <span class="hljs-string">'PAY-789'</span>
    <span class="hljs-keyword">return</span> payment_id

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lambda_handler</span>(<span class="hljs-params">event, context</span>):</span>
    order_id = event[<span class="hljs-string">'orderId'</span>]
    payment_details = event[<span class="hljs-string">'payment_details'</span>]
    payment_id = charge_payment(order_id, payment_details)
    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">'paymentId'</span>: payment_id
    }
</code></pre>
<ul>
<li><strong>FulfillOrderFunction</strong>: Ships the ordered items to the customer.</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> boto3

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">fulfill_order</span>(<span class="hljs-params">order_id</span>):</span>
    <span class="hljs-comment"># Dummy logic to fulfill order and ship items</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lambda_handler</span>(<span class="hljs-params">event, context</span>):</span>
    order_id = event[<span class="hljs-string">'orderId'</span>]
    success = fulfill_order(order_id)
    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">'success'</span>: success
    }
</code></pre>
<ul>
<li><strong>CompensatePlaceOrderFunction</strong>: Compensates for the PlaceOrder step (e.g., releases reserved inventory).</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> boto3

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">compensate_place_order</span>(<span class="hljs-params">order_id</span>):</span>
    <span class="hljs-comment"># Dummy logic to release reserved inventory</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lambda_handler</span>(<span class="hljs-params">event, context</span>):</span>
    order_id = event[<span class="hljs-string">'orderId'</span>]
    success = compensate_place_order(order_id)
    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">'success'</span>: success
    }
</code></pre>
<ul>
<li><strong>CompensateChargePaymentFunction</strong>: Compensates for the ChargePayment step (e.g., refunds the charged amount).</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> boto3

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">compensate_charge_payment</span>(<span class="hljs-params">payment_id</span>):</span>
    <span class="hljs-comment"># Dummy logic to refund charged amount</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lambda_handler</span>(<span class="hljs-params">event, context</span>):</span>
    payment_id = event[<span class="hljs-string">'paymentId'</span>]
    success = compensate_charge_payment(payment_id)
    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">'success'</span>: success
    }
</code></pre>
<ul>
<li><strong>CompensateFulfillOrderFunction</strong>: Compensates for the FulfillOrder step (e.g., cancels the order shipment).</li>
</ul>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> boto3

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">compensate_fulfill_order</span>(<span class="hljs-params">order_id</span>):</span>
    <span class="hljs-comment"># Dummy logic to cancel order shipment</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lambda_handler</span>(<span class="hljs-params">event, context</span>):</span>
    order_id = event[<span class="hljs-string">'orderId'</span>]
    success = compensate_fulfill_order(order_id)
    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">'success'</span>: success
    }
</code></pre>
<h4 id="heading-step-3-test-the-saga"><strong>Step 3: Test the Saga</strong></h4>
<p>Once the state machine and Lambda functions are implemented, test the saga by simulating different scenarios:</p>
<ul>
<li><strong>Scenario 1 (Success)</strong>: All steps are completed successfully.</li>
</ul>
<p><strong>Input</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"order_details"</span>: {
    <span class="hljs-attr">"item_id"</span>: <span class="hljs-string">"123"</span>,
    <span class="hljs-attr">"quantity"</span>: <span class="hljs-number">2</span>,
    <span class="hljs-attr">"customer_id"</span>: <span class="hljs-string">"456"</span>
  },
  <span class="hljs-attr">"payment_details"</span>: {
    <span class="hljs-attr">"amount"</span>: <span class="hljs-number">100</span>,
    <span class="hljs-attr">"payment_method"</span>: <span class="hljs-string">"credit_card"</span>
  }
}
</code></pre>
<p><strong>Output</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"orderId"</span>: <span class="hljs-string">"123456"</span>,
  <span class="hljs-attr">"paymentId"</span>: <span class="hljs-string">"PAY-789"</span>,
  <span class="hljs-attr">"success"</span>: <span class="hljs-literal">true</span>
}
</code></pre>
<ul>
<li><strong>Scenario 2 (Partial Failure)</strong>: PlaceOrder and ChargePayment are completed successfully, but FulfillOrder fails. Ensure that compensating transactions are triggered to undo the completed steps.</li>
</ul>
<p><strong>Input</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"order_details"</span>: {
    <span class="hljs-attr">"item_id"</span>: <span class="hljs-string">"789"</span>,
    <span class="hljs-attr">"quantity"</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">"customer_id"</span>: <span class="hljs-string">"123"</span>
  },
  <span class="hljs-attr">"payment_details"</span>: {
    <span class="hljs-attr">"amount"</span>: <span class="hljs-number">50</span>,
    <span class="hljs-attr">"payment_method"</span>: <span class="hljs-string">"paypal"</span>
  }
}
</code></pre>
<p><strong>Output</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"success"</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-attr">"compensatePlaceOrder"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"compensateChargePayment"</span>: <span class="hljs-literal">true</span>
}
</code></pre>
<ul>
<li><strong>Scenario 3 (Complete Failure)</strong>: PlaceOrder fails. Ensure that compensating transactions are triggered to undo any partially completed steps.</li>
</ul>
<p><strong>Input</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"order_details"</span>: {
    <span class="hljs-attr">"item_id"</span>: <span class="hljs-string">"789"</span>,
    <span class="hljs-attr">"quantity"</span>: <span class="hljs-number">3</span>,
    <span class="hljs-attr">"customer_id"</span>: <span class="hljs-string">"123"</span>
  },
  <span class="hljs-attr">"payment_details"</span>: {
    <span class="hljs-attr">"amount"</span>: <span class="hljs-number">150</span>,
    <span class="hljs-attr">"payment_method"</span>: <span class="hljs-string">"credit_card"</span>
  }
}
</code></pre>
<p><strong>Output</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"success"</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-attr">"compensateChargePayment"</span>: <span class="hljs-literal">true</span>
}
</code></pre>
<h3 id="heading-conclusion"><strong>Conclusion</strong></h3>
<p>In conclusion, implementing the Saga pattern using AWS Step Functions provides a powerful mechanism for managing distributed transactions in serverless applications. By breaking down long-lived transactions into smaller, independent steps and orchestrating them with Step Functions, you can achieve eventual consistency and fault tolerance in your distributed systems. Experiment with different saga designs and workflows to find the best approach for your application needs.</p>
]]></content:encoded></item><item><title><![CDATA[Exploring AWS Step Function Using TestState API]]></title><description><![CDATA[Introduction
AWS Step Functions offers an array of testing and debugging tools. In this blog post, we'll focus on the TestState API.
TestState API helps us to test a single state in the state machine workflow. So, we can test a state without creating...]]></description><link>https://rahullokurte.com/exploring-aws-step-function-using-teststate-api</link><guid isPermaLink="true">https://rahullokurte.com/exploring-aws-step-function-using-teststate-api</guid><category><![CDATA[AWS]]></category><category><![CDATA[AWS Step Functions]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Tue, 28 Nov 2023 18:10:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1701194922981/5e6e3581-14b2-47ad-a531-d6e8366feec8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>AWS Step Functions offers an array of testing and debugging tools. In this blog post, we'll focus on the TestState API.</p>
<p>TestState API helps us to test a single state in the state machine workflow. So, we can test a state without creating a state machine and execute the complete flow. This helps resolve common errors such as path issues, and input/output issues at the development time and at a fast pace.</p>
<h2 id="heading-supported-states">Supported States</h2>
<p>The following states are supported for TestState.</p>
<ul>
<li><p>Pass</p>
</li>
<li><p>Wait</p>
</li>
<li><p>Task</p>
</li>
<li><p>Choice</p>
</li>
<li><p>Succeed</p>
</li>
<li><p>Fail</p>
</li>
</ul>
<h2 id="heading-informative-levels">Informative levels</h2>
<p>Step Functions offers three distinct inspection levels: INFO, DEBUG, and TRACE. The different inspection levels, provide us the right amount of detail to suit our debugging and validation needs.</p>
<ul>
<li><p><strong>INFO:</strong> At this level, we receive essential information about the state execution, including the status and the next state to transition to.</p>
</li>
<li><p><strong>DEBUG:</strong> At this level, it provides additional insights into the state execution. Beyond the status and next state, you gain access to intermediate details, especially useful when using input and output data processing filters like InputPath or ResultPath.</p>
</li>
<li><p><strong>TRACE:</strong> At this level, it not only includes the status and next state but also provides deep into the intermediate and final data processing results.</p>
</li>
</ul>
<h2 id="heading-using-aws-cli-with-teststate">Using AWS CLI with TestState</h2>
<p>We can use AWS CLI with TestState for testing each state of the state machine in silo.</p>
<p>Suppose, we have a <code>pass</code>state in our Stepfunction as shown below.</p>
<pre><code class="lang-json">{
      <span class="hljs-attr">"Type"</span>: <span class="hljs-string">"Pass"</span>,
      <span class="hljs-attr">"Next"</span>: <span class="hljs-string">"Second State"</span>,
      <span class="hljs-attr">"Parameters"</span>: {
        <span class="hljs-attr">"payload.$"</span>: <span class="hljs-string">"$.customer.firstName"</span>
      }
    }
</code></pre>
<p>To test this state, we can use below AWS CLI command</p>
<pre><code class="lang-json">aws stepfunctions test-state \
    --definition '{
      <span class="hljs-attr">"Type"</span>: <span class="hljs-string">"Pass"</span>,
      <span class="hljs-attr">"Next"</span>: <span class="hljs-string">"Second State"</span>,
      <span class="hljs-attr">"Parameters"</span>: {
        <span class="hljs-attr">"payload.$"</span>: <span class="hljs-string">"$.customer.firstName"</span>
      }
    }' \
    --role-arn arn:aws:iam::<span class="hljs-number">1234567843</span>:role/example \
    --input '{<span class="hljs-attr">"customer"</span>: {
        <span class="hljs-attr">"firstName"</span>: <span class="hljs-string">"rahul"</span>,
        <span class="hljs-attr">"lastName"</span>: <span class="hljs-string">"lokurte"</span>
    }
    }'
</code></pre>
<p>The output is as shown below.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"nextState"</span>: <span class="hljs-string">"Second State"</span>,
  <span class="hljs-attr">"output"</span>: <span class="hljs-string">"{\"payload\":\"rahul\"}"</span>,
  <span class="hljs-attr">"status"</span>: <span class="hljs-string">"SUCCEEDED"</span>
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>As seen in this short blog, we can use the TestState API to test the individual states in a state machine which opens up a lot of opportunities for testing the step functions thoroughly.</p>
]]></content:encoded></item><item><title><![CDATA[AWS Lambda using CDK]]></title><description><![CDATA[Infrastructure as code has become a go-to process to automatically provision and manage cloud resources. AWS provides two options for infrastructure as code.

AWS CloudFormation
AWS Cloud Development Kit

With CloudFormation, we have to write a lot o...]]></description><link>https://rahullokurte.com/aws-lambda-using-cdk</link><guid isPermaLink="true">https://rahullokurte.com/aws-lambda-using-cdk</guid><category><![CDATA[AWS]]></category><category><![CDATA[aws-cdk]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Thu, 08 Jun 2023 18:15:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1686246025208/11381132-93bf-4fe8-b0e5-20db0069dbba.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Infrastructure as code has become a go-to process to automatically provision and manage cloud resources. AWS provides two options for infrastructure as code.</p>
<ol>
<li>AWS CloudFormation</li>
<li>AWS Cloud Development Kit</li>
</ol>
<p>With CloudFormation, we have to write a lot of YAML templates or JSON files. As AWS adds more services, we have to add more files to CloudFormation. It becomes difficult to work with lots of files. YAML/JSON is based on data serialization and not an actual programming language. The AWS CDK will overcome the limitations of cloud formation by enabling the reuse of code and proper testing.</p>
<p>AWS CDK is a framework that allows developers to use familiar programming languages to define AWS cloud infrastructure and provision it. CDK provides the <strong><em>Constructs</em></strong> cloud component that cover many of the AWS services and features. It helps us to define our application infrastructure at high level.</p>
<p>we will create a Lambda Function and the infrastructure around lambda function using AWS CDK.</p>
<p>Create a new directory on your system.</p>
<pre><code class="lang-sh">mkdir cdk-greetapp &amp;&amp; <span class="hljs-built_in">cd</span> cdk-greetapp
</code></pre>
<p>We will use cdk init to create a new Javascript CDK project:</p>
<pre><code class="lang-sh">cdk init --language javascript
</code></pre>
<p>The cdk init command creates a number of files and folders inside the <strong><em>cdk-greetapp</em></strong> directory to help us organize the source code for your AWS CDK app.</p>
<p>We can list the stacks in our app by running the below command. It will show CdkGreetappStack.</p>
<pre><code class="lang-sh">_$ cdk ls
CdkGreetappStack
</code></pre>
<p>Let us install AWS lambda construct library.</p>
<pre><code class="lang-sh">npm install @aws-cdk/aws-lambda
</code></pre>
<p>Edit the file ***lib/cdk-greetapp-stack.js to create an AWS lambda resource as shown below.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> cdk = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@aws-cdk/core"</span>);
<span class="hljs-keyword">const</span> lambda = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@aws-cdk/aws-lambda"</span>);

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CdkGreetappStack</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">cdk</span>.<span class="hljs-title">Stack</span> </span>{
  <span class="hljs-comment">/**
   *
   * <span class="hljs-doctag">@param <span class="hljs-type">{cdk.Construct}</span> <span class="hljs-variable">scope</span></span>
   * <span class="hljs-doctag">@param <span class="hljs-type">{string}</span> <span class="hljs-variable">id</span></span>
   * <span class="hljs-doctag">@param <span class="hljs-type">{cdk.StackProps=}</span> <span class="hljs-variable">props</span></span>
   */</span>
  <span class="hljs-keyword">constructor</span>(scope, id, props) {
    <span class="hljs-built_in">super</span>(scope, id, props);
    <span class="hljs-comment">// defines an AWS Lambda resource</span>
    <span class="hljs-keyword">const</span> greet = <span class="hljs-keyword">new</span> lambda.Function(<span class="hljs-built_in">this</span>, <span class="hljs-string">"GreetHandler"</span>, {
      <span class="hljs-attr">runtime</span>: lambda.Runtime.NODEJS_14_X,
      <span class="hljs-attr">code</span>: lambda.Code.fromAsset(<span class="hljs-string">"lambda"</span>),
      <span class="hljs-attr">handler</span>: <span class="hljs-string">"greet.handler"</span>,
    });
  }
}

<span class="hljs-built_in">module</span>.exports = { CdkGreetappStack };
</code></pre>
<ul>
<li>Lambda Function uses NodeJS 14.x runtime</li>
<li>The handler code is loaded from the directory named <strong><em>lambda</em></strong> where we will add the lambda code.</li>
<li>The name of the handler function is greet.handler where <strong><em>greet</em></strong> is the name of file and <strong><em>handler</em></strong> is exported function name.</li>
</ul>
<p>Lets create a directory name <strong><em>lambda</em></strong> in root folder and add a file <strong><em>greet.js</em></strong>.</p>
<pre><code class="lang-sh">mkdir lambda
<span class="hljs-built_in">cd</span> lambda
touch greet.js
</code></pre>
<p>Add the lambda code to <strong><em>greet.js</em></strong></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"request:"</span>, <span class="hljs-built_in">JSON</span>.stringify(event, <span class="hljs-literal">undefined</span>, <span class="hljs-number">2</span>));
  <span class="hljs-keyword">let</span> response = {
    <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
    <span class="hljs-attr">body</span>: <span class="hljs-string">`Hello <span class="hljs-subst">${event.path}</span>. Welcome to CDK!`</span>,
  };
  <span class="hljs-keyword">return</span> response;
};
</code></pre>
<p>Before deploying the AWS resource, we can take a look on what resources will be getting created by using below command.</p>
<pre><code class="lang-sh">cdk diff
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1625853620573/F4g4htNnw.png" alt="resource-created.png" /></p>
<p><strong>NOTE</strong>: If we have multiple profiles set in our system, we need to tell cdk to look into particular profile. This can be done, by adding below key-value in <strong><em>cdk.json</em></strong> which was generated when we created a CDK project.</p>
<pre><code class="lang-json"><span class="hljs-string">"profile"</span>: <span class="hljs-string">"&lt;YOUR_PROFILE_NAME&gt;"</span>
</code></pre>
<p>Now, once we are ok with the resources which will be created, we can deploy it using below command</p>
<pre><code class="lang-sh">cdk deploy
</code></pre>
<p>Let us open the AWS Lambda console</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1625853648087/yFU_b_ehI.png" alt="greet-function.png" /></p>
<p>Select Amazon API Gateway AWS Proxy from the Event template list.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1625853659915/3cmTFb3MU.png" alt="sample-test.png" /></p>
<p>Click on Test, we can see that, we get the proper response as shown below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1625853673304/EbL_mKsEE.png" alt="test-pass-lambda.png" /></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>we saw how to create a lambda function and also lambda resource by using AWS Cloud Development Kit. We also saw various commands related to CDK for initiating projects, deploying the resources to AWS. The code repository link is  <a target="_blank" href="https://github.com/rahulmlokurte/aws-usage/tree/main/aws-cdk/cdk-greetapp">here</a></p>
<p>If you want to do hands-on, I have created a Youtube Video. You can check out <a target="_blank" href="https://www.youtube.com/watch?v=kALoDWqFIX4&amp;t=3s">here</a></p>
]]></content:encoded></item><item><title><![CDATA[Serverless Event Scheduling with AWS EventBridge and Lambda using Terraform and Localstack]]></title><description><![CDATA[Introduction
In modern cloud development, infrastructure-as-code has become a crucial practice. Tools like Terraform enable developers and operations teams to define and manage their infrastructure in a declarative manner. However, provisioning and t...]]></description><link>https://rahullokurte.com/serverless-event-scheduling-with-aws-eventbridge-and-lambda-using-terraform-and-localstack</link><guid isPermaLink="true">https://rahullokurte.com/serverless-event-scheduling-with-aws-eventbridge-and-lambda-using-terraform-and-localstack</guid><category><![CDATA[AWS]]></category><category><![CDATA[localstack]]></category><category><![CDATA[Terraform]]></category><category><![CDATA[AWS EventBridge]]></category><category><![CDATA[lambda]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Tue, 30 May 2023 12:02:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1685447943523/d16d3046-627f-412f-8b5c-5052123c1379.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction"><strong>Introduction</strong></h2>
<p>In modern cloud development, infrastructure-as-code has become a crucial practice. Tools like Terraform enable developers and operations teams to define and manage their infrastructure in a declarative manner. However, provisioning and testing infrastructure in a live AWS environment can be time-consuming and costly. That's where LocalStack comes in. LocalStack provides a local development environment that emulates various AWS services, allowing developers to create and test AWS resources locally. In this blog post, we'll explore how to leverage Terraform and LocalStack together for local development and testing of AWS infrastructure.</p>
<h2 id="heading-what-is-localstack">What is LocalStack?</h2>
<p>LocalStack is an open-source development tool that provides a local environment for emulating various AWS services. It allows developers to create and test AWS resources locally, without the need for a live AWS account or incurring any costs. With LocalStack, you can emulate services such as AWS Lambda, S3, DynamoDB, SQS, SNS, EventBridge, and many more.</p>
<h2 id="heading-getting-started-with-localstack"><strong>Getting Started with LocalStack</strong></h2>
<p>To get started with LocalStack, follow these steps to install and set it up on your local development machine:</p>
<ol>
<li><p><strong>Prerequisites</strong>: Ensure that you have Docker installed on your machine. LocalStack runs inside a Docker container, so having Docker installed is a requirement.</p>
</li>
<li><p><strong>Install LocalStack</strong>: Open your terminal or command prompt and run the following command. We are installing Localstack CLI, but there are multiple ways to install <code>localstack</code>. Check the official guide <a target="_blank" href="https://docs.localstack.cloud/getting-started/installation/#localstack-cli">https://docs.localstack.cloud/getting-started/installation/#localstack-cli</a></p>
<pre><code class="lang-bash"> python -m pip install localstack
</code></pre>
</li>
<li><p><strong>Start LocalStack:</strong> To start LocalStack with the default configuration, run the following command in your terminal:</p>
<pre><code class="lang-bash"> localstack start
</code></pre>
</li>
</ol>
<h2 id="heading-getting-started-with-terraform"><strong>Getting Started with Terraform</strong></h2>
<p>Terraform is a powerful infrastructure-as-code tool that allows you to define and manage your infrastructure using declarative configuration files. In this section, we'll guide you through the process of installing Terraform and writing Terraform configuration files to manage your AWS infrastructure.</p>
<h3 id="heading-installing-terraform"><strong>Installing Terraform</strong></h3>
<p>To get started with Terraform, follow these steps to install it on your local development machine:</p>
<ol>
<li><p><strong>Download and Install Terraform</strong>: Visit the official Terraform website at <a target="_blank" href="https://developer.hashicorp.com/terraform/downloads?product_intent=terraform"><strong>terraform.io</strong></a> and download the appropriate Terraform binary for your operating system.</p>
</li>
<li><p><strong>Verify Installation</strong>: Open a new terminal or command prompt window and run the following command to verify that Terraform is installed correctly:</p>
<pre><code class="lang-bash"> terraform --version
</code></pre>
<p> You should see the Terraform version displayed in the output, indicating a successful installation.</p>
</li>
</ol>
<h3 id="heading-tflocal-terraform-with-localstack"><strong>tflocal: Terraform with LocalStack</strong></h3>
<p><code>tflocal</code> - a small wrapper script to run <a target="_blank" href="https://terraform.io/">Terraform</a> against <a target="_blank" href="https://localstack.cloud/">LocalStack</a>. The <code>tflocal</code> command line interface can be installed using <code>pip</code> as shown below.</p>
<pre><code class="lang-bash">pip install terraform-local
</code></pre>
<h2 id="heading-application-development-with-terraform-and-localstack"><strong>Application Development with Terraform and LocalStack</strong></h2>
<p>In this section, we'll explore how to develop an application locally using Terraform and LocalStack. We'll deploy a sample AWS Lambda function that performs an addition operation on two numbers. By leveraging Terraform and LocalStack together, we can develop and test our application locally without incurring any AWS costs.</p>
<p>Our sample application consists of the following components:</p>
<ol>
<li><p><strong>Lambda Function</strong>: The core logic of the application resides in an AWS Lambda function. This function takes two input numbers from the Eventbridge event, adds them together, and returns the result.</p>
</li>
<li><p><strong>EventBridge Rule</strong>: We'll use AWS EventBridge to schedule the execution of our Lambda function. We'll configure a scheduled rule in EventBridge to trigger the Lambda function every minute.</p>
</li>
</ol>
<h3 id="heading-terraform-configuration"><strong>Terraform Configuration</strong></h3>
<ol>
<li><strong>Create a new directory</strong>: Create a new directory on your local machine to store the Terraform configuration files for the sample application. Navigate to this directory in your terminal.</li>
</ol>
<pre><code class="lang-bash">mkdir scheduler-localstack &amp;&amp; <span class="hljs-built_in">cd</span> scheduler-localstack
</code></pre>
<ol>
<li><strong>Create the Terraform configuration files</strong>: Create a new file named <code>main.tf</code> and open it in a text editor. Add the following Terraform configuration to the file:</li>
</ol>
<pre><code class="lang-bash">provider <span class="hljs-string">"aws"</span> {
  region = <span class="hljs-string">"us-east-1"</span> <span class="hljs-comment"># Replace with your desired region</span>
}

resource <span class="hljs-string">"aws_cloudwatch_event_rule"</span> <span class="hljs-string">"scheduled_event"</span> {
  name                = <span class="hljs-string">"my_scheduled_event"</span>
  description         = <span class="hljs-string">"My scheduled event rule"</span>
  schedule_expression = <span class="hljs-string">"rate(1 minutes)"</span>
  is_enabled = <span class="hljs-literal">true</span>
}

resource <span class="hljs-string">"aws_cloudwatch_event_target"</span> <span class="hljs-string">"lambda_target"</span> {
  rule      = aws_cloudwatch_event_rule.scheduled_event.name
  target_id = <span class="hljs-string">"my_lambda_function"</span>
  arn       = aws_lambda_function.my_lambda_function.arn
  input     = &lt;&lt;JSON
            {
  <span class="hljs-string">"number1"</span>: 4,
  <span class="hljs-string">"number2"</span>: 5
}
JSON
}

data <span class="hljs-string">"archive_file"</span> <span class="hljs-string">"my_lambda_function_archive"</span> {
  <span class="hljs-built_in">type</span>        = <span class="hljs-string">"zip"</span>
  source_file = <span class="hljs-string">"<span class="hljs-variable">${path.module}</span>/code/lambda_function.py"</span>
  output_path = <span class="hljs-string">"my_lambda_function.zip"</span>
}


resource <span class="hljs-string">"aws_lambda_function"</span> <span class="hljs-string">"my_lambda_function"</span> {
  function_name    = <span class="hljs-string">"my_lambda_function"</span>
  role             = aws_iam_role.lambda_role.arn
  handler          = <span class="hljs-string">"lambda_function.lambda_handler"</span>
  runtime          = <span class="hljs-string">"python3.8"</span>
  filename         = <span class="hljs-string">"my_lambda_function.zip"</span>
  source_code_hash = data.archive_file.my_lambda_function_archive.output_base64sha256
}

resource <span class="hljs-string">"aws_lambda_permission"</span> <span class="hljs-string">"allow_cloudwatch"</span> {
  statement_id  = <span class="hljs-string">"AllowCloudWatch"</span>
  action        = <span class="hljs-string">"lambda:InvokeFunction"</span>
  function_name = aws_lambda_function.my_lambda_function.arn
  principal     = <span class="hljs-string">"events.amazonaws.com"</span>
  source_arn    = aws_cloudwatch_event_rule.scheduled_event.arn
}

resource <span class="hljs-string">"aws_iam_role"</span> <span class="hljs-string">"lambda_role"</span> {
  name = <span class="hljs-string">"my_lambda_role"</span>

  assume_role_policy = &lt;&lt;EOF
{
  <span class="hljs-string">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
  <span class="hljs-string">"Statement"</span>: [
    {
      <span class="hljs-string">"Sid"</span>: <span class="hljs-string">""</span>,
      <span class="hljs-string">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
      <span class="hljs-string">"Principal"</span>: {
        <span class="hljs-string">"Service"</span>: <span class="hljs-string">"lambda.amazonaws.com"</span>
      },
      <span class="hljs-string">"Action"</span>: <span class="hljs-string">"sts:AssumeRole"</span>
    }
  ]
}
EOF

  inline_policy {
    name = <span class="hljs-string">"cloudwatch_logs_policy"</span>

    policy = &lt;&lt;EOF
{
  <span class="hljs-string">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
  <span class="hljs-string">"Statement"</span>: [
    {
      <span class="hljs-string">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
      <span class="hljs-string">"Action"</span>: [
        <span class="hljs-string">"logs:CreateLogGroup"</span>,
        <span class="hljs-string">"logs:CreateLogStream"</span>,
        <span class="hljs-string">"logs:PutLogEvents"</span>
      ],
      <span class="hljs-string">"Resource"</span>: <span class="hljs-string">"*"</span>
    }
  ]
}
EOF
  }
}

resource <span class="hljs-string">"aws_cloudwatch_log_group"</span> <span class="hljs-string">"lambda_log_group"</span> {
  name = <span class="hljs-string">"/aws/lambda/<span class="hljs-variable">${aws_lambda_function.my_lambda_function.function_name}</span>"</span>
}

output <span class="hljs-string">"log_group_name"</span> {
  value = aws_cloudwatch_log_group.lambda_log_group.name
}
</code></pre>
<p>Below is a brief overview of the configuration:</p>
<ol>
<li><p><strong>Provider Configuration</strong>: Defines the provider configuration for AWS. The <code>aws</code> the provider is responsible for authenticating and interacting with AWS services. In this case, we specify the desired region as <code>us-east-1</code>.</p>
</li>
<li><p><strong>CloudWatch Event Rule</strong>: Defines an AWS CloudWatch Event Rule. The rule is used to schedule the execution of an action, in this case, our Lambda function. We provide a name and description for the rule and set the <code>schedule_expression</code> to <code>"rate(1 minutes)"</code>, which triggers the rule every minute. The <code>is_enabled</code> line will enable the rule immediately upon deployment.</p>
</li>
<li><p><strong>CloudWatch Event Target</strong>: Defines the target for the CloudWatch Event Rule. The <code>rule</code> attribute references the name of the CloudWatch Event Rule defined earlier. The <code>target_id</code> is set to <code>"my_lambda_function"</code> that identify the Lambda function as the target. The <code>arn</code> attribute points to the ARN (Amazon Resource Name) of the Lambda function. The <code>input</code> field specifies the input data to be passed to the Lambda function. In this case, it's a JSON object with <code>number1</code> and <code>number2</code> as inputs.</p>
</li>
<li><p><strong>Lambda Function Code Archive</strong>: The <code>archive_file</code> data source creates a zip archive of the Lambda function code. The <code>source_file</code> attribute specifies the path to the <code>lambda_function.py</code> file, which contains the code for the Lambda function. The resulting zip archive is named <code>my_lambda_function.zip</code> and will be used in the subsequent resource block.</p>
</li>
<li><p><strong>Lambda Function</strong>: Defines the AWS Lambda function. The <code>function_name</code> attribute specifies the name of the Lambda function. The <code>role</code> attribute references the ARN of the IAM role associated with the Lambda function. The <code>handler</code> attribute specifies the entry point for the Lambda function code. The <code>runtime</code> attribute specifies the runtime environment for the Lambda function, in this case, Python 3.8. The <code>filename</code> attribute specifies the name of the zip file containing the Lambda function code. The <code>source_code_hash</code> attribute references the output of the <code>archive_file</code> data source, ensuring that the Lambda function is updated when the code changes.</p>
</li>
<li><p><strong>Lambda Function Permission</strong>: Grants permission to CloudWatch Events to invoke the Lambda function. The <code>statement_id</code> attribute is a unique identifier for the permission statement. The <code>action</code> attribute specifies the action that is allowed, which is <code>lambda:InvokeFunction</code> in this case. The <code>function_name</code> attribute references the ARN of the Lambda function. The <code>principal</code> attribute specifies the entity that is granted permission, which is <code>events.amazonaws.com</code> for CloudWatch Events. The <code>source_arn</code> attribute references the ARN of the CloudWatch Event Rule.</p>
</li>
<li><p><strong>IAM Role for Lambda</strong>: Defines an IAM role for the Lambda function. The <code>name</code> attribute specifies the name of the role. The <code>assume_role_policy</code> attribute defines the trust relationship policy document, allowing the Lambda service to assume this role. The <code>inline_policy</code> block defines an inline IAM policy attached to the role. In this case, it grants permissions for CloudWatch Logs to create log groups, create log streams, and put log events.</p>
</li>
<li><p><strong>AWS CloudWatch Log Group:</strong> Creates an AWS CloudWatch Log Group for the Lambda function. The name attribute is set using an interpolated string, <code>"/aws/lambda/${aws_lambda_function.my_lambda_function.function_name}"</code> which represents the desired naming convention for the log group. It includes the function name of the AWS Lambda function.</p>
</li>
<li><p><strong>Terraform Output:</strong> output block defines an output variable named <code>log_group_name</code> that displays the name of the AWS CloudWatch Log Group created for the Lambda function. The value is set as <code>aws_cloudwatch_log_group.lambda_log_group.name</code>, which retrieves the name attribute of the <code>aws_cloudwatch_log_group</code> resource.</p>
</li>
</ol>
<h3 id="heading-lambda-function"><strong>Lambda Function:</strong></h3>
<p>The core functionality of our application is implemented using an AWS Lambda function. The lambda function is responsible for processing the input event, performing the desired computation, and returning the result. The lambda function, located at the root of our project under the <code>code</code> folder, contains the following code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> boto3
<span class="hljs-keyword">import</span> json

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lambda_handler</span>(<span class="hljs-params">event, context</span>):</span>
    print(<span class="hljs-string">"Lambda Function Name: "</span>, context.function_name)
    print(<span class="hljs-string">"Event is: "</span>, event)
    number1 = event[<span class="hljs-string">"number1"</span>]
    number2 = event[<span class="hljs-string">"number2"</span>]

    result = number1 + number2

    print(<span class="hljs-string">"Sum:"</span>, result)

    <span class="hljs-keyword">return</span> {<span class="hljs-string">"statusCode"</span>: <span class="hljs-number">200</span>, <span class="hljs-string">"body"</span>: json.dumps({<span class="hljs-string">"result"</span>: result})}
</code></pre>
<p>The lambda function is written in Python and consists of a single handler function named <code>lambda_handler</code>. This function is automatically invoked by AWS Lambda when an event triggers its execution.</p>
<p>Upon invocation, the function receives two parameters: <code>event</code> and <code>context</code>. The <code>event</code> parameter contains the input data passed to the lambda function, while the <code>context</code> parameter provides information about the execution context and runtime environment.</p>
<p>In our lambda function, we first print the name of the lambda function using <code>context.function_name</code> and the input event using <code>event</code>. This allows us to verify the function execution logs and ensure the correct inputs are received.</p>
<p>Next, we extract the values of <code>number1</code> and <code>number2</code> from the event payload. These values represent the numbers to be added together.</p>
<p>The lambda function performs the addition operation and stores the result in the <code>result</code> variable.</p>
<p>We then print the calculated sum using <code>print("Sum:", result)</code>. This line helps us verify that the computation was successful and the expected result is obtained.</p>
<p>Finally, we construct a JSON response with a status code of 200 and a body containing the calculated result.</p>
<p>The lambda function encapsulates the business logic of our application and is executed by LocalStack in response to the scheduled event defined earlier in the Terraform configuration.</p>
<h2 id="heading-deploying-the-infrastructure-with-terraform-and-localstack"><strong>Deploying the Infrastructure with Terraform and LocalStack</strong></h2>
<p>Now that we have our Terraform configuration file ready, let's deploy the infrastructure using Terraform and LocalStack. Follow the steps below:</p>
<ol>
<li><p>Open your command line interface (CLI) or terminal.</p>
</li>
<li><p>Navigate to the directory where you have saved the Terraform configuration file.</p>
</li>
<li><p>Initialize the Terraform working directory by running the following command:</p>
<pre><code class="lang-bash"> tflocal init
</code></pre>
<p> This command initializes the working directory and downloads the necessary provider plugins defined in your Terraform configuration.</p>
</li>
<li><p>Once the initialization is complete, you can run the following command to deploy the infrastructure:</p>
<pre><code class="lang-bash"> tflocal apply
</code></pre>
<p> Terraform will read the configuration file, create the necessary resources, and prompt you to confirm the execution. Type <code>yes</code> and press Enter to proceed.</p>
</li>
<li><p>Terraform will start provisioning the infrastructure components specified in the configuration file. It will create the CloudWatch Event Rule, Lambda function, IAM role, and associated permissions.</p>
</li>
<li><p>Once the command is executed, the <code>log_group_name</code> output variable will be displayed, which contains the name of the AWS CloudWatch Log Group associated with the Lambda function. This output value can be noted down and used to verify the application's behavior and inspect the logs generated by the Lambda function in the next section.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1685366562019/2f6c6657-0430-40f3-9a81-5496646d0b51.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-verifying-the-application"><strong>Verifying the Application</strong></h2>
<p>To ensure a seamless verification process of the application, let's begin by installing the <code>awslocal</code> command-line tool. <code>awslocal</code> is a utility that is a thin wrapper around the <code>aws</code> command line interface for use with <a target="_blank" href="https://github.com/localstack/localstack">LocalStack</a>.</p>
<p>To install <code>awslocal</code>, follow the steps below:</p>
<p>Open your command-line interface and execute the following command to install <code>awslocal</code> using pip:</p>
<pre><code class="lang-bash">pip install awscli-local
</code></pre>
<p>This command will download and install the <code>awslocal</code> package. With <code>awslocal</code> installed, we are now ready to proceed with the verification of our application.</p>
<p>Now that we have deployed our infrastructure using Terraform and LocalStack, let's verify the application by checking the result printed by the Lambda function.</p>
<ul>
<li><p>Open your command line interface (CLI) or terminal.</p>
</li>
<li><p>Make sure that LocalStack is still running and that the necessary AWS services are emulated.</p>
</li>
<li><p>Use the AWS CLI to check the logs generated by the Lambda function. Run the following command:</p>
<pre><code class="lang-bash">  awslocal --endpoint-url=http://localhost:4566 logs describe-log-groups
</code></pre>
<p>  We get the output as shown below</p>
<pre><code class="lang-bash">  {
      <span class="hljs-string">"logGroups"</span>: [
          {
              <span class="hljs-string">"logGroupName"</span>: <span class="hljs-string">"/aws/lambda/my_lambda_function"</span>,
              <span class="hljs-string">"creationTime"</span>: 1685365365216,
              <span class="hljs-string">"metricFilterCount"</span>: 0,
              <span class="hljs-string">"arn"</span>: <span class="hljs-string">"arn:aws:logs:us-east-1:000000000000:log-group:/aws/lambda/my_lambda_function:*"</span>,
              <span class="hljs-string">"storedBytes"</span>: 7639
          }
      ]
  }
</code></pre>
<p>  This command will list all the log groups available in LocalStack.</p>
</li>
<li><p>Identify the log group associated with the Lambda function. It should have a name similar to <code>my_lambda_function</code>. Note the name of the log group for the next step.</p>
</li>
<li><p>Fetch the logs for the Lambda function by running the following command:</p>
<pre><code class="lang-bash">  awslocal --endpoint-url=http://localhost:4566 logs filter-log-events --log-group-name /aws/lambda/my_lambda_function
</code></pre>
</li>
</ul>
<p>The command will display the log events generated by the Lambda function. Look for the log entry that includes the result of the addition.</p>
<pre><code class="lang-bash"> {
            <span class="hljs-string">"logStreamName"</span>: <span class="hljs-string">"2023/05/29/[<span class="hljs-variable">$LATEST</span>]0a013874ae1b00b65f24f5930a8da077"</span>,
            <span class="hljs-string">"timestamp"</span>: 1685366855633,
            <span class="hljs-string">"message"</span>: <span class="hljs-string">"START RequestId: 8acdc34d-f5cb-4f30-97d2-97522f0d22dc Version: <span class="hljs-variable">$LATEST</span>"</span>,
            <span class="hljs-string">"ingestionTime"</span>: 1685366855727,
            <span class="hljs-string">"eventId"</span>: <span class="hljs-string">"144"</span>
        },
        {
            <span class="hljs-string">"logStreamName"</span>: <span class="hljs-string">"2023/05/29/[<span class="hljs-variable">$LATEST</span>]0a013874ae1b00b65f24f5930a8da077"</span>,
            <span class="hljs-string">"timestamp"</span>: 1685366855644,
            <span class="hljs-string">"message"</span>: <span class="hljs-string">"Lambda Function Name:  my_lambda_function"</span>,
            <span class="hljs-string">"ingestionTime"</span>: 1685366855727,
            <span class="hljs-string">"eventId"</span>: <span class="hljs-string">"145"</span>
        },
        {
            <span class="hljs-string">"logStreamName"</span>: <span class="hljs-string">"2023/05/29/[<span class="hljs-variable">$LATEST</span>]0a013874ae1b00b65f24f5930a8da077"</span>,
            <span class="hljs-string">"timestamp"</span>: 1685366855656,
            <span class="hljs-string">"message"</span>: <span class="hljs-string">"Event is:  {'number1': 4, 'number2': 5}"</span>,
            <span class="hljs-string">"ingestionTime"</span>: 1685366855727,
            <span class="hljs-string">"eventId"</span>: <span class="hljs-string">"146"</span>
        },
        {
            <span class="hljs-string">"logStreamName"</span>: <span class="hljs-string">"2023/05/29/[<span class="hljs-variable">$LATEST</span>]0a013874ae1b00b65f24f5930a8da077"</span>,
            <span class="hljs-string">"timestamp"</span>: 1685366855668,
            <span class="hljs-string">"message"</span>: <span class="hljs-string">"Sum: 9"</span>,
            <span class="hljs-string">"ingestionTime"</span>: 1685366855727,
            <span class="hljs-string">"eventId"</span>: <span class="hljs-string">"147"</span>
        }
</code></pre>
<p>The log events show the following details:</p>
<ul>
<li><p>The start of the Lambda function execution with a unique RequestId and the corresponding version.</p>
</li>
<li><p>Confirmation of the Lambda function name, which in this case is <code>my_lambda_function</code>.</p>
</li>
<li><p>The event data is passed to the Lambda function, which is a dictionary containing the numbers <code>4</code> and <code>5</code>.</p>
</li>
<li><p>The result of the computation, which is the sum of the numbers <code>4</code> and <code>5</code>, equaling <code>9</code>.</p>
</li>
</ul>
<p>These log events demonstrate that the Lambda function was executed successfully and performed the expected computation. By inspecting the log events, we can confirm that the application is functioning correctly and producing the desired outcome.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In conclusion, LocalStack combined with Terraform provides a powerful solution for developing and testing AWS-based applications locally. With LocalStack, developers can emulate various AWS services, allowing them to build and test their infrastructure and applications without incurring any costs or dependencies on the actual AWS cloud environment. Terraform complements LocalStack by enabling infrastructure-as-code, making it easy to manage and provision AWS resources.</p>
<p>Throughout this blog, we explored the installation and setup of LocalStack, starting and configuring it to emulate AWS services. We also delved into developing infrastructure using Terraform, guiding readers through the process of installation, writing Terraform configuration files, and deploying resources. By combining Terraform with LocalStack, developers can create, manage, and provision AWS resources in a consistent and repeatable manner.</p>
<p>Furthermore, we examined a sample application deployment using Terraform and LocalStack, showcasing how to deploy a Lambda function triggered by a scheduled event. This practical example highlighted the seamless integration between LocalStack, Terraform, and the AWS services they emulate, providing a comprehensive local development and testing environment.</p>
<p>Finally, we verified the application's functionality by inspecting the log events generated by the deployed Lambda function. By filtering the log events, we observed the successful execution of the Lambda function, along with the input event data and the expected result.</p>
<p>In summary, LocalStack and Terraform offer a compelling combination for local AWS development and testing, enabling developers to build, deploy, and validate AWS infrastructure and applications efficiently. Incorporating these tools into your development workflow can significantly enhance productivity and accelerate the delivery of reliable and robust AWS solutions.<br />At the end of this blog, if you're interested in exploring a practical implementation of the concepts discussed, you can refer to the <a target="_blank" href="https://github.com/rahulmlokurte/scheduler-localstack"><strong>scheduler-localstack</strong></a> GitHub project. This project provides a code reference and example infrastructure setup using Terraform and LocalStack for developing and testing AWS Lambda functions locally.</p>
]]></content:encoded></item><item><title><![CDATA[A Guide to Using AWS Intrinsic Functions in Your SAM Templates]]></title><description><![CDATA[AWS Serverless Application Model (SAM) is a framework for building serverless applications on AWS. It provides a simplified way to define and deploy AWS Lambda functions, Amazon API Gateway APIs, and other serverless resources, using AWS CloudFormati...]]></description><link>https://rahullokurte.com/a-guide-to-using-aws-intrinsic-functions-in-your-sam-templates</link><guid isPermaLink="true">https://rahullokurte.com/a-guide-to-using-aws-intrinsic-functions-in-your-sam-templates</guid><category><![CDATA[AWS]]></category><category><![CDATA[aws sam]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Tue, 23 May 2023 14:28:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1684852048123/b1492252-e0f3-4636-8986-e3333ba019fb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>AWS Serverless Application Model (SAM) is a framework for building serverless applications on AWS. It provides a simplified way to define and deploy AWS Lambda functions, Amazon API Gateway APIs, and other serverless resources, using AWS CloudFormation. One of the critical components of AWS SAM is the AWS SAM template, which describes your serverless application and its resources.</p>
<p>AWS intrinsic functions in AWS SAM templates are a powerful feature that allows you to dynamically generate values at runtime based on the context of your application. These functions are provided by AWS CloudFormation and can be used in your AWS SAM templates as well.</p>
<p>In this blog post, we will explore the basics of AWS intrinsic functions in AWS SAM templates and how you can use them to build dynamic and flexible serverless applications on AWS.</p>
<h3 id="heading-what-are-aws-intrinsic-functions-in-aws-sam-templates">What are AWS Intrinsic Functions in AWS SAM templates?</h3>
<p>AWS Intrinsic Functions in AWS SAM templates are a set of special functions that allow you to generate values dynamically at runtime based on the context of your serverless application. These functions are provided by AWS CloudFormation, which is the underlying service on which AWS SAM templates are built. It can be used to simplify the creation and management of serverless resources in your AWS SAM templates.</p>
<h3 id="heading-why-use-aws-intrinsic-functions-in-aws-sam-templates">Why use AWS Intrinsic Functions in AWS SAM templates?</h3>
<p>AWS Intrinsic Functions in AWS SAM templates allow you to create dynamic and flexible serverless applications on AWS. They allow you to reference other resources, perform string manipulation, and even generate random numbers, among other things. By leveraging these functions, you can reduce duplication, increase reusability, and simplify the management of your serverless resources.</p>
<p>Let's discuss some of the commonly used AWS intrinsic functions in AWS SAM templates.</p>
<h3 id="heading-ref">Ref</h3>
<p><code>Ref</code> is an intrinsic function in AWS SAM templates that are used to reference other resources in your template. It returns the value of the named resource or parameter.</p>
<p>When you use <code>Ref</code> in your AWS SAM template, AWS CloudFormation automatically resolves the reference to the value of the named resource or parameter at deployment time. This means that you can use <code>Ref</code> to get the ARN, ID, or other attributes of a resource in your template.</p>
<p>Here's an example of how you can use <code>Ref</code> in your AWS SAM template:</p>
<pre><code class="lang-javascript">Resources:
  MyBucket:
    Type: AWS::S3::Bucket
    <span class="hljs-attr">Properties</span>:
      BucketName: my-bucket
  <span class="hljs-attr">MyLambdaFunction</span>:
    Type: AWS::Serverless::<span class="hljs-built_in">Function</span>
    <span class="hljs-attr">Properties</span>:
      CodeUri: ./my-lambda-<span class="hljs-function"><span class="hljs-keyword">function</span>
      <span class="hljs-title">Handler</span>: <span class="hljs-title">index</span>.<span class="hljs-title">handler</span>
      <span class="hljs-title">Runtime</span>: <span class="hljs-title">nodejs14</span>.<span class="hljs-title">x</span>
      <span class="hljs-title">Environment</span>:
        <span class="hljs-title">Variables</span>:
          <span class="hljs-title">BucketName</span>: !<span class="hljs-title">Ref</span> <span class="hljs-title">MyBucket</span></span>
</code></pre>
<p>In this example, we have defined an S3 bucket named "MyBucket" and a Lambda function named "MyLambdaFunction". The Lambda function has an environment variable named "BucketName" that references the S3 bucket using Ref. This allows us to dynamically set the bucket name in our Lambda function based on the name of the S3 bucket resource defined in our AWS SAM template.</p>
<h3 id="heading-fnsub">Fn::Sub</h3>
<p><code>Fn::Sub</code> is an intrinsic function in AWS SAM templates that are used to substitute variables in a string with their corresponding values at deployment time. It allows you to create more flexible and dynamic templates that can adapt to different environments and configurations. The syntax for Fn::Sub is as follows:</p>
<pre><code class="lang-javascript">!Sub string [ variablesMap ]
</code></pre>
<p>The string parameter is the string that you want to substitute variables in. The variablesMap parameter is an optional map that defines the variables and their values that you want to substitute. Here's an example of how you can use Fn::Sub in your AWS SAM template:</p>
<pre><code class="lang-javascript">Resources:
  MyBucket:
    Type: AWS::S3::Bucket
    <span class="hljs-attr">Properties</span>:
      BucketName: !Sub ${EnvironmentName}-my-bucket-${<span class="hljs-attr">AWS</span>::Region}
</code></pre>
<p>In this example, we have defined an S3 bucket named "MyBucket" with a dynamic bucket name that includes the environment name and AWS region. The environment name is a variable defined in the variables map using the <code>!Sub</code> intrinsic function. The AWS::Region variable is automatically provided by AWS CloudFormation.</p>
<h2 id="heading-condition-functions">Condition functions</h2>
<p>In some cases, you may want to conditionally create resources based on certain criteria. AWS Intrinsic Functions such as Fn::If and Fn::Equals allow you to define conditions and control the creation of resources accordingly. For example, you can conditionally create an S3 bucket only if a specific parameter is set to true, or you can enable different features based on the environment or deployment stage.</p>
<pre><code>Resources:
  MyBucket:
    Type: AWS::S3::Bucket
    <span class="hljs-attr">Condition</span>: CreateBucketCondition
    <span class="hljs-attr">Properties</span>:
      ...
Conditions:
  CreateBucketCondition: !Equals [!Ref CreateBucket, <span class="hljs-string">"true"</span>]
</code></pre><p>In this example, the Fn::Equals function is used to conditionally create an S3 bucket based on the value of the CreateBucket parameter.</p>
<h3 id="heading-input-and-output-data-manipulation">Input and output data manipulation:</h3>
<p>SAM templates often require exchanging data between resources or accessing outputs from other AWS CloudFormation stacks. AWS Intrinsic Functions provide powerful capabilities for manipulating input and output data within your templates.</p>
<h4 id="heading-cross-stack-references">Cross-stack references:</h4>
<p>When you have multiple AWS CloudFormation stacks that need to communicate with each other, you can use the Fn::ImportValue function to reference outputs from other stacks. This function allows you to retrieve values exported from another stack and use them as input for your resources. For example:</p>
<pre><code>Resources:
  MyFunction:
    Type: AWS::Serverless::<span class="hljs-built_in">Function</span>
    <span class="hljs-attr">Properties</span>:
      Environment:
        Variables:
          API_URL: !ImportValue MyApiStack-ApiUrl-${Stage}
          ...
</code></pre><p>In this example, the Fn::ImportValue function is used to retrieve the API URL from the MyApiStack stack's output and assign it to the API_URL environment variable. This enables your serverless function to interact with the API exposed by the MyApiStack.</p>
<h4 id="heading-selecting-specific-elements-from-arrays">Selecting specific elements from arrays:</h4>
<p>In some cases, you may have an array of values and only need to extract specific elements. The Fn::Select function allows you to retrieve a single element from a list based on its index. For example:</p>
<pre><code>Resources:
  MyFunction:
    Type: AWS::Serverless::<span class="hljs-built_in">Function</span>
    <span class="hljs-attr">Properties</span>:
      Environment:
        Variables:
          Region: !Select [<span class="hljs-number">0</span>, !Split [<span class="hljs-string">"-"</span>, !Ref AWS::Region]]
          ...
</code></pre><p>In this example, the Fn::Select function is used to split the AWS::Region value using the dash ("-") as the separator and then select the first element from the resulting array. This can be useful when you need to extract a specific portion of a value for configuration purposes.</p>
<h4 id="heading-getting-attribute-values-from-resources">Getting attribute values from resources:</h4>
<p>SAM templates often require accessing specific attribute values from resources. The Fn::GetAtt function allows you to retrieve specific attributes of a resource. For example:</p>
<pre><code>Resources:
  MyBucket:
    Type: AWS::S3::Bucket
    <span class="hljs-attr">Properties</span>:
      ...
  MyBucketName:
    Type: AWS::SSM::Parameter
    <span class="hljs-attr">Properties</span>:
      Type: <span class="hljs-built_in">String</span>
      <span class="hljs-attr">Value</span>: !GetAtt MyBucket.Name
      ...
</code></pre><p>n this example, the Fn::GetAtt function is used to retrieve the Name attribute of the MyBucket resource and assign it as the value for an AWS Systems Manager Parameter (AWS::SSM::Parameter). This enables you to store and reference the bucket name in a parameter that can be used by other resources.</p>
<p>By leveraging these AWS Intrinsic Functions for input and output data manipulation, you can create more dynamic and interconnected serverless applications within your SAM templates. These functions enable you to exchange data between resources, retrieve specific attribute values, and perform transformations to adapt the data for your application's needs.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>AWS Intrinsic Functions provide powerful capabilities for manipulating input and output data within SAM templates. Whether you need to reference outputs from other stacks, select specific elements from arrays, or retrieve attribute values from resources, these functions allow you to effectively exchange and transform data within your serverless applications. By incorporating input and output data manipulation using AWS Intrinsic Functions, you can enhance the flexibility and interoperability of your SAM templates, leading to more robust and scalable serverless applications on AWS.</p>
]]></content:encoded></item><item><title><![CDATA[Small snippets of AWS CLI commands for the AWS Lambda function]]></title><description><![CDATA[AWS Command Line Interface (CLI) is a powerful tool that allows developers and system administrators to manage their AWS resources from the command line.
AWS Lambda is a powerful serverless computing service that lets you run code without provisionin...]]></description><link>https://rahullokurte.com/small-snippets-of-aws-cli-commands-for-the-aws-lambda-function</link><guid isPermaLink="true">https://rahullokurte.com/small-snippets-of-aws-cli-commands-for-the-aws-lambda-function</guid><category><![CDATA[aws cli]]></category><category><![CDATA[aws lambda]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Sun, 16 Apr 2023 19:15:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1681672450247/a9e60f6c-1d38-4fd6-a7bf-e0b1f526d7df.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>AWS Command Line Interface (CLI) is a powerful tool that allows developers and system administrators to manage their AWS resources from the command line.</p>
<p>AWS Lambda is a powerful serverless computing service that lets you run code without provisioning or managing servers. In this blog post, we will explore some of the most useful AWS CLI commands for the AWS Lambda function.</p>
<p>Before running the AWS CLI commands, we need to Install the AWS CLI on your computer. You can download the CLI from <a target="_blank" href="https://aws.amazon.com/cli/">AWS CLI</a>. Open a command prompt or terminal window and run the following command.</p>
<pre><code class="lang-plaintext">aws configure
</code></pre>
<p>When prompted, enter the access key ID and secret access key. Enter the default region you want to use for the CLI. Optionally, you can also set a default output format for the CLI. Once you have entered all the required information, the AWS CLI will be configured and ready to use.</p>
<p>With the AWS CLI setup, you can now start using it to manage your AWS resources from the command line.</p>
<h3 id="heading-create-a-lambda-function">Create a Lambda function</h3>
<p>You'll need to provide the function name, runtime, handler function name, IAM role ARN, and the path to your function package:</p>
<pre><code class="lang-javascript">aws lambda create-<span class="hljs-function"><span class="hljs-keyword">function</span> --<span class="hljs-title">function</span>-<span class="hljs-title">name</span> <span class="hljs-title">my</span>-<span class="hljs-title">lambda</span>-<span class="hljs-title">function</span> --<span class="hljs-title">runtime</span> <span class="hljs-title">nodejs16</span>.<span class="hljs-title">x</span> --<span class="hljs-title">role</span> <span class="hljs-title">arn</span>:<span class="hljs-title">aws</span>:<span class="hljs-title">iam</span>::123456789012:<span class="hljs-title">role</span>/<span class="hljs-title">my</span>-<span class="hljs-title">lambda</span>-<span class="hljs-title">role</span> --<span class="hljs-title">handler</span> <span class="hljs-title">index</span>.<span class="hljs-title">handler</span> --<span class="hljs-title">zip</span>-<span class="hljs-title">file</span> <span class="hljs-title">fileb</span>://<span class="hljs-title">function</span>-<span class="hljs-title">package</span>.<span class="hljs-title">zip</span></span>
</code></pre>
<p>To allow your Lambda function to access other AWS resources, you need to create an IAM role that grants the necessary permissions. You can use the AWS CLI to create an IAM role with the required permissions.</p>
<pre><code class="lang-javascript">aws iam create-role --role-name my-lambda-role --assume-role-policy-<span class="hljs-built_in">document</span> file:<span class="hljs-comment">//trust-policy.json</span>
aws iam attach-role-policy --role-name my-lambda-role --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
</code></pre>
<h3 id="heading-invoke-a-lambda-function">Invoke a Lambda Function</h3>
<p>Once your function is created, you can test it using the AWS CLI. You can use the following command to invoke your function: For example, you can create a role with the "AWSLambdaBasicExecutionRole" policy attached to it:</p>
<pre><code class="lang-javascript">aws lambda invoke --<span class="hljs-function"><span class="hljs-keyword">function</span>-<span class="hljs-title">name</span> <span class="hljs-title">my</span>-<span class="hljs-title">lambda</span>-<span class="hljs-title">function</span> --<span class="hljs-title">payload</span> '</span>{ <span class="hljs-string">"name"</span>: <span class="hljs-string">"Rahul"</span> }<span class="hljs-string">' response.json</span>
</code></pre>
<h3 id="heading-update-the-lambda-function">Update the Lambda function</h3>
<p>If you need to make changes to your Lambda function, you can use the AWS CLI to update it. You'll need to provide the function name, the path to the updated function package, and any other parameters that need to be updated:</p>
<pre><code class="lang-javascript">aws lambda update-<span class="hljs-function"><span class="hljs-keyword">function</span>-<span class="hljs-title">code</span> --<span class="hljs-title">function</span>-<span class="hljs-title">name</span> <span class="hljs-title">my</span>-<span class="hljs-title">lambda</span>-<span class="hljs-title">function</span> --<span class="hljs-title">zip</span>-<span class="hljs-title">file</span> <span class="hljs-title">fileb</span>://<span class="hljs-title">function</span>-<span class="hljs-title">package</span>.<span class="hljs-title">zip</span></span>
</code></pre>
<h3 id="heading-delete-the-lambda-function">Delete the Lambda function</h3>
<p>When you're done with your Lambda function, you can use the AWS CLI to delete it:</p>
<pre><code class="lang-javascript">aws lambda <span class="hljs-keyword">delete</span>-<span class="hljs-function"><span class="hljs-keyword">function</span> --<span class="hljs-title">function</span>-<span class="hljs-title">name</span> <span class="hljs-title">my</span>-<span class="hljs-title">lambda</span>-<span class="hljs-title">function</span></span>
</code></pre>
<h3 id="heading-list-all-lambda-functions">List all Lambda functions</h3>
<p>This command lists all the Lambda functions in your AWS account region ap-south-1.</p>
<pre><code class="lang-javascript">aws lambda list-functions --region ap-south<span class="hljs-number">-1</span>
</code></pre>
<h3 id="heading-single-lambda-function">Single Lambda Function</h3>
<p>This command retrieves information about the specified Lambda function, including its configuration and code.</p>
<pre><code class="lang-javascript">aws lambda get-<span class="hljs-function"><span class="hljs-keyword">function</span> --<span class="hljs-title">function</span>-<span class="hljs-title">name</span> <span class="hljs-title">my</span>-<span class="hljs-title">lambda</span>-<span class="hljs-title">function</span> --<span class="hljs-title">region</span> <span class="hljs-title">ap</span>-<span class="hljs-title">south</span>-1</span>
</code></pre>
<h3 id="heading-publish-a-new-version-of-lambda">Publish a new version of Lambda</h3>
<p>This command publishes a new version of the specified Lambda function with the specified description.</p>
<pre><code class="lang-javascript">aws lambda publish-version --<span class="hljs-function"><span class="hljs-keyword">function</span>-<span class="hljs-title">name</span> <span class="hljs-title">my</span>-<span class="hljs-title">lambda</span>-<span class="hljs-title">function</span> --<span class="hljs-title">description</span> "<span class="hljs-title">New</span> <span class="hljs-title">version</span>"</span>
</code></pre>
<h3 id="heading-list-all-versions-of-a-lambda-function">List all versions of a Lambda function</h3>
<p>This command lists all the versions of the specified Lambda function</p>
<pre><code class="lang-javascript">aws lambda list-versions-by-<span class="hljs-function"><span class="hljs-keyword">function</span> --<span class="hljs-title">function</span>-<span class="hljs-title">name</span> <span class="hljs-title">my</span>-<span class="hljs-title">lambda</span>-<span class="hljs-title">function</span></span>
</code></pre>
<h3 id="heading-create-an-alias-for-a-lambda-function">Create an alias for a Lambda function</h3>
<p>This command creates a new alias for the specified Lambda function with the specified version number.</p>
<pre><code class="lang-javascript">aws lambda create-alias --<span class="hljs-function"><span class="hljs-keyword">function</span>-<span class="hljs-title">name</span> <span class="hljs-title">my</span>-<span class="hljs-title">lambda</span>-<span class="hljs-title">function</span> --<span class="hljs-title">name</span> <span class="hljs-title">my</span>-<span class="hljs-title">alias</span> --<span class="hljs-title">function</span>-<span class="hljs-title">version</span> 1</span>
</code></pre>
<h3 id="heading-list-all-lambda-function-names">List all Lambda function names</h3>
<p>This command lists the names of all Lambda functions in your account.</p>
<pre><code class="lang-javascript">aws lambda list-functions --query <span class="hljs-string">'Functions[*].FunctionName'</span>
</code></pre>
<h3 id="heading-list-all-versions-of-a-lambda-function-1">List all versions of a Lambda function</h3>
<p>This command lists all the versions of the specified Lambda function.</p>
<pre><code class="lang-javascript">aws lambda list-versions-by-<span class="hljs-function"><span class="hljs-keyword">function</span> --<span class="hljs-title">function</span>-<span class="hljs-title">name</span> <span class="hljs-title">my</span>-<span class="hljs-title">lambda</span>-<span class="hljs-title">function</span> --<span class="hljs-title">query</span> '<span class="hljs-title">Versions</span>[*].<span class="hljs-title">Version</span>'</span>
</code></pre>
<h3 id="heading-get-the-arn-of-a-lambda-function">Get the ARN of a Lambda function</h3>
<p>This command retrieves the ARN of the specified Lambda function.</p>
<pre><code class="lang-javascript">aws lambda get-<span class="hljs-function"><span class="hljs-keyword">function</span> --<span class="hljs-title">function</span>-<span class="hljs-title">name</span> <span class="hljs-title">my</span>-<span class="hljs-title">lambda</span>-<span class="hljs-title">function</span> --<span class="hljs-title">query</span> '<span class="hljs-title">Configuration</span>.<span class="hljs-title">FunctionArn</span>'</span>
</code></pre>
<h3 id="heading-lambda-functions-with-a-specific-prefix">Lambda functions with a specific prefix</h3>
<p>This command lists all Lambda functions in your account that start with the prefix "my-lambda-function"</p>
<pre><code class="lang-javascript">aws lambda list-functions --query <span class="hljs-string">"Functions[?starts_with(FunctionName, 'my-lambda-function')].FunctionName"</span>
</code></pre>
<h3 id="heading-lambda-functions-with-a-specific-prefix-but-not-containing-another-string">Lambda functions with a specific prefix but not containing another string</h3>
<p>This command lists all Lambda functions in your account that start with the prefix "my-lambda-function" but does not contain the string "test"</p>
<pre><code class="lang-javascript">aws lambda list-functions --query <span class="hljs-string">"Functions[?starts_with(FunctionName, 'my-lambda-function') &amp;&amp; !contains(FunctionName, 'test')].FunctionName"</span>
</code></pre>
<h3 id="heading-get-the-environment-variables-of-a-specific-lambda-function">Get the environment variables of a specific Lambda function</h3>
<p>This command retrieves the environment variables of the specified Lambda function.</p>
<pre><code class="lang-javascript">aws lambda get-<span class="hljs-function"><span class="hljs-keyword">function</span>-<span class="hljs-title">configuration</span> --<span class="hljs-title">function</span>-<span class="hljs-title">name</span> <span class="hljs-title">my</span>-<span class="hljs-title">lambda</span>-<span class="hljs-title">function</span> --<span class="hljs-title">query</span> '<span class="hljs-title">Environment</span>.<span class="hljs-title">Variables</span>'</span>
</code></pre>
<h3 id="heading-list-all-the-layers-of-a-specific-lambda-function">List all the layers of a specific Lambda function</h3>
<p>This command lists the names of all the layers of the specified Lambda function.</p>
<pre><code class="lang-javascript">aws lambda list-layers --query <span class="hljs-string">'Layers[].LayerName'</span>
</code></pre>
<h3 id="heading-event-source-mappings-of-a-specific-lambda-function">Event source mappings of a specific Lambda function</h3>
<p>This command lists the ARNs of all the event source mappings of the specified Lambda function.</p>
<pre><code class="lang-javascript">aws lambda list-event-source-mappings --<span class="hljs-function"><span class="hljs-keyword">function</span>-<span class="hljs-title">name</span> <span class="hljs-title">my</span>-<span class="hljs-title">lambda</span>-<span class="hljs-title">function</span> --<span class="hljs-title">query</span> '<span class="hljs-title">EventSourceMappings</span>[].<span class="hljs-title">EventSourceArn</span>'</span>
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In conclusion, AWS CLI is a powerful tool that allows you to manage your Lambda functions and their configurations from the command line. With AWS CLI, you can easily create, update, delete, and test Lambda functions, as well as retrieve information about their configurations, versions, aliases, and event source mappings.</p>
<p>In this blog, we discussed several AWS CLI commands with examples to help you manage your Lambda functions. We covered how to create a new Lambda function, update an existing function, invoke a function, and test a function locally using the <code>aws lambda</code> command-line tool. Additionally, we explored how to use JMESPath queries to extract specific data from Lambda functions, such as their configuration, environment variables, aliases, and versions.</p>
<p>By leveraging the power of AWS CLI and its rich feature set, you can automate the deployment and management of your Lambda functions and streamline your serverless workflows. Whether you are a developer, DevOps engineer, or system administrator, AWS CLI is a must-have tool in your arsenal for managing AWS Lambda functions efficiently and effectively.</p>
]]></content:encoded></item><item><title><![CDATA[How to connect to S3 from EKS using the IAM role for the service account]]></title><description><![CDATA[In this blog post, we will see how the applications running in the EKS pods can connect to the S3 bucket using the IAM role for the service account (IRSA).
The applications running in EKS pods can use AWS SDK/AWS CLI to make a call to the S3 bucket. ...]]></description><link>https://rahullokurte.com/how-to-connect-to-s3-from-eks-using-the-iam-role-for-the-service-account</link><guid isPermaLink="true">https://rahullokurte.com/how-to-connect-to-s3-from-eks-using-the-iam-role-for-the-service-account</guid><category><![CDATA[AWS]]></category><category><![CDATA[EKS]]></category><category><![CDATA[IAM]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Mon, 20 Mar 2023 17:26:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1679333101355/20af8d45-b493-4d1c-b7f4-4e54d61db0a3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this blog post, we will see how the applications running in the EKS pods can connect to the S3 bucket using the IAM role for the service account (IRSA).</p>
<p>The applications running in EKS pods can use AWS SDK/AWS CLI to make a call to the S3 bucket. In order to connect successfully, we need to distribute the AWS credentials to the containers. If we have multiple applications running, we have to distribute the credentials to all the containers. In order to have control of the distribution, AWS recommends creating the IAM role for the service account, which will help in providing the least privileged access to a particular container. With IRSA, the pod which is associated with the service account will get permission to talk to the other AWS services.</p>
<p>EKS pods are the Kubernetes resources, whereas S3 is an AWS resource. So, to have a good handshake between the two, we need an IRSA to be created.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ul>
<li><p>Running Elastic Kubernetes cluster (EKS)</p>
</li>
<li><p>kubectl</p>
</li>
<li><p>AWS account</p>
</li>
<li><p>Permission to create the IAM role and Policies</p>
</li>
<li><p>AWS CLI</p>
</li>
<li><p>eksctl</p>
</li>
</ul>
<h3 id="heading-create-an-oidc-provider">Create an OIDC provider</h3>
<p>For the EKS cluster created, by default there will be an OIDC issuer URL associated with it. To use the IAM role for the service account, the OIDC provider needs to exist with OIDC issuer URL. To create the OIDC provider, we will use eksctl command line utility.</p>
<pre><code class="lang-bash">&gt;&gt; eksctl utils associate-iam-oidc-provider --cluster demo-cluster --approve
</code></pre>
<p>The <strong><em>demo-cluster</em></strong> is the name of the EKS cluster.</p>
<p>To verify if an OIDC provider is created, we need to run the following command.</p>
<pre><code class="lang-bash">&gt;&gt; aws iam list-open-id-connect-providers
</code></pre>
<p>We will get the below details if the OIDC provider was successfully created.</p>
<pre><code class="lang-bash">{
    <span class="hljs-string">"OpenIDConnectProviderList"</span>: [
        {
            <span class="hljs-string">"Arn"</span>: <span class="hljs-string">"arn:aws:iam::17483678901:oidc-provider/oidc.eks.ap-south-1.amazonaws.com/id/D510BCG4F7E27AM04AZNS95G6914V4A0"</span>
        }
    ]
}
</code></pre>
<h3 id="heading-create-an-iam-policy">Create an IAM policy</h3>
<p>Create the file <code>s3-policy.json</code> with the below contents.</p>
<pre><code class="lang-bash">{
    <span class="hljs-string">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
    <span class="hljs-string">"Statement"</span>: [
        {
            <span class="hljs-string">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-string">"Action"</span>: <span class="hljs-string">"s3:GetObject"</span>,
            <span class="hljs-string">"Resource"</span>: <span class="hljs-string">"arn:aws:s3:::demo-bucket"</span>
        }
    ]
}
</code></pre>
<p>We will create an IAM policy to allow access to get the object from the S3 bucket named <code>demo-bucket</code> using the below command.</p>
<pre><code class="lang-bash">aws iam create-policy --policy-name s3-policy --policy-document file://s3-policy.json
</code></pre>
<h3 id="heading-create-a-service-account">Create a service account</h3>
<p>Create a file <code>demo-service-account.yaml</code>. We have to create a Kubernetes service account with the name <code>demo-sa</code> in the namespace <code>demo-s3</code> using the below yaml file.</p>
<pre><code class="lang-bash">apiVersion: v1
kind: ServiceAccount
metadata:
  name: demo-sa
  namespace: demo-s3
</code></pre>
<p>Now apply the below command to create a service account.</p>
<p><code>kubectl apply -f demo-service-account.yaml</code></p>
<h3 id="heading-create-an-iam-role-and-attach-the-policy">Create an IAM Role and attach the policy</h3>
<p>Create a file <code>demo-role-relationship.yaml</code> for the trust policy.</p>
<pre><code class="lang-plaintext">{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::17483678901:oidc-provider/oidc.eks.ap-south-1.amazonaws.com/id/D510BCG4F7E27AM04AZNS95G6914V4A0"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "oidc.eks.ap-south-1.amazonaws.com/id/D510BCG4F7E27AM04AZNS95G6914V4A0:aud": "sts.amazonaws.com",
          "oidc.eks.ap-south-1.amazonaws.com/id/D510BCG4F7E27AM04AZNS95G6914V4A0:sub": "system:serviceaccount:demo-s3:demo-sa"
        }
      }
    }
  ]
}
</code></pre>
<p>Once it is done, we need to create the IAM role and attach the trust policy.</p>
<pre><code class="lang-plaintext">aws iam create-role --role-name s3-role --assume-role-policy-document file://demo-role-relationship.json"
</code></pre>
<p>Now, we will attach the IAM policy to the role created above.</p>
<pre><code class="lang-plaintext">aws iam attach-role-policy --role-name s3-role --policy-arn=arn:aws:iam::17483678901:policy/s3-policy
</code></pre>
<h3 id="heading-annotate-the-service-account-with-the-iam-role">Annotate the service account with the IAM role</h3>
<p>The service account needs to be annotated to the IAM role using the below command.</p>
<pre><code class="lang-plaintext">kubectl annotate serviceaccount -n demo-s3 demo-sa eks.amazonaws.com/role-arn=arn:aws:iam::17483678901:role/s3-role
</code></pre>
<h3 id="heading-testing-the-connectivity-between-pod-and-s3-bucket">Testing the connectivity between Pod and S3 Bucket</h3>
<p>Create a file <code>s3-demo-deployment.yaml</code> . Notice that, we are using the 'default' service account that does not have access to the S3 bucket as we have only configured access to the 'demo-sa' service account.</p>
<pre><code class="lang-plaintext">apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: demo-s3
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      serviceAccountName: default
      initContainers:
      - name: demo-aws-cli
        image: amazon/aws-cli
        command: ['aws', 's3', 'cp', 's3://demo-bucket/test.txt, '-'
      containers:
      - name: my-app
        image: nginx
</code></pre>
<p>Now, let us run the deployment file using <code>kubectl apply -f s3-demo-deployment.yaml</code>.</p>
<pre><code class="lang-plaintext">kubectl get pods -n demo-s3
NAME                                                READY   STATUS    
nginx-8665cf9655-7h5fs                 0/1         Init:CrashLoopBackoff
</code></pre>
<p>Observe above that, the pod did not come up successfully as the Init container was not able to access the s3 bucker <code>demo-bucket</code>. We can also check this using the below command.</p>
<pre><code class="lang-plaintext">kubectl logs -l app=nginx -c demo-aws-cli -n demo-s3

download failed: s3://demo-bucket/test.txt to - An error occurred (403) when calling the HeadObject operation: Forbidden
</code></pre>
<p>Now, let us change the service account to <code>demo-sa</code> in the init container as shown below.</p>
<pre><code class="lang-plaintext"> spec:
      serviceAccountName: demo-sa
      initContainers:
      - name: demo-aws-cli
        image: amazon/aws-cli
        command: ['aws', 's3', 'cp', 's3://demo-bucket/test.txt, '-'
</code></pre>
<p>Now reapply using the command <code>kubectl apply -f s3-demo-deployment.yaml</code>.</p>
<pre><code class="lang-plaintext">NAME                                                READY   STATUS    RESTARTS   AGE
nginx-12455f5f955-9g8gs                    1/1     Running       0               1d
</code></pre>
<p>Let us verify if it was able to connect to S3 and print the contents in the console.</p>
<pre><code class="lang-plaintext">kubectl logs -f nginx-12455f5f955-9g8gs -n demo-s3

Hi!!!! Testing IAM role for a service account.
</code></pre>
<p>As we have used the <code>demo-sa</code> service account, it was able to successfully connect to S3 bucket.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In this blog, we have seen what is IRSA and how to use it to access the S3 bucket from the EKS pods. We have also seen, how the pod is connected to a particular service account and how it uses IRSA to connect to the S3 bucket.</p>
]]></content:encoded></item><item><title><![CDATA[ArgoCD - the GitOps way]]></title><description><![CDATA[As we adapt to building cloud-native applications, we are coming up with lots of toolings and processes. Gitops is one of the processes of implementing continuous deployment for cloud-native applications. ArgoCD is the declarative, Gitops tool specif...]]></description><link>https://rahullokurte.com/argocd-the-gitops-way</link><guid isPermaLink="true">https://rahullokurte.com/argocd-the-gitops-way</guid><category><![CDATA[gitops]]></category><category><![CDATA[ArgoCD]]></category><category><![CDATA[Kubernetes]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Sat, 11 Mar 2023 18:52:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1678560520306/de3a6dc1-61cb-4fdc-b8ff-2e6287676521.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As we adapt to building cloud-native applications, we are coming up with lots of toolings and processes. Gitops is one of the processes of implementing continuous deployment for cloud-native applications. ArgoCD is the declarative, Gitops tool specifically designed for cloud-native applications deployed in Kubernetes. In this blog post, we will see the below things.</p>
<ul>
<li><p>What is GitOps?</p>
</li>
<li><p>ArgoCD tool</p>
</li>
<li><p>Installing ArgoCD locally</p>
</li>
<li><p>Setting up the app for Argocd</p>
</li>
<li><p>Syncing out-of-sync app</p>
</li>
</ul>
<h1 id="heading-what-is-gitops">What is GitOps?</h1>
<p>GitOps is a framework used to automate the process of provisioning the infrastructure. The Git repository is the center of the GitOps framework. The concept has evolved from the DevOps culture. DevOps is a mature framework that focuses more on the communication and collaboration of different teams in an organization. GitOps is the kind of branch of DevOps that focuses on using Git repositories for infrastructure and application deployments.</p>
<p>GitOps uses configuration files stored as code(Infrastructure as Code). The good practice of GitOps requires us to keep the infrastructure-related code repository separate from the application-related code repository. The Git repository is a source of truth for all the infrastructure definitions. Any configuration change of manual updates to the infrastructure would be overwritten to the configuration defined in the Git repository.</p>
<h1 id="heading-argocd-tool">ArgoCD tool</h1>
<p>ArgoCD is a declarative, GitOps tool used to do continuous delivery for Kubernetes. ArgoCD uses the GitOps principles of using the Git repository as a source of truth. The application definitions, configurations, and environments will be stored in a separate Git repository from the application code repository and also declarative and version controlled.</p>
<p>ArgoCD will be part of the k8s cluster. ArgoCD agent pulls the k8s manifest changes and applies them to the k8s cluster. ArgoCD is configured to track the Git repository that contains manifest files. ArgoCD monitors for any changes to manifest files and tries to be in sync with the k8s cluster.</p>
<h3 id="heading-argocd-workflow">ArgoCD workflow</h3>
<ul>
<li><p>The developer commits the code and creates a tag that will trigger the CI pipeline.</p>
</li>
<li><p>The CI pipeline builds the artifact and then creates a docker image which then will be stored in the docker hub.</p>
</li>
<li><p>Once the docker image is created, the CI server can update the manifest files with a new/updated docker image.</p>
</li>
<li><p>Once manifest files are updated, the argoCD will be out of sync.</p>
</li>
<li><p>ArgoCD will update the cluster to be in sync with the manifest files as Git is the source of truth.</p>
</li>
</ul>
<h1 id="heading-installing-argocd-locally">Installing ArgoCD locally</h1>
<h3 id="heading-prerequisite">Prerequisite:</h3>
<p>The tools which are required for ArgoCD to be installed locally are:</p>
<ul>
<li><p>Docker</p>
</li>
<li><p>Kubernetes cluster ( <a target="_blank" href="https://minikube.sigs.k8s.io/docs/">minikube</a>, <a target="_blank" href="https://kind.sigs.k8s.io/">kind</a>, <a target="_blank" href="https://docs.docker.com/desktop/">docker-desktop</a>)</p>
</li>
<li><p>Kubectl tool</p>
</li>
</ul>
<p>ArgoCD can be installed within the Kubernetes cluster or external to the cluster. We will be installing ArgoCD inside the docker-desktop Kubernetes cluster.</p>
<p>First, we need to create a namespace to install ArgoCD by using the below command.</p>
<pre><code class="lang-bash">kubectl create namespace argocd
</code></pre>
<p>ArgoCD provides the manifest yaml file that can be applied to the locally running Kubernetes cluster.</p>
<pre><code class="lang-bash">kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
</code></pre>
<p>verify the installation</p>
<pre><code class="lang-bash">kubectl get pods -n argocd
NAME                                                READY   STATUS    RESTARTS   AGE
argocd-application-controller-0                     1/1     Running   1          2d10h
argocd-applicationset-controller-695df5687d-lcghx   1/1     Running   1          2d10h
argocd-dex-server-54c99dd844-vt69d                  1/1     Running   1          2d10h
argocd-notifications-controller-7d7568845c-m5txb    1/1     Running   4          2d10h
argocd-redis-6d8cffcc47-jbwpd                       1/1     Running   1          2d10h
argocd-repo-server-647c6568c9-m6gt6                 1/1     Running   2          2d10h
argocd-server-8bdf66d5-jfxjh                        1/1     Running   2          2d10h
</code></pre>
<p>Once it is installed, we can open the argoCD instance locally by port-forwarding it to a host port.</p>
<pre><code class="lang-bash">kubectl port-forward svc/argocd-server -n argocd 8080:443
</code></pre>
<p>We will get the login screen as shown below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678547889629/9ffa2719-970e-40c2-b6fa-22a12e535efd.png" alt class="image--center mx-auto" /></p>
<p>The username is <strong>admin</strong> and password we need to get using the below command.</p>
<pre><code class="lang-bash">kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath=<span class="hljs-string">"{.data.password}"</span> | base64 -d
</code></pre>
<p>Once logged in, we should see the homepage as shown below. The application tiles will be empty.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678548155536/2046c2ca-54df-4917-8280-bd3dbfb35ef6.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-setting-up-the-app-for-argocd">Setting up the app for Argocd</h3>
<p>To set up the application, we need a Git repository that would contain the manifest yaml files to install into the Kubernetes cluster. We will be using the git repository. <a target="_blank" href="https://github.com/rahulmlokurte/argocd-demo">https://github.com/rahulmlokurte/argocd-demo</a> . The repository contains the manifest file for creating a namespace, deployment that uses Nginx image, and the service yaml.</p>
<p>test-ns.yaml</p>
<pre><code class="lang-bash">apiVersion: v1
kind: Namespace
metadata:
  name: <span class="hljs-built_in">test</span>
spec: {}
</code></pre>
<p>test-deployment.yaml</p>
<pre><code class="lang-bash">apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx-demo
  name: nginx-demo
  namespace: <span class="hljs-built_in">test</span>
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-demo
  strategy: {}
  template:
    metadata:
      labels:
        app: nginx-demo
    spec:
      containers:
      - image: nginx
        name: nginx-demo
        resources: {}
</code></pre>
<p>test-svc.yaml</p>
<pre><code class="lang-bash">apiVersion: v1
kind: Service
metadata:
  creationTimestamp: null
  labels:
    app: nginx-demo
  name: nginx-demo
  namespace: <span class="hljs-built_in">test</span>
spec:
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: nginx-demo
</code></pre>
<p>To create an application on ArgoCD, click on the New APP and add the following details.</p>
<ul>
<li><p><strong>Application Name</strong>: argocd-demo</p>
</li>
<li><p><strong>Project Name:</strong> default</p>
</li>
<li><p><strong>Sync Policy:</strong> manual</p>
</li>
<li><p><strong>Source Repository URL:</strong> <a target="_blank" href="https://github.com/rahulmlokurte/argocd-demo">https://github.com/rahulmlokurte/argocd-demo</a></p>
</li>
<li><p><strong>revision:</strong> main</p>
</li>
<li><p><strong>path:</strong> deployments</p>
</li>
<li><p><strong>Cluster URL</strong>: <a target="_blank" href="https://kubernetes.default.svc">https://kubernetes.default.svc</a></p>
</li>
<li><p><strong>namespace</strong>: test</p>
</li>
</ul>
<p>Once the above details are entered, click on Create App, then we can see the app listed in the application tiles.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678549177072/6765d76a-8f1e-4db0-8fbf-b5b1b5da568b.png" alt class="image--center mx-auto" /></p>
<p>We can see the status as Missing and out of sync as there is a difference in the manifest files defined in Git and the actual Kubernetes cluster.</p>
<p>As we have selected the sync policy as manual, we need to manually click on the nginx-demo button. Once we click on sync, it will try to sync the Kubernetes cluster with the configuration stored in Git.</p>
<pre><code class="lang-bash">Desktop kubectl get pods -n <span class="hljs-built_in">test</span>
NAME                          READY   STATUS    RESTARTS   AGE
nginx-demo-5d7c8d874d-c5l7w   1/1     Running   0          11s
</code></pre>
<p>Once it is running, we can see the ArgoCD application is in-sync as shown below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678549496245/51ae423d-a88f-4d50-a9df-d029d5e4549b.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-syncing-out-of-sync-app">Syncing out-of-sync app</h3>
<p>Let us now change the replicas in the deployment file from '1' to '3'.</p>
<pre><code class="lang-bash">apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx-demo
  name: nginx-demo
  namespace: <span class="hljs-built_in">test</span>
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-demo
  strategy: {}
  template:
    metadata:
      labels:
        app: nginx-demo
    spec:
      containers:
      - image: nginx
        name: nginx-demo
        resources: {}
</code></pre>
<p>Now, we see the argoCD app shows out of sync because, in the Kubernetes cluster, we only have 1 pod, but in Git, it is 3 replicas.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678559744303/96bb574c-1138-4d24-9216-cf4d32822972.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-bash">kubectl get pods -n <span class="hljs-built_in">test</span>
NAME                          READY   STATUS    RESTARTS   AGE
nginx-demo-5d7c8d874d-c5l7w   1/1     Running   0          11s
</code></pre>
<p>Now, Once we click on the sync button, we see the Kubernetes cluster with 3 replicas of pods and the argoCD application tile shows synced.</p>
<pre><code class="lang-bash">Desktop kubectl get pods -n <span class="hljs-built_in">test</span>
NAME                          READY   STATUS    RESTARTS   AGE
nginx-demo-5d7c8d874d-c5l7w   1/1     Running   0          175m
nginx-demo-5d7c8d874d-n8895   1/1     Running   0          15s
nginx-demo-5d7c8d874d-qm4zt   1/1     Running   0          15s
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678560024854/3cf1b015-fb0b-4f5c-a80d-fa80aae62482.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In this blog post, we have seen the GitOps process and how the ArgoCD tool can be used that implements GitOps. We also see, how argoCD keeps track of changes in Git and tries to be in sync between the Kubernetes cluster and Git repository.</p>
<p>The GitHub repository for this blog post can be seen at <a target="_blank" href="https://github.com/rahulmlokurte/argocd-demo">https://github.com/rahulmlokurte/argocd-demo</a></p>
]]></content:encoded></item><item><title><![CDATA[How to prevent Terraform misconfiguration with Checkov]]></title><description><![CDATA[As more and more organizations are adopting Terraform, it is increasingly important to ensure that the configuration is correct. 
This is especially true for large organizations that have hundreds of thousands of resources getting deployed to the clo...]]></description><link>https://rahullokurte.com/how-to-prevent-terraform-misconfiguration-with-checkov</link><guid isPermaLink="true">https://rahullokurte.com/how-to-prevent-terraform-misconfiguration-with-checkov</guid><category><![CDATA[Terraform]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Thu, 13 Jan 2022 16:44:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1642092188671/kHyKzxS7X.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As more and more organizations are adopting Terraform, it is increasingly important to ensure that the configuration is correct. 
This is especially true for large organizations that have hundreds of thousands of resources getting deployed to the cloud. Checkov is a command-line tool that analyzes your Infrastructure as Code (IaC) configuration across various platforms like Terraform, CloudFormation, Kubernetes, and serverless frameworks.
Checkov contains a set of policies against which you can configure your IaC configuration. It can also be embedded into your continuous integration pipeline such as GitHub Actions, Jenkins, CircleCI, Gitlab CI, etc.</p>
<h2 id="heading-install-checkov">Install Checkov</h2>
<p>We can install checkov from pip using the following command:</p>
<pre><code class="lang-shell">pip install checkov
</code></pre>
<p>Once installed, we can verify that it is working by running the following command:</p>
<pre><code class="lang-shell">checkov -v
</code></pre>
<h2 id="heading-configure-checkov">Configure Checkov</h2>
<p>There are many ways to configure Checkov. We can configure it to run against a specific file or directory using the options <strong>--file</strong> and <strong>--directory</strong> respectively.
We can also specify multiple files using the <strong>-f</strong> option.</p>
<p>We will see how to use Checkov to scan Terraform plan file in the following section.</p>
<p>When we write a Terraform code, we first need to initialize the Terraform configuration. This is done by running the following command:</p>
<pre><code class="lang-shell">terraform init
</code></pre>
<p>Next, we need to generate the Terraform plan. This is done by running the following command:</p>
<pre><code class="lang-shell">terraform plan -out my-tf.plan
</code></pre>
<p>So, terraform plan will be stored in the file <strong>my-tf.plan</strong>.</p>
<p>For checkov to scan, we can convert the Terraform plan file to JSON using the following command:</p>
<pre><code class="lang-shell">terraform show -json my-tf.plan | jq '.' &gt; my-tf.json
</code></pre>
<p>We used <strong>jq</strong> to convert the JSON to a multi-line format to make it easier to read and scan the result.</p>
<p>Once, we have a JSON file, we can run the following command to scan the JSON file to verify our Terraform configuration:</p>
<pre><code class="lang-shell">checkov -f my-tf.json
</code></pre>
<h2 id="heading-checkov-policy">Checkov Policy</h2>
<p>Checkov has a set of predefined policies which are used to scan our Terraform configuration. The policies are provided for various providers like AWS, GCP, Azure, etc.
The policies which it checks depend on which provider we are using and which resources are being used. For example, if we are using AWS, there is a policy <strong>CKV_AWS_41</strong> which ensures that no hardcoded credentials such as AWS access key ID and secret access key are used.
If we use checkov in the continuous integration pipeline, we can scan the Terraform plan file for such configurations, and revert the changes made to terraform configuration if any of the policies are violated.</p>
<p>The checkov has many policies and if we do not want checkov to scan for all the policies, we can specify the policies to be skipped using the option <strong>--skip-check</strong>. For example, if we do not want 
checkov to scan for the policy <strong>CKV_AWS_41</strong>, we can use the following command:</p>
<pre><code class="lang-shell">checkov -f my-tf.json --skip-check CKV_AWS_41
</code></pre>
<p>If we want to skip a certain part of Terraform definition block, we can add the comment inside our terraform file, so checkov can ignore the block. For example, if we want to skip the block <strong>resource "aws_s3_bucket" "my-bucket"</strong>, we can add the following comment in the terraform file:</p>
<pre><code class="lang-hcl">resource "aws_s3_bucket" "my-bucket" {
  region        = var.region
    #checkov:skip=CKV_AWS_20:The bucket is a public static content host
  bucket        = local.bucket_name
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>As we saw in this article, we can use checkov to scan various configuration files and policies. We have also seen how to use checkov to scan Terraform plan files. We can skip some policies, and we can skip certain parts of terraform configuration.
If you are interested to learn more about checkov policies, do check out the <a target="_blank" href="https://www.checkov.io/5.Policy%20Index/terraform.html#terraform-resource-scans-auto-generated">Checkov Policies</a> and do check the <a target="_blank" href="https://www.checkov.io/">checkov website</a> for more information.</p>
]]></content:encoded></item><item><title><![CDATA[pre-commit hooks for Terraform]]></title><description><![CDATA[As you work on the large codebase, you may want to run a set of checks before you submit a pull request for code review. This is a great way to ensure that your code is ready for review.
Git hooks are a great way to do this. The pre-commit hook is a ...]]></description><link>https://rahullokurte.com/pre-commit-hooks-for-terraform</link><guid isPermaLink="true">https://rahullokurte.com/pre-commit-hooks-for-terraform</guid><category><![CDATA[Terraform]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Sun, 26 Dec 2021 18:08:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1640542074117/gY_HiM_O_.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As you work on the large codebase, you may want to run a set of checks before you submit a pull request for code review. This is a great way to ensure that your code is ready for review.
Git hooks are a great way to do this. The pre-commit hook is a framework for creating git hooks that can be used to run a set of checks before you commit the code into a repository.</p>
<p>pre-commit hooks automatically scan the codebase and point out the issues with a code such as linting errors, style violations, missing semicolons, etc. This helps you to ensure that the
reviewers can focus on the business logic and not on the trivial details. The pre-commit framework supports multiple programming languages and can be used to run a set of checks for multiple programming languages.</p>
<h3 id="heading-install-pre-commit">Install pre-commit</h3>
<p>The pre-commit package manager can be installed using the pip command:</p>
<pre><code class="lang-shell">pip install pre-commit
</code></pre>
<p>Once done, verify that the pre-commit is installed by running the following command:</p>
<pre><code class="lang-shell">pre-commit --version
</code></pre>
<h3 id="heading-how-to-use-pre-commit-hooks">How to use pre-commit hooks</h3>
<p>The pre-commit hooks can be used in any programming language. The only requirement is to have a <code>.pre-commit-config.yaml</code> file in the root of the repository.
Inside this file, you can specify the hooks that you want to run. </p>
<ul>
<li><p>The hooks are defined as a list of dictionaries. Each dictionary contains the following keys:
<code>repos</code> is the root key that contains the repository of the hook. <code>rev</code> is the revision of the hook. <code>hooks</code> is the list of hooks that you want to run.</p>
</li>
<li><p>Each hook is defined as a dictionary. The hook contains the following keys:
<code>id</code> is the id of the hook. <code>name</code> is the name of the hook. <code>entry</code> is the entry point of the hook. <code>language</code> is the programming language of the hook.</p>
</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-attr">repos:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">repo:</span> <span class="hljs-string">https://github.com/pre-commit/pre-commit-hooks</span>
  <span class="hljs-attr">rev:</span> <span class="hljs-string">v3.2.0</span>
  <span class="hljs-attr">hooks:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">check-yaml</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">end-of-file-fixer</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">trailing-whitespace</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">repo:</span> <span class="hljs-string">https://github.com/psf/black</span>
  <span class="hljs-attr">rev:</span> <span class="hljs-number">19.</span><span class="hljs-string">3b0</span>
  <span class="hljs-attr">hooks:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">black</span>
</code></pre>
<p>If we want to run pre-commit hooks on every commit, we need to install the pre-commit hook using the following command:</p>
<pre><code class="lang-shell">pre-commit install
</code></pre>
<p>We can also run the pre-commit against all the files in the repository using the following command:</p>
<pre><code class="lang-shell">pre-commit run --all-files
</code></pre>
<h3 id="heading-how-to-use-pre-commit-hooks-in-terraform">How to use pre-commit hooks in Terraform</h3>
<p>Let us now see how to use pre-commit hooks in Terraform. The project <strong>pre-commit-terraform</strong> already has the pre-commit hooks to take care of Terraform files.</p>
<p>Please Check out the code from the GitHub repository <a target="_blank" href="https://github.com/rahulmlokurte/aws-usage/tree/main/terraform/terraform-modules">TerraformCode</a> and then add the file <code>.pre-commit-config.yaml</code> to the root of the repository with the following content:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">repos:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">repo:</span> <span class="hljs-string">https://github.com/antonbabenko/pre-commit-terraform</span>
    <span class="hljs-attr">rev:</span> <span class="hljs-string">v1.62.3</span>
    <span class="hljs-attr">hooks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">terraform_fmt</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">terraform_validate</span>
</code></pre>
<p>We are using the pre-commit-terraform repository to run the hooks such as <code>terraform fmt</code> and <code>terraform validate</code>. <code>terraform fmt</code> is a tool that formats the Terraform files.
<code>terraform validate</code> is a tool that validates the Terraform files. These two hooks are run on every commit.</p>
<p>We can install the pre-commit hooks using the following command:</p>
<pre><code class="lang-shell">pre-commit install
</code></pre>
<p>Now, for every commit you make locally, it will check the above two hooks for formatting the terraform files and validating the terraform files. In this way, we can ensure that the terraform files are formatted and validated before we commit them.
There are various hooks supported by pre-commit-terraform. Please refer to the <a target="_blank" href="https://github.com/antonbabenko/pre-commit-terraform">pre-commit-terraform repository</a> for more details.</p>
<p>Github Link: <a target="_blank" href="https://github.com/rahulmlokurte/aws-usage/tree/main/terraform/terraform-modules">https://github.com/rahulmlokurte/aws-usage/tree/main/terraform/terraform-modules</a></p>
]]></content:encoded></item><item><title><![CDATA[Concept of Terraform module and module scope]]></title><description><![CDATA[When I started with Terraform learning, I was mostly writing independent files for each of the resources which needs to be provisioned. After few days, I had left with so many terraform files, which need to be maintained. That is the time, I thought ...]]></description><link>https://rahullokurte.com/concept-of-terraform-module-and-module-scope</link><guid isPermaLink="true">https://rahullokurte.com/concept-of-terraform-module-and-module-scope</guid><category><![CDATA[2Articles1Week]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Terraform]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Thu, 16 Sep 2021 18:54:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1631728110147/ke4TjPuk5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When I started with Terraform learning, I was mostly writing independent files for each of the resources which needs to be provisioned. After few days, I had left with so many terraform files, which need to be maintained. That is the time, I thought this is not it with terraform. I started exploring more and ended up with the concept of <strong>modules</strong>.</p>
<p>In simpler terms, when you code the application, you try to make it modular. In the same way, we can create our terraform files in a modular way. The main advantage of doing that is, the modules become reusable. I can then call the modules from the root module or call the module inside the module. We can also publish the modules to the terraform registry or your private registries in your organization. Then any team can pull the modules and terraform automatically downloads the modules using the source and version information.</p>
<p>In this blog, we will see how to create modules and what module_scope is, using terraform.</p>
<h2 id="module-blocks">Module Blocks</h2>
<p>Terraform HCL language provides us the <strong>Module Block</strong> resource, which we can use to define the multiple resources together to form one module. In each repository, we will have a minimum of one root module which is the current working directory where you initialize the terraform using <code>terraform init</code>. We can call the child module using the below syntax.</p>
<pre><code class="lang-sh">module <span class="hljs-string">"module_name"</span> {
    <span class="hljs-built_in">source</span> = <span class="hljs-string">""</span>
    version = <span class="hljs-string">""</span>
}
</code></pre>
<ul>
<li><strong>module_name</strong> is the local name, which can be used for referring to the instance.</li>
<li><strong>source</strong> is the mandatory argument where we can point to a local directory containing terraform configuration files or a remote registry in which case, you specify the URL where terraform configuration files are defined. </li>
<li><strong>version</strong> is mandatory only when you use the modules from the registry.</li>
</ul>
<p>The module also supports input variables and meta-arguments. We can create multiple instances using one module block definition using the meta-argument <strong>count</strong> and we can iterate over the multiple instances using meta-argument <strong>for_each</strong>. We can also explicitly mention the dependencies, if there are any dependent modules that must be completed, before the current module. It is done through meta-argument <strong>depends_on</strong></p>
<h2 id="module-scopes">module scopes</h2>
<p>The resources defined in one module is not visible to another module. This helps in making each resource unique in a particular module namespace. If you want to use the resources between the modules, we need to explicitly output the resource. </p>
<p>To understand terraform module, we need to understand how module scope works. So let us understand the module scope with an example. </p>
<p>Suppose we are provisioning an AWS lambda function resource and we need to associate the role to the lambda function, we can use the module concept here.</p>
<h2 id="project-structure">Project Structure</h2>
<ul>
<li>Create a new directory and move to the directory.</li>
</ul>
<pre><code class="lang-sh">mkdir terraform-modules &amp;&amp; <span class="hljs-built_in">cd</span> terraform-modules
</code></pre>
<ul>
<li>Create a <strong>main.tf</strong> and <strong>variables.tf</strong> file in the root directory.</li>
</ul>
<p>main.tf</p>
<pre><code class="lang-sh">terraform {
  required_providers {
    aws = {
      <span class="hljs-built_in">source</span>  = <span class="hljs-string">"hashicorp/aws"</span>
      version = <span class="hljs-string">"3.50.0"</span>
    }
  }
}

provider <span class="hljs-string">"aws"</span> {
  <span class="hljs-comment"># Configuration options</span>
  region                  = var.region
  profile                 = var.aws_profile
  shared_credentials_file = var.shared_credentials_file
  default_tags {
    tags = var.tags
  }
}
</code></pre>
<p>variables.tf</p>
<pre><code class="lang-sh">variable <span class="hljs-string">"region"</span> {
  description = <span class="hljs-string">"Deployment Region"</span>
  default     = <span class="hljs-string">"ap-south-1"</span>
}

variable <span class="hljs-string">"aws_profile"</span> {
  description = <span class="hljs-string">"Given name in the credential file"</span>
  <span class="hljs-built_in">type</span>        = string
  default     = <span class="hljs-string">"rahul-admin"</span>
}

variable <span class="hljs-string">"shared_credentials_file"</span> {
  description = <span class="hljs-string">"Profile file with credentials to the AWS account"</span>
  <span class="hljs-built_in">type</span>        = string
  default     = <span class="hljs-string">"~/.aws/credentials"</span>
}

variable <span class="hljs-string">"tags"</span> {
  description = <span class="hljs-string">"A map of tags to add to all resources."</span>
  <span class="hljs-built_in">type</span>        = map(string)
  default = {
    application = <span class="hljs-string">"Learning-Tutor"</span>
    env         = <span class="hljs-string">"Test"</span>
  }
}
</code></pre>
<ul>
<li>Create a folder modules/lambda for lambda configurations and modules/roles for role configuration.</li>
</ul>
<ul>
<li>Create a <strong>hello-lambda.tf</strong> file and add the resources as shown below.</li>
</ul>
<pre><code class="lang-sh">resource <span class="hljs-string">"aws_lambda_function"</span> <span class="hljs-string">"hello_lambda"</span> {
  function_name = <span class="hljs-string">"hello-lambda"</span>
  role = var.aws_lambda_function_role_arn
}
</code></pre>
<ul>
<li>Now create a <strong>variables.tf</strong> in modules/lambda and add the below variables.</li>
</ul>
<pre><code class="lang-sh">variable <span class="hljs-string">"aws_lambda_function_role_arn"</span> {
  <span class="hljs-built_in">type</span> = string
}
</code></pre>
<p>Now, we can call the lambda module from our root module i.e <strong>main.tf</strong> as shown below.</p>
<pre><code class="lang-sh">module <span class="hljs-string">"lambdas"</span> {
  <span class="hljs-built_in">source</span> = <span class="hljs-string">"./modules/lambda"</span>
  aws_lambda_function_role_arn = module.roles.lambda_role_arn
}
</code></pre>
<p>As we see, for the role, we need to call another module i.e roles module, and access the resource <strong>lambda_role_arn</strong> which is defined in the roles module. As we cannot directly access the resources between each module, we have to explicitly expose it as an output as will be done next. </p>
<ul>
<li>Create an <strong>iam.tf</strong> file in module/role folder and add the below content.</li>
</ul>
<pre><code class="lang-sh">resource <span class="hljs-string">"aws_iam_role"</span> <span class="hljs-string">"lambda_role"</span> {
  name = <span class="hljs-string">"Hello-lambda-role"</span>
  assume_role_policy = data.template_file.lambda_assume_role_policy.rendered
}
</code></pre>
<ul>
<li>Create a <strong>source.tf</strong> file in the module/role folder and add the below content.</li>
</ul>
<pre><code class="lang-sh">data <span class="hljs-string">"template_file"</span> <span class="hljs-string">"lambda_assume_role_policy"</span> {
  template = file(<span class="hljs-string">"<span class="hljs-variable">${path.module}</span>/templates/lambda_assume_role_policy.json"</span>)
}
</code></pre>
<ul>
<li>Create a folder <strong>module/role/templates</strong> and add the below policy.</li>
</ul>
<pre><code class="lang-sh">{
  <span class="hljs-string">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
  <span class="hljs-string">"Statement"</span>: [
    {
      <span class="hljs-string">"Sid"</span>: <span class="hljs-string">"Lambda"</span>,
      <span class="hljs-string">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
      <span class="hljs-string">"Principal"</span>: {
        <span class="hljs-string">"Service"</span>: <span class="hljs-string">"lambda.amazonaws.com"</span>
      },
      <span class="hljs-string">"Action"</span>: <span class="hljs-string">"sts:AssumeRole"</span>
    }
  ]
}
</code></pre>
<p>Now, as we cannot access the resources from one module into another module, we need to explicitly create an output and expose the resource for another module.</p>
<ul>
<li>Create a <strong>outputs.tf</strong> file in the module/role folder and add the below output.</li>
</ul>
<pre><code class="lang-sh">output <span class="hljs-string">"lambda_role_arn"</span> {
  value = aws_iam_role.lambda_role.arn
}
</code></pre>
<p>Now, we can access lambda_role_arn in another module, so we can use <code>aws_lambda_function_role_arn = module.roles.lambda_role_arn</code> in our lambda module as shown below.</p>
<pre><code class="lang-sh">module <span class="hljs-string">"lambdas"</span> {
  <span class="hljs-built_in">source</span> = <span class="hljs-string">"./modules/lambda"</span>
  aws_lambda_function_role_arn = module.roles.lambda_role_arn
}
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>In this blog post, we saw what are Terraform modules and what a Terraform scope is. We have learned how to use the resources from one module in another module.</p>
<p><strong>GitHub Repo:</strong>  <a target="_blank" href="https://github.com/rahulmlokurte/aws-usage/tree/main/terraform/terraform-modules">https://github.com/rahulmlokurte/aws-usage/tree/main/terraform/terraform-modules</a></p>
]]></content:encoded></item><item><title><![CDATA[Create EC2 instance using Terraform]]></title><description><![CDATA[Terraform code is written in a language called HCL in files with the extension .tf. It is a
declarative language, so your goal is to describe the infrastructure you want, and
Terraform will figure out how to create it. Terraform can create infrastruc...]]></description><link>https://rahullokurte.com/create-ec2-instance-using-terraform</link><guid isPermaLink="true">https://rahullokurte.com/create-ec2-instance-using-terraform</guid><category><![CDATA[AWS]]></category><category><![CDATA[ec2]]></category><category><![CDATA[Terraform]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Mon, 13 Sep 2021 17:07:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1631552809525/hqPRt7ifD.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Terraform code is written in a language called HCL in files with the extension .tf. It is a
declarative language, so your goal is to describe the infrastructure you want, and
Terraform will figure out how to create it. Terraform can create infrastructure across a
wide variety of platforms using providers, including AWS, Azure, GCP, and many others.</p>
<p>In this blog post, we will create an AWS EC2 instance using Terraform.</p>
<h2 id="create-an-aws-ec2-instance">Create an AWS EC2 instance</h2>
<ul>
<li>Create a file called main.tf and put the following code in it:</li>
</ul>
<pre><code class="lang-sh">provider <span class="hljs-string">"aws"</span> {
region = <span class="hljs-string">"ap-south-1b"</span>
}
</code></pre>
<p>This tells Terraform that you are going to be using the AWS provider and that you wish
to deploy your infrastructure in the ap-south-1b region.</p>
<ul>
<li>Add the following code to main.tf, which uses the aws_instance resource to deploy
an EC2 Instance</li>
</ul>
<pre><code class="lang-sh">resource <span class="hljs-string">"aws_instance"</span> <span class="hljs-string">"hello"</span> {
ami = <span class="hljs-string">"ami-0d51ghh9cbfagedf68"</span>
instance_type = <span class="hljs-string">"t2.micro"</span>
}
</code></pre>
<p>The general syntax for a Terraform resource is:</p>
<pre><code class="lang-sh">resource <span class="hljs-string">"&lt;PROVIDER&gt;_&lt;TYPE&gt;"</span> <span class="hljs-string">"&lt;NAME&gt;"</span> {
[CONFIG …]
}
</code></pre>
<p>Where PROVIDER is the name of a provider (e.g., aws), TYPE is the type of resources
to create in that provider (e.g., instance), NAME is an identifier you can use throughout the Terraform code to refer to this resource (e.g., hello), and CONFIG consists of one or more arguments that are specific to that resource (e.g., ami = "aim 0d51ghh9cbfagedf68").</p>
<ul>
<li>In a terminal, go into the folder where you created main.tf , and run the <code>terraform
init</code></li>
</ul>
<pre><code class="lang-sh">$ terraform init
Initializing the backend...
Initializing provider plugins...
- Checking <span class="hljs-keyword">for</span> available provider plugins...
- Downloading plugin <span class="hljs-keyword">for</span> provider <span class="hljs-string">"aws"</span>
....
Terraform has been successfully initialized!
</code></pre>
<p><code>terraform init</code> will tell Terraform to scan the code, figure out what providers you’re
using, and download the code for them. By default, the provider code will be
downloaded into a .terraform folder.</p>
<ul>
<li>Now that you have the provider code downloaded, run the <code>terraform plan</code>
command:</li>
</ul>
<pre><code class="lang-sh">$ terraform plan
Refreshing Terraform state in-memory prior to plan...
(...)
+ aws_instance.example
ami: <span class="hljs-string">"ami-4567803n"</span>
availability_zone: <span class="hljs-string">"&lt;computed&gt;"</span>
ebs_block_device.<span class="hljs-comment">#: "&lt;computed&gt;"</span>
ephemeral_block_device.<span class="hljs-comment">#: "&lt;computed&gt;"</span>
instance_state: <span class="hljs-string">"&lt;computed&gt;"</span>
instance_type: <span class="hljs-string">"t2.micro"</span>
key_name: <span class="hljs-string">"&lt;computed&gt;"</span>
network_interface_id: <span class="hljs-string">"&lt;computed&gt;"</span>
placement_group: <span class="hljs-string">"&lt;computed&gt;"</span>
private_dns: <span class="hljs-string">"&lt;computed&gt;"</span>
private_ip: <span class="hljs-string">"&lt;computed&gt;"</span>
public_dns: <span class="hljs-string">"&lt;computed&gt;"</span>
public_ip: <span class="hljs-string">"&lt;computed&gt;"</span>
root_block_device.<span class="hljs-comment">#: "&lt;computed&gt;"</span>
security_groups.<span class="hljs-comment">#: "&lt;computed&gt;"</span>
source_dest_check: <span class="hljs-string">"true"</span>
subnet_id: <span class="hljs-string">"&lt;computed&gt;"</span>
tenancy: <span class="hljs-string">"&lt;computed&gt;"</span>
vpc_security_group_ids.<span class="hljs-comment">#: "&lt;computed&gt;"</span>
Plan: 1 to add, 0 to change, 0 to destroy.
</code></pre>
<p>The plan command lets you see what Terraform will do before actually doing it. This is a great way to sanity-check your changes before unleashing them onto the world. The output of the plan command is a little like the output of the diff command: resources with a plus sign (+) are going to be created, resources with a minus sign (-) are going to be deleted, and resources with a tilde sign (~) are going to be modified in-place. In the output above, you can see that Terraform is planning on creating a single EC2 Instance and nothing else, which is exactly what we want.</p>
<ul>
<li>To actually create the instance, run the <code>terraform apply</code> command:</li>
</ul>
<pre><code class="lang-sh">$ terraform apply
(...)
Terraform will perform the following actions:
<span class="hljs-comment"># aws_instance.example will be created</span>
+ resource <span class="hljs-string">"aws_instance"</span> <span class="hljs-string">"hello"</span> {
+ ami = <span class="hljs-string">"ami-0c55b159cbfafe1f0"</span>
+ arn = (known after apply)
+ associate_public_ip_address = (known after apply)
+ availability_zone = (known after apply)
+ cpu_core_count = (known after apply)
+ cpu_threads_per_core = (known after apply)
+ get_password_data = <span class="hljs-literal">false</span>
+ host_id = (known after apply)
+ id = (known after apply)
+ instance_state = (known after apply)
+ instance_type = <span class="hljs-string">"t2.micro"</span>
+ ipv6_address_count = (known after apply)
+ ipv6_addresses = (known after apply)
+ key_name = (known after apply)
(...)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only <span class="hljs-string">'yes'</span> will be accepted to approve.
Enter a value:
</code></pre>
<p>You’ll notice that the <code>terraform apply</code> command shows you the same plan output and asks you to confirm if you actually want to proceed with this plan. So while the plan is available as a separate command, it’s mainly useful for quick sanity checks and during code reviews, and most of the time you’ll run apply directly and review the planned output it shows you.
Type in “yes” and hit enter to deploy the EC2 Instance.</p>
<pre><code class="lang-sh">Do you want to perform these actions?
Terraform will perform the actions described above.
Only <span class="hljs-string">'yes'</span> will be accepted to approve.
Enter a value: yes
aws_instance.example: Creating…
aws_instance.example: Still creating… [10s elapsed]
aws_instance.example: Still creating… [20s elapsed]
aws_instance.example: Creation complete after 26s
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
</code></pre>
<p>Congrats, you’ve just deployed a server with Terraform! To verify this, you can log in to the EC2 console, and you’ll see something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1631552562500/rUCKWX_Pr.png" alt="ec2-1.png" /></p>
<h2 id="conclusion">Conclusion</h2>
<p>In this blog post, we saw how to provision the EC2 instance using Terraform. We also learned the terraform commands.</p>
<p>Github Link: <a target="_blank" href="https://github.com/rahulmlokurte">https://github.com/rahulmlokurte</a></p>
]]></content:encoded></item><item><title><![CDATA[Amazon Virtual Private Cloud using Terraform]]></title><description><![CDATA[When you create any resources in AWS, by default it will be accessible to the Internet. Anyone with internet access can see the resources and interact with them. How do we secure the resources? How do we authorize only certain entities to interact wi...]]></description><link>https://rahullokurte.com/amazon-virtual-private-cloud-using-terraform</link><guid isPermaLink="true">https://rahullokurte.com/amazon-virtual-private-cloud-using-terraform</guid><category><![CDATA[AWS]]></category><category><![CDATA[Terraform]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Sat, 11 Sep 2021 19:45:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1631389468418/V255LFLb8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When you create any resources in AWS, by default it will be accessible to the Internet. Anyone with internet access can see the resources and interact with them. How do we secure the resources? How do we authorize only certain entities to interact with those resources? The answer to the question is using Amazon Virtual Private Cloud. In this, we will define our own virtual network and then deploy the resources into it. It is like having our own data center, but with all the utilities and goodies from AWS.</p>
<p>In this blog post, we will do the following below items: </p>
<ul>
<li>Create a VPC</li>
<li>Create Subnets</li>
<li>Create an Internet Gateway</li>
<li>Create Route Tables</li>
<li>Create Routes in RouteTable</li>
<li>Connect Route Table to Subnets</li>
<li>Create a Security Group</li>
</ul>
<h2 id="create-a-vpc">Create a VPC</h2>
<p>When you create an AWS account, you will get the default VPC in your account. We can create a custom VPC by using Terraform resource <strong>aws_vpc</strong>. The required attribute is <strong>cidr_block</strong>.  It is the short form of Classless Inter-Domain Routing block. It provides the required number of IP addresses to the users. It also has a number after '/' which indicates, the number of bits for the network ID. In cidr_block, each number is of 8bits. so, in the example 10.0.0.0/16, there are a total of 8 * 4 = 32 bits and 16 is the number of bits that represent network ID. So, first 16 bits that is, 10.0 is fixed and from the remaining 16 bits, we will be able to create 2 ^ 16 unique IP addresses. </p>
<pre><code class="lang-sh">resource <span class="hljs-string">"aws_vpc"</span> <span class="hljs-string">"vpc-learning"</span> {
  cidr_block = <span class="hljs-string">"10.0.0.0/16"</span>
  tags = {
    <span class="hljs-string">"Name"</span> = <span class="hljs-string">"vpc-learning"</span>
  }
}
</code></pre>
<h2 id="create-subnets">Create Subnets</h2>
<p>If we have a large network, then it becomes complex to manage the network. So, it is logical divided into Subnets. There are two types of Subnets. One is a private subnet that is not exposed to the outside world and another is a public subnet that is exposed to the internet. </p>
<p>We can create a Subnet by using Terraform resource <strong>aws_subnet</strong>. The required attribute is <strong>cidr_block</strong> and <strong>VPC</strong>, in which the subnet needs to be created. Here the cidr_block will have fixed the first 16 bits as defined by the VPC which we created earlier. Here 24 bits represent network ID. So, we have only 8 bits, which can be changed. So, we will be able to create 2 ^ 8 unique IP addresses. The <strong>map_public_ip_on_launch</strong> indicates that the public IP address will be assigned to the instances within the subnet. </p>
<pre><code class="lang-sh">resource <span class="hljs-string">"aws_subnet"</span> <span class="hljs-string">"vpc-learning-public-subnet-1"</span> {
  cidr_block = <span class="hljs-string">"10.0.1.0/24"</span>
  vpc_id = aws_vpc.vpc-learning.id
  map_public_ip_on_launch = <span class="hljs-literal">true</span>
  availability_zone = <span class="hljs-string">"ap-south-1a"</span>
}
</code></pre>
<h2 id="create-an-internet-gateway">Create an Internet Gateway</h2>
<p>As the public IP address are assigned earlier, we can allow the instances to be accessible through the internet. we can use the <strong>aws_internet_gateway</strong> terraform resource and we need to point to the vpc as shown below.</p>
<pre><code class="lang-sh">resource <span class="hljs-string">"aws_internet_gateway"</span> <span class="hljs-string">"vpc-learning-internet-gateway"</span> {
  vpc_id = aws_vpc.vpc-learning.id
}
</code></pre>
<h2 id="create-route-tables">Create Route Tables</h2>
<p>We can create Route Tables using the <strong>aws_route_table</strong> resource with terraform.</p>
<pre><code class="lang-sh">resource <span class="hljs-string">"aws_route_table"</span> <span class="hljs-string">"vpc-learning-public-route-table"</span> {
  vpc_id = aws_vpc.vpc-learning.id
}
</code></pre>
<h2 id="create-routes-in-routetable">Create Routes in RouteTable</h2>
<p>We need to create a Routes in the RouteTable which was created earlier. We can use <strong>aws_route</strong> resource using terraform. Also, we make sure that these routes will be access through the internet. So, we need to provide the internet gateway using the attribute <strong>gateway_id </strong> as shown below.</p>
<pre><code class="lang-sh">resource <span class="hljs-string">"aws_route"</span> <span class="hljs-string">"vpc-learning-public-route"</span> {
  route_table_id = aws_route_table.vpc-learning-public-route-table.id
  destination_cidr_block = <span class="hljs-string">"0.0.0.0/0"</span>
  gateway_id = aws_internet_gateway.vpc-learning-internet-gateway.id
}
</code></pre>
<h2 id="connect-route-table-to-subnets">Connect Route Table to Subnets</h2>
<p>Once the route tables are created, we need to associate the route table to the Subnets which were created earlier. We can use <strong>aws_route_table_association</strong> resource of terraform.</p>
<pre><code class="lang-sh">resource <span class="hljs-string">"aws_route_table_association"</span> <span class="hljs-string">"vpc-learning-public-route-table-association"</span> {
  route_table_id = aws_route_table.vpc-learning-public-route-table.id
  subnet_id = aws_subnet.vpc-learning-public-subnet-1.id
}
</code></pre>
<h2 id="create-a-security-group">Create a Security Group</h2>
<p>We need to define the security group to control the incoming and outgoing network calls. we can use the <strong>aws_security_group</strong> resource in terraform. We need to give the incoming and outgoing network call by using terraform attributes <strong>ingress</strong> and <strong>outgress</strong> respectively. Below, we are only allowing incoming from SSH i.e from 22 port and from HTTP i.e. 80 port, and the outgoing to unrestricted.</p>
<pre><code class="lang-sh">resource <span class="hljs-string">"aws_security_group"</span> <span class="hljs-string">"vpc-learning-security-group"</span> {
  name = <span class="hljs-string">"vpc-learning-default-security-group"</span>
  vpc_id = aws_vpc.vpc-learning.id
  ingress {
    from_port = 22
    protocol = <span class="hljs-string">"tcp"</span>
    to_port = 22
    cidr_blocks = [var.ingress_egress_cidr_blocks]
  }
  ingress {
    from_port = 80
    protocol = <span class="hljs-string">"tcp"</span>
    to_port = 80
    cidr_blocks = [var.ingress_egress_cidr_blocks]
  }

  egress {
    from_port = 0
    protocol = <span class="hljs-string">"-1"</span>
    to_port = 0
    cidr_blocks = [var.ingress_egress_cidr_blocks]
  }
}
</code></pre>
<h2 id="deploy-using-terraform">Deploy using Terraform</h2>
<p>Let us initiate the directory with terraform by issuing a command <code>terraform init</code>.</p>
<p>Once done, issue a command <code>terraform plan -out plan-out</code> where it will display what all resources will be provisioned and put the plan in the file named <strong>plan-out</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1631388567261/s8WBsyAju.png" alt="vpc-1.png" /></p>
<p>Now, we can apply the actual resource by using the command <code>terraform apply "plan-out"</code></p>
<h2 id="verify-the-resources-in-aws-console">Verify the resources in AWS Console.</h2>
<ul>
<li>VPC</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1631388785963/ZUfgxrpXq.png" alt="vpc-2.png" /></p>
<ul>
<li>Subnets</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1631388904658/YKVnC-Fsz.png" alt="vpc-3.png" /></p>
<ul>
<li>Internet Gateway</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1631388981823/lTH-ewr4i.png" alt="vpc-4.png" /></p>
<ul>
<li>Route Table showing association with subnets.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1631389120765/MRqhebv0g.png" alt="vpc-5.png" /></p>
<ul>
<li>Security Groups showing Inbound and outbound</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1631389240091/BGcwX9-ub.png" alt="vpc-6.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1631389279978/S5NN418hl.png" alt="vpc-7.png" /></p>
<h2 id="conclusion">Conclusion</h2>
<p>In this blog post, we have looked into the working of virtual private cloud in your AWS. We have looked into creating VPC and Subnets. Also, we have exposed the endpoints using the internet gateway and created a route table with subnet association. We also looked into how the security group helps us with the traffic to the network. </p>
<p><strong>GitHub Repo:</strong> <a target="_blank" href="https://github.com/rahulmlokurte/aws-usage/tree/main/terraform/vpc-learning">https://github.com/rahulmlokurte/aws-usage/tree/main/terraform/vpc-learning</a></p>
]]></content:encoded></item><item><title><![CDATA[How to invoke an AWS Lambda Function at scheduled intervals with AWS EventBridge Rule using Terraform]]></title><description><![CDATA[Amazon EventBridge is a serverless event bus service that you can use to connect your applications with data from a variety of sources. Events are central to EventBridge and the events are observable. Event bridge also supports integration with many ...]]></description><link>https://rahullokurte.com/how-to-invoke-an-aws-lambda-function-at-scheduled-intervals-with-aws-eventbridge-rule-using-terraform</link><guid isPermaLink="true">https://rahullokurte.com/how-to-invoke-an-aws-lambda-function-at-scheduled-intervals-with-aws-eventbridge-rule-using-terraform</guid><category><![CDATA[AWS]]></category><category><![CDATA[Terraform]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Wed, 08 Sep 2021 14:05:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1631109872024/V2xirtzcM.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://aws.amazon.com/eventbridge/"><strong>Amazon EventBridge</strong></a> is a serverless event bus service that you can use to connect your applications with data from a variety of sources. Events are central to EventBridge and the events are observable. Event bridge also supports integration with many SAAS-based applications and third-party applications. It also supports sending events to other AWS services like Lambda functions, step functions.</p>
<p>In this blog, we will set up a <strong>rule</strong> to run the lambda function on schedule every 2 minutes using <a target="_blank" href="https://www.terraform.io/"><strong>Terraform</strong></a>.</p>
<h2 id="project-structure">Project Structure</h2>
<ul>
<li><p>Create a new directory and move to the directory</p>
<pre><code class="lang-sh">mkdir lambda-schedule-event-bridge &amp;&amp; <span class="hljs-built_in">cd</span> lambda-schedule-event-bridge
</code></pre>
</li>
<li><p>Create a resource folder to store the code for lambda</p>
<pre><code class="lang-sh">mkdir resources
</code></pre>
</li>
</ul>
<h2 id="create-a-lambda">Create a Lambda</h2>
<p>Create a folder <strong>profile-generator-lambda</strong> in the <strong>resources</strong> folder and add the <strong>index.js</strong> file with the below content. Also, initialize the node project and install the dependencies.</p>
<pre><code class="lang-sh">mkdir profile-generator-lambda &amp;&amp; <span class="hljs-built_in">cd</span> profile-generator-lambda
touch index.js
npm init -y
npm install faker
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> faker = <span class="hljs-built_in">require</span>(<span class="hljs-string">"faker/locale/en_IND"</span>);

<span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> (event, context) =&gt; {
  <span class="hljs-keyword">let</span> firstName = faker.name.firstName();
  <span class="hljs-keyword">let</span> lastName = faker.name.lastName();
  <span class="hljs-keyword">let</span> phoneNumber = faker.phone.phoneNumber();
  <span class="hljs-keyword">let</span> vehicleType = faker.vehicle.vehicle();

  <span class="hljs-keyword">let</span> response = {
    <span class="hljs-attr">firstName</span>: firstName,
    <span class="hljs-attr">lastName</span>: lastName,
    <span class="hljs-attr">phoneNumber</span>: phoneNumber,
    <span class="hljs-attr">vehicleType</span>: vehicleType,
  };

  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
    },
    <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
      <span class="hljs-attr">profile</span>: response,
    }),
  };
};
</code></pre>
<h2 id="terraform-providers">Terraform Providers</h2>
<p>Now, we will write the terraform scripts in HCL language where we will utilize the AWS provider. Create a main.tf file in the root directory of the project and add the below content.</p>
<pre><code class="lang-sh">touch main.tf
</code></pre>
<pre><code class="lang-sh">terraform {
  required_providers {
    aws = {
      <span class="hljs-built_in">source</span>  = <span class="hljs-string">"hashicorp/aws"</span>
      version = <span class="hljs-string">"3.50.0"</span>
    }
  }
}

provider <span class="hljs-string">"aws"</span> {
  <span class="hljs-comment"># Configuration options</span>
  region                  = var.region
  profile                 = var.aws_profile
  shared_credentials_file = var.shared_credentials_file
  default_tags {
    tags = var.tags
  }
}
</code></pre>
<ul>
<li><p>Each Terraform module must declare which providers it requires so that Terraform can install and use them. Provider requirements are declared in a _requiredproviders block.</p>
</li>
<li><p>AWS providers require configurations like cloud regions, profiles, credential_files to be available before they can be used.</p>
</li>
<li><p>All the values will be provided from the variables parameters.</p>
</li>
</ul>
<p>Let's create <strong>variables.tf</strong> in the root directory of the project and add the below content.</p>
<pre><code class="lang-sh">touch variables.tf
</code></pre>
<pre><code class="lang-sh">variable <span class="hljs-string">"region"</span> {
  description = <span class="hljs-string">"Deployment Region"</span>
  default     = <span class="hljs-string">"ap-south-1"</span>
}

variable <span class="hljs-string">"aws_profile"</span> {
  description = <span class="hljs-string">"Given name in the credential file"</span>
  <span class="hljs-built_in">type</span>        = string
  default     = <span class="hljs-string">"rahul-admin"</span>
}

variable <span class="hljs-string">"shared_credentials_file"</span> {
  description = <span class="hljs-string">"Profile file with credentials to the AWS account"</span>
  <span class="hljs-built_in">type</span>        = string
  default     = <span class="hljs-string">"~/.aws/credentials"</span>
}

variable <span class="hljs-string">"tags"</span> {
  description = <span class="hljs-string">"A map of tags to add to all resources."</span>
  <span class="hljs-built_in">type</span>        = map(string)
  default = {
    application = <span class="hljs-string">"Learning-Tutor"</span>
    env         = <span class="hljs-string">"Test"</span>
  }
}
</code></pre>
<p>Each input variable accepted by a module must be declared using a variable block. The label after the variable keyword is a name for the variable, which must be unique among all variables in the same module.</p>
<ul>
<li>A default value makes the variable optional.</li>
<li>A Type indicates what value types are accepted for the variable.</li>
<li>A description specifies the input variable's documentation.</li>
</ul>
<h2 id="terraform-modules">Terraform Modules</h2>
<p>Now, we will use the module terraform-aws-modules/lambda/aws to create lambda and lambda layer infrastructure.</p>
<p>Create the files lambda.tf in the root directory of the project and add the below content.</p>
<pre><code class="lang-sh">touch lambda.tf
</code></pre>
<pre><code class="lang-sh">module <span class="hljs-string">"profile_generator_lambda"</span> {
  <span class="hljs-built_in">source</span>  = <span class="hljs-string">"terraform-aws-modules/lambda/aws"</span>
  version = <span class="hljs-string">"2.7.0"</span>
  <span class="hljs-comment"># insert the 28 required variables here</span>
  function_name = <span class="hljs-string">"profile-generator-lambda"</span>
  description   = <span class="hljs-string">"Generates a new profiles"</span>
  handler       = <span class="hljs-string">"index.handler"</span>
  runtime       = <span class="hljs-string">"nodejs14.x"</span>
  source_path   = <span class="hljs-string">"<span class="hljs-variable">${path.module}</span>/resources/profile-generator-lambda"</span>

  tags = {
    Name = <span class="hljs-string">"profile-generator-lambda"</span>
  }
}
</code></pre>
<h2 id="eventbridge-rule">EventBridge Rule</h2>
<p>Create the files <strong>event_bridge.tf</strong> in the root directory of the project and add the below content.</p>
<pre><code class="lang-sh">touch event_bridge.tf
</code></pre>
<pre><code class="lang-sh">resource <span class="hljs-string">"aws_cloudwatch_event_rule"</span> <span class="hljs-string">"profile_generator_lambda_event_rule"</span> {
  name = <span class="hljs-string">"profile-generator-lambda-event-rule"</span>
  description = <span class="hljs-string">"retry scheduled every 2 min"</span>
  schedule_expression = <span class="hljs-string">"rate(2 minutes)"</span>
}

resource <span class="hljs-string">"aws_cloudwatch_event_target"</span> <span class="hljs-string">"profile_generator_lambda_target"</span> {
  arn = module.profile_generator_lambda.lambda_function_arn
  rule = aws_cloudwatch_event_rule.profile_generator_lambda_event_rule.name
}

resource <span class="hljs-string">"aws_lambda_permission"</span> <span class="hljs-string">"allow_cloudwatch_to_call_rw_fallout_retry_step_deletion_lambda"</span> {
  statement_id = <span class="hljs-string">"AllowExecutionFromCloudWatch"</span>
  action = <span class="hljs-string">"lambda:InvokeFunction"</span>
  function_name = module.profile_generator_lambda.lambda_function_name
  principal = <span class="hljs-string">"events.amazonaws.com"</span>
  source_arn = aws_cloudwatch_event_rule.profile_generator_lambda_event_rule.arn
}
</code></pre>
<p>As can be seen above, the <strong>schedule_expression</strong> attribute has a rate of 2 minutes. It means, it triggers the lambda function which is in the target using the resource <strong>aws_cloudwatch_event_target.profile_generator_lambda_target</strong>. We also give lambda permission, so that, the event will be able to invoke the function.</p>
<h2 id="run-terraform-scripts">Run Terraform scripts</h2>
<p>Let us run the 3 basic commands of terraform to create the resources in AWS.</p>
<ul>
<li>Initialize the Terraform which will download all the providers and modules used in the configuration.</li>
</ul>
<pre><code class="lang-sh">terraform init
</code></pre>
<ul>
<li>Check if a plan matches the expectation and also store the plan in output file plan-out</li>
</ul>
<pre><code class="lang-sh">terraform plan -out  <span class="hljs-string">"plan-out"</span>
</code></pre>
<ul>
<li>Once the plan is verified, apply the changes to get the desired infrastructure components.</li>
</ul>
<pre><code class="lang-sh">terraform apply <span class="hljs-string">"plan-out"</span>
</code></pre>
<h2 id="verify-the-infrastructure-on-aws">Verify the infrastructure on AWS</h2>
<ul>
<li>Lambda with Event Bridge</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1631108515528/ke3rRgKIg.png" alt="event-bridge-1.png" /></p>
<p>If you check the cloud watch logs, you can see that the lambda function is getting invoked every 2 minutes.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1631108595881/OI3SrcL6t.png" alt="event-bridge-2.png" /></p>
<h2 id="delete-the-resources">Delete the resources</h2>
<p>We can delete the resources created by terraform by issuing the below command so that we do not get billed for the resources.</p>
<pre><code class="lang-sh">terraform destroy
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>In this blog post, we saw how to create a lambda function and event bridge rule using terraform. We also saw, how to schedule the lambda function every 2 minutes by creating an event bus rule.</p>
<p><strong>Github Repository</strong> : <a target="_blank" href="https://github.com/rahulmlokurte/aws-usage/tree/main/terraform/lambda-schedule-event-bridge">https://github.com/rahulmlokurte/aws-usage/tree/main/terraform/lambda-schedule-event-bridge</a></p>
]]></content:encoded></item><item><title><![CDATA[Creating Kubernetes Cluster Using Kind on local computer]]></title><description><![CDATA[Kubernetes is an open-source tool for automating deployment, scaling, and management of containerized applications. 
Developers push the code multiple times a day. Packaging our applications as containers, enables our application to be updated multip...]]></description><link>https://rahullokurte.com/creating-kubernetes-cluster-using-kind-on-local-computer</link><guid isPermaLink="true">https://rahullokurte.com/creating-kubernetes-cluster-using-kind-on-local-computer</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Tue, 31 Aug 2021 14:12:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1630411671114/GmDYTHihl.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://kubernetes.io/">Kubernetes</a> is an open-source tool for automating deployment, scaling, and management of containerized applications. </p>
<p>Developers push the code multiple times a day. Packaging our applications as containers, enables our application to be updated multiple times without downtime. Kubernetes helps us to run the containerized applications.</p>
<p>We can create a Kubernetes cluster in our local computer by using tools such as <a target="_blank" href="https://minikube.sigs.k8s.io/docs/">minikube</a> and <a target="_blank" href="https://kind.sigs.k8s.io/">kind</a>. In this blog, we will look into the <strong>Kind</strong>.</p>
<h2 id="prerequisite">Prerequisite:</h2>
<ul>
<li><strong>Docker</strong></li>
</ul>
<h2 id="tools">Tools :</h2>
<ul>
<li><p><strong>kubectl </strong>- A Kubernetes command-line tool that allows us to run commands against Kubernetes clusters. We can deploy applications, inspect and monitor the cluster resources, and view logs using kubectl.</p>
</li>
<li><p><strong>kind</strong> - It is a tool that lets us run the Kubernetes cluster on our local computer. </p>
</li>
</ul>
<h2 id="install-kubectl">Install Kubectl</h2>
<ol>
<li><p><a target="_blank" href="https://kubernetes.io/docs/tasks/tools/install-kubectl-macos/">macOS</a></p>
</li>
<li><p><a target="_blank" href="https://kubernetes.io/docs/tasks/tools/install-kubectl-windows/">Windows</a></p>
</li>
</ol>
<h2 id="install-kind">Install Kind</h2>
<h3 id="macos">macOS</h3>
<pre><code class="lang-sh"><span class="hljs-variable">$brew</span> install kind
</code></pre>
<h3 id="windows">Windows</h3>
<pre><code class="lang-sh"><span class="hljs-variable">$curl</span>.exe -Lo kind-windows-amd64.exe https://kind.sigs.k8s.io/dl/v0.11.1/kind-windows-amd64
<span class="hljs-variable">$Move</span>-Item .\kind-windows-amd64.exe c:\some-dir-in-your-PATH\kind.exe
</code></pre>
<h2 id="create-a-kubernetes-cluster">Create a Kubernetes cluster</h2>
<p>Once kind is installed, create a kubernetes cluster using the command</p>
<pre><code class="lang-sh"><span class="hljs-variable">$kind</span> create cluster --name learning-kubernetes-dev
</code></pre>
<p>When the cluster is created, the cluster configuration file is stored at the location <code>${home}/.kube/config</code></p>
<p>We can create multiple kubernetes clusters. Let us define one more cluster for <strong>production</strong></p>
<pre><code class="lang-sh"><span class="hljs-variable">$kind</span> create cluster --name learning-kubernetes-prod
</code></pre>
<p>To verify, if the cluster has been created, issue the below command.</p>
<pre><code class="lang-sh"><span class="hljs-variable">$kind</span> get clusters

learning-kubernetes-dev
learning-kubernetes-prod
</code></pre>
<p>To interact with a particular cluster, we need to specify the <code>kind-&lt;cluster-name&gt;</code> as a context in kubectl as shown below.</p>
<pre><code class="lang-sh">➜  <span class="hljs-variable">$kubectl</span> cluster-info --context kind-learning-kubernetes-dev
Kubernetes control plane is running at https://127.0.0.1:52083
CoreDNS is running at https://127.0.0.1:52083/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

➜  <span class="hljs-variable">$kubectl</span> cluster-info --context kind-learning-kubernetes-prod
Kubernetes control plane is running at https://127.0.0.1:52449
CoreDNS is running at https://127.0.0.1:52449/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
</code></pre>
<p>To verify which context is currently used, we can issue the below command.</p>
<pre><code class="lang-sh"><span class="hljs-variable">$kubectl</span> config get-contexts

CURRENT   NAME                            CLUSTER                         AUTHINFO                        NAMESPACE
          kind-learning-kubernetes-dev    kind-learning-kubernetes-dev    kind-learning-kubernetes-dev
*         kind-learning-kubernetes-prod   kind-learning-kubernetes-prod   kind-learning-kubernetes-prod
</code></pre>
<p>The <em> indicates the current cluster. So the current cluster context is <em>*kind-learning-kubernetes-prod</em></em></p>
<p>To switch between the clusters, we can issue the below command.</p>
<pre><code class="lang-sh"><span class="hljs-variable">$kubectl</span> config use-context kind-learning-kubernetes-dev

Switched to context <span class="hljs-string">"kind-learning-kubernetes-dev"</span>
</code></pre>
<p>Let us verify again, this time, the current context is <strong>kind-learning-kubernetes-dev</strong></p>
<pre><code class="lang-sh"><span class="hljs-variable">$kubectl</span> config get-contexts
CURRENT   NAME                            CLUSTER                         AUTHINFO                        NAMESPACE
*         kind-learning-kubernetes-dev    kind-learning-kubernetes-dev    kind-learning-kubernetes-dev
          kind-learning-kubernetes-prod   kind-learning-kubernetes-prod   kind-learning-kubernetes-prod
</code></pre>
<h2 id="namespace">Namespace</h2>
<p>The virtual clusters which are created within the physical cluster are called <strong>namespaces</strong>. If we have multiple teams/projects, we can use the same cluster, but for each team/project, we can define the namespaces.</p>
<p>If we do not define any namespaces, then we get a <code>default</code> namespace. We can use the below command to see the namespaces.</p>
<pre><code class="lang-sh">kubectl get namespaces

NAME                 STATUS   AGE
default              Active   23m
</code></pre>
<h3 id="create-a-namespace">Create a namespace</h3>
<p>We can define multiple namespaces in kubernetes clusters. In our <strong>learning-kubernetes-dev</strong>, we can have two namespaces. One is used by team-1 and the other is used by team-2.</p>
<p>Let's first switch the context to create a namespace in the <strong>learning-kubernetes-dev</strong> cluster</p>
<pre><code class="lang-sh"><span class="hljs-variable">$kubectl</span> config use-context kind-learning-kubernetes-dev
</code></pre>
<p>create a file called team1-namespace.json with the below content.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"apiVersion"</span>: <span class="hljs-string">"v1"</span>,
  <span class="hljs-attr">"kind"</span>: <span class="hljs-string">"Namespace"</span>,
  <span class="hljs-attr">"metadata"</span>: {
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"team-1"</span>,
    <span class="hljs-attr">"labels"</span>: {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"team-1"</span>
    }
  }
}
</code></pre>
<p>Use the below command to create a namespace.</p>
<pre><code class="lang-sh"><span class="hljs-variable">$kubectl</span> create -f team1-namespace.json

namespace/team-1 created
</code></pre>
<p>create a file called team2-namespace.json with the below content.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"apiVersion"</span>: <span class="hljs-string">"v1"</span>,
  <span class="hljs-attr">"kind"</span>: <span class="hljs-string">"Namespace"</span>,
  <span class="hljs-attr">"metadata"</span>: {
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"team-2"</span>,
    <span class="hljs-attr">"labels"</span>: {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"team-2"</span>
    }
  }
}
</code></pre>
<p>Use the below command to create a namespace.</p>
<pre><code class="lang-sh"><span class="hljs-variable">$kubectl</span> create -f team2-namespace.json

namespace/team-2 created
</code></pre>
<p>Use the below command to verify the namespaces.</p>
<pre><code class="lang-sh"><span class="hljs-variable">$kubectl</span> get namespace

NAME                 STATUS   AGE
default              Active   44m
team-1               Active   2m13s
team-2               Active   18s
</code></pre>
<h3 id="create-a-pod-in-a-namespace">Create a Pod in a Namespace</h3>
<p>A Kubernetes namespace provides the scope for Pods, Services, and Deployments in the cluster.</p>
<p>let's create a simple Deployment and Pods for both Team-1 and Team-2 namespace. Team-1 needs an NGINX server of 1 instance and Team-2 needs 2 instances.</p>
<pre><code class="lang-sh"><span class="hljs-variable">$kubectl</span> create deployment nginx-team-1 --image nginx -n team-1 --replicas=1

deployment.apps/nginx-team-1 created
</code></pre>
<pre><code class="lang-sh"><span class="hljs-variable">$kubectl</span> create deployment nginx-team-2 --image nginx -n team-2 --replicas=2
deployment.apps/nginx-team-2 created
</code></pre>
<p>Let's verify the number of pods created for team-1 and team-2</p>
<pre><code class="lang-sh">kubectl get pods -n team-1

NAME                            READY   STATUS    RESTARTS   AGE
nginx-team-1-544dd7f5bd-qk5g6   1/1     Running   0          113s
</code></pre>
<pre><code class="lang-sh">kubectl get pods -n team-2

NAME                            READY   STATUS    RESTARTS   AGE
nginx-team-2-55f89d7667-2wd9n   1/1     Running   0          87s
nginx-team-2-55f89d7667-8xd95   1/1     Running   0          87s
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>In this blog post, we saw how easy it is to create a Kubernetes cluster in your local computer. Once installed, we can play with Kubernetes and it helps us learn about pods, deployments. We saw how to create a namespace so that we can use one cluster with multiple teams deploying applications on the same cluster. </p>
<p><strong>GitHub Repository:</strong> <a target="_blank" href="https://github.com/rahulmlokurte/devops-stuff/tree/main/kubernetes/kubernetes-kind">https://github.com/rahulmlokurte/devops-stuff/tree/main/kubernetes/kubernetes-kind</a></p>
<p>This is just the starting point. Check out the below documents to get into the depth of Kubernetes.</p>
<ul>
<li>https://kubernetes.io/</li>
<li>https://kind.sigs.k8s.io/docs/user/quick-start/</li>
<li>https://www.katacoda.com/courses/kubernetes/playground</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How do you write a Basic Terraform Workflow]]></title><description><![CDATA[Terraform is an open-source infrastructure as code (IAC) tool. It allows us to build, change and version the infrastructure efficiently. It allows us to declaratively define a set of configuration files. We can evolve our infrastructure as move on to...]]></description><link>https://rahullokurte.com/how-do-you-write-a-basic-terraform-workflow</link><guid isPermaLink="true">https://rahullokurte.com/how-do-you-write-a-basic-terraform-workflow</guid><category><![CDATA[AWS]]></category><category><![CDATA[Terraform]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[2Articles1Week]]></category><dc:creator><![CDATA[Rahul Lokurte]]></dc:creator><pubDate>Fri, 27 Aug 2021 17:12:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1630084220493/k1xCIcGMp.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Terraform is an open-source infrastructure as code (IAC) tool. It allows us to build, change and version the infrastructure efficiently. It allows us to declaratively define a set of configuration files. We can evolve our infrastructure as move on to provisioning different cloud resources. For example, you may want to create a VPC and on top of it, you want to provision security group rules and within that environment, we want to spin up some virtual machines. With terraform, we define the high-level VPC resources and the fields that are required to create the element of our infrastructure. It also allows us to refer to other components of our infrastructure. Terraform gives us a workflow for creating and managing our infrastructure resources.</p>
<p>There are three main commands that we run when we want to provision an Infrastructure.</p>
<ul>
<li><code>terraform init</code>: This command is used to initialize the working directory containing terraform configuration files. It is safe to run multiple times. The command will never delete your existing infrastructure or a state. During initialization, it searches the terraform configuration files for the providers defined. These providers are published as a plugin. Once it finds the providers, it tries to download and install the providers in the terraform directory.</li>
<li><code>terraform plan</code>: With this command, terraform figures out what it needs to do. It sees the real provisioned resources and compares it to the desired configuration. If Terraform detects that no changes are needed to resource instances or to root module output values, <code>terraform plan</code> will report that no actions need to be taken. You can use the optional <code>-out=FILE</code> option to save the generated plan to a file on disk.</li>
<li><code>terraform apply</code>: This command provisions the resources proposed in the <code>terraform plan</code>. Before actually applying the plan, it prompts us to confirm the changes. We can also pass the filename of a saved plan file created earlier with <code>terraform plan -out=...</code>. In this case, Terraform will apply the changes in the plan without any confirmation prompt. </li>
</ul>
<p>When we provide the terraform configurations, the terraform builds a dependency graph. By looking into the dependency graph, it can make sure that the resources are provisioned accordingly. Whenever we run the <code>terraform plan</code>and <code>terraform apply</code>, the terraform looks into a dependency graph to generate the plans and refresh the states.</p>
<h2 id="workflow-in-action">Workflow in Action:</h2>
<h3 id="1-create-a-new-directory-and-write-the-configuration-files">1. Create a new directory and write the configuration Files.</h3>
<pre><code class="lang-sh"><span class="hljs-variable">$mkdir</span> terraform-in-action &amp;&amp; <span class="hljs-built_in">cd</span> terraform-in-action

<span class="hljs-variable">$touch</span> main.tf
</code></pre>
<p>Add the below code in <code>main.tf</code></p>
<pre><code class="lang-json">terraform {
  required_providers {
    aws = {
      source  = <span class="hljs-attr">"hashicorp/aws"</span>
      version = <span class="hljs-attr">"3.50.0"</span>
    }
  }
}

provider <span class="hljs-string">"aws"</span> {
  # Configuration options
  region                  = var.region
  profile                 = var.aws_profile
  shared_credentials_file = var.shared_credentials_file
  default_tags {
    tags = var.tags
  }
}
</code></pre>
<p>You might see an error on the line <code>12</code>,<code>13</code>,<code>14</code>. It is because, we are referencing the variables, which are not present. So let us create a new file <code>variable.tf</code></p>
<pre><code class="lang-sh"><span class="hljs-variable">$touch</span> variables.tf
</code></pre>
<p>Add the below code in <code>variables.tf</code></p>
<pre><code class="lang-json">variable <span class="hljs-string">"region"</span> {
  description = <span class="hljs-attr">"Deployment Region"</span>
  default     = <span class="hljs-attr">"ap-south-1"</span>
}

variable <span class="hljs-string">"aws_profile"</span> {
  description = <span class="hljs-attr">"Given name in the credential file"</span>
  type        = string
  default     = <span class="hljs-attr">"rahul-admin"</span>
}

variable <span class="hljs-string">"shared_credentials_file"</span> {
  description = <span class="hljs-attr">"Profile file with credentials to the AWS account"</span>
  type        = string
  default     = <span class="hljs-attr">"~/.aws/credentials"</span>
}

variable <span class="hljs-string">"tags"</span> {
  description = <span class="hljs-attr">"A map of tags to add to all resources."</span>
  type        = map(string)
  default = {
    application = <span class="hljs-attr">"Learning-Tutor"</span>
    env         = <span class="hljs-attr">"Test"</span>
  }
}
</code></pre>
<p>Here you can define the <strong>region</strong> and <strong>aws_profile</strong>. We have to point to the location of credentials of the AWS account. We can also define the tags.</p>
<p>Let us now create the new file <code>lambda.tf</code> to create the lambda function.</p>
<pre><code class="lang-sh">$ touch lambda.tf
</code></pre>
<p>Add the below code in <code>lambda.tf</code></p>
<pre><code class="lang-json">module <span class="hljs-string">"profile_generator_lambda"</span> {
  source  = <span class="hljs-attr">"terraform-aws-modules/lambda/aws"</span>
  version = <span class="hljs-attr">"2.7.0"</span>
  # insert the 28 required variables here
  function_name = <span class="hljs-attr">"profile-generator-lambda"</span>
  description   = <span class="hljs-attr">"Generates a new profiles"</span>
  handler       = <span class="hljs-attr">"index.handler"</span>
  runtime       = <span class="hljs-attr">"nodejs14.x"</span>
  source_path   = <span class="hljs-attr">"${path.module}/resources/profile-generator-lambda"</span>
  tags = {
    Name = <span class="hljs-attr">"profile-generator-lambda"</span>
  }
}
</code></pre>
<ul>
<li><p><strong>source</strong> field is used to indicate the terraform module which we intend to use.</p>
</li>
<li><p><strong>function_name</strong> field is the unique name for our lambda function.</p>
</li>
<li><p><strong>handler</strong> field is the Lambda Function entry point for our code.</p>
</li>
<li><p><strong>runtime</strong> field is Lambda Function runtime</p>
</li>
<li><p><strong>source_path</strong> field is the absolute path to a local file or directory containing our Lambda source code</p>
</li>
</ul>
<p>Let us now create a file <strong>index.js</strong> in the folder <strong>resources/profile-generator-lambda</strong></p>
<pre><code class="lang-sh"><span class="hljs-variable">$mkdir</span> resources/profile-generator-lambda
<span class="hljs-variable">$touch</span> index.js
</code></pre>
<p>Add the below code in <strong>index.js</strong></p>
<pre><code class="lang-json">const faker = require(<span class="hljs-string">"faker/locale/en_IND"</span>);

exports.handler = async (event, context) =&gt; {
  let firstName = faker.name.firstName();
  let lastName = faker.name.lastName();
  let phoneNumber = faker.phone.phoneNumber();
  let vehicleType = faker.vehicle.vehicle();

  let response = {
    firstName: firstName,
    lastName: lastName,
    phoneNumber: phoneNumber,
    vehicleType: vehicleType,
  };

  return {
    statusCode: <span class="hljs-number">200</span>,
    headers: {
      <span class="hljs-attr">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
    },
    body: JSON.stringify({
      profile: response,
    }),
  };
};
</code></pre>
<p>Let us initialize the folder <strong>resources/profile-generator-lambda</strong> with <strong>npm</strong> and also install the <strong>faker</strong> dependency which is used in our lambda code to get the random profiles.</p>
<pre><code class="lang-sh"><span class="hljs-variable">$npm</span> init &amp;&amp; <span class="hljs-variable">$npm</span> install faker
</code></pre>
<h3 id="2-terraform-init">2. Terraform Init</h3>
<p>At the root of the folder, let us now initialize the directory, which will download all the providers and modules used in the configuration.</p>
<pre><code class="lang-sh"><span class="hljs-variable">$cd</span> ../../
<span class="hljs-variable">$terraform</span> init
</code></pre>
<h3 id="3-terraform-plan">3. Terraform plan</h3>
<p>Let us check if a plan matches the expectation and also store the plan in output file <strong><em>plan-out</em></strong></p>
<pre><code class="lang-sh"><span class="hljs-variable">$terraform</span> plan --out <span class="hljs-string">"plan-out"</span>
</code></pre>
<h3 id="4-terraform-apply">4. Terraform apply</h3>
<p>Once the plan is verified, apply the changes to get the desired infrastructure components.</p>
<pre><code class="lang-sh"><span class="hljs-variable">$terraform</span> apply <span class="hljs-string">"plan-out"</span>
</code></pre>
<p>Now, let us verify the infrastructure created on the AWS console.</p>
<ul>
<li><strong>Lambda Function</strong></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1626527660871/HAtUNLrtw.png" alt="lambda_function.png" /></p>
<ul>
<li><strong>Test the Lambda Function</strong></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1626527672074/JzBKxiiQS.png" alt="lambda_function_output.png" /></p>
<h2 id="conclusion">Conclusion</h2>
<p>In this blog post, we saw how the basic terraform workflow works and also, how it maintains the dependency graph. We also saw a basic example involving the Lambda function.</p>
]]></content:encoded></item></channel></rss>