<?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[RAJAN PANCHAL]]></title><description><![CDATA[Learn AWS concepts with complete solutions, hands on and free code repository]]></description><link>https://blog.rajanpanchal.net</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1598848797840/w7qbhA2Pj.png</url><title>RAJAN PANCHAL</title><link>https://blog.rajanpanchal.net</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 06:34:25 GMT</lastBuildDate><atom:link href="https://blog.rajanpanchal.net/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[What is Lambda Layers and how to use it with Java runtime?]]></title><description><![CDATA[When developing serverless functions,  you often need additional dependencies or libraries for the function to work. For Lambda functions in java,  you need amazon dependencies of the services used in the function. Then you zip the function and its d...]]></description><link>https://blog.rajanpanchal.net/what-is-lambda-layers-and-how-to-use-it-with-java-runtime</link><guid isPermaLink="true">https://blog.rajanpanchal.net/what-is-lambda-layers-and-how-to-use-it-with-java-runtime</guid><category><![CDATA[aws lambda]]></category><category><![CDATA[Java]]></category><category><![CDATA[Cloud]]></category><category><![CDATA[tutorials]]></category><dc:creator><![CDATA[Rajan Panchal]]></dc:creator><pubDate>Thu, 19 Nov 2020 05:02:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1605760880768/fQPUAF724.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When developing serverless functions,  you often need additional dependencies or libraries for the function to work. For <a target="_blank" href="https://rajanpanchal.net/write-your-first-aws-lambda-in-java/">Lambda functions in java</a>,  you need amazon dependencies of the services used in the function. Then you zip the function and its dependencies into a fat jar and upload it to the Lambda function. If you are not familiar with creating lambda function in java then refer to <a target="_blank" href="https://rajanpanchal.net/write-your-first-aws-lambda-in-java">this</a> post for a quick start.</p>
<p>The fat jar approach works fine but if you have multiple functions that use the same dependencies then creating a fat jar of each function is not a good approach and maintenance of such code is also difficult. A change in the version of a jar requires a change in each of the individual lambda functions.</p>
<p>A more elegant solution is to use Lambda layers. </p>
<h3 id="what-is-lambda-layers">What is Lambda Layers?</h3>
<p>A layer is a ZIP archive that contains libraries, a custom runtime, or other dependencies. With layers, you can use libraries in your function without needing to include them in your deployment package. Layers let you keep your deployment package small, which makes development easier.</p>
<p>Layers are extracted to the /opt directory in the function execution environment. Each runtime looks for libraries in a different location under /opt, depending on the language.</p>
<p>You can specify up to 5 layers in your function's configuration, during or after function creation. You choose a specific version of a layer to use. If you want to use a different version later, update your function's configuration.</p>
<h3 id="lets-see-it-in-action">Let's see it in action</h3>
<p>We will write a basic Lambda function that would just print lambda environment variables and event details. We will create 2 maven projects. In the first project, we include amazon and other essential dependencies. This project will serve as our Lambda Layer. In the second project, we include our first project as a dependency and will write the Lambda handler. </p>
<p>Create the first maven project called Lambda-Layer-Base and update pom as below:
This project contains all common dependencies required by the functions. In our case, we added <code>lamdba-core</code> and <code>gson</code> dependency. We also added <code>maven-shade-plugin</code> to create fat jar. </p>
<pre><code class="lang-Pom.xml">&lt;project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;groupId&gt;net.rajanpanchal&lt;/groupId&gt;
    &lt;artifactId&gt;lambda-layer-base&lt;/artifactId&gt;
    &lt;version&gt;1&lt;/version&gt;
    &lt;name&gt;Lambda-Layer-Base&lt;/name&gt;
    &lt;description&gt;Lambda base&lt;/description&gt;
    &lt;properties&gt;
        &lt;maven.compiler.source&gt;1.8&lt;/maven.compiler.source&gt;
        &lt;maven.compiler.target&gt;1.8&lt;/maven.compiler.target&gt;
        &lt;aws.java.sdk.version&gt;2.14.11&lt;/aws.java.sdk.version&gt;
    &lt;/properties&gt;
    &lt;dependencyManagement&gt;
        &lt;dependencies&gt;
            &lt;dependency&gt;
                &lt;groupId&gt;software.amazon.awssdk&lt;/groupId&gt;
                &lt;artifactId&gt;bom&lt;/artifactId&gt;
                &lt;version&gt;${aws.java.sdk.version}&lt;/version&gt;
                &lt;type&gt;pom&lt;/type&gt;
                &lt;scope&gt;import&lt;/scope&gt;
            &lt;/dependency&gt;
        &lt;/dependencies&gt;
    &lt;/dependencyManagement&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.amazonaws&lt;/groupId&gt;
            &lt;artifactId&gt;aws-lambda-java-core&lt;/artifactId&gt;
            &lt;version&gt;1.2.0&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;com.google.code.gson&lt;/groupId&gt;
            &lt;artifactId&gt;gson&lt;/artifactId&gt;
            &lt;version&gt;2.8.6&lt;/version&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
    &lt;build&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
                &lt;configuration&gt;
                    &lt;source&gt;${maven.compiler.source}&lt;/source&gt;
                    &lt;target&gt;${maven.compiler.target}&lt;/target&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-shade-plugin&lt;/artifactId&gt;
                &lt;version&gt;3.2.4&lt;/version&gt;
                &lt;executions&gt;
                    &lt;execution&gt;
                        &lt;phase&gt;package&lt;/phase&gt;
                        &lt;goals&gt;
                            &lt;goal&gt;shade&lt;/goal&gt;
                        &lt;/goals&gt;
                    &lt;/execution&gt;
                &lt;/executions&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;
&lt;/project&gt;
</code></pre>
<p>This project will only contain dependencies. Now execute maven <code>clean install</code> and it should generate a jar (<code>lambda-layer-base-1.jar</code>) in the target folder.</p>
<p>Create another maven project and update pom with the below contents. We included our first project as a dependency (<code>lambda-layer-base</code>). Also note, we no longer use maven shade plugin since we don't want dependencies to be included in the jar.</p>
<pre><code class="lang-POM.xml">&lt;project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;groupId&gt;net.rajanpanchal&lt;/groupId&gt;
    &lt;artifactId&gt;lambda-java-demo&lt;/artifactId&gt;
    &lt;version&gt;1&lt;/version&gt;
    &lt;packaging&gt;jar&lt;/packaging&gt;
    &lt;name&gt;Lambda-Layers-Demo&lt;/name&gt;
    &lt;description&gt;HelloWorld Lambda Layers Demo&lt;/description&gt;
    &lt;properties&gt;
        &lt;maven.compiler.source&gt;1.8&lt;/maven.compiler.source&gt;
        &lt;maven.compiler.target&gt;1.8&lt;/maven.compiler.target&gt;
        &lt;aws.java.sdk.version&gt;2.14.11&lt;/aws.java.sdk.version&gt;
    &lt;/properties&gt;
    &lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;net.rajanpanchal&lt;/groupId&gt;
        &lt;artifactId&gt;lambda-layer-base&lt;/artifactId&gt;
        &lt;version&gt;1&lt;/version&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
    &lt;build&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
                &lt;configuration&gt;
                    &lt;source&gt;${maven.compiler.source}&lt;/source&gt;
                    &lt;target&gt;${maven.compiler.target}&lt;/target&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;
&lt;/project&gt;
</code></pre>
<p>Add a class file in src/main/java that will contain Lambda logic. Here we are just logging some stuff.</p>
<pre><code class="lang-Java"><span class="hljs-keyword">package</span> net.rajanpanchal.handlers;

<span class="hljs-keyword">import</span> java.util.Map;

<span class="hljs-keyword">import</span> com.amazonaws.services.lambda.runtime.Context;
<span class="hljs-keyword">import</span> com.amazonaws.services.lambda.runtime.LambdaLogger;
<span class="hljs-keyword">import</span> com.amazonaws.services.lambda.runtime.RequestHandler;
<span class="hljs-keyword">import</span> com.google.gson.Gson;
<span class="hljs-keyword">import</span> com.google.gson.GsonBuilder;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HelloWorldHandler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">RequestHandler</span>&lt;<span class="hljs-title">Map</span>&lt;<span class="hljs-title">String</span>,<span class="hljs-title">String</span>&gt;, <span class="hljs-title">String</span>&gt;</span>{

      <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">handleRequest</span><span class="hljs-params">(Map&lt;String,String&gt; event, Context context)</span>
      </span>{
        LambdaLogger logger = context.getLogger();
        String response = <span class="hljs-keyword">new</span> String(<span class="hljs-string">"200 OK"</span>);
        logger.log(<span class="hljs-string">"ENVIRONMENT:"</span>+System.getenv());
        <span class="hljs-comment">// log execution details</span>
        Gson gson = <span class="hljs-keyword">new</span> GsonBuilder().setPrettyPrinting().create();
        logger.log(<span class="hljs-string">"ENVIRONMENT VARIABLES: "</span> + gson.toJson(System.getenv()));
        logger.log(<span class="hljs-string">"CONTEXT: "</span> + gson.toJson(context));
        <span class="hljs-comment">// process event</span>
        logger.log(<span class="hljs-string">"EVENT: "</span> + gson.toJson(event));
        logger.log(<span class="hljs-string">"EVENT TYPE: "</span> + event.getClass().toString());
        <span class="hljs-keyword">return</span> response;
      }
    }
</code></pre>
<p>Execute maven <code>package</code> command to create the jar for the project. A jar (<code>lambda-java-demo-1.jar</code>) should get created in target folder.</p>
<h3 id="creating-lamdba-layer">Creating Lamdba Layer</h3>
<p>Before we head to the console to create the layer, let's complete one important step. Remember that layers gets extracted to a folder in /opt directory. You need to package the layer in folder structure as per your runtime. For java, you need to package jar in <code>java/lib</code> folder. The final folder structure,  in the lambda runtime, would be <code>/opt/java/lib/&lt;your jar&gt;</code>. So now create folders java/lib and copy jar from the first project and zip the folder. Now we are ready to upload the jar as lambda layer.</p>
<p><img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/layers/Layer_create.PNG" alt="Lambda Layers" /></p>
<h3 id="creating-lambda-function">Creating Lambda function</h3>
<p>Now create the lambda function and specify the layer that we just created. Also, upload the jar from the second project as our function code.</p>
<p><img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/layers/lambda-function.PNG" alt="Lambda Layers create function" /></p>
<p>Add Layers to the function</p>
<p><img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/layers/layer_add.PNG" alt="Lambda Layers add " /></p>
<p>Upload function code jar and update runtime settings
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/layers/lambda.PNG" alt="Lambda Layers add " /></p>
<p>Create a test event and execute the test.</p>
<p><img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/layers/test.PNG" alt="Lambda Layers add " /></p>
<h3 id="source-code">Source Code:</h3>
<p><a target="_blank" href="https://github.com/rajanpanchal/lambda-layers"><img src="https://github-readme-stats.vercel.app/api/pin/?username=rajanpanchal&amp;repo=lambda-layers" alt="AWS Lambda dynamic content" /></a></p>
<h3 id="conclusion">Conclusion</h3>
<p>Lambda layer makes your development easy and error-free. It reduces the size of the function deployment package. You can use the same layer in multiple functions. </p>
<p><em>If you like the post, feel free to share and follow me on <a target="_blank" href="https://twitter.com/PanchalRajan">Twitter</a> for updates!</em></p>
]]></content:encoded></item><item><title><![CDATA[How to create dynamic content using AWS Lambda?]]></title><description><![CDATA[Hello Everyone! Today we are going to see how you can generate dynamic content in  Lamdba. We are going to use Java 8, Apache Maven, AWS SDK for Java, Apache Velocity to achieve this.
Uses Cases
There are several scenarios where you want to create dy...]]></description><link>https://blog.rajanpanchal.net/how-to-create-dynamic-content-using-aws-lambda</link><guid isPermaLink="true">https://blog.rajanpanchal.net/how-to-create-dynamic-content-using-aws-lambda</guid><category><![CDATA[AWS]]></category><category><![CDATA[Java]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[tutorials]]></category><dc:creator><![CDATA[Rajan Panchal]]></dc:creator><pubDate>Wed, 21 Oct 2020 03:38:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1601178404614/I1nUcZp2C.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello Everyone! Today we are going to see how you can generate dynamic content in  Lamdba. We are going to use Java 8, Apache Maven, AWS SDK for Java, Apache Velocity to achieve this.</p>
<h2 id="uses-cases">Uses Cases</h2>
<p>There are several scenarios where you want to create dynamic content. For example, to create an HTML page response for an API method instead of returning just data in JSON/ XML. The other scenario is to generate a file or message with dynamic content. For instance, create welcome letters for your customers.</p>
<h2 id="lets-dive-in">Let's Dive In</h2>
<p>We are going to create welcome letters for our credit card customers. The letter's template is :</p>
<pre><code class="lang-Template">
Hello ${name},

Say hi to your new credit card!
Your credit card number is ${creditCardNumber}.

Regards,
Your Credit Company
</code></pre>
<p>Lambda would populate <code>name</code> and <code>credit card number</code>. The generated file is uploaded to S3 for further processing like printing and mailing.</p>
<h4 id="step-1">STEP 1:</h4>
<p>Create an empty maven project and update pom to the following contents:</p>
<pre><code class="lang-XML">
<span class="hljs-tag">&lt;<span class="hljs-name">project</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0"</span>
    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>
    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">modelVersion</span>&gt;</span>4.0.0<span class="hljs-tag">&lt;/<span class="hljs-name">modelVersion</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>net.rajanpanchal<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>lambda-java<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span>Dynamic-content-Lambda<span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">description</span>&gt;</span>Generate Dynamic content in Lambda<span class="hljs-tag">&lt;/<span class="hljs-name">description</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">properties</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">maven.compiler.source</span>&gt;</span>1.8<span class="hljs-tag">&lt;/<span class="hljs-name">maven.compiler.source</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">maven.compiler.target</span>&gt;</span>1.8<span class="hljs-tag">&lt;/<span class="hljs-name">maven.compiler.target</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">aws.java.sdk.version</span>&gt;</span>2.14.11<span class="hljs-tag">&lt;/<span class="hljs-name">aws.java.sdk.version</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">properties</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dependencyManagement</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>software.amazon.awssdk<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>bom<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>${aws.java.sdk.version}<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">type</span>&gt;</span>pom<span class="hljs-tag">&lt;/<span class="hljs-name">type</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>import<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependencyManagement</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-s3 --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.amazonaws<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>aws-java-sdk-s3<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.11.865<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.amazonaws<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>aws-lambda-java-core<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.2.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.velocity<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>velocity-engine-parent<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.2<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">type</span>&gt;</span>pom<span class="hljs-tag">&lt;/<span class="hljs-name">type</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.velocity<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>velocity-tools<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">build</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">plugins</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.maven.plugins<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>maven-compiler-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">configuration</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">source</span>&gt;</span>${maven.compiler.source}<span class="hljs-tag">&lt;/<span class="hljs-name">source</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">target</span>&gt;</span>${maven.compiler.target}<span class="hljs-tag">&lt;/<span class="hljs-name">target</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">configuration</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.maven.plugins<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>maven-shade-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.2.4<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">executions</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">execution</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">phase</span>&gt;</span>package<span class="hljs-tag">&lt;/<span class="hljs-name">phase</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">goals</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">goal</span>&gt;</span>shade<span class="hljs-tag">&lt;/<span class="hljs-name">goal</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">goals</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">execution</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">executions</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">plugins</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">build</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">project</span>&gt;</span>
</code></pre>
<p>We are adding AWS dependencies and apache velocity dependencies. The <code>maven-shade-plugin</code> to create a fat jar.</p>
<h4 id="step-2">STEP 2:</h4>
<p>We will have our template, discussed earlier, in the <code>resources</code> folder (<code>src/main/resources</code>) with the name <code>welcomeLetter.vm</code>. </p>
<h4 id="step-3">STEP 3:</h4>
<p>Create a class file with the following content:</p>
<pre><code class="lang-Java"><span class="hljs-keyword">package</span> net.rajanpanchal.handlers;

<span class="hljs-keyword">import</span> java.io.File;
<span class="hljs-keyword">import</span> java.io.FileWriter;
<span class="hljs-keyword">import</span> java.util.Map;
<span class="hljs-keyword">import</span> java.util.Properties;

<span class="hljs-keyword">import</span> org.apache.velocity.Template;
<span class="hljs-keyword">import</span> org.apache.velocity.VelocityContext;
<span class="hljs-keyword">import</span> org.apache.velocity.app.VelocityEngine;
<span class="hljs-keyword">import</span> org.apache.velocity.exception.ParseErrorException;
<span class="hljs-keyword">import</span> org.apache.velocity.exception.ResourceNotFoundException;
<span class="hljs-keyword">import</span> org.apache.velocity.runtime.RuntimeConstants;

<span class="hljs-keyword">import</span> com.amazonaws.regions.Regions;
<span class="hljs-keyword">import</span> com.amazonaws.services.lambda.runtime.Context;
<span class="hljs-keyword">import</span> com.amazonaws.services.lambda.runtime.RequestHandler;
<span class="hljs-keyword">import</span> com.amazonaws.services.s3.AmazonS3;
<span class="hljs-keyword">import</span> com.amazonaws.services.s3.AmazonS3ClientBuilder;
<span class="hljs-keyword">import</span> com.amazonaws.services.s3.model.ObjectMetadata;
<span class="hljs-keyword">import</span> com.amazonaws.services.s3.model.PutObjectRequest;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WelcomeLetterGeneratorHandler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">RequestHandler</span>&lt;<span class="hljs-title">Map</span>&lt;<span class="hljs-title">String</span>, <span class="hljs-title">String</span>&gt;, <span class="hljs-title">String</span>&gt; </span>{
    Regions clientRegion = Regions.US_EAST_1;
    String fileObjKeyName = <span class="hljs-string">"welcome_letter.txt"</span>;
    String bucketName = <span class="hljs-string">"welcomelettersbucket"</span>;
    <span class="hljs-keyword">static</span> VelocityContext vContext;
    <span class="hljs-keyword">static</span> Template t;
    AmazonS3 s3Client = AmazonS3ClientBuilder.standard().withRegion(clientRegion).build();
    <span class="hljs-keyword">static</span> {
        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">// Create a new Velocity Engine</span>
            VelocityEngine velocityEngine = <span class="hljs-keyword">new</span> VelocityEngine();
            <span class="hljs-comment">// Set properties that allow reading vm file from classpath.</span>
            Properties p = <span class="hljs-keyword">new</span> Properties();
            velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, <span class="hljs-string">"file,class"</span>);
            velocityEngine.setProperty(<span class="hljs-string">"class.resource.loader.class"</span>,
                    <span class="hljs-string">"org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"</span>);
            velocityEngine.init(p);
            t = velocityEngine.getTemplate(<span class="hljs-string">"welcomeLetter.vm"</span>);
            vContext = <span class="hljs-keyword">new</span> VelocityContext();

        } <span class="hljs-keyword">catch</span> (Exception e) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(e);

        }
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">handleRequest</span><span class="hljs-params">(Map&lt;String, String&gt; event, Context context)</span> </span>{
        String response = <span class="hljs-keyword">null</span>;
        LambdaLogger logger = context.getLogger();
        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">// Add data to velocity context</span>
            vContext.put(<span class="hljs-string">"name"</span>, <span class="hljs-string">"Rajan"</span>);
            vContext.put(<span class="hljs-string">"creditCardNumber"</span>, <span class="hljs-string">"1234 5678 9012 3456"</span>);
            File f = <span class="hljs-keyword">new</span> File(<span class="hljs-string">"/tmp/"</span>+fileObjKeyName);

            FileWriter writer = <span class="hljs-keyword">new</span> FileWriter(f);
            <span class="hljs-comment">// merge template and Data</span>
            t.merge(vContext, writer);
            writer.flush();
            writer.close();

            <span class="hljs-comment">// Upload a file as a new object with ContentType and title specified.</span>
            PutObjectRequest request = <span class="hljs-keyword">new</span> PutObjectRequest(bucketName, fileObjKeyName, f);
            ObjectMetadata metadata = <span class="hljs-keyword">new</span> ObjectMetadata();
            metadata.setContentType(<span class="hljs-string">"plain/text"</span>);
            metadata.addUserMetadata(<span class="hljs-string">"title"</span>, <span class="hljs-string">"Welcome Letter"</span>);
            request.setMetadata(metadata);
            s3Client.putObject(request);
            response  = <span class="hljs-keyword">new</span> String(<span class="hljs-string">"Success"</span>);
        } <span class="hljs-keyword">catch</span> (Exception ex) {
            response =  <span class="hljs-keyword">new</span> String(<span class="hljs-string">"Error:"</span>+ex.getMessage());
        }

        <span class="hljs-keyword">return</span> response;
    }
}
</code></pre>
<p>In the <code>static</code> block we are creating a velocity engine, setting resource loaders for the engine ('class' and 'file' resource loaders). Then we get the template and create a context to hold data to populate on the template.</p>
<p>In Lambda handler <code>handleRequest</code>, we add data like name and credit card number to the context, and using a FileWriter object we merge the template and the data. Note that we create the file in <code>/tmp/</code> directory in the Lambda execution environment. Lambda provides some disk space to the function to use. Then we upload this file to the S3 bucket (<code>welcomelettersbucket</code>) with the filename <code>welcome_letter.txt</code>. My bucket exists in US-EAST-1 as indicated by the variable <code>clientRegion</code>.</p>
<h4 id="step-4">STEP 4</h4>
<p>Now execute maven <code>package</code> command to generate the fat jar with all dependencies. 
A jar would be generated in the <strong>target</strong> folder.</p>
<h4 id="step-5">STEP 5:</h4>
<p>Go to AWS Console, create a Lambda function with runtime Java 8, let it create a default execution role. Upload the jar under <em>Function Code</em> section. Edit <em>Basic Settings</em> and add our handler package and method name</p>
<pre><code><span class="hljs-selector-tag">net</span><span class="hljs-selector-class">.rajanpanchal</span><span class="hljs-selector-class">.handlers</span><span class="hljs-selector-class">.WelcomeLetterGeneratorHandler</span><span class="hljs-selector-pseudo">::handleRequest</span>
</code></pre><h4 id="step-6">STEP 6:</h4>
<p>Add S3 access policy to the Lambda execution role.  Click on <em>Permissions</em> tab - &gt; <em>Execution Role</em> hyperlink. It will take you to the IAM Role. For simplicity, I added   <strong>AmazonS3FullAccess</strong> permission, but you can add fine-grain S3 access.</p>
<h4 id="step-7">STEP 7:</h4>
<p>Go back to lambda and create Test event. Lambda executed successfully.
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/Lambda-dynamic-content/success.PNG" alt="AWS Lamdba Dynamic Content" /></p>
<p>Check S3 bucket, the file is saved.
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/Lambda-dynamic-content/file_generated.PNG" alt="AWS Lambda Dynamic Content" /></p>
<p><img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/Lambda-dynamic-content/file_content.PNG" alt="AWS Lambda Dynamic Content" /></p>
<p>The file is ready for further processing!</p>
<h4 id="source-code">Source Code:</h4>
<p><a target="_blank" href="https://github.com/rajanpanchal/Lambda-dynamic-content"><img src="https://github-readme-stats.vercel.app/api/pin/?username=rajanpanchal&amp;repo=Lambda-dynamic-content" alt="AWS Lambda dynamic content" /></a></p>
<h3 id="conclusion">Conclusion</h3>
<p>It's super easy to create dynamic content in Lambda using Apache Velocity and Java SDK. In addition, using lambda you only pay for what you use.   Let me know your thoughts on the article in the comments and how you are going to use this?</p>
<p><em>If you like the post, feel free to share and follow me on <a target="_blank" href="https://twitter.com/PanchalRajan">Twitter</a> for updates!</em></p>
]]></content:encoded></item><item><title><![CDATA[What Is IKEA Effect And How To Benefit From It?]]></title><description><![CDATA[For those who don't know,  IKEA is a Swedish multinational group designs and sells ready-to-assemble furniture, kitchen appliances, home accessories, etc.
What is IKEA Effect
The IKEA effect is a cognitive bias in which consumers place a disproportio...]]></description><link>https://blog.rajanpanchal.net/what-is-ikea-effect-and-how-to-benefit-from-it</link><guid isPermaLink="true">https://blog.rajanpanchal.net/what-is-ikea-effect-and-how-to-benefit-from-it</guid><category><![CDATA[General Advice]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[learn coding]]></category><category><![CDATA[learning]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Rajan Panchal]]></dc:creator><pubDate>Tue, 06 Oct 2020 20:21:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1601579174071/18i2wkqA0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For those who don't know,  IKEA is a Swedish multinational group designs and sells <strong>ready-to-assemble furniture</strong>, kitchen appliances, home accessories, etc.</p>
<h3 id="what-is-ikea-effect">What is IKEA Effect</h3>
<p>The IKEA effect is a cognitive bias in which consumers place a disproportionately high value on products they partially created. A 2011 study found that subjects were willing to pay more for furniture they had assembled themselves, than for equivalent pre-assembled items. </p>
<p>Now you might be wondering why we are discussing it on a tech blog? Read on and you would understand how it impacts you as an IT Professional.</p>
<h3 id="when-labor-leads-to-love">When Labor Leads to Love</h3>
<p>Normally, when you buy a product from IKEA it requires some level of assembly before the product can be used. The study found that when someone puts labor into something, they start liking it more, even if it is poorly constructed. Hence, the phenomenon,“IKEA effect”, is named in honor of the Swedish manufacturer whose products typically arrive with some
assembly required.  The study also found that the "IKEA Effect" dissipates when the creations are destroyed.  The creation has to stay intact for IKEA effect to be effective.</p>
<h3 id="how-you-can-use-ikea-effect-to-your-advantage">How you can use IKEA Effect to your advantage?</h3>
<p>You might have experienced the IKEA effect even if you didn't know the term before. To take advantage of this, you need to build things. No matter how small it is. If you are learning a new language, a library or a technology, think of a small business problem and try to solve it using the knowledge that you have.  For example, I am upskilling myself in AWS. So first I learn the concepts and then I think of a problem that can be solved using different AWS services.  Once you solve it you will value your solution more than anything else even if it's not of production grade or perfect. This will keep you motivate to learn more and create more. You can also contribute to open source projects. Open source projects are more popular because IKEA effect is in place. Your name gets connected to projects you make contribution to. This gives a sense of accomplishment and motivates you to contribute more.</p>
<p>Also, don't throw away your solution, instead preserve it using your favorite version control like GitHub, GitLab,  etc. Or you can also write blog about it. </p>
<p>Let me know your thoughts in comments !</p>
<p>Now, this post will be incomplete without these memes!</p>
<p><img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/ikea-effect/01.png" alt="IKEA Effect " /></p>
<p><img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/ikea-effect/02.jpg" alt="IKEA Effect " /></p>
<p><img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/ikea-effect/03.jpg" alt="IKEA Effect " /></p>
<p><img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/ikea-effect/04.jpg" alt="IKEA Effect " /></p>
<p><em>If you like my articles feel free to follow me on <a target="_blank" href="https://twitter.com/PanchalRajan">Twitter</a> for updates!</em></p>
]]></content:encoded></item><item><title><![CDATA[AWS Serverless Application Model : Guide to writing your first AWS SAM Application]]></title><description><![CDATA[Today we are going to see how one can write a serverless application using Serverless Application Model (SAM).
First, let's understand what Serverless Application Model is.
What is Serverless Application Model?
What do you do when you want to create ...]]></description><link>https://blog.rajanpanchal.net/aws-serverless-application-model-guide-to-writing-your-first-aws-sam-application</link><guid isPermaLink="true">https://blog.rajanpanchal.net/aws-serverless-application-model-guide-to-writing-your-first-aws-sam-application</guid><category><![CDATA[AWS]]></category><category><![CDATA[serverless]]></category><category><![CDATA[Cloud]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[Amazon Web Services]]></category><dc:creator><![CDATA[Rajan Panchal]]></dc:creator><pubDate>Tue, 29 Sep 2020 22:24:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1601089878740/sbM3Aue9H.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today we are going to see how one can write a serverless application using Serverless Application Model (SAM).
First, let's understand what Serverless Application Model is.</p>
<h3 id="what-is-serverless-application-model">What is Serverless Application Model?</h3>
<p>What do you do when you want to create a resource on AWS, for example, Lambda function? You log in to the AWS console and manually go to the Lambda service to create a function. Now say you want to create 10 such functions you have to do the same process 10 times. This can be error-prone and time-consuming. With SAM you can just write once and deploy as many times you want flawlessly. It is similar to Cloud Formation. You can say SAM is the superset of Cloudformation.</p>
<p>The AWS Serverless Application Model (SAM) is an open-source framework for building serverless applications. It provides shorthand syntax to express functions, APIs, databases, and event source mappings. With just a few lines of code, you can define the application you want and model it using YAML. During deployment, SAM transforms and expands the SAM syntax into AWS CloudFormation syntax, enabling you to build serverless applications faster. </p>
<p>With the use of Environment variables and parameters, you can dynamically name all your resources and create different environments like DEV, TEST, PROD with the same set of resources.</p>
<h3 id="getting-started-with-sam">Getting started with SAM</h3>
<p>To use SAM you will need SAM-CLI installed. SAM CLI provides a Lambda-like execution environment that lets you locally build, test, and debug applications defined by SAM templates. You can also use the SAM CLI to deploy your applications to AWS.
You can follow the guide <a target="_blank" href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html">here</a> to install SAM CLI.
Before we proceed make sure your user has all the permissions to create resources and configured on the machine. To configure AWS account, install AWS CLI and run <code>aws configure</code> command to set up credentials. I am using the admin user that has all the permissions. </p>
<p>Now we are ready to download sample application and examine its contents. Run the following command on terminal</p>
<pre><code class="lang-Command">sam init
</code></pre>
<p>It will ask you to make several choices. Note: you need to have your runtime already installed. </p>
<pre><code><span class="hljs-string">ubuntu@ubuntu-VirtualBox:~/MyWorkspace/BlogPosts/SAM$</span> <span class="hljs-string">sam</span> <span class="hljs-string">init</span>
<span class="hljs-string">Which</span> <span class="hljs-string">template</span> <span class="hljs-string">source</span> <span class="hljs-string">would</span> <span class="hljs-string">you</span> <span class="hljs-string">like</span> <span class="hljs-string">to</span> <span class="hljs-string">use?</span>
    <span class="hljs-number">1</span> <span class="hljs-bullet">-</span> <span class="hljs-string">AWS</span> <span class="hljs-string">Quick</span> <span class="hljs-string">Start</span> <span class="hljs-string">Templates</span>
    <span class="hljs-number">2</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Custom</span> <span class="hljs-string">Template</span> <span class="hljs-string">Location</span>
<span class="hljs-attr">Choice:</span> <span class="hljs-number">1</span>

<span class="hljs-string">Which</span> <span class="hljs-string">runtime</span> <span class="hljs-string">would</span> <span class="hljs-string">you</span> <span class="hljs-string">like</span> <span class="hljs-string">to</span> <span class="hljs-string">use?</span>
    <span class="hljs-number">1</span> <span class="hljs-bullet">-</span> <span class="hljs-string">nodejs12.x</span>
    <span class="hljs-number">2</span> <span class="hljs-bullet">-</span> <span class="hljs-string">python3.8</span>
    <span class="hljs-number">3</span> <span class="hljs-bullet">-</span> <span class="hljs-string">ruby2.7</span>
    <span class="hljs-number">4</span> <span class="hljs-bullet">-</span> <span class="hljs-string">go1.x</span>
    <span class="hljs-number">5</span> <span class="hljs-bullet">-</span> <span class="hljs-string">java11</span>
    <span class="hljs-number">6</span> <span class="hljs-bullet">-</span> <span class="hljs-string">dotnetcore3.1</span>
    <span class="hljs-number">7</span> <span class="hljs-bullet">-</span> <span class="hljs-string">nodejs10.x</span>
    <span class="hljs-number">8</span> <span class="hljs-bullet">-</span> <span class="hljs-string">python3.7</span>
    <span class="hljs-number">9</span> <span class="hljs-bullet">-</span> <span class="hljs-string">python3.6</span>
    <span class="hljs-number">10</span> <span class="hljs-bullet">-</span> <span class="hljs-string">python2.7</span>
    <span class="hljs-number">11</span> <span class="hljs-bullet">-</span> <span class="hljs-string">ruby2.5</span>
    <span class="hljs-number">12</span> <span class="hljs-bullet">-</span> <span class="hljs-string">java8.al2</span>
    <span class="hljs-number">13</span> <span class="hljs-bullet">-</span> <span class="hljs-string">java8</span>
    <span class="hljs-number">14</span> <span class="hljs-bullet">-</span> <span class="hljs-string">dotnetcore2.1</span>
<span class="hljs-attr">Runtime:</span> <span class="hljs-number">2</span>

<span class="hljs-string">Project</span> <span class="hljs-string">name</span> [<span class="hljs-string">sam-app</span>]<span class="hljs-string">:</span> <span class="hljs-string">SAM</span> <span class="hljs-string">application</span>

<span class="hljs-string">Cloning</span> <span class="hljs-string">app</span> <span class="hljs-string">templates</span> <span class="hljs-string">from</span> <span class="hljs-string">https://github.com/awslabs/aws-sam-cli-app-templates.git</span>

<span class="hljs-attr">AWS quick start application templates:</span>
    <span class="hljs-number">1</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Hello</span> <span class="hljs-string">World</span> <span class="hljs-string">Example</span>
    <span class="hljs-number">2</span> <span class="hljs-bullet">-</span> <span class="hljs-string">EventBridge</span> <span class="hljs-string">Hello</span> <span class="hljs-string">World</span>
    <span class="hljs-number">3</span> <span class="hljs-bullet">-</span> <span class="hljs-string">EventBridge</span> <span class="hljs-string">App</span> <span class="hljs-string">from</span> <span class="hljs-string">scratch</span> <span class="hljs-string">(100+</span> <span class="hljs-string">Event</span> <span class="hljs-string">Schemas)</span>
    <span class="hljs-number">4</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Step</span> <span class="hljs-string">Functions</span> <span class="hljs-string">Sample</span> <span class="hljs-string">App</span> <span class="hljs-string">(Stock</span> <span class="hljs-string">Trader)</span>
    <span class="hljs-number">5</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Elastic</span> <span class="hljs-string">File</span> <span class="hljs-string">System</span> <span class="hljs-string">Sample</span> <span class="hljs-string">App</span>
<span class="hljs-attr">Template selection:</span> <span class="hljs-number">1</span>

<span class="hljs-string">-----------------------</span>
<span class="hljs-attr">Generating application:</span>
<span class="hljs-string">-----------------------</span>
<span class="hljs-attr">Name:</span> <span class="hljs-string">SAM</span> <span class="hljs-string">application</span>
<span class="hljs-attr">Runtime:</span> <span class="hljs-string">python3.8</span>
<span class="hljs-attr">Dependency Manager:</span> <span class="hljs-string">pip</span>
<span class="hljs-attr">Application Template:</span> <span class="hljs-string">hello-world</span>
<span class="hljs-attr">Output Directory:</span> <span class="hljs-string">.</span>

<span class="hljs-string">Next</span> <span class="hljs-string">steps</span> <span class="hljs-string">can</span> <span class="hljs-string">be</span> <span class="hljs-string">found</span> <span class="hljs-string">in</span> <span class="hljs-string">the</span> <span class="hljs-string">README</span> <span class="hljs-string">file</span> <span class="hljs-string">at</span> <span class="hljs-string">./SAM</span> <span class="hljs-string">application/README.md</span>
</code></pre><p>Go to the file system and it would have created following file structure:</p>
<pre><code><span class="hljs-attribute">SAM</span> application/
   ├── README.md
   ├── events/
   │   └── event.json
   ├── hello_world/
   │   ├── __init__.py
   │   ├── app.py            <span class="hljs-comment">#Contains your AWS Lambda handler logic.</span>
   │   └── requirements.txt  <span class="hljs-comment">#Contains any Python dependencies the application requires, used for sam build</span>
   ├── template.yaml         <span class="hljs-comment">#Contains the AWS SAM template defining your application's AWS resources.</span>
   └── tests/
       └── unit/
           ├── __init__.py
           └── test_handler.py
</code></pre><p>Open template.yml and let's breakdown the contents:</p>
<pre><code class="lang-YAML"><span class="hljs-attr">AWSTemplateFormatVersion:</span> <span class="hljs-string">'2010-09-09'</span>
<span class="hljs-attr">Transform:</span> <span class="hljs-string">AWS::Serverless-2016-10-31</span>
<span class="hljs-attr">Description:</span> <span class="hljs-string">&gt;
  SAM application
</span>
  <span class="hljs-string">Sample</span> <span class="hljs-string">SAM</span> <span class="hljs-string">Template</span> <span class="hljs-string">for</span> <span class="hljs-string">SAM</span> <span class="hljs-string">application</span>

<span class="hljs-comment"># More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst</span>
<span class="hljs-attr">Globals:</span>
  <span class="hljs-attr">Function:</span>
    <span class="hljs-attr">Timeout:</span> <span class="hljs-number">3</span>

<span class="hljs-attr">Resources:</span>
  <span class="hljs-attr">HelloWorldFunction:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::Serverless::Function</span> <span class="hljs-comment"># More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-attr">CodeUri:</span> <span class="hljs-string">hello_world/</span>
      <span class="hljs-attr">Handler:</span> <span class="hljs-string">app.lambda_handler</span>
      <span class="hljs-attr">Runtime:</span> <span class="hljs-string">python3.8</span>
      <span class="hljs-attr">Events:</span>
        <span class="hljs-attr">HelloWorld:</span>
          <span class="hljs-attr">Type:</span> <span class="hljs-string">Api</span> <span class="hljs-comment"># More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api</span>
          <span class="hljs-attr">Properties:</span>
            <span class="hljs-attr">Path:</span> <span class="hljs-string">/hello</span>
            <span class="hljs-attr">Method:</span> <span class="hljs-string">get</span>

<span class="hljs-attr">Outputs:</span>
  <span class="hljs-comment"># ServerlessRestApi is an implicit API created out of Events key under Serverless::Function</span>
  <span class="hljs-comment"># Find out more about other implicit resources you can reference within SAM</span>
  <span class="hljs-comment"># https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api</span>
  <span class="hljs-attr">HelloWorldApi:</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">"API Gateway endpoint URL for Prod stage for Hello World function"</span>
    <span class="hljs-attr">Value:</span> <span class="hljs-type">!Sub</span> <span class="hljs-string">"https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"</span>
  <span class="hljs-attr">HelloWorldFunction:</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">"Hello World Lambda Function ARN"</span>
    <span class="hljs-attr">Value:</span> <span class="hljs-type">!GetAtt</span> <span class="hljs-string">HelloWorldFunction.Arn</span>
  <span class="hljs-attr">HelloWorldFunctionIamRole:</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">"Implicit IAM Role created for Hello World function"</span>
    <span class="hljs-attr">Value:</span> <span class="hljs-type">!GetAtt</span> <span class="hljs-string">HelloWorldFunctionRole.Arn</span>
</code></pre>
<p><code>AWSTemplateFormatVersion</code> - The first line always stays the same for every template.yml. </p>
<p><code>Transform</code> - It specifies one or more macros that AWS CloudFormation uses to process your template. This is a required section.</p>
<p><code>Description</code> is self-explanatory.</p>
<p><code>Globals</code> - It is unique to the SAM and defines properties shared by the whole template. In this case, the <code>timeout</code> will apply to all functions. Globals don't exist in cloud formation.</p>
<p><code>Resources</code> -  You define all AWS resources under this. This is also a mandatory section.
Here we have defined a function with the logical name <code>HelloWorldFunction</code>, it is of type <code>AWS::Serverless::Function</code> with a runtime of python 3.8, <code>CodeUri</code> points to the folder where our handler resides on the filesystem. <code>Handler</code> is the filename and the method. <code>Events</code> would create an API in the API Gateway with method type <code>GET</code> and path <code>/hello</code>.</p>
<p><code>Outputs</code>- Describes the values that are returned and available for use after the stack is created. For example, in our template, <code>HelloWorldApi</code> output gives the API Url generated by the function. We are also setting Lambda and IAM Role ARN as output.
Also note <code>!Sub</code> &amp; <code>GetAtt</code> functions in output,  <code>!Sub</code> is used to substitute a variable with a value. Here it will substitute ${AWS::Region} and ${ServerlessRestApi}.  <code>GetAtt</code> function returns the value of an attribute from a resource in the template. Here we are asking for ARN of the HelloWorldfunction.</p>
<p>Now examine the app.py file. It is straightforward and returns "hello world" as a response.</p>
<pre><code class="lang-Python"><span class="hljs-keyword">import</span> json

<span class="hljs-comment"># import requests</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>
    <span class="hljs-string">"""Sample pure Lambda function

    Parameters
    ----------
    event: dict, required
        API Gateway Lambda Proxy Input Format

        Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format

    context: object, required
        Lambda Context runtime methods and attributes

        Context doc: https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html

    Returns
    ------
    API Gateway Lambda Proxy Output Format: dict

        Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
    """</span>

    <span class="hljs-comment"># try:</span>
    <span class="hljs-comment">#     ip = requests.get("http://checkip.amazonaws.com/")</span>
    <span class="hljs-comment"># except requests.RequestException as e:</span>
    <span class="hljs-comment">#     # Send some context about this error to Lambda Logs</span>
    <span class="hljs-comment">#     print(e)</span>

    <span class="hljs-comment">#     raise e</span>

    <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">"message"</span>: <span class="hljs-string">"hello world"</span>,
            <span class="hljs-comment"># "location": ip.text.replace("\n", "")</span>
        }),
    }
</code></pre>
<p>Get back to the terminal and <code>cd</code> into the directory where the <code>template.yml</code> resides,  run the command: <code>sam build</code></p>
<pre><code>ubuntu@ubuntu-VirtualBox:~/MyWorkspace/BlogPosts/SAM/SAM application$ sam build
Building <span class="hljs-keyword">function</span> <span class="hljs-string">'HelloWorldFunction'</span>
Running PythonPipBuilder:ResolveDependencies
Running PythonPipBuilder:CopySource

Build Succeeded

Built Artifacts  : .aws-sam/build
Built <span class="hljs-keyword">Template</span>   : .aws-sam/build/<span class="hljs-keyword">template</span>.yaml

Commands you can use next
=========================
[*] Invoke <span class="hljs-keyword">Function</span>: sam <span class="hljs-keyword">local</span> invoke
[*] Deploy: sam deploy <span class="hljs-comment">--guided</span>
</code></pre><p>The <code>sam build</code> command builds any dependencies that your application has, and copies your application source code to folders under .aws-sam/build to be zipped and uploaded to Lambda. Check the contents of <code>.aws-sam</code> folder in project directory. It would be something similar to shown below. In HelloWorldFunction folder, there would be many other files along with app.py</p>
<pre><code>.aws_sam/
   └── build/
       ├── HelloWorldFunction/
       └── <span class="hljs-keyword">template</span>.yaml
</code></pre><p>Now its time to run <code>sam deploy -g</code>. It will ask you couple of configuration information like application name,  region name where you want to create the stack, etc, and will wait for your confirmation if you asked for confirmation before deploy.</p>
<pre><code>buntu@ubuntu-VirtualBox:~/MyWorkspace/BlogPosts/SAM/SAM application$ sam deploy -g

Configuring SAM deploy
======================

    Looking <span class="hljs-keyword">for</span> samconfig.toml :  <span class="hljs-keyword">Not</span> <span class="hljs-built_in">found</span>

    Setting <span class="hljs-keyword">default</span> arguments <span class="hljs-keyword">for</span> <span class="hljs-string">'sam deploy'</span>
    =========================================
    Stack <span class="hljs-type">Name</span> [sam-app]: my-first-sam-app
    AWS Region [us-east<span class="hljs-number">-1</span>]: us-east<span class="hljs-number">-1</span>
    #Shows you resources changes <span class="hljs-keyword">to</span> be deployed <span class="hljs-keyword">and</span> require a <span class="hljs-string">'Y'</span> <span class="hljs-keyword">to</span> initiate deploy
    Confirm changes <span class="hljs-keyword">before</span> deploy [y/N]: y
    #SAM needs permission <span class="hljs-keyword">to</span> be able <span class="hljs-keyword">to</span> <span class="hljs-keyword">create</span> roles <span class="hljs-keyword">to</span> <span class="hljs-keyword">connect</span> <span class="hljs-keyword">to</span> the resources <span class="hljs-keyword">in</span> your <span class="hljs-keyword">template</span>
    Allow SAM CLI IAM <span class="hljs-keyword">role</span> creation [Y/n]: y
    HelloWorldFunction may <span class="hljs-keyword">not</span> have <span class="hljs-keyword">authorization</span> defined, <span class="hljs-keyword">Is</span> this okay? [y/N]: y
    Save arguments <span class="hljs-keyword">to</span> samconfig.toml [Y/n]: y

    Looking <span class="hljs-keyword">for</span> resources needed <span class="hljs-keyword">for</span> deployment: <span class="hljs-built_in">Found</span>!

        Managed S3 bucket: aws-sam-cli-managed-<span class="hljs-keyword">default</span>-samclisourcebucket<span class="hljs-number">-1</span>xyg1t2j2ws5k
        A different <span class="hljs-keyword">default</span> S3 bucket can be <span class="hljs-keyword">set</span> <span class="hljs-keyword">in</span> samconfig.toml

    Saved arguments <span class="hljs-keyword">to</span> config file
    Running <span class="hljs-string">'sam deploy'</span> <span class="hljs-keyword">for</span> future deployments will use the parameters saved above.
    The above parameters can be changed <span class="hljs-keyword">by</span> modifying samconfig.toml
    Learn more about samconfig.toml syntax at 
    https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html
Uploading <span class="hljs-keyword">to</span> my-first-sam-app/<span class="hljs-number">9601808</span>ead19b558184dcec8285866a3  <span class="hljs-number">538937</span> / <span class="hljs-number">538937.0</span>  (<span class="hljs-number">100.00</span>%)

    Deploying <span class="hljs-keyword">with</span> <span class="hljs-keyword">following</span> <span class="hljs-keyword">values</span>
    ===============================
    Stack <span class="hljs-type">name</span>                 : my-first-sam-app
    Region                     : us-east<span class="hljs-number">-1</span>
    Confirm changeset          : <span class="hljs-keyword">True</span>
    Deployment s3 bucket       : aws-sam-cli-managed-<span class="hljs-keyword">default</span>-samclisourcebucket<span class="hljs-number">-1</span>xyg1t2j2ws5k
    Capabilities               : ["CAPABILITY_IAM"]
    Parameter overrides        : {}

Initiating deployment
=====================
HelloWorldFunction may <span class="hljs-keyword">not</span> have <span class="hljs-keyword">authorization</span> defined.
Uploading <span class="hljs-keyword">to</span> my-first-sam-app/<span class="hljs-number">362</span>accae02d25f5921348967d73b9d29.<span class="hljs-keyword">template</span>  <span class="hljs-number">1115</span> / <span class="hljs-number">1115.0</span>  (<span class="hljs-number">100.00</span>%)

Waiting <span class="hljs-keyword">for</span> changeset <span class="hljs-keyword">to</span> be created..
CloudFormation stack changeset
<span class="hljs-comment">------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------</span>
Operation                                                          LogicalResourceId                                                  ResourceType                                                     
<span class="hljs-comment">------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------</span>
+ <span class="hljs-keyword">Add</span>                                                              HelloWorldFunctionHelloWorldPermissionProd                         AWS::Lambda::Permission                                          
+ <span class="hljs-keyword">Add</span>                                                              HelloWorldFunctionRole                                             AWS::IAM::<span class="hljs-keyword">Role</span>                                                   
+ <span class="hljs-keyword">Add</span>                                                              HelloWorldFunction                                                 AWS::Lambda::<span class="hljs-keyword">Function</span>                                            
+ <span class="hljs-keyword">Add</span>                                                              ServerlessRestApiDeployment47fc2d5f9d                              AWS::ApiGateway::Deployment                                      
+ <span class="hljs-keyword">Add</span>                                                              ServerlessRestApiProdStage                                         AWS::ApiGateway::Stage                                           
+ <span class="hljs-keyword">Add</span>                                                              ServerlessRestApi                                                  AWS::ApiGateway::RestApi                                         
<span class="hljs-comment">------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------</span>

Changeset created successfully. arn:aws:cloudformation:us-east<span class="hljs-number">-1</span>:&lt;account #&gt;:changeSet/samcli-deploy1599948737/<span class="hljs-number">03</span>d65ab9-a943<span class="hljs-number">-494</span>d<span class="hljs-number">-8</span>db6-abf6aad17537


Previewing CloudFormation changeset <span class="hljs-keyword">before</span> deployment
======================================================
Deploy this changeset? [y/N]:
</code></pre><p>Once you confirm, it will start creating stack. Below is the output after the stack creation completes:</p>
<pre><code>2020-09-12 17:13:45 - Waiting for stack <span class="hljs-keyword">create</span>/<span class="hljs-keyword">update</span> <span class="hljs-keyword">to</span> <span class="hljs-keyword">complete</span>

CloudFormation <span class="hljs-keyword">events</span> <span class="hljs-keyword">from</span> changeset
<span class="hljs-comment">-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------</span>
ResourceStatus                                    ResourceType                                      LogicalResourceId                                 ResourceStatusReason                            
<span class="hljs-comment">-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------</span>
CREATE_IN_PROGRESS                                AWS::IAM::<span class="hljs-keyword">Role</span>                                    HelloWorldFunctionRole                            -                                               
CREATE_IN_PROGRESS                                AWS::IAM::<span class="hljs-keyword">Role</span>                                    HelloWorldFunctionRole                            <span class="hljs-keyword">Resource</span> <span class="hljs-keyword">creation</span> Initiated                     
CREATE_COMPLETE                                   AWS::IAM::<span class="hljs-keyword">Role</span>                                    HelloWorldFunctionRole                            -                                               
CREATE_IN_PROGRESS                                AWS::Lambda::<span class="hljs-keyword">Function</span>                             HelloWorldFunction                                -                                               
CREATE_IN_PROGRESS                                AWS::Lambda::<span class="hljs-keyword">Function</span>                             HelloWorldFunction                                <span class="hljs-keyword">Resource</span> <span class="hljs-keyword">creation</span> Initiated                     
CREATE_COMPLETE                                   AWS::Lambda::<span class="hljs-keyword">Function</span>                             HelloWorldFunction                                -                                               
CREATE_IN_PROGRESS                                AWS::ApiGateway::RestApi                          ServerlessRestApi                                 -                                               
CREATE_IN_PROGRESS                                AWS::ApiGateway::RestApi                          ServerlessRestApi                                 <span class="hljs-keyword">Resource</span> <span class="hljs-keyword">creation</span> Initiated                     
CREATE_COMPLETE                                   AWS::ApiGateway::RestApi                          ServerlessRestApi                                 -                                               
CREATE_IN_PROGRESS                                AWS::Lambda::Permission                           HelloWorldFunctionHelloWorldPermissionProd        <span class="hljs-keyword">Resource</span> <span class="hljs-keyword">creation</span> Initiated                     
CREATE_IN_PROGRESS                                AWS::ApiGateway::Deployment                       ServerlessRestApiDeployment47fc2d5f9d             -                                               
CREATE_IN_PROGRESS                                AWS::Lambda::Permission                           HelloWorldFunctionHelloWorldPermissionProd        -                                               
CREATE_COMPLETE                                   AWS::ApiGateway::Deployment                       ServerlessRestApiDeployment47fc2d5f9d             -                                               
CREATE_IN_PROGRESS                                AWS::ApiGateway::Deployment                       ServerlessRestApiDeployment47fc2d5f9d             <span class="hljs-keyword">Resource</span> <span class="hljs-keyword">creation</span> Initiated                     
CREATE_IN_PROGRESS                                AWS::ApiGateway::Stage                            ServerlessRestApiProdStage                        -                                               
CREATE_IN_PROGRESS                                AWS::ApiGateway::Stage                            ServerlessRestApiProdStage                        <span class="hljs-keyword">Resource</span> <span class="hljs-keyword">creation</span> Initiated                     
CREATE_COMPLETE                                   AWS::ApiGateway::Stage                            ServerlessRestApiProdStage                        -                                               
CREATE_COMPLETE                                   AWS::Lambda::Permission                           HelloWorldFunctionHelloWorldPermissionProd        -                                               
CREATE_COMPLETE                                   AWS::CloudFormation::Stack                        my-<span class="hljs-keyword">first</span>-sam-app                                  -                                               
<span class="hljs-comment">-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------</span>

CloudFormation outputs <span class="hljs-keyword">from</span> deployed stack
<span class="hljs-comment">--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------</span>
Outputs                                                                                                                                                                                                
<span class="hljs-comment">--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------</span>
<span class="hljs-keyword">Key</span>                 HelloWorldFunctionIamRole                                                                                                                                                          
Description         Implicit IAM <span class="hljs-keyword">Role</span> created <span class="hljs-keyword">for</span> Hello World <span class="hljs-keyword">function</span>                                                                                                                                 
<span class="hljs-keyword">Value</span>               arn:aws:iam::&lt;<span class="hljs-keyword">account</span> <span class="hljs-comment">#&gt;:role/my-first-sam-app-HelloWorldFunctionRole-27OIM6WD99F0                                                                                                </span>

<span class="hljs-keyword">Key</span>                 HelloWorldApi                                                                                                                                                                      
Description         API Gateway endpoint <span class="hljs-keyword">URL</span> <span class="hljs-keyword">for</span> Prod stage <span class="hljs-keyword">for</span> Hello World <span class="hljs-keyword">function</span>                                                                                                                   
<span class="hljs-keyword">Value</span>               https://<span class="hljs-number">3</span>kuuurt63m.execute-api.us-east<span class="hljs-number">-1.</span>amazonaws.com/Prod/hello/                                                                                                                 

<span class="hljs-keyword">Key</span>                 HelloWorldFunction                                                                                                                                                                 
Description         Hello World Lambda <span class="hljs-keyword">Function</span> ARN                                                                                                                                                    
<span class="hljs-keyword">Value</span>               arn:aws:lambda:us-east<span class="hljs-number">-1</span>:&lt;<span class="hljs-keyword">account</span> <span class="hljs-comment">#&gt;:function:my-first-sam-app-HelloWorldFunction-1U5YD9NICU5LP                                                                                   </span>
<span class="hljs-comment">--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------</span>

Successfully created/<span class="hljs-keyword">updated</span> stack - my-<span class="hljs-keyword">first</span>-sam-app <span class="hljs-keyword">in</span> us-east<span class="hljs-number">-1</span>
</code></pre><p>As you can see in the outputs, the <code>HelloWorldApi</code> URL was generated which you can hit in the browser and verify your API is working fine. You can also see the same information in Cloud Formation's output section on AWS console.</p>
<p>Great Job! You have successfully completed your First SAM App!</p>
<h3 id="what-else-you-can-do-in-sam">What else you can do in SAM?</h3>
<p>This is not the end, you can also test the function locally before deploy. The AWS SAM CLI provides the <code>sam local</code> command to run your application using Docker containers that simulate the execution environment of Lambda.
I am on linux machine, hence I am going to run command : <code>sudo /home/linuxbrew/.linuxbrew/bin/sam local start-api</code></p>
<pre><code>SAM CLI now collects telemetry <span class="hljs-keyword">to</span> better understand customer needs.

    You can OPT <span class="hljs-keyword">OUT</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">disable</span> telemetry collection <span class="hljs-keyword">by</span> setting the
    environment variable SAM_CLI_TELEMETRY=<span class="hljs-number">0</span> <span class="hljs-keyword">in</span> your shell.
    Thanks <span class="hljs-keyword">for</span> your help!

    Learn More: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-telemetry.html

Mounting HelloWorldFunction at http://<span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">3000</span>/hello [<span class="hljs-keyword">GET</span>]
You can now browse <span class="hljs-keyword">to</span> the above endpoints <span class="hljs-keyword">to</span> invoke your <span class="hljs-keyword">functions</span>. You <span class="hljs-keyword">do</span> <span class="hljs-keyword">not</span> need <span class="hljs-keyword">to</span> <span class="hljs-keyword">restart</span>/reload SAM CLI <span class="hljs-keyword">while</span> working <span class="hljs-keyword">on</span> your <span class="hljs-keyword">functions</span>, changes will be reflected instantly/automatically. You <span class="hljs-keyword">only</span> need <span class="hljs-keyword">to</span> <span class="hljs-keyword">restart</span> SAM CLI <span class="hljs-keyword">if</span> you <span class="hljs-keyword">update</span> your AWS SAM <span class="hljs-keyword">template</span>
<span class="hljs-number">2020</span><span class="hljs-number">-09</span><span class="hljs-number">-12</span> <span class="hljs-number">22</span>:<span class="hljs-number">31</span>:<span class="hljs-number">06</span>  * Running <span class="hljs-keyword">on</span> http://<span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">3000</span>/ (Press CTRL+C <span class="hljs-keyword">to</span> quit)
</code></pre><p>When you hit the URL(http://127.0.0.1:3000/hello) in the browser, you should see the same response as before "hello world" and on the terminal, it should show something similar to this</p>
<pre><code><span class="hljs-attribute">Invoking</span> app.lambda_handler (python<span class="hljs-number">3</span>.<span class="hljs-number">8</span>)
<span class="hljs-attribute">Failed</span> to download a new amazon/aws-sam-cli-emulation-image-python<span class="hljs-number">3</span>.<span class="hljs-number">8</span>:rapid-<span class="hljs-number">1</span>.<span class="hljs-number">1</span>.<span class="hljs-number">0</span> image. Invoking with the already downloaded image.
<span class="hljs-attribute">Mounting</span> /home/ubuntu/MyWorkspace/BlogPosts/SAM/SAM application/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
<span class="hljs-attribute">START</span> RequestId: f<span class="hljs-number">4945824</span>-<span class="hljs-number">0</span>a<span class="hljs-number">1</span>a-<span class="hljs-number">1742</span>-<span class="hljs-number">2</span>f<span class="hljs-number">46</span>-<span class="hljs-number">4</span>d<span class="hljs-number">0</span>bc<span class="hljs-number">2</span>bbe<span class="hljs-number">515</span> Version: $LATEST
<span class="hljs-attribute">END</span> RequestId: f<span class="hljs-number">4945824</span>-<span class="hljs-number">0</span>a<span class="hljs-number">1</span>a-<span class="hljs-number">1742</span>-<span class="hljs-number">2</span>f<span class="hljs-number">46</span>-<span class="hljs-number">4</span>d<span class="hljs-number">0</span>bc<span class="hljs-number">2</span>bbe<span class="hljs-number">515</span>
<span class="hljs-attribute">REPORT</span> RequestId: f<span class="hljs-number">4945824</span>-<span class="hljs-number">0</span>a<span class="hljs-number">1</span>a-<span class="hljs-number">1742</span>-<span class="hljs-number">2</span>f<span class="hljs-number">46</span>-<span class="hljs-number">4</span>d<span class="hljs-number">0</span>bc<span class="hljs-number">2</span>bbe<span class="hljs-number">515</span>    Init Duration: <span class="hljs-number">216</span>.<span class="hljs-number">83</span> ms    Duration: <span class="hljs-number">2</span>.<span class="hljs-number">88</span> ms    Billed Duration: <span class="hljs-number">100</span> ms    Memory Size: <span class="hljs-number">128</span> MB    Max Memory Used: <span class="hljs-number">24</span> MB    
<span class="hljs-attribute">No</span> Content-Type given. Defaulting to 'application/json'.
<span class="hljs-attribute">2020</span>-<span class="hljs-number">09</span>-<span class="hljs-number">12</span> <span class="hljs-number">22</span>:<span class="hljs-number">38</span>:<span class="hljs-number">28</span> <span class="hljs-number">127.0.0.1</span> - -<span class="hljs-meta"> [12/Sep/2020 22:38:28] "GET /hello HTTP/1.1" 200 -</span>
</code></pre><h3 id="where-to-go-from-here">Where to go from here?</h3>
<p>This was a very basic SAM application but I hope you got the gist of it. As an exercise, you can add more resources like CloudFront, Route53, DynamoDB, ACM etc, and see how to model it in YAML. Take a look <a target="_blank" href="https://blog.rajanpanchal.net/aws-kms-use-case-with-serverless-application-model-sam-an-end-to-end-solution">here</a> and <a target="_blank" href="https://blog.rajanpanchal.net/my-experiments-with-aws-cloud-cloud-resume-challenge">here</a> for more complex SAM Templates.</p>
<p><em>If you like my articles feel free to follow me on <a target="_blank" href="https://twitter.com/PanchalRajan">Twitter</a> for updates!</em></p>
]]></content:encoded></item><item><title><![CDATA[REST API using AWS Java SDK]]></title><description><![CDATA[In the previous post, we looked at a simple Lambda handler using the AWS Java SDK. In this post, we're going to implement Rest API with Lambda (using Lambda Proxy Integration).  
What is Lambda Proxy Integration
When a client submits an API request, ...]]></description><link>https://blog.rajanpanchal.net/rest-api-using-aws-java-sdk</link><guid isPermaLink="true">https://blog.rajanpanchal.net/rest-api-using-aws-java-sdk</guid><category><![CDATA[aws lambda]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Java]]></category><category><![CDATA[REST API]]></category><category><![CDATA[sdk]]></category><dc:creator><![CDATA[Rajan Panchal]]></dc:creator><pubDate>Tue, 22 Sep 2020 18:13:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1599889844206/rr1sWMAY4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the <a target="_blank" href="https://blog.rajanpanchal.net/write-your-first-aws-lambda-in-java">previous post</a>, we looked at a simple Lambda handler using the AWS Java SDK. In this post, we're going to implement Rest API with Lambda (using Lambda Proxy Integration).  </p>
<h2 id="what-is-lambda-proxy-integration">What is Lambda Proxy Integration</h2>
<p>When a client submits an API request, API Gateway passes the request to the integrated Lambda function as-is, except that the order of the request parameters is not preserved. This request data includes the request headers, query string parameters, URL path variables, payload, and API configuration data. </p>
<p>The configuration data can include current deployment stage name, stage variables, user identity, or authorization context (if any). The backend Lambda function parses the incoming request data. The API gateway simply acts as a postman to pass requests and responses. One advantage is that you can change the implementation of the Lambda without impacting the API.</p>
<h2 id="lets-implement-using-aws-java-sdk">Let's implement using AWS Java SDK</h2>
<p>Create an empty maven project and update the pom.xml with the following contents.</p>
<pre><code class="lang-XML"><span class="hljs-tag">&lt;<span class="hljs-name">project</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0"</span>
    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>
    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">modelVersion</span>&gt;</span>4.0.0<span class="hljs-tag">&lt;/<span class="hljs-name">modelVersion</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>net.rajanpanchal<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>lambda-api-demo<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span>LambdaApiDemo<span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">description</span>&gt;</span>Demonstrates API Request <span class="hljs-symbol">&amp;amp;</span> Response Using Lambda and Java SDK for AWS<span class="hljs-tag">&lt;/<span class="hljs-name">description</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">properties</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">maven.compiler.source</span>&gt;</span>1.8<span class="hljs-tag">&lt;/<span class="hljs-name">maven.compiler.source</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">maven.compiler.target</span>&gt;</span>1.8<span class="hljs-tag">&lt;/<span class="hljs-name">maven.compiler.target</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">aws.java.sdk.version</span>&gt;</span>2.14.11<span class="hljs-tag">&lt;/<span class="hljs-name">aws.java.sdk.version</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">properties</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dependencyManagement</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>software.amazon.awssdk<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>bom<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>${aws.java.sdk.version}<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">type</span>&gt;</span>pom<span class="hljs-tag">&lt;/<span class="hljs-name">type</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>import<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependencyManagement</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- https://mvnrepository.com/artifact/com.amazonaws/aws-lambda-java-events --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.amazonaws<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>aws-lambda-java-events<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.2.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.amazonaws<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>aws-lambda-java-core<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.2.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">build</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">plugins</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.maven.plugins<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>maven-compiler-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">configuration</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">source</span>&gt;</span>${maven.compiler.source}<span class="hljs-tag">&lt;/<span class="hljs-name">source</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">target</span>&gt;</span>${maven.compiler.target}<span class="hljs-tag">&lt;/<span class="hljs-name">target</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">configuration</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.maven.plugins<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>maven-shade-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.2.4<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">executions</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">execution</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">phase</span>&gt;</span>package<span class="hljs-tag">&lt;/<span class="hljs-name">phase</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">goals</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">goal</span>&gt;</span>shade<span class="hljs-tag">&lt;/<span class="hljs-name">goal</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">goals</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">execution</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">executions</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">plugins</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">build</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">project</span>&gt;</span>
</code></pre>
<p>We are adding 2 main dependencies here:</p>
<ol>
<li>aws-lambda-java-core - This provides <code>RequestHandler</code></li>
<li>aws-lambda-java-events -  This provides <code>APIGatewayProxyRequestEvent</code> and <code>APIGatewayProxyResponseEvent</code></li>
</ol>
<p>Added 2 plugins:</p>
<ol>
<li>Compiler plugin - determines java version for the compiler.</li>
<li>maven-shade-plugin -  to generate fat jar aka Uber jar (with all dependencies)</li>
</ol>
<p>Create a class <code>LambdaHandler</code>, with the following contents.</p>
<pre><code class="lang-Java"><span class="hljs-keyword">package</span> net.rajanpanchal.handlers;

<span class="hljs-keyword">import</span> com.amazonaws.services.lambda.runtime.Context;
<span class="hljs-keyword">import</span> com.amazonaws.services.lambda.runtime.LambdaLogger;
<span class="hljs-keyword">import</span> com.amazonaws.services.lambda.runtime.RequestHandler;
<span class="hljs-keyword">import</span> com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
<span class="hljs-keyword">import</span> com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LambdaHandler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">RequestHandler</span>&lt;<span class="hljs-title">APIGatewayProxyRequestEvent</span>, <span class="hljs-title">APIGatewayProxyResponseEvent</span>&gt; </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> APIGatewayProxyResponseEvent <span class="hljs-title">handleRequest</span><span class="hljs-params">(APIGatewayProxyRequestEvent requestEvent, Context context)</span> </span>{
        APIGatewayProxyResponseEvent responseEvent = <span class="hljs-keyword">new</span> APIGatewayProxyResponseEvent();
        <span class="hljs-keyword">try</span> {
            LambdaLogger logger = context.getLogger();
            logger.log(<span class="hljs-string">"requestEventString:"</span> + requestEvent.toString());
            logger.log(<span class="hljs-string">"query string param:"</span> + requestEvent.getQueryStringParameters());
            logger.log(<span class="hljs-string">"headers:"</span> + requestEvent.getHeaders());
            <span class="hljs-comment">// fetching the value send in the request body</span>
            String message = requestEvent.getBody();
            <span class="hljs-comment">// Request request = gson.fromJson(message, Request.class);</span>
            logger.log(<span class="hljs-string">"message body:"</span> + message);

            <span class="hljs-comment">// setting up the response message</span>
            responseEvent.setBody(<span class="hljs-string">"Hello from Lambda!"</span>);
            responseEvent.setStatusCode(<span class="hljs-number">200</span>);

            <span class="hljs-keyword">return</span> responseEvent;

        } <span class="hljs-keyword">catch</span> (Exception ex) {
            responseEvent.setBody(<span class="hljs-string">"Invalid Response"</span>);
            responseEvent.setStatusCode(<span class="hljs-number">500</span>);
            <span class="hljs-keyword">return</span> responseEvent;
        }
    }
}
</code></pre>
<p>The above code is simple. <code>requestEvent</code> object is of type <code>APIGatewayProxyRequestEvent</code>. You will get headers, body, query-string, etc. from this object.
<code>responseEvent</code> object is of type <code>APIGatewayProxyResponseEvent</code>. You can set response headers, body, status, etc in this object.</p>
<p>Now execute maven command <code>mvn package</code> to generate a jar file in the target folder.
Login to AWS console, create Lambda function with Java 8 runtime, let it create execution role, and upload the jar file under <strong>Function Code</strong> section.
In Basic Settings, set the handler name in format :</p>
<pre><code>*<span class="hljs-selector-tag">net</span><span class="hljs-selector-class">.rajanpanchal</span><span class="hljs-selector-class">.handlers</span><span class="hljs-selector-class">.LambdaHandler</span><span class="hljs-selector-pseudo">::handleRequest</span>*
</code></pre><p>Now go to API Gateway, create a REST API
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/aws-sdk-lambda-api/lambda+api.PNG" alt="AWS Java SDK Rest API using Lambda Proxy Integration" />
Go to actions and create a new method of type <code>GET</code>. Select the following configuration:
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/aws-sdk-lambda-api/rest+method.PNG" alt="AWS Java SDK Rest API using Lambda Proxy Integration" />
To enable proxy integration, make sure to check the checkbox. Save to create a method.</p>
<p>Go to actions and click deploy API and give the following information and click deploy.
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/aws-sdk-lambda-api/deploy.PNG" alt="Lambda proxy integration using java sdk" />
Use the URL generated to access the API
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/aws-sdk-lambda-api/url.PNG" alt="AWS Java SDK Rest APIk" /></p>
<h2 id="testing">Testing</h2>
<p>Hit the URL in the browser and you should see the below response:
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/aws-sdk-lambda-api/access-api.PNG" alt="AWS Java SDK Rest API using Lambda Proxy Integration" /></p>
<p>Go to Cloud Watch, access Log Groups from the left-hand navigation, and access the logs corresponding to your lambda function. You should see the log statements from the lambda handler.
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/aws-sdk-lambda-api/log-group.PNG" alt="AWS Java SDK Rest API using Lambda Proxy Integration" /></p>
<h4 id="source-code">Source Code:</h4>
<p><a target="_blank" href="https://github.com/rajanpanchal/lambda-api-aws-sdk"><img src="https://github-readme-stats.vercel.app/api/pin/?username=rajanpanchal&amp;repo=lambda-api-aws-sdk" alt="AWS Java SDK Rest API using Lambda Proxy Integration" /></a>
Here we complete our REST API with Lambda!</p>
<p><em>If you like the post, feel free to share and follow me on <a target="_blank" href="https://twitter.com/PanchalRajan">Twitter</a> for updates!</em></p>
]]></content:encoded></item><item><title><![CDATA[Amazon S3 Quick Reference]]></title><description><![CDATA[Amazon S3 Quick Reference for Certification, interview and study!

If you like the post, feel free to share and follow me on Twitter for updates!]]></description><link>https://blog.rajanpanchal.net/amazon-s3-quick-reference</link><guid isPermaLink="true">https://blog.rajanpanchal.net/amazon-s3-quick-reference</guid><category><![CDATA[AWS]]></category><category><![CDATA[Certification]]></category><category><![CDATA[Amazon S3]]></category><dc:creator><![CDATA[Rajan Panchal]]></dc:creator><pubDate>Tue, 15 Sep 2020 05:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1600230526210/FyAxvS1Zf.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Amazon S3 Quick Reference for Certification, interview and study!</p>
<p><img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/s3-infographics/s3-infographics.png" alt /></p>
<p><em>If you like the post, feel free to share and follow me on <a target="_blank" href="https://twitter.com/PanchalRajan">Twitter</a> for updates!</em></p>
]]></content:encoded></item><item><title><![CDATA[AWS Lambda Performance Optimizations]]></title><description><![CDATA[In this post we are going to see how you can optimize performance of AWS Lambda function. Although Lambda is a managed service by AWS and it automatically scales, based on shared responsibility model, the underlying service details will be managed by...]]></description><link>https://blog.rajanpanchal.net/aws-lambda-performance-optimizations</link><guid isPermaLink="true">https://blog.rajanpanchal.net/aws-lambda-performance-optimizations</guid><category><![CDATA[aws lambda]]></category><category><![CDATA[performance]]></category><category><![CDATA[Java]]></category><category><![CDATA[AWS]]></category><category><![CDATA[optimization]]></category><dc:creator><![CDATA[Rajan Panchal]]></dc:creator><pubDate>Thu, 10 Sep 2020 03:51:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1599671585163/bnarnIlOL.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this post we are going to see how you can optimize performance of AWS Lambda function. Although Lambda is a managed service by AWS and it automatically scales, based on shared responsibility model, the underlying service details will be managed by AWS but its client's responsibility to correctly use the service. By using these optimizations you can lower your Lambda bill.</p>
<h3 id="lets-look-at-some-of-the-lambda-performance-optimizations">Lets look at some of the Lambda Performance Optimizations</h3>
<h4 id="execution-context-reuse">Execution context Reuse</h4>
<p>When a Lambda function is invoked, AWS Lambda launches an execution context based on the configuration settings you provide like Memory, runtime, etc. The execution context is a temporary runtime environment that initializes any external dependencies of your Lambda function code, such as database connections or HTTP endpoints, etc. After the Lambda function completes the execution it maintains the execution context in hopes that another invocation of same function will happen. If it does happen, then it will reuse the context. 
So the objects declared outside of the lambda handler (Lambda global variables) can be reused. You can declare any objects there. Following code snippets defines S3 as client that will be reused in subsequent invocation. </p>
<pre><code class="lang-Python">
<span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> boto3
<span class="hljs-comment">#DynamoDB defined outside of handler</span>
dynamodb = boto3.resource(<span class="hljs-string">'dynamodb'</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>
    table = dynamodb.Table(<span class="hljs-string">"MyTable"</span>)
    response = table.get_item(Key={<span class="hljs-string">'userid'</span>: username})

    <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(response[<span class="hljs-string">'Item) 
      }</span>
</code></pre>
<p>A word of caution: Its is not guaranteed that subsequent function invocation would go to the same execution context.  Lambda may decide to create a new invocation context runtime so don't maintain any state information. Always have logic in Lambda handler to instantiate or reconnect the connections objects if cannot be reused.</p>
<h4 id="minimize-deployment-package">Minimize Deployment Package</h4>
<p>A deployment package is a ZIP archive that contains your compiled function code and dependencies. In case your Lambda handler is in Java,  you need to have a fat-jar aka Uber Jar to upload to Lambda. Uber jar will contain all the dependencies required to execute the handler. Its is important that you only import those packages that are required. For example, if you only need basic Lambda implementation, then import only <em>lambda-java-core</em> dependency - It defines handler method interfaces and the context object that the runtime passes to the handler. If you define your own input types, this is the only library you need.   You don't need whole <em>aws-sdk</em> dependency to write simple Lambda handlers. Use Bill of Materials POM (https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-bom) in the maven project and provides individual dependencies from BOM POM to import. This will reduce the fat jar size. Keep in mind that in lambda, you are working off a limited disk size and memory as defined in the configuration.
In Python and other languages you can only import modules that you need. For example, in TypeScript</p>
<pre><code class="lang-TypeScript"><span class="hljs-comment">// import entire SDK</span>
<span class="hljs-keyword">import</span> AWS <span class="hljs-keyword">from</span> <span class="hljs-string">'aws-sdk'</span>;
<span class="hljs-comment">// import AWS object without services</span>
<span class="hljs-keyword">import</span> AWS <span class="hljs-keyword">from</span> <span class="hljs-string">'aws-sdk/global'</span>;
<span class="hljs-comment">// import individual service</span>
<span class="hljs-keyword">import</span> S3 <span class="hljs-keyword">from</span> <span class="hljs-string">'aws-sdk/clients/s3'</span>;
</code></pre>
<h4 id="use-simple-frameworks">Use Simple Frameworks</h4>
<p>Instead of using full blown spring framework try to use lightweight frameworks that accomplish the same tasks. For example:  If you need Java dependency injection in your app then use lightweight frameworks like Guice (pronounced 'juice' is a lightweight dependency injection framework for Java 6 and above, brought to you by Google)</p>
<p>Are there any other Lambda optimization techniques? Let us know in comments!</p>
<p><em>If you like my articles feel free to follow me on <a target="_blank" href="https://twitter.com/PanchalRajan">Twitter</a> for updates!</em></p>
]]></content:encoded></item><item><title><![CDATA[Write your first AWS Lambda in Java]]></title><description><![CDATA[So far I have been writing Lambda function in Python for all my  AWS projects . In Python its easy, just import boto3 module and starting coding. Things are bit different when you write Lambda handlers in Java. Lets explore and see how you can write ...]]></description><link>https://blog.rajanpanchal.net/write-your-first-aws-lambda-in-java</link><guid isPermaLink="true">https://blog.rajanpanchal.net/write-your-first-aws-lambda-in-java</guid><category><![CDATA[aws lambda]]></category><category><![CDATA[Java]]></category><category><![CDATA[Amazon Web Services]]></category><dc:creator><![CDATA[Rajan Panchal]]></dc:creator><pubDate>Sun, 06 Sep 2020 04:50:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1599368382956/xrewhMgqx.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>So far I have been writing Lambda function in Python for all my  AWS <a target="_blank" href="https://github.com/rajanpanchal">projects</a> . In Python its easy, just import boto3 module and starting coding. Things are bit different when you write Lambda handlers in Java. Lets explore and see how you can write a simple HelloWorld Lambda handler in java. I have planned couple of how-to projects that I am going to write in Java. Hence I thought to do HelloWorld post before we dive into complex handlers. Feel free to follow me on <a target="_blank" href="https://twitter.com/PanchalRajan"> twitter</a>  for upcoming updates.</p>
<p>To implement handler, we are going to use Maven, Eclipse and AWS SDK for Java. Below are the version:</p>
<ul>
<li>Java:  1.8</li>
<li>Eclipse:  Version: 2020-06 (4.16.0)</li>
<li>AWS SDK: 2.14.11</li>
<li>Maven: 3.6.3 (Included with eclipse)</li>
</ul>
<p>To get started, create a new empty maven project in eclipse. Go to File-&gt; New-&gt; Maven Project. In new project dialog box. select "create simple project" and hit next. On next screen. add appropriate information about the project and hit finish. For example:</p>
<p> <img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/aws-sdk-java/new-maven-project.PNG" alt="aws sdk java new maven project" /> </p>
<p>Now your project is created. Open pom.xml and add following code.</p>
<pre><code class="lang-Pom.xml">&lt;project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;groupId&gt;net.rajanpanchal&lt;/groupId&gt;
    &lt;artifactId&gt;lambda-java-demo&lt;/artifactId&gt;
    &lt;version&gt;1&lt;/version&gt;
    &lt;name&gt;Lambda-Java-Demo&lt;/name&gt;
    &lt;description&gt;HelloWorld Lambda handler in java&lt;/description&gt;
    &lt;properties&gt;
        &lt;maven.compiler.source&gt;1.8&lt;/maven.compiler.source&gt;
        &lt;maven.compiler.target&gt;1.8&lt;/maven.compiler.target&gt;
        &lt;aws.java.sdk.version&gt;2.14.11&lt;/aws.java.sdk.version&gt;
    &lt;/properties&gt;
    &lt;dependencyManagement&gt;
        &lt;dependencies&gt;
            &lt;dependency&gt;
                &lt;groupId&gt;software.amazon.awssdk&lt;/groupId&gt;
                &lt;artifactId&gt;bom&lt;/artifactId&gt;
                &lt;version&gt;${aws.java.sdk.version}&lt;/version&gt;
                &lt;type&gt;pom&lt;/type&gt;
                &lt;scope&gt;import&lt;/scope&gt;
            &lt;/dependency&gt;
        &lt;/dependencies&gt;
    &lt;/dependencyManagement&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.amazonaws&lt;/groupId&gt;
            &lt;artifactId&gt;aws-lambda-java-core&lt;/artifactId&gt;
            &lt;version&gt;1.2.0&lt;/version&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;com.google.code.gson&lt;/groupId&gt;
            &lt;artifactId&gt;gson&lt;/artifactId&gt;
            &lt;version&gt;2.8.6&lt;/version&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
    &lt;build&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
                &lt;configuration&gt;
                    &lt;source&gt;${maven.compiler.source}&lt;/source&gt;
                    &lt;target&gt;${maven.compiler.target}&lt;/target&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-shade-plugin&lt;/artifactId&gt;
                &lt;version&gt;3.2.4&lt;/version&gt;
                &lt;executions&gt;
                    &lt;execution&gt;
                        &lt;phase&gt;package&lt;/phase&gt;
                        &lt;goals&gt;
                            &lt;goal&gt;shade&lt;/goal&gt;
                        &lt;/goals&gt;
                    &lt;/execution&gt;
                &lt;/executions&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;
&lt;/project&gt;
</code></pre>
<p>We are setting some properties in <code>&lt;properties&gt;</code> tag like AWS SDK version and Java Version. We are adding AWS SDK dependency and Lambda core dependency with desired version. Google JSON dependency is added to convert between JSON and Java object and vice versa.
In plugins, we define maven compiler plugin to compile the code and another important plugin called <code>maven-shade-plugin</code>. This plugin helps to create <strong>fat jar</strong> a.k.a <strong>Uber jar</strong>. This jar will contain all the dependencies that are required to successfully run the lambda function.</p>
<p>Now lets create the handler. </p>
<pre><code class="lang-Java"><span class="hljs-keyword">package</span> net.rajanpanchal.handlers;

<span class="hljs-keyword">import</span> java.util.Map;

<span class="hljs-keyword">import</span> com.amazonaws.services.lambda.runtime.Context;
<span class="hljs-keyword">import</span> com.amazonaws.services.lambda.runtime.LambdaLogger;
<span class="hljs-keyword">import</span> com.amazonaws.services.lambda.runtime.RequestHandler;
<span class="hljs-keyword">import</span> com.google.gson.Gson;
<span class="hljs-keyword">import</span> com.google.gson.GsonBuilder;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HelloWorldHandler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">RequestHandler</span>&lt;<span class="hljs-title">Map</span>&lt;<span class="hljs-title">String</span>,<span class="hljs-title">String</span>&gt;, <span class="hljs-title">String</span>&gt;</span>{
      Gson gson = <span class="hljs-keyword">new</span> GsonBuilder().setPrettyPrinting().create();
      <span class="hljs-meta">@Override</span>
      <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">handleRequest</span><span class="hljs-params">(Map&lt;String,String&gt; event, Context context)</span>
      </span>{
        LambdaLogger logger = context.getLogger();
        String response = <span class="hljs-keyword">new</span> String(<span class="hljs-string">"200 OK"</span>);
        <span class="hljs-comment">// log execution details</span>
        logger.log(<span class="hljs-string">"ENVIRONMENT VARIABLES: "</span> + gson.toJson(System.getenv()));
        logger.log(<span class="hljs-string">"CONTEXT: "</span> + gson.toJson(context));
        <span class="hljs-comment">// process event</span>
        logger.log(<span class="hljs-string">"EVENT: "</span> + gson.toJson(event));
        logger.log(<span class="hljs-string">"EVENT TYPE: "</span> + event.getClass().toString());
        <span class="hljs-keyword">return</span> response;
      }
    }
</code></pre>
<p>Here were are implementing <code>RequestHandler</code> that will accept a <code>Map</code> of <code>String</code> and outputs a <code>String</code> response. The handler is not doing much. Just logging some stuff from <code>context</code> and <code>event</code>  and in response it sends <code>200 OK</code> string.
After this right click on project, go to 'Run as' and click 'Maven build'. A configuration window will open. In 'Goals' type <code>package</code> and hit 'Run' . Build should be successful and will create a fat jar in <code>target</code> folder.</p>
<p>Now go to AWS console and create a new Lambda function with Java 8 as runtime and upload this jar from <strong>Function Code</strong> section. In Basic Settings, you have to specify package.class::methodName in the handler textbox. To test this Lambda, create a test event and execute the test.
 <img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/aws-sdk-java/test-event.PNG" alt="Java SDK Lambda Test Event" /> </p>
<p>The ouput might look similar to this:</p>
<pre><code><span class="hljs-keyword">START</span> RequestId: c40b4095<span class="hljs-number">-27</span>f5<span class="hljs-number">-4153</span>-ac37-fd2c103f4476 <span class="hljs-keyword">Version</span>: $LATEST
ENVIRONMENT <span class="hljs-keyword">VARIABLES</span>: {
  <span class="hljs-string">"PATH"</span>: <span class="hljs-string">"/usr/local/bin:/usr/bin/:/bin:/opt/bin"</span>,
  <span class="hljs-string">"_AWS_XRAY_DAEMON_ADDRESS"</span>: <span class="hljs-string">"169.254.79.2"</span>,
  <span class="hljs-string">"LAMBDA_TASK_ROOT"</span>: <span class="hljs-string">"/var/task"</span>,
  <span class="hljs-string">"AWS_LAMBDA_FUNCTION_MEMORY_SIZE"</span>: <span class="hljs-string">"128"</span>,
  <span class="hljs-string">"TZ"</span>: <span class="hljs-string">":UTC"</span>,
  <span class="hljs-string">"AWS_SECRET_ACCESS_KEY"</span>: <span class="hljs-string">"&lt;keyid&gt;"</span>,
  <span class="hljs-string">"AWS_EXECUTION_ENV"</span>: <span class="hljs-string">"AWS_Lambda_java8"</span>,
  <span class="hljs-string">"AWS_DEFAULT_REGION"</span>: <span class="hljs-string">"us-east-1"</span>,
  <span class="hljs-string">"AWS_LAMBDA_LOG_GROUP_NAME"</span>: <span class="hljs-string">"/aws/lambda/helloworldjava"</span>,
  <span class="hljs-string">"_HANDLER"</span>: <span class="hljs-string">"net.rajanpanchal.handlers.HelloWorldHandler::handleRequest"</span>,
  <span class="hljs-string">"LANG"</span>: <span class="hljs-string">"en_US.UTF-8"</span>,
  <span class="hljs-string">"LAMBDA_RUNTIME_DIR"</span>: <span class="hljs-string">"/var/runtime"</span>,
  <span class="hljs-string">"AWS_SESSION_TOKEN"</span>: <span class="hljs-string">"&lt;session token&gt;"</span>,
  <span class="hljs-string">"LD_LIBRARY_PATH"</span>: <span class="hljs-string">"/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib"</span>,
  <span class="hljs-string">"_X_AMZN_TRACE_ID"</span>: <span class="hljs-string">"Root\u003d1-5f545c5d-22c8a803badd636859a0f387;Parent\u003d23df427f78bc46e4;Sampled\u003d0"</span>,
  <span class="hljs-string">"AWS_SECRET_KEY"</span>: <span class="hljs-string">"&lt;secret key&gt;"</span>,
  <span class="hljs-string">"AWS_REGION"</span>: <span class="hljs-string">"us-east-1"</span>,
  <span class="hljs-string">"AWS_LAMBDA_LOG_STREAM_NAME"</span>: <span class="hljs-string">"2020/09/06/[$LATEST]57c598b33b164acf8e8151660249e50e"</span>,
  <span class="hljs-string">"AWS_XRAY_DAEMON_ADDRESS"</span>: <span class="hljs-string">"169.254.79.2:2000"</span>,
  <span class="hljs-string">"_AWS_XRAY_DAEMON_PORT"</span>: <span class="hljs-string">"2000"</span>,
  <span class="hljs-string">"AWS_XRAY_CONTEXT_MISSING"</span>: <span class="hljs-string">"LOG_ERROR"</span>,
  <span class="hljs-string">"AWS_LAMBDA_FUNCTION_VERSION"</span>: <span class="hljs-string">"$LATEST"</span>,
  <span class="hljs-string">"AWS_ACCESS_KEY"</span>: <span class="hljs-string">"&lt;access key&gt;"</span>,
  <span class="hljs-string">"AWS_LAMBDA_FUNCTION_NAME"</span>: <span class="hljs-string">"helloworldjava"</span>
}<span class="hljs-keyword">CONTEXT</span>: {
  <span class="hljs-string">"memoryLimit"</span>: <span class="hljs-number">128</span>,
  <span class="hljs-string">"awsRequestId"</span>: <span class="hljs-string">"c40b4095-27f5-4153-ac37-fd2c103f4476"</span>,
  <span class="hljs-string">"logGroupName"</span>: <span class="hljs-string">"/aws/lambda/helloworldjava"</span>,
  <span class="hljs-string">"logStreamName"</span>: <span class="hljs-string">"2020/09/06/[$LATEST]57c598b33b164acf8e8151660249e50e"</span>,
  <span class="hljs-string">"functionName"</span>: <span class="hljs-string">"helloworldjava"</span>,
  <span class="hljs-string">"functionVersion"</span>: <span class="hljs-string">"$LATEST"</span>,
  <span class="hljs-string">"invokedFunctionArn"</span>: <span class="hljs-string">"arn:aws:lambda:us-east-1:&lt;AccountId&gt;:function:helloworldjava"</span>,
  <span class="hljs-string">"cognitoIdentity"</span>: {
    <span class="hljs-string">"identityId"</span>: <span class="hljs-string">""</span>,
    <span class="hljs-string">"poolId"</span>: <span class="hljs-string">""</span>
  },
  <span class="hljs-string">"logger"</span>: {}
}<span class="hljs-keyword">EVENT</span>: {
  <span class="hljs-string">"key1"</span>: <span class="hljs-string">"value1"</span>,
  <span class="hljs-string">"key2"</span>: <span class="hljs-string">"value2"</span>,
  <span class="hljs-string">"key3"</span>: <span class="hljs-string">"value3"</span>
}<span class="hljs-keyword">EVENT</span> <span class="hljs-keyword">TYPE</span>: <span class="hljs-keyword">class</span> <span class="hljs-keyword">java</span>.util.LinkedHashMapEND RequestId: c40b4095<span class="hljs-number">-27</span>f5<span class="hljs-number">-4153</span>-ac37-fd2c103f4476
REPORT RequestId: c40b4095<span class="hljs-number">-27</span>f5<span class="hljs-number">-4153</span>-ac37-fd2c103f4476    <span class="hljs-keyword">Duration</span>: <span class="hljs-number">516.04</span> ms    Billed <span class="hljs-keyword">Duration</span>: <span class="hljs-number">600</span> ms    <span class="hljs-keyword">Memory</span> <span class="hljs-keyword">Size</span>: <span class="hljs-number">128</span> MB    <span class="hljs-keyword">Max</span> <span class="hljs-keyword">Memory</span> Used: <span class="hljs-number">79</span> MB    Init <span class="hljs-keyword">Duration</span>: <span class="hljs-number">393.54</span> ms
</code></pre><p>Here we complete our first Lambda handler in Java. In next post we will see how we can implement API using AWS SDK.</p>
]]></content:encoded></item><item><title><![CDATA[Automate Application deployment using GitHub Actions]]></title><description><![CDATA[GitHub Actions makes it easy to automate all your software workflows. You can Build, test, and deploy your code right from GitHub. In this post we will explore how you can use GitHub Actions to automate serverless application deployment on AWS. You c...]]></description><link>https://blog.rajanpanchal.net/automate-application-deployment-using-github-actions-1</link><guid isPermaLink="true">https://blog.rajanpanchal.net/automate-application-deployment-using-github-actions-1</guid><category><![CDATA[AWS]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[serverless]]></category><dc:creator><![CDATA[Rajan Panchal]]></dc:creator><pubDate>Wed, 02 Sep 2020 03:19:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1599016760186/QSbNqTHKB.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>GitHub Actions makes it easy to automate all your software workflows. You can Build, test, and deploy your code right from GitHub. In this post we will explore how you can use GitHub Actions to automate serverless application deployment on AWS. You can also use AWS's own CI/CD services to achieve the same. But here we are going to keep our discussion limited to GitHub Actions.</p>
<h3 id="how-to-use-github-actions">How to use GitHub Actions?</h3>
<p>Creating a GitHub action is simple. Go to your GitHub repository that you want to automate and click on "Actions"
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/github-actions/github-action-create-new.PNG" alt="github actions new workflow" /></p>
<p>You will be taken to Actions page where you can create a new Blank workflow or select existing actions from the marketplace. The actions from marketplace are reusable actions that you can use in your workflow. We are going to create a blank action and we will also use some actions from marketplace.
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/github-actions/github-actions-blank-workflow.PNG" alt="github actions blank action" /></p>
<p>Lets rename the YAML file to workflow.yml. You can name anything you like. We are going to create a Lambda function with API gateway in Serverless Application Model (SAM) template and deploy it using GitHub Actions. 
Below is our SAM template.</p>
<pre><code class="lang-YAML">AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: &gt;
  GitHub Actions demonstration App
Resources:
  ApiGatewayApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
    Auth:
     UsagePlan:
      CreateUsagePlan: PER_API
      Description: Usage plan for this API
      Quota:
       Limit: 500
       Period: MONTH
      Throttle:
       BurstLimit: 100
       RateLimit: 50
  LamdbaFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./    
      Handler: lambda.handler
      Runtime: python3.8
      Events:
        getCounter:
          Type: Api
          Properties:
            Path: /hello
            Method: get
            RestApiId: !Ref ApiGatewayApi
</code></pre>
<p>lambda.py</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handler</span><span class="hljs-params">(event, context)</span>:</span>
            <span class="hljs-keyword">return</span> {
        <span class="hljs-string">'statusCode'</span>: <span class="hljs-number">200</span>,
        <span class="hljs-string">'headers'</span>: {
            <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
            <span class="hljs-string">'Access-Control-Allow-Origin'</span>: <span class="hljs-string">'*'</span>
        },
        <span class="hljs-string">'body'</span>:<span class="hljs-string">'Hello from Lambda!'</span>
        ,
        <span class="hljs-string">"isBase64Encoded"</span>: <span class="hljs-keyword">False</span>
    }
</code></pre><p>This contains one Lambda function and API with path hello. Lets first deploy manually using SAM CLI and then we will automate it. Create samconfig.toml with below details.
create s3_bucket that will be used for SAM deploy and update in samconfig.toml.</p>
<pre><code><span class="hljs-attr">version</span> = <span class="hljs-number">0.1</span>
<span class="hljs-section">[default]</span>
<span class="hljs-section">[default.deploy]</span>
<span class="hljs-section">[default.deploy.parameters]</span>
<span class="hljs-attr">stack_name</span> = <span class="hljs-string">"sam-github-actions-app"</span>
<span class="hljs-attr">s3_bucket</span> = <span class="hljs-string">"aws-sam-cli-managed-default-samclisourcebucket-1xyg1t2j2ws5k"</span>
<span class="hljs-attr">s3_prefix</span> = <span class="hljs-string">"sam-app"</span>
<span class="hljs-attr">region</span> = <span class="hljs-string">"us-east-1"</span>
<span class="hljs-attr">confirm_changeset</span> = <span class="hljs-literal">false</span>
<span class="hljs-attr">capabilities</span> = <span class="hljs-string">"CAPABILITY_IAM"</span>
</code></pre><p>Also create empty requirements.txt along with template.yml. Run <strong>SAM build</strong> and <strong>SAM deploy -g</strong> on CLI. 
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/github-actions/sam-deploy.PNG" alt="SAM Deploy" /></p>
<p>Go to API gateway and hit the url in browser. You should get "hello from Lambda!" response.
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/github-actions/api.PNG" alt="github actions api" /></p>
<p>Go back to our workflow file on GitHub. We will deploy as soon as we push updates to the repo.
Below is our workflow.yml</p>
<pre><code class="lang-YAML"># This is a basic workflow to help you get started with Actions
name: AWS Lambda &amp; API gateway deployment demonstration
# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
  push:
    branches: [ master ]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest
    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
    # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
    - uses: actions/checkout@v2
    # Installs Python
    - name: Set up Python 3.8
      uses: actions/setup-python@v2
      with:
        python-version: 3.8
    # Installs PIP
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
    # Configures AWS credentials from github secrets
    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-1
    # Build using SAM 
    - name: SAM Build
      uses: youyo/aws-sam-action/python3.8@master
      with:
        sam_command: build
      env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION:  us-east-1
    # Deploy on AWS
    - name: sam deploy
      uses: youyo/aws-sam-action/python3.8@master
      with:
          sam_command: 'deploy --stack-name myApp --no-fail-on-empty-changeset'
      env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: us-east-1
</code></pre>
<p>We first indicate that we want to run this action on push to the master. Then we select runner (Ubuntu) on which our steps will execute. In Steps, we first checkout code, then install python and its dependencies. Then we use other actions from marketplace to configure AWS credentials and then we use another action to do SAM build and SAM deploy.  Please note that we need ti supply AWS ACCESS KEY and SECRET ACCESS KEY to actions for commands to work. We setup here GitHub Secrets.</p>
<p><img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/github-actions/secrets.PNG" alt="Github secrets" /></p>
<p>As soon as you check-in workflow.yml, the action would trigger.
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/github-actions/workflow_build.PNG" alt="Github actions workflow trigger" /></p>
<p>Deploying stack
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/github-actions/deploy.PNG" alt="Github actions deploying AWS stack" />
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/github-actions/job_complete.PNG" alt="Github Actions job completes" /></p>
<p>Now you can login to the AWS console and confirm the stack is created.
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/github-actions/stack_created.PNG" alt="AWS cloud formation stack created" /></p>
<p>Go to Resources tab and access the API Gateway. Go to stages and access the Prod stage API.
Open the URL in browser with the path /hello and you should see below output!</p>
<p><img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/github-actions/testing.PNG" alt="AWS Lambda function output" /></p>
<p>Congratulations! You have successfully automated AWS deployment using GitHub Actions!
You can download the code from here:<br /><a target="_blank" href="https://github.com/rajanpanchal/aws-github-actions"><img src="https://github-readme-stats.vercel.app/api/pin/?username=rajanpanchal&amp;repo=aws-github-actions" alt="github actions repo card" /></a></p>
]]></content:encoded></item><item><title><![CDATA[AWS Certified Developer - Associate Exam Tips (2020)]]></title><description><![CDATA[In June 2020, I passed the AWS Certified Solution Architect Exam after almost preparing for 5 months ( an hour or so everyday) and I was so pumped that I decided to take another AWS exam. I started preparing for the Developer Associate exam at the en...]]></description><link>https://blog.rajanpanchal.net/aws-certified-developer-associate-exam-tips-2020</link><guid isPermaLink="true">https://blog.rajanpanchal.net/aws-certified-developer-associate-exam-tips-2020</guid><category><![CDATA[AWS]]></category><category><![CDATA[Certification]]></category><category><![CDATA[Developer]]></category><category><![CDATA[tips]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Rajan Panchal]]></dc:creator><pubDate>Fri, 28 Aug 2020 04:43:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1598589819134/3cj_-7RHy.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In June 2020, I passed the AWS Certified Solution Architect Exam after almost preparing for 5 months ( an hour or so everyday) and I was so pumped that I decided to take another AWS exam. I started preparing for the Developer Associate exam at the end of June and cleared by July end.</p>
<p>Here is my badage: <a target="_blank" href="https://www.youracclaim.com/badges/2f8b7d13-cea5-421c-99eb-53c157c75558">AWS Badge</a></p>
<p>Developer associate is Developer focused and its bit different than the solution architect exam. I found Developer associate exam harder than the Solution Architect, but YMMV.</p>
<p>So here are my tips and preparation topics:</p>
<ul>
<li><p>You success in exam depends upon recognizing keywords in the questions.  All answers may look similar but keywords will lead to right answer. Pay attention to words. For example: 'low cost', 'encryption at rest vs encryption in transit'.</p>
</li>
<li><p>Know how to do Read Capacity Units (RCU) and Write Capacity Units (WCU) calculations. You might get one or two questions on determining RCU/WCU. Don't forget to pay attention to strongly consistent/ Eventual consistent keyword in question.</p>
</li>
<li><p>Sometimes you may find answer to questions in other questions. While reviewing see if any other question has answer to it. </p>
</li>
<li><p>Use all the time available for the exam. If finished early, spend time in reviewing all questions.</p>
</li>
<li><p>Attempt as many mock exams you can. This will prepare you on time management and questions patterns. Questions will be verbose in real exam.</p>
</li>
<li><p>There could be command level questions like what this command do, when to use etc.</p>
</li>
<li><p>You need to understand the concepts very well and should have some hands on. It is easy to eliminate two answers.</p>
</li>
<li><p>Know CI/CD services like CodeDeploy, CodeBuild, CodeCommit. Understand BuildSpec and AppSpec difference. Code Deploy lifecycle hooks. CodeDeploy Deployment Types for Lambda, ECS and EC2.</p>
</li>
<li><p>Cloudformation, KMS, SQS, SNS, ECS, API gateway, IAM (Inline policies/ Managed Policies) and Lambda (Concurrent execution Limit, </p>
</li>
<li><p>DynamoDB. Local Secondary Index vs Global. DynamoDB Accelerator. DynamoDB Streams. SCAN vs Query. </p>
</li>
<li><p>Amazon S3, EC2, Elastic Beanstalk and its deployment types. Knows CORS.</p>
</li>
<li><p>CloudWatch, CloudTrail, App X-Ray</p>
</li>
<li><p>You might also get architecture level questions but not much.</p>
</li>
</ul>
<p>Good Luck!</p>
]]></content:encoded></item><item><title><![CDATA[How to serve premium/private content on your site?]]></title><description><![CDATA[A lot of online companies (for example: Netflix, Udemy etc ) distribute contents over internet that is accessible only to the members of the site or who have subscribed or paid a premium. Today in this post we see how you can achieve this for your we...]]></description><link>https://blog.rajanpanchal.net/how-to-serve-premiumprivate-content-on-your-site</link><guid isPermaLink="true">https://blog.rajanpanchal.net/how-to-serve-premiumprivate-content-on-your-site</guid><category><![CDATA[Python 3]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Amazon Web Services]]></category><category><![CDATA[Cloud]]></category><dc:creator><![CDATA[Rajan Panchal]]></dc:creator><pubDate>Sun, 23 Aug 2020 02:06:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1598549115771/RWPaNJmMu.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A lot of online companies (for example: Netflix, Udemy etc ) distribute contents over internet that is accessible only to the members of the site or who have subscribed or paid a premium. Today in this post we see how you can achieve this for your website using Amazon Web Services. We will see how you can securely serve private content to your users from AWS S3 bucket using S3 Presigned URLs</p>
<h3 id="what-are-presigned-urls">What are Presigned URLs?</h3>
<p>A <strong>Presigned URL</strong> is a <strong>URL</strong> that provides limited permission and time to make a request. Anyone who receives the presigned URL can then access the object. For example, if you have a file in your bucket and both the bucket and the object are private, you can share the file with others by generating a presigned URL. </p>
<h4 id="some-important-points-on-presigned-urls">Some important points on Presigned URLs</h4>
<ul>
<li>The creator of Presigned URL should have access to object for URL to work, otherwise URL wont work.</li>
<li>You can create a presigned URL that's are not usable or doesn’t work. </li>
<li>It doesn’t cost anything to create Presigned URLs.</li>
<li>You can set expiration time on the URLs </li>
<li>If you created a presigned URL using a temporary token, then the URL expires when the token expires, even if the URL was created with a later expiration time</li>
<li>You can revoke the URL by removing the permissions to access the object from the user created the URL.</li>
</ul>
<h3 id="how-to-create-presigned-url">How to create Presigned URL?</h3>
<p>When you create a presigned URL for your object, you must provide your security credentials, specify a bucket name, an object key, specify method and expiration time.</p>
<p>In Python, using Boto3, you can use <em>generate_presigned_url</em> method to generate the URL</p>
<pre><code>response = s3_client.generate_presigned_url(<span class="hljs-string">'get_object'</span>,
            Params={<span class="hljs-string">'Bucket'</span>: bucket_name,
            <span class="hljs-string">'Key'</span>: object_name},
            ExpiresIn=expiration)
</code></pre><p>The <em>response</em> object will contain the URL which will look similar to this</p>
<pre><code><span class="hljs-attribute">https</span>://somebucketname-rep.s3.amazonaws.com/someFileName.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=ASIAQS3IOUWUZF7YSXGA<span class="hljs-number">%2</span>F20200821<span class="hljs-number">%2</span>Fus-east-2<span class="hljs-number">%2</span>Fs3<span class="hljs-number">%2</span>Faws4_request&amp;X-Amz-Date=20200821T051228Z&amp;X-Amz-Expires=3600&amp;X-Amz-SignedHeaders=host&amp;X-Amz-Security-Token=FwoGZXIvYXdzEH8aDCfJDxO0y6xQxYmdGCK2AXe71W<span class="hljs-number">%2</span>FgZEg<span class="hljs-number">%2</span>FSnSWC<span class="hljs-number">%2</span>Fw<span class="hljs-number">%2</span>FaJHeZ20M7OI7AqMEum5c98Chl6pSNPwE5Awsc3ySwokDF6L8a9wP0ceXWAmxT3WXLSoFeNHDbbEHfUKWnvGL8yFzAxdmf<span class="hljs-number">%2</span>Fmi<span class="hljs-number">%2</span>B5Tnl62td8Nad<span class="hljs-number">%2</span>F0Ct1Sx11Mip1h2qdYxw80OX5bCTq7cAHHjpmupvaDt<span class="hljs-number">%2</span>BZ3qVyIA9WZmeS63dCPOlieE9IiBZf<span class="hljs-number">%2</span>FjxF4Mcs5w4ZIHtZL<span class="hljs-number">%2</span>F3LvqMXAy3XfzCgnlYVZeCNczKLuv<span class="hljs-number">%2</span>FfkFMi0mStwkzyO<span class="hljs-number">%2</span>BfMIxWJ82GJmyNi7LZuY5r0Hx0mE<span class="hljs-number">%2</span>BxLnre8jp9<span class="hljs-number">%2</span>FACoV<span class="hljs-number">%2</span>FM92GnsR0<span class="hljs-number">%3</span>D&amp;X-Amz-Signature=17046b630ad4dede85af1cd57204bba8adc462a1825a35d93e81b656c683ad75
</code></pre><p>You can use this URL in the browser and access the object! Simple.. isn't it?</p>
<h3 id="lets-see-it-in-action">Lets see it in Action</h3>
<p>We are going to implement this on top of previous two posts, <a target="_blank" href="https://blog.rajanpanchal.net/aws-kms-use-case-with-serverless-application-model-sam-an-end-to-end-solution-ckdfenqag00lsqqs1csp05vhj">in the first post</a>, we implemented custom identity broker(signup/login), using AWS KMS to encrypt decryt password. In <a target="_blank" href="https://blog.rajanpanchal.net/how-to-give-access-to-aws-resources-without-creating-100s-of-iam-users-ckds91qj100pf97s1gc3v8vwb">second post</a>, we used AWS STS to AssumeRole to read bucket files names and now we will extend that to use presigned url. Download code for second post from github.com/rajanpanchal/aws-kms-sts and modify it.
Here is how overall process looks like
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/presignedUrl/presignedurl_diagram.jpg" alt="free cloud storage on s3, presigned URL diagram" /></p>
<p>Open file showFiles.py from the Lamdba folder and lets add function to generate presigned URL. Call this function from <em>getFilesList</em> function.</p>
<pre><code class="lang-PYTHON"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">getSignedUrl</span><span class="hljs-params">(key,s3_client)</span>:</span>
    KeyUrl = {}

    response = s3_client.generate_presigned_url(<span class="hljs-string">'get_object'</span>,
                        Params={<span class="hljs-string">'Bucket'</span>: os.environ[<span class="hljs-string">'filesBucket'</span>],
                        <span class="hljs-string">'Key'</span>: key},
                        ExpiresIn=<span class="hljs-number">3600</span>)
    KeyUrl[key] = response
    <span class="hljs-keyword">return</span> KeyUrl


<span class="hljs-comment"># Returns list of files from bucket using STS    </span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">getFilesList</span><span class="hljs-params">()</span>:</span>
    sts_client = boto3.client(<span class="hljs-string">'sts'</span>)

    <span class="hljs-comment"># Call the assume_role method of the STSConnection object and pass the role</span>
    <span class="hljs-comment"># ARN and a role session name.</span>
    assumed_role_object=sts_client.assume_role(
        RoleArn=os.environ[<span class="hljs-string">'s3role'</span>],
        RoleSessionName=<span class="hljs-string">"AssumeRoleSession1"</span>
    )

    <span class="hljs-comment"># From the response that contains the assumed role, get the temporary </span>
    <span class="hljs-comment"># credentials that can be used to make subsequent API calls</span>
    credentials=assumed_role_object[<span class="hljs-string">'Credentials'</span>]

    <span class="hljs-comment"># Use the temporary credentials that AssumeRole returns to make a </span>
    <span class="hljs-comment"># connection to Amazon S3  </span>
    s3_resource=boto3.resource(
        <span class="hljs-string">'s3'</span>,
        aws_access_key_id=credentials[<span class="hljs-string">'AccessKeyId'</span>],
        aws_secret_access_key=credentials[<span class="hljs-string">'SecretAccessKey'</span>],
        aws_session_token=credentials[<span class="hljs-string">'SessionToken'</span>],
    )
    s3_client = boto3.client(<span class="hljs-string">'s3'</span>,aws_access_key_id=credentials[<span class="hljs-string">'AccessKeyId'</span>],
aws_secret_access_key=credentials[<span class="hljs-string">'SecretAccessKey'</span>],
aws_session_token=credentials[<span class="hljs-string">'SessionToken'</span>])
    bucket = s3_resource.Bucket(os.environ[<span class="hljs-string">'filesBucket'</span>])
    files=[]
    <span class="hljs-keyword">for</span> obj <span class="hljs-keyword">in</span> bucket.objects.all():
        files.append(getSignedUrl(obj.key,s3_client))
    <span class="hljs-keyword">return</span> files
</code></pre>
<p>We are using the temporary credentials obtained from <em>AssumeRole</em> to generate Presigned URL in <em>getSignedUrl</em> function. The <em>getFilesList</em> functions returns list of file names and presigned URL.</p>
<p>Now, modify showFiles.html, to iterate over response and create links for files</p>
<pre><code class="lang-HTML"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css"</span> <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha512-8bHTC73gkZ7rZ7vpqUQThUDhqcNFyYi2xgDgPDHc+GXVGHXq+xPjynxIopALmOPqzo9JZj0k6OqqewdGO3EsrQ=="</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span>
  <span class="hljs-attr">src</span>=<span class="hljs-string">"https://code.jquery.com/jquery-3.1.1.min.js"</span>
  <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="</span>
  <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span><span class="undefined"></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js"</span>&gt;</span><span class="undefined"></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"ui raised very text container"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"ui header"</span>&gt;</span>File Access System<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"folder open icon"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"ui label"</span>&gt;</span>Files<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"files"</span> &gt;</span>Loading..<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">

fetch(<span class="hljs-string">"    https://g4m3zpzp95.execute-api.us-east-2.amazonaws.com/Prod/showFiles/"</span>, {
  credentials: <span class="hljs-string">'include'</span>
})
  .then(response =&gt; response.text())
  .then((body) =&gt; {
    <span class="hljs-keyword">var</span> files=<span class="hljs-string">""</span>;

    <span class="hljs-keyword">var</span> obj = <span class="hljs-built_in">JSON</span>.parse(body)
    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; obj.length; i++) {
            <span class="hljs-keyword">var</span> o = obj[i]

            <span class="hljs-keyword">for</span>(x <span class="hljs-keyword">in</span> o){
                files =  files+ <span class="hljs-string">"&lt;i class='file alternate outline icon'&gt;&lt;a href='"</span>+o[x]+<span class="hljs-string">"' target='_blank'&gt;&amp;nbsp;&amp;nbsp;"</span>+x+<span class="hljs-string">"&lt;/a&gt;"</span>
            }
    }
    <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"files"</span>).innerHTML= files
  })
  .catch(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">error</span>) </span>{
    <span class="hljs-built_in">console</span>.log(error); 
  });

</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Do SAM build and Deploy.
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/presignedUrl/presignedUrlStack.PNG" alt="Free cloud storage Presigned URL" /></p>
<p>Modify login.html, signup.html and showFiles.html to update the api urls from cloudformation outputs.
Upload these files to the bucket <em>stsexamplebucket</em> or the bucket you created. Keep these files Public</p>
<p>You can find the code here:<br /><a target="_blank" href="https://github.com/rajanpanchal/aws-kms-sts-presigned-url"><img src="https://github-readme-stats.vercel.app/api/pin/?username=rajanpanchal&amp;repo=aws-kms-sts-presigned-url" alt="AWS S3 Presigned Url Card" /></a></p>
<h3 id="testing">Testing</h3>
<p><img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/presignedUrl/testingVideo.gif" alt="private content serve using Presigned URL" /></p>
<p>Let me know if you have any questions or comments!</p>
]]></content:encoded></item><item><title><![CDATA[How to give access to AWS resources without creating 100s of IAM Users?]]></title><description><![CDATA[Scenario:
Imagine you are a solution architect in a company with 100s of Sales employees and you are migrating from on premise to AWS Cloud. You want to use existing employee authentication system and you want to store files on S3 that employee uses ...]]></description><link>https://blog.rajanpanchal.net/how-to-give-access-to-aws-resources-without-creating-100s-of-iam-users</link><guid isPermaLink="true">https://blog.rajanpanchal.net/how-to-give-access-to-aws-resources-without-creating-100s-of-iam-users</guid><category><![CDATA[AWS]]></category><category><![CDATA[serverless]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Rajan Panchal]]></dc:creator><pubDate>Thu, 13 Aug 2020 03:33:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1598549236067/mIFqhmmPL.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="scenario">Scenario:</h3>
<p>Imagine you are a solution architect in a company with 100s of Sales employees and you are migrating from on premise to AWS Cloud. You want to use existing employee authentication system and you want to store files on S3 that employee uses in their day to day work. You don't want to keep S3 bucket public, that will expose all files to everybody. 
You have 2 options:</p>
<ol>
<li><p>Create single role with S3 bucket access and login credentials for all of the employees. With this user have to use 2 different logins. One to access their existing system and other to access S3 files.</p>
</li>
<li><p>Use AWS Security Token Service (STS) to assume role with S3 access and use that to give access to the files. User will still authenticate with their existing system. </p>
</li>
</ol>
<p>In this post, we will explore and implement option # 2. Please note that we are building this example on top of <a target="_blank" href="https://blog.rajanpanchal.net/aws-kms-use-case-with-serverless-application-model-sam-an-end-to-end-solution-ckdfenqag00lsqqs1csp05vhj">previous</a> post.</p>
<h3 id="about-security-token-service-sts">About Security Token Service (STS)</h3>
<p>AWS Security Token Service (AWS STS) is a web service that enables you to request temporary, limited-privilege credentials for AWS Identity and Access Management (IAM) users or for users that you authenticate. You can use <strong>AssumeRole</strong> action on STS that returns a set of temporary security credentials that you can use to access AWS resources that you might not normally have access to. These temporary credentials consist of an access key ID, a secret access key, and a security token. Typically, you use AssumeRole within your account or for cross-account access. In our case, we are using AssumeRole within same account. </p>
<h3 id="how-to-setup-users-and-roles">How to setup users and roles?</h3>
<p>In our case, we are going to create a role called <em>S3ReadOnlyAccessAssumeRole</em>. As name suggests it has only S3 read access policy. We will also create a Trust Policy and attached to this S3 role. Trust policy will allow this role to be assumed by our lambda execution role. Here is how our SAM will look like for this role.</p>
<pre><code class="lang-YAML">IamS3Role:
    Type: AWS::IAM::Role
    Properties: 
      AssumeRolePolicyDocument:
       Version: 2012-10-17
       Statement:
          - Effect: Allow
            Principal:
              AWS: !GetAtt ShowFilesFunctionRole.Arn
            Action:
              - 'sts:AssumeRole'
      Description: 'Readonly S3 role for lamdba to Assume at runtime'
      ManagedPolicyArns: 
      - arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
      RoleName: S3ReadOnlyAccessAssumeRole
</code></pre>
<p>In above snippet, <em>AssumeRolePolicyDocument</em> attribute specifies trust policy that allow Lamdba execution role identified by principle <strong>AWS: !GetAtt ShowFilesFunctionRole.Arn</strong> to AssumeRole to which this policy is attached to i.e S3ReadOnlyAccessAssumeRole .  The <em>ManagedPolicyArns</em> attribute species the policy for <strong>S3ReadOnlyAccessAssumeRole</strong> that allows read only access to S3 buckets.</p>
<h3 id="lambda-handler">Lambda Handler</h3>
<p>Now, lets write our Lamdba handler that will use this role. Here is the SAM confgiuration</p>
<pre><code class="lang-YAML">Origin:
    Type: String
    Default: https://stsexamplebucket.s3.us-east-2.amazonaws.com
  FilesBucket:
    Type: String
    Default: s3-sales-rep
</code></pre>
<pre><code class="lang-YAML">ApiGatewayShowFilesApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
    Auth:
     UsagePlan:
      CreateUsagePlan: PER_API
      Description: Usage plan for this API
      Quota:
       Limit: 500
       Period: MONTH
      Throttle:
       BurstLimit: 100
       RateLimit: 50
  ShowFilesFunction:
    Type: AWS::Serverless::Function
    Properties:
      Environment:
        Variables:
          userTable: !Ref myDynamoDBTable
          s3role: !GetAtt IamS3Role.Arn
          origin: !Sub ${Origin}
          filesBucket: !Sub ${FilesBucket}
      CodeUri: Lambda/
      Handler: showfiles.lambda_handler
      Runtime: python3.8
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref myDynamoDBTable      
      Events:
        getCounter:
          Type: Api
          Properties:
            Path: /showFiles
            Method: GET
            RestApiId: !Ref ApiGatewayShowFilesApi
</code></pre>
<p>We are defining here couple of parameters that will be set as environment variables for Lambda. With <strong>Origin</strong> we specifying the origin domain for <a target="_blank" href="https://www.w3.org/wiki/CORS_Enabled">CORS</a>. It is our S3 bucket's <a target="_blank" href="https://docs.aws.amazon.com/AmazonS3/latest/dev/VirtualHosting.html">Virtual hosted style URL</a>. <strong>FilesBucket</strong> is the bucket where files are stored. In Serverless Function definition, it uses showfiles.py as lambda handler. Function has permissions to use DB. We also create API for this lamdba with path <em>/showFiles</em>.</p>
<p>Lets see what we do in the Lambda handlers. We modified login.py lambda handler from previous post. We are setting a cookie once the user is authenticated. This is completely optional and really not required for STS to work but you might need some kind of system to identify that user is already authenticated. </p>
<pre><code class="lang-Python"> <span class="hljs-keyword">if</span> decryptedPass == pwd : 
      token = secrets.token_hex(<span class="hljs-number">16</span>)  
      response = table.update_item(
        Key={
            <span class="hljs-string">'userid'</span>: uname
        },
        AttributeUpdates={
            <span class="hljs-string">'token'</span>: {
                <span class="hljs-string">'Value'</span>: token,
            }
            }
        )

      <span class="hljs-keyword">return</span> {
        <span class="hljs-string">'statusCode'</span>: <span class="hljs-number">200</span>,
        <span class="hljs-string">'headers'</span>:{
             <span class="hljs-string">'Set-Cookie'</span>:<span class="hljs-string">'tkn='</span>+uname+<span class="hljs-string">'&amp;'</span>+token+<span class="hljs-string">';Secure;SameSite=None;HttpOnly;Domain=.amazonaws.com;Path=/'</span>,
             <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'text/html'</span>
         },
        <span class="hljs-string">'body'</span>: <span class="hljs-string">'&lt;html&gt;&lt;head&gt;&lt;script&gt;window.location.href = \''</span>+ os.environ[<span class="hljs-string">'showFilesUrl'</span>]+<span class="hljs-string">'\' &lt;/script&gt;&lt;/head&gt;&lt;body&gt;Hello&lt;/body&gt;&lt;/html&gt;'</span>
      }
    <span class="hljs-keyword">else</span>:
     response[<span class="hljs-string">'status'</span>] = <span class="hljs-string">'Login Failed'</span>
     <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(response) 
      }
</code></pre>
<p>When user submits the username and password, Login lambda handler will authenticate the user, store the unique id in DB, set the cookie in response with location to showFiles url html page from S3 bucket. On page load, browser will change the location to showfiles url that will trigger the showFiles Lambda handler defined in showFiles.py</p>
<p><strong>showFiles.html</strong></p>
<pre><code class="lang-HTML"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css"</span> <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha512-8bHTC73gkZ7rZ7vpqUQThUDhqcNFyYi2xgDgPDHc+GXVGHXq+xPjynxIopALmOPqzo9JZj0k6OqqewdGO3EsrQ=="</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span>
  <span class="hljs-attr">src</span>=<span class="hljs-string">"https://code.jquery.com/jquery-3.1.1.min.js"</span>
  <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="</span>
  <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span><span class="undefined"></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js"</span>&gt;</span><span class="undefined"></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"ui raised very text container"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"ui header"</span>&gt;</span>File Access System<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"folder open icon"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"ui label"</span>&gt;</span>Files<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"files"</span> &gt;</span>Loading..<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">

fetch(<span class="hljs-string">"https://9nimlkmz74.execute-api.us-east-2.amazonaws.com/Prod/showFiles/"</span>, {
  credentials: <span class="hljs-string">'include'</span>
})
  .then(response =&gt; response.text())
  .then((body) =&gt; {
    <span class="hljs-keyword">var</span> files=<span class="hljs-string">""</span>;
    <span class="hljs-keyword">var</span> obj =  <span class="hljs-built_in">JSON</span>.parse(body)
    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; obj.length; i++) {
            files =  files+ <span class="hljs-string">"&lt;i class='file alternate outline icon'&gt;&lt;a href='#'&gt;&amp;nbsp;&amp;nbsp;"</span>+obj[i]+<span class="hljs-string">"&lt;/a&gt;"</span>
    }
    <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"files"</span>).innerHTML= files
  })
  .catch(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">error</span>) </span>{
    <span class="hljs-built_in">console</span>.log(error); 
  });

</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>We call the showFiles API, that gets the list of Files from S3 bucket and display on the page.</p>
<p><strong>showFiles.py</strong></p>
<pre><code class="lang-Python"><span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> logging
<span class="hljs-keyword">import</span> boto3
<span class="hljs-keyword">import</span> os

log = logging.getLogger()
log.setLevel(logging.INFO)

<span class="hljs-comment">#retuns login cookie information userid and unique token</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">getLoginCookie</span><span class="hljs-params">(cookies)</span>:</span>
    data ={}
    <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> cookies:
      keyValue = x.split(<span class="hljs-string">'='</span>)

      <span class="hljs-keyword">if</span> keyValue[<span class="hljs-number">0</span>].strip() ==<span class="hljs-string">'tkn'</span>:
        cookieValue = keyValue[<span class="hljs-number">1</span>]
        tknvalues = cookieValue.split(<span class="hljs-string">'&amp;'</span>)
        data[<span class="hljs-string">'uid'</span>]=tknvalues[<span class="hljs-number">0</span>]
        data[<span class="hljs-string">'tkn'</span>]=tknvalues[<span class="hljs-number">1</span>]
      <span class="hljs-keyword">else</span>:
        cookieValue =<span class="hljs-string">''</span>
      <span class="hljs-keyword">return</span> data

<span class="hljs-comment">#verifies unique token that is saved in database vs in request      </span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">verifyLogin</span><span class="hljs-params">(data)</span>:</span>
    dynamodb = boto3.resource(<span class="hljs-string">'dynamodb'</span>)
    table = dynamodb.Table(os.environ[<span class="hljs-string">'userTable'</span>])
    response = table.get_item(Key={<span class="hljs-string">'userid'</span>: data[<span class="hljs-string">'uid'</span>]})
    json_str =  json.dumps( response[<span class="hljs-string">'Item'</span>])

    resp_dict = json.loads(json_str)
    token = resp_dict.get(<span class="hljs-string">"token"</span>)
    <span class="hljs-keyword">return</span> bool(token == data[<span class="hljs-string">'tkn'</span>])

<span class="hljs-comment"># Returns list of files from bucket using STS    </span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">getFilesList</span><span class="hljs-params">()</span>:</span>
    sts_client = boto3.client(<span class="hljs-string">'sts'</span>)

    <span class="hljs-comment"># Call the assume_role method of the STSConnection object and pass the role</span>
    <span class="hljs-comment"># ARN and a role session name.</span>
    assumed_role_object=sts_client.assume_role(
        RoleArn=os.environ[<span class="hljs-string">'s3role'</span>],
        RoleSessionName=<span class="hljs-string">"AssumeRoleSession1"</span>
    )

    <span class="hljs-comment"># From the response that contains the assumed role, get the temporary </span>
    <span class="hljs-comment"># credentials that can be used to make subsequent API calls</span>
    credentials=assumed_role_object[<span class="hljs-string">'Credentials'</span>]

    <span class="hljs-comment"># Use the temporary credentials that AssumeRole returns to make a </span>
    <span class="hljs-comment"># connection to Amazon S3  </span>
    s3_resource=boto3.resource(
        <span class="hljs-string">'s3'</span>,
        aws_access_key_id=credentials[<span class="hljs-string">'AccessKeyId'</span>],
        aws_secret_access_key=credentials[<span class="hljs-string">'SecretAccessKey'</span>],
        aws_session_token=credentials[<span class="hljs-string">'SessionToken'</span>],
    )

    bucket = s3_resource.Bucket(os.environ[<span class="hljs-string">'filesBucket'</span>])
    files=[]
    <span class="hljs-keyword">for</span> obj <span class="hljs-keyword">in</span> bucket.objects.all():
        files.append(obj.key)
    <span class="hljs-keyword">return</span> files


<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>
    headers = event.get(<span class="hljs-string">"headers"</span>)
    cookies = headers[<span class="hljs-string">'Cookie'</span>].split(<span class="hljs-string">";"</span>)
    data = getLoginCookie(cookies)
    isVerified = verifyLogin(data)

    <span class="hljs-keyword">if</span>(isVerified):
        response = getFilesList()

    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">'statusCode'</span>: <span class="hljs-number">200</span>,
        <span class="hljs-string">'headers'</span>: {
            <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
            <span class="hljs-string">'Access-Control-Allow-Origin'</span>:os.environ[<span class="hljs-string">'origin'</span>],
            <span class="hljs-string">'Access-Control-Allow-Credentials'</span>: <span class="hljs-string">'true'</span>
        },
        <span class="hljs-string">'body'</span>: json.dumps(response)
    }
</code></pre>
<p>Focus on the lamdba_handler function here. We first get the cookie and verify the login.  If verified we call the <strong>getFilesList</strong> function where the magic of STS happens. We get the arn of role to be assumed from Lambda environment variables. The <em>assume_role</em> function returns the credentials that contains the access key id, secret access key and session token. You can use this credentials to get S3 resources and access the bucket. We create list of files as array and send it as response. </p>
<p>You can find full SAM template.yml and other code for this here:
<a target="_blank" href="https://github.com/rajanpanchal/aws-kms-sts"><img src="https://github-readme-stats.vercel.app/api/pin/?username=rajanpanchal&amp;repo=aws-kms-sts" alt="AWS STS Card" /></a></p>
<p>Before you run SAM deploy, create a S3 bucket that will store the html files. In our case, its <strong>stsexamplebucket</strong>. We use urls from this bucket in our SAM Template. On SAM Deploy, It will generate output with 3 URLS for the APIs. Modify the html files to point to those url and upload to S3 bucket. Make sure you make those files public.</p>
<h3 id="testing">Testing</h3>
<p>You need to create a bucket with name specified in <em>FilesBucket</em> parameter in SAM template. This bucket will store the files for display.</p>
<p><img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/kms-sts/testing.gif" alt="Testing" /></p>
<h3 id="summary">Summary</h3>
<p>In summary, we used a custom identity broker that authenticates the user and then AWS STS that allows access to AWS resources to those users. You might wonder we could have given access to S3 to Lambda Execution role instead of using STS. Of course you can and it will work. But the idea here is to use STS and you can use STS irrespective of Lambda i.e in your other applications like spring boot, java, python application, etc. 
In next post, we will further extend this to use S3 signed url to give access to files stored in S3.</p>
<p>Feel free to point out any suggestions, errors and give your feedback in comments!</p>
]]></content:encoded></item><item><title><![CDATA[AWS KMS use case with Serverless Application Model (SAM):  An end to end solution]]></title><description><![CDATA[The Basics
AWS KMS is a Key Management Service that let you create Cryptographic keys that you can use to encrypt and decrypt data and also other keys. You can read more about it here.
Important points about Keys
Please note that the CMK generated ca...]]></description><link>https://blog.rajanpanchal.net/aws-kms-use-case-with-serverless-application-model-sam-an-end-to-end-solution</link><guid isPermaLink="true">https://blog.rajanpanchal.net/aws-kms-use-case-with-serverless-application-model-sam-an-end-to-end-solution</guid><category><![CDATA[AWS]]></category><category><![CDATA[serverless]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[#howtos]]></category><dc:creator><![CDATA[Rajan Panchal]]></dc:creator><pubDate>Tue, 04 Aug 2020 03:48:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1598549378681/9bYdEyYxY.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="the-basics">The Basics</h3>
<p><strong>AWS KMS</strong> is a <strong>K</strong>ey <strong>M</strong>anagement <strong>S</strong>ervice that let you create Cryptographic keys that you can use to encrypt and decrypt data and also other keys. You can read more about it <a target="_blank" href="https://docs.aws.amazon.com/kms/latest/developerguide/overview.html">here</a>.</p>
<h3 id="important-points-about-keys">Important points about Keys</h3>
<p>Please note that the CMK generated can only be used to encrypt small amount of data like passwords, RSA key. You can use AWS KMS customer master keys (CMKs) to generate, encrypt, and decrypt data keys. However, AWS KMS does not store, manage, or track your data keys, or perform cryptographic operations with data keys. You must use and manage data keys outside of AWS KMS. KMS API uses AWS KMS customer master key (CMK) in the encryption operations and they cannot accept more than 4 KB (4096 bytes) of data. To encrypt application data, use the server-side encryption features of an AWS service, or a client-side encryption library, such as the AWS Encryption SDK or the Amazon S3 encryption client.</p>
<h3 id="scenario">Scenario</h3>
<p>We want to create signup and login forms for a website.Passwords should be encrypted and stored in dynamobDB database. </p>
<h3 id="what-do-we-need">What do we need?</h3>
<ol>
<li>KMS key to encrypt and decrypt data</li>
<li>DynamoDB table to store password.</li>
<li>Lamdba functions &amp; APIs to process Login and Sign up forms.
4.Sign up/ Login forms in HTML</li>
</ol>
<h3 id="lets-implement-it-as-serverless-appication-model-sam">Lets Implement it as Serverless Appication Model (SAM)!</h3>
<p>Lets first create the Key that we will use to encrypt and decrypt password.</p>
<pre><code class="lang-YAML">KmsKey:
    Type: AWS::KMS::Key
    Properties: 
      Description: CMK for encrypting and decrypting
      KeyPolicy:
        Version: '2012-10-17'
        Id: key-default-1
        Statement:
        - Sid: Enable IAM User Permissions
          Effect: Allow
          Principal:
            AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
          Action: kms:*
          Resource: '*'
        - Sid: Allow administration of the key
          Effect: Allow
          Principal:
            AWS: !Sub arn:aws:iam::${AWS::AccountId}:user/${KeyAdmin}
          Action:
          - kms:Create*
          - kms:Describe*
          - kms:Enable*
          - kms:List*
          - kms:Put*
          - kms:Update*
          - kms:Revoke*
          - kms:Disable*
          - kms:Get*
          - kms:Delete*
          - kms:ScheduleKeyDeletion
          - kms:CancelKeyDeletion
          Resource: '*'
        - Sid: Allow use of the key
          Effect: Allow
          Principal:
            AWS: !Sub arn:aws:iam::${AWS::AccountId}:user/${KeyUser}
          Action:
          - kms:DescribeKey
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey
          - kms:GenerateDataKeyWithoutPlaintext
          Resource: '*'
</code></pre>
<p>The important thing in above snippet is the KeyPolicy. KMS requires a Key Administrator and Key User. As a best practice your Key Administrator and Key User should be 2 separate user in your Organisation. We are allowing all permissions to the root users.  So if your key Administrator leaves the organisation, the root user will be able to delete this key. As you can see KeyAdmin can manage the key but not use it and KeyUser can only use the key. <em>${KeyAdmin}</em> and <em>${KeyUser}</em> are parameters in the SAM template. You would be asked to provide values for these parameters during SAM Deploy. </p>
<pre><code class="lang-YAML">Parameters:
  KeyAdmin:
    Type: String
  KeyUser:
    Type: String
</code></pre>
<p><img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/kms/parameters.jpg" alt="SAM parameters" />
Next, we will create DynamoDB table. The partition key will be user id. This is enough. You can additional attributes as required.</p>
<pre><code class="lang-YAML">myDynamoDBTable: 
    Type: AWS::DynamoDB::Table
    Properties: 
      BillingMode: PAY_PER_REQUEST 
      AttributeDefinitions: 
        - 
          AttributeName: "userid"
          AttributeType: "S"

      KeySchema: 
        - 
          AttributeName: "userid"
          KeyType: "HASH"
</code></pre>
<p>Now, lets create the API and Lamdba handler that will process the Signup and Login requests.</p>
<pre><code class="lang-YAML">ApiGatewaySignupApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
    Auth:
     UsagePlan:
      CreateUsagePlan: PER_API
      Description: Usage plan for this API
      Quota:
       Limit: 500
       Period: MONTH
      Throttle:
       BurstLimit: 100
       RateLimit: 50
  SignupFunction:
    Type: AWS::Serverless::Function
    Properties:
      Environment:
        Variables:
          userTable: !Ref myDynamoDBTable
          keyid: !Ref KmsKey
      CodeUri: Lambda/
      Handler: signup.lambda_handler
      Runtime: python3.8
      Policies:
       - DynamoDBCrudPolicy:
          TableName: !Ref myDynamoDBTable
       - KMSEncryptPolicy:
          KeyId: !Ref KmsKey 
       - KMSDecryptPolicy:
          KeyId: !Ref KmsKey
      Events:
        getCounter:
          Type: Api
          Properties:
            Path: /signup
            Method: POST
            RestApiId: !Ref ApiGatewaySignupApi
  ApiGatewayLoginApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
    Auth:
     UsagePlan:
      CreateUsagePlan: PER_API
      Description: Usage plan for this API
      Quota:
       Limit: 500
       Period: MONTH
      Throttle:
       BurstLimit: 100
       RateLimit: 50
  LoginFunction:
    Type: AWS::Serverless::Function
    Properties:
      Environment:
        Variables:
          userTable: !Ref myDynamoDBTable
          keyid: !Ref KmsKey
      CodeUri: Lambda/
      Handler: login.lambda_handler
      Runtime: python3.8
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref myDynamoDBTable
        - KMSEncryptPolicy:
            KeyId: !Ref KmsKey 
        - KMSDecryptPolicy:
            KeyId: !Ref KmsKey
      Events:
        getCounter:
          Type: Api
          Properties:
            Path: /login
            Method: POST
            RestApiId: !Ref ApiGatewayLoginApi
</code></pre>
<p>Here, we are creating 2 Lamdba handlers, one that handles singup and other handles the login. We are limiting the usage of API under <em>Auth</em> property. This is to prevent abuse of our API. We are setting two environment variables, one for database table name and another for KMS key name. We are not hard-coding the table/key names instead we let cloud formation to name it and we set it as environment variables with auto generated values. This will prevent name conflicts of resources and you can run multiple versions of your whole cloud environment. Then we set policy that allow Lamdba to access dynamoDB and the key.  You can find 'out of box' SAM policy templates <a target="_blank" href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-policy-templates.html">here</a>.With <em>Events</em>, we create the API Gateway resources.
The final part of SAM template is Output section.</p>
<pre><code class="lang-YAML">Outputs:
  ApiGatewaySignupApi:
    Description: "API Gateway endpoint URL for Prod stage for Hello World function"
    Value: !Sub "https://${ApiGatewaySignupApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/signup/"
  SignupFunction:
    Description: "Sign Up Lambda Function ARN"
    Value: !GetAtt SignupFunction.Arn
  ApiGatewayLoginApi:
    Description: "API Gateway endpoint URL for Prod stage for Hello World function"
    Value: !Sub "https://${ApiGatewayLoginApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/login/"
  LoginFunction:
    Description: "Login Lambda Function ARN"
    Value: !GetAtt LoginFunction.Arn
</code></pre>
<p>We output 2 important urls in above section, <em>ApiGatewayLoginApi</em> and <em>ApiGatewaySignupApi</em>,  We use these Urls in the frontend HTML form's <em>action</em> attribute. See below:</p>
<pre><code class="lang-HTML"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Sign Up<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"https://51v5ifsje1.execute-api.us-east-2.amazonaws.com/Prod/signup/"</span> <span class="hljs-attr">enctype</span>=<span class="hljs-string">"application/x-www-form-urlencoded"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"POST"</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Username:<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"uname"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"uname"</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">input</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Password:<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"password"</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">input</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span>
         <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">input</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
   <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<pre><code class="lang-HTML"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"https://4ezqeugqlf.execute-api.us-east-2.amazonaws.com/Prod/login/"</span> <span class="hljs-attr">enctype</span>=<span class="hljs-string">"application/x-www-form-urlencoded"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"POST"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>username:<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"uname"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"uname"</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">input</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>password:<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"password"</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">input</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">submit</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>In HTML, we are using encoding type as "enctype="application/x-www-form-urlencoded". With this type of encoding, the form data is sent in below format</p>
<pre><code><span class="hljs-attr">username</span>=doryfish&amp;password=nemo
</code></pre><p>The last piece in the puzzle is the Lamdba handlers. Lets check it out. Python uses <a target="_blank" href="https://boto3.amazonaws.com/v1/documentation/api/latest/index.html">Boto3</a> to access AWS.</p>
<pre><code class="lang-Python"><span class="hljs-keyword">import</span> json
<span class="hljs-keyword">from</span> urllib.parse <span class="hljs-keyword">import</span> parse_qs
<span class="hljs-keyword">import</span> urllib.parse
<span class="hljs-keyword">import</span> boto3
<span class="hljs-keyword">import</span> logging
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> base64

log = logging.getLogger()
log.setLevel(logging.INFO)

<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>
    log.info(event)
    log.info(event.get(<span class="hljs-string">"body"</span>))
    qs = parse_qs(event.get(<span class="hljs-string">"body"</span>))
    log.info(qs)
    uname = qs.get(<span class="hljs-string">"uname"</span>)[<span class="hljs-number">0</span>] 
    pwd = qs.get(<span class="hljs-string">"password"</span>)[<span class="hljs-number">0</span>]

    dynamodb = boto3.resource(<span class="hljs-string">'dynamodb'</span>)
    table = dynamodb.Table(os.environ[<span class="hljs-string">'userTable'</span>])

    log.info(<span class="hljs-string">'key id:'</span>+os.environ[<span class="hljs-string">'keyid'</span>])
    key = os.environ[<span class="hljs-string">'keyid'</span>]
    client = boto3.client(<span class="hljs-string">'kms'</span>)
    <span class="hljs-comment">#Encrypt password</span>
    response = client.encrypt(
    Plaintext=pwd,
    KeyId=key
    )
    log.info(response[<span class="hljs-string">'CiphertextBlob'</span>])
    b64_pass = str(base64.b64encode(response[<span class="hljs-string">'CiphertextBlob'</span>]),<span class="hljs-string">'utf-8'</span>)
    log.info(b64_pass)

    response = table.update_item(
        Key={
            <span class="hljs-string">'userid'</span>: uname
        },
        AttributeUpdates={
            <span class="hljs-string">'password'</span>: {
                <span class="hljs-string">'Value'</span>: b64_pass,
            }
            }
        )
    data = {}
    data[<span class="hljs-string">'status'</span>] = <span class="hljs-string">'Signup Success'</span>
    json_data = json.dumps(data)    
    <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_data
    }
</code></pre>
<p>The first two statements are logging statements, this is useful in debugging and the log statements will be logged in CloudWatch logs. The <em>parse_qs</em> module is used to read the form data from <em>event</em> object. Data are returned as a dictionary. The dictionary keys are the unique query variable names and the values are lists of values for each name. Hence, in next 2 statements, we get the first value for each of type - uname and password. 
We retrieve the DynamoDB table using the table name from environment variable - <em>userTable</em>.  Environment variables in Lambda can be accessed using <em>os.environ['key']</em>. 
Now we got the username and password.  Its time to encrypt the password and store it in DB. Password to encrypt is passed to <em>Plaintext</em> attribute of the encrypt request and key id is retrieved from environment variable. <em>CiphertextBlob</em> is the encrypted binary value in the response object. For example:</p>
<pre><code>b<span class="hljs-string">'\x01\x02\x02\x00x\x9dN"\xa4\xf9\xfe\xb4\xc7&amp;\x01\xdc\xb6J\xdf\xf1\xdc\xf2;)|7\x1b\'</span>{8\xe6(\x80Q\xe5\x11\x8c\x010w<span class="hljs-string">"-\x11w\x10b\x9d\xd0w\xa7+\xd1\xa5\xc5\x00\x00\x00e0c\x06\t*\x86H\x86\xf7\r\x01\x07\x06\xa0V0T\x02\x01\x000O\x06\t*\x86H\x86\xf7\r\x01\x07\x010\x1e\x06\t`\x86H\x01e\x03\x04\x01.0\x11\x04\x0ca\xb4\xaa\x00\x10\xc0\xd1\xa6r\x07\xce\xc7\x02\x01\x10\x80"</span>@\nL\xde&lt;\x03s\xc6\xe0g\x80\xd4\x87\x8e\x1e\t\xa2\xac\x10\xfek\xb6\x1d\xf3\x87\x910\xabf\xd1d}x\xdb<span class="hljs-string">'</span>
</code></pre><p>Now we convert this binary value to ASCII text using </p>
<pre><code>str(base64.b64encode(response[<span class="hljs-string">'CiphertextBlob'</span>]),<span class="hljs-string">'utf-8'</span>)
</code></pre><p>and the end result we store it in database.</p>
<pre><code>AQI<span class="hljs-built_in">CAHidTiKk</span>+f60xyYB3LZK3/Hc8jspfDcbJ3s45iiAUeURjAEwdyItEXcQYp3Qd6cr0aXFAAAAZTBjBgkqhkiG9w0BBwagVjBUAgEAME8GCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMYbSqABDA0aZyB87HAgEQgCJACkzePANzxuBng<span class="hljs-built_in">NSHjh4JoqwQ</span>/mu2HfOHkTCrZtFkfXjb
</code></pre><p>In Login Lamdba handler, username and password is retrieved from the request like we did in Sign Up.</p>
<pre><code class="lang-Python"><span class="hljs-keyword">import</span> json
<span class="hljs-keyword">from</span> urllib.parse <span class="hljs-keyword">import</span> parse_qs
<span class="hljs-keyword">import</span> urllib.parse
<span class="hljs-keyword">import</span> boto3
<span class="hljs-keyword">import</span> secrets
<span class="hljs-keyword">import</span> logging
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> base64

log = logging.getLogger()
log.setLevel(logging.INFO)

<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>

    log.info(event.get(<span class="hljs-string">"body"</span>))
    qs = parse_qs(event.get(<span class="hljs-string">"body"</span>))

    uname = qs.get(<span class="hljs-string">"uname"</span>)[<span class="hljs-number">0</span>] 
    pwd = qs.get(<span class="hljs-string">"password"</span>)[<span class="hljs-number">0</span>]

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

    table = dynamodb.Table(os.environ[<span class="hljs-string">'userTable'</span>])
    response = table.get_item(Key={<span class="hljs-string">'userid'</span>: uname})
    json_str =  json.dumps( response[<span class="hljs-string">'Item'</span>])

    <span class="hljs-comment">#using json.loads will turn your data into a python dictionary</span>
    resp_dict = json.loads(json_str)
    dbpass = resp_dict.get(<span class="hljs-string">"password"</span>)

    <span class="hljs-comment">#Decrypt password</span>
    log.info(<span class="hljs-string">'key id:'</span>+os.environ[<span class="hljs-string">'keyid'</span>])
    key = os.environ[<span class="hljs-string">'keyid'</span>]
    client = boto3.client(<span class="hljs-string">'kms'</span>)

    response = client.decrypt(
    CiphertextBlob=(base64.b64decode(dbpass)),
    KeyId=key
    )
    log.info(<span class="hljs-string">"Decrypted value"</span>)
    decryptedPass = response[<span class="hljs-string">'Plaintext'</span>].decode(<span class="hljs-string">'UTF-8'</span>)

    response = {}

    <span class="hljs-keyword">if</span> decryptedPass == pwd : 
      response[<span class="hljs-string">'status'</span>] = <span class="hljs-string">'Login Success'</span>
      <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(response) 
      }
    <span class="hljs-keyword">else</span>:
     response[<span class="hljs-string">'status'</span>] = <span class="hljs-string">'Login Failed'</span>
     <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(response) 
      }
</code></pre>
<p>In decrypt request, the encrypted password string from database is converted into binary using <em>base64.b64decode(dbpass)</em> and passed to the <em>CiphertextBlob</em> attribute. Decrypted password is returned as bytes in <em>Plaintext</em> attribute of response. The password is encoded in UTF-8 to get the final decrypted password. The decrypted password is then compared with the password from the request and if it matches, <em>'Login Success'</em> response is sent back.</p>
<p>Phew! Finally, we are done coding.. Lets test it out.
Lets do SAM build and Deploy.
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/kms/sam-build.JPG" alt="SAM build" /></p>
<p><img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/kms/sam-deploy-kms-p1.JPG" alt="SAM Deploy" />
Stack creation complete!
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/kms/sam-deploy-kms-p2.JPG" alt="Stack complete" /></p>
<h3 id="testing">Testing</h3>
<p>Note that we are not deploying HTML forms as static site on S3 but we will locally open the forms in browser and hit the APIs.
Add username/password, hit submit.
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/kms/signup.JPG" alt="Signup" /></p>
<p>Sign Up is Successful!
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/kms/signup-success.JPG" alt="Signup Success" /></p>
<p>Item in database. Password is encrypted
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/kms/dbtable.JPG" alt="Db Item" />
Now, Login using the same username and password
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/kms/login.JPG" alt="Login" /></p>
<p>Login Sucess!
<img src="https://s3.amazonaws.com/blog-images-rajanpanchal.net/kms/login-success.JPG" alt="Login success" /></p>
<p><strong>Source Code:  </strong>
<a target="_blank" href="https://github.com/rajanpanchal/aws-kms-signup-login"><img src="https://github-readme-stats.vercel.app/api/pin/?username=rajanpanchal&amp;repo=aws-kms-signup-login" alt="aws-kms-signup-login Card" /></a></p>
<p>In next post, we will extend this example to use AWS STS!</p>
]]></content:encoded></item><item><title><![CDATA[My Experiments with AWS Cloud -  Cloud Resume Challenge]]></title><description><![CDATA[Updates: 7/30/2020:  Added github Urls for the project.
Few weeks ago I came across Cloud Resume Challenge on Reddit while searching something. I passed my Amazon Solution Architect certification on Jun 26, So I was looking for something to do next o...]]></description><link>https://blog.rajanpanchal.net/my-experiments-with-aws-cloud-cloud-resume-challenge</link><guid isPermaLink="true">https://blog.rajanpanchal.net/my-experiments-with-aws-cloud-cloud-resume-challenge</guid><category><![CDATA[challenge]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Amazon Web Services]]></category><category><![CDATA[Python 3]]></category><dc:creator><![CDATA[Rajan Panchal]]></dc:creator><pubDate>Sat, 18 Jul 2020 21:08:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1598549485039/Kty8nAtyT.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Updates: 7/30/2020:  Added github Urls for the project.</p>
<p>Few weeks ago I came across <a target="_blank" href="https://cloudresumechallenge.dev/instructions">Cloud Resume Challenge</a> on Reddit while searching something. I passed my Amazon Solution Architect certification on Jun 26, So I was looking for something to do next on AWS. Initially I was hesitant to take this challenge because it looked too easy to do. However, that was not the case, I realized only later when I progressed on the challenge. But what really caught my attention was to do Infrastructure as Code(IaC). Its easy to do stuff on AWS console but not on SAM/IaC if you are a beginner. So now I was determined to take up this challenge.  </p>
<blockquote>
<p>If you are not willing to learn, no one can help you. If you are determined to learn no one can stop you. — Zig Ziglar</p>
</blockquote>
<h2 id="challenge-instructions">Challenge Instructions:</h2>
<p>Below were the high level requirements of the challenge.</p>
<ol>
<li><p>Certification
Get AWS Cloud Practitioner Certification</p>
</li>
<li><p>HTML
Your resume needs to be written in HTML.</p>
</li>
<li><p>CSS
Your resume needs to be styled with CSS.</p>
</li>
<li><p>Static S3 Website
Your HTML resume should be deployed online as an Amazon S3 static website.</p>
</li>
<li><p>HTTPS
The S3 website URL should use HTTPS for security.</p>
</li>
<li><p>DNS
Point a custom DNS domain name to the CloudFront distribution</p>
</li>
<li><p>Javascript
Your resume webpage should include a visitor counter that displays how many people have accessed the site.</p>
</li>
<li><p>Database
The visitor counter will need to retrieve and update its count in a database somewhere.</p>
</li>
<li><p>API
Do not communicate directly with DynamoDB from your Javascript code. Instead, you will need to create an API that accepts requests from your web app and communicates with the database.</p>
</li>
<li><p>Python
Write Lambda function in Python;</p>
</li>
<li><p>Tests
You should also include some tests for your Python code.</p>
</li>
<li><p>Infrastructure as Code
Implement API resources – the DynamoDB table, the API Gateway, the Lambda function as Infrastructure as Code (IaC)</p>
</li>
<li><p>Source Control
Use Git hub as source control</p>
</li>
<li><p>CI/CD (Back end)
Set up GitHub Actions to automate build, test and deployment of Code.</p>
</li>
<li><p>CI/CD (Front end)
Set up git hub actions to automate deployment of frontend code in AWS including cloudfront cache invalidation.</p>
</li>
<li><p>Blog post on the learnings.</p>
</li>
</ol>
<h2 id="implementing-the-challenge">Implementing the Challenge</h2>
<p>The first few steps were easy but things got more messier when I reached Lambda, SAM/IaC portion of the challenge. I got the Resume ready, registered the domain, setup the s3 bucket for static website, setup cloudfront for s3 bucket. At this point I was able to access site using the domain name. I never worked on python before. I spent sometime learning python, Boto3 for interacting with AWS and moto for writing unit test in Python. You can decorate the test to indicate to use mock dynamodb instead of hitting actual services. Python is very particular about indentation. If you are a java developer,you are going to hate Python! </p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/7v618g7ahhzqfr61mic4.jpg" alt="Alt Text" /></p>
<p>Most time consuming part of the challenge was Serverless Application Model (SAM). There are not enough good articles available on SAM. Only source is AWS Documentation. With some trial and Error I started getting the hang of it. I coded DynamoDB table and Lamdba function in SAM. DynamoDB table would keep track of visitors for each page. I wanted it to be scalable so I added counter per page. PageName being the Primary key.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/jxpegwgj0l4j0s2xo4jq.JPG" alt="Alt Text" /></p>
<p>I got my Lambda function, database and API as IaC in SAM and was working flawlessly using SAM CLI. I can't describe the happiness you feel when you see below line after SAM Deploy.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/pcfyp3guh3jfn51hl9v9.JPG" alt="Alt Text" /></p>
<p>Although things were working I was not quite satisfied because part of my infrastructure was still configured manually like Route53, Cloudfront, API Gateway. So I decided to make it truly IaC and Implemented All of the above in SAM. I also added custom domain name to the API so that API can be accessed through a URL that doesn't change. For Example:</p>
<pre><code>https:<span class="hljs-comment">//api.rajanpanchal.net/Prod/someMethod</span>
</code></pre><p>As If Python was not enough to torture me on Indentation , I used YAML for SAM templates instead of JSON. Once in a while I would feel like:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/ududsh69u5p56b1aarfz.jpg" alt="Alt Text" /></p>
<p>I used only Notepad/GEdit to write the SAM templates. I would recommend you do the same if you are beginner.</p>
<p>Next step was to automate CI/CD for both backend and frontend.  I experimented with GitHub Actions to understand how it works and its very powerful. I was ignorant on AWS S3 Sync command and due to this I had to resort to bash scripting to get the changed files in commit for frontend and upload to S3. Later I simply changed to AWS S3 sync command. However, my bash script was not wasted. I used it to invalidate cache on the cloudfront for the files committed.
<em>Backend CI/CD</em></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/imtdvcys18cjwddj9uow.JPG" alt="Alt Text" /></p>
<p><em>Frontend CI/CD</em></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/re1axuxi0log6zy22916.JPG" alt="Alt Text" /></p>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>You can check the final outcome here:  https://www.rajanpanchal.net 
Here is the github url: </p>
<p>https://github.com/rajanpanchal
Projects: aws-cloud-resume-frontend
          aws-cloud-resume-backend</p>
<p>I want to thank <a target="_blank" href="https://forrestbrazeal.com/">Forrest Brazeal</a> for bringing up this challenge and the  <a target="_blank" href="https://discord.gg/mr63ws">Cloud Resume Challenge Discord community</a> that has been helpful during this challenge. It was a great learning experience. I have sacrificed several nights and weekends for this challenge but it was really worth it. </p>
<p>Where do I go from here?</p>
<p>I want to build more such projects with various other AWS services and grow my skills. I've got a Cloud Fever and the only prescription is more Cloud!</p>
]]></content:encoded></item></channel></rss>