<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>serverless on Barney Parker</title><link>https://barneyparker.com/tags/serverless/</link><description>Recent content in serverless on Barney Parker</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.</copyright><lastBuildDate>Tue, 16 Jun 2020 20:05:28 +0000</lastBuildDate><atom:link href="https://barneyparker.com/tags/serverless/index.xml" rel="self" type="application/rss+xml"/><item><title>Deploying Lambda Functions with Terraform</title><link>https://barneyparker.com/posts/deploying-lambda-functions-with-terraform/</link><pubDate>Tue, 16 Jun 2020 20:05:28 +0000</pubDate><guid>https://barneyparker.com/posts/deploying-lambda-functions-with-terraform/</guid><description>&lt;p>Serverless architectures are built from a collection of Managed service wired together to create a specific service. This could be anything from a shopping cart to a full ETL solution. The advantage of building applications this way is that each of the component parts are fully managed by AWS. That means you (either the developer or company) don&amp;rsquo;t have to worry about them functioning, you just need to make sure they&amp;rsquo;re wired together correctly.&lt;/p>
&lt;p>Managed services are great, but these service only offer basic functionality such as queues or databases. What they can&amp;rsquo;t do is know how your business actually adds value to the world.&lt;/p>
&lt;p>Lambda functions are the Serverless super-hero because they let you run your own code without ever having to worry about how the server operates (yes, there are servers!) or even how how to install the software that runs your code.&lt;/p>
&lt;p>There are a lot of ways to deploy AWS infrastructure, and even more for Serverless infrastructure, but my personal favourite is Terraform. Its designed to allow you to write your infrastructure as code (IaC) and is surprisingly flexible. It also allows you to interact with non-AWS services which can really help tie everything together.&lt;/p>
&lt;p>Terraform can be a little verbose, but its very easy to understand, and very simple to work with. On the face of it Terraform doesn&amp;rsquo;t obviously lend itself to Serverless, but that&amp;rsquo;s because it designed to give you extremely low-level access to APIs in the form of Resources.&lt;/p>
&lt;p>In this article I will show you the minimal set of code required to deploy a lambda function. Its not going to be production-ready, or even do anything very impressive, but its going to introduce at an entry level the minimal components required.&lt;/p>
&lt;h2 id="about-terraform">About Terraform&lt;/h2>
&lt;p>Terraform works by creating a direct-graph of resources. The resource dependency order can be determined by using outputs from one resource as the inputs to another. Terraform can then deploy resources which have no outstanding dependencies recursively until all resources have been deployed with their dependencies satisfied.&lt;/p>
&lt;p>Terraform then creates a state file which it uses on further deployments to identify which specific resources in AWS it should be adding, deleting or modifying. State files can be stored in local files or remotely, but for now we will just keep them locally.&lt;/p>
&lt;h2 id="terraform-meta-resources">Terraform Meta-Resources&lt;/h2>
&lt;p>Terraform is incredibly configurable, and to do that it happily eats it own dog-food by configuring with Terraform resources! This is the content of a file I like to call &lt;code>meta.tf&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="k">terraform&lt;/span> {
&lt;span class="n"> required_version&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;gt; 0.12&amp;#34;&lt;/span>
}
&lt;span class="k">provider&lt;/span> &lt;span class="s2">&amp;#34;aws&amp;#34;&lt;/span> {
&lt;span class="n"> region&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;eu-west-1&amp;#34;&lt;/span>
&lt;span class="n"> version&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="n"> &amp;#34;&amp;gt;&lt;/span>&lt;span class="o">=&lt;/span> &lt;span class="m">2&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="m">61&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="err">&amp;#34;&lt;/span>
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>terraform&lt;/code> block tells Terraform to use a version no less that 0.12.0. While older versions are available, this version added some functionality that makes it a little easier to code and so is a good base.&lt;/p>
&lt;p>The &lt;code>provider&lt;/code> block tells Terraform that we want to use AWS resources, we want to place them in the &lt;code>eu-west-1&lt;/code> region and we want to use a provider version no less than 2.61.0&lt;/p>
&lt;h2 id="creating-the-lambda-function-bundle">Creating the Lambda Function Bundle&lt;/h2>
&lt;p>A Lambda Function needs some code to run. The code can be in a &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html">variety of languages&lt;/a> but in this case we&amp;rsquo;re going to use Javascript. Here is some simple code that we&amp;rsquo;re going to deploy:&lt;/p>
&lt;p>To supply the code to the Lambda Function it needs to be in a Zip file. Terraform can create these zip files for us, and for simplicity we&amp;rsquo;re going to define the Function code inline:&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="k">data&lt;/span> &lt;span class="s2">&amp;#34;archive_file&amp;#34; &amp;#34;lambda&amp;#34;&lt;/span> {
&lt;span class="n"> type&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;zip&amp;#34;&lt;/span>
&lt;span class="n"> output_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;${path.module}/.terraform/lambda.zip&amp;#34;&lt;/span>
&lt;span class="k">source&lt;/span> {
&lt;span class="n"> filename&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;index.js&amp;#34;&lt;/span>
&lt;span class="n"> content&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="err">&amp;lt;&amp;lt;-&lt;/span>&lt;span class="k">EOF&lt;/span>
&lt;span class="n"> module.exports.handler&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="n"> async (event, context)&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="err">&amp;gt;&lt;/span> {
&lt;span class="k">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;EVENT: \n&amp;#34;&lt;/span> &lt;span class="err">+&lt;/span> &lt;span class="k">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">stringify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">event&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">2&lt;/span>&lt;span class="p">))&lt;/span>
&lt;span class="k">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;CONTEXT: \n&amp;#34;&lt;/span> &lt;span class="err">+&lt;/span> &lt;span class="k">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">stringify&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">null&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">2&lt;/span>&lt;span class="p">))&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="k">Promise&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">resolve&lt;/span>&lt;span class="p">()&lt;/span>
}
&lt;span class="k">EOF&lt;/span>
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Lines 7-10 will be written to a file called &lt;code>index.js&lt;/code> which will be added to a zip file called lambda.zip. ${path.module} will be interpolated as the local path during deployment. The code exports a function called &lt;code>handler&lt;/code>. Keep these in mind as they will be useful when we actually deploy our function.&lt;/p>
&lt;p>The code is extremely simple: it&amp;rsquo;s going to print out the Event and Context objects which will be passed to it upon execution. This is how the function will get information related to its execution.&lt;/p>
&lt;h2 id="the-log-file">The Log File&lt;/h2>
&lt;p>Since the Lambda Function is running inside of AWS, we need some way to capture its output. The Lambda service automatically captures anything which would normally be sent to stdin and stderr and store that in a CloudWatch Log Stream. Although the Lambda service will create the Log Group, its best practice to create this yourself and set an expiry period so that the logs are automatically cleared out after a period of time:&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_cloudwatch_log_group&amp;#34; &amp;#34;lambda_log_group&amp;#34;&lt;/span> {
&lt;span class="n"> name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;/aws/lambda/${aws_lambda_function.lambda.function_name}&amp;#34;&lt;/span>
&lt;span class="n"> retention_in_days&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">30&lt;/span>
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>All Lambda functions log to a log group called &lt;code>/aws/lambda/&amp;lt;lambda name&amp;gt;&lt;/code> and so we&amp;rsquo;re creating that and capturing the name from the Lambda Function resource (created very soon!)&lt;/p>
&lt;p>We also set the logs to expire after 30 days. Logs are pretty cheap, but if the function is called a lot or it generates a lot of log output the cost can quickly mount up so after a short period we want AWS to delete them on our behalf.&lt;/p>
&lt;h2 id="permissions">Permissions&lt;/h2>
&lt;p>In order to be able to interact with any other service, AWS resources need an IAM Role which can be &amp;ldquo;assumed&amp;rdquo; by the calling service. In our case that&amp;rsquo;s the Lambda service:&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_iam_role&amp;#34; &amp;#34;lambda&amp;#34;&lt;/span> {
&lt;span class="n"> name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;simplest_lambda_role&amp;#34;&lt;/span>
&lt;span class="n"> assume_role_policy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="err">&amp;lt;&amp;lt;-&lt;/span>&lt;span class="k">EOF&lt;/span>
{
&lt;span class="s2">&amp;#34;Version&amp;#34;: &amp;#34;2012-10-17&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s2">&amp;#34;Statement&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">[&lt;/span>
{
&lt;span class="s2">&amp;#34;Effect&amp;#34;: &amp;#34;Allow&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s2">&amp;#34;Action&amp;#34;: &amp;#34;sts:AssumeRole&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s2">&amp;#34;Principal&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> {
&lt;span class="s2">&amp;#34;Service&amp;#34;: &amp;#34;lambda.amazonaws.com&amp;#34;&lt;/span>
}
}
&lt;span class="p">]&lt;/span>
}
&lt;span class="k">EOF&lt;/span>
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The role needs to have policies associated with it in order to define what its allowed to do. In our case the only thing we need permission to do is to create Log Streams within the Log Group, and write to them. Policies can be stand-alone, re-usable policies, or in-line policies specific to this function. For simplicity in this case we&amp;rsquo;re going to create an in-line policy:&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_iam_role_policy&amp;#34; &amp;#34;logging&amp;#34;&lt;/span> {
&lt;span class="n"> name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Cloudwatch-Logs&amp;#34;&lt;/span>
&lt;span class="n"> role&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">aws_iam_role&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">lambda&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">name&lt;/span>
&lt;span class="n"> policy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="err">&amp;lt;&amp;lt;-&lt;/span>&lt;span class="k">EOF&lt;/span>
{
&lt;span class="s2">&amp;#34;Statement&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">[&lt;/span>
{
&lt;span class="s2">&amp;#34;Action&amp;#34;&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;span class="s2">&amp;#34;logs:CreateLogGroup&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s2">&amp;#34;logs:CreateLogStream&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s2">&amp;#34;logs:PutLogEvents&amp;#34;&lt;/span>
&lt;span class="p">],&lt;/span>
&lt;span class="s2">&amp;#34;Effect&amp;#34;: &amp;#34;Allow&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="s2">&amp;#34;Resource&amp;#34;: &amp;#34;arn:aws:logs:*:*:*&amp;#34;&lt;/span>
}
&lt;span class="p">]&lt;/span>
}
&lt;span class="k">EOF&lt;/span>
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="the-lambda-function">The Lambda Function&lt;/h2>
&lt;p>At last we have all the pieces in place to be able to create our Lambda Function resource. Here we will give it a name, point it to our code bundle, tell it what runtime to use and what exported function to execute and what Role it should be assuming to give itself the correct permissions:&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_lambda_function&amp;#34; &amp;#34;lambda&amp;#34;&lt;/span> {
&lt;span class="n"> function_name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;simplest_lambda_function&amp;#34;&lt;/span>
&lt;span class="n"> filename&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">archive_file&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">lambda&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">output_path&lt;/span>
&lt;span class="n"> source_code_hash&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">archive_file&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">lambda&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">output_base64sha256&lt;/span>
&lt;span class="n"> runtime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;nodejs12.x&amp;#34;&lt;/span>
&lt;span class="n"> handler&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;index.handler&amp;#34;&lt;/span>
&lt;span class="n"> role&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">aws_iam_role&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">lambda&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">arn&lt;/span>
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Terraform hides some nasty internal values such as ARNs and file hashes by taking the outputs from some of the resources we created earlier as inputs to this resource. In doing so we have also implied the dependencies of resources and helped Terraform work out what order things should be deployed.&lt;/p>
&lt;h2 id="deploying-the-function">Deploying the Function&lt;/h2>
&lt;p>To start the deployment process, Terraform needs to be initialised with the command:&lt;/p>
&lt;p>&lt;code>terraform init&lt;/code>&lt;/p>
&lt;p>This will ensure we&amp;rsquo;re using a valid version of Terraform and download the providers specified in &lt;code>meta.tf&lt;/code> (or a default if we didn&amp;rsquo;t specify a version). All of its internal &amp;ldquo;stuff&amp;rdquo; will be stored in a directory called &lt;code>.terraform&lt;/code>&lt;/p>
&lt;p>To see what Terraform is going to do execute the following command:&lt;/p>
&lt;p>&lt;code>terraform plan&lt;/code>&lt;/p>
&lt;p>You should get an output something like:&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="k">Refreshing&lt;/span> &lt;span class="k">Terraform&lt;/span> &lt;span class="k">state&lt;/span> &lt;span class="k">in&lt;/span>&lt;span class="err">-&lt;/span>&lt;span class="k">memory&lt;/span> &lt;span class="k">prior&lt;/span> &lt;span class="k">to&lt;/span> &lt;span class="k">plan&lt;/span>&lt;span class="p">...&lt;/span>
&lt;span class="k">The&lt;/span> &lt;span class="k">refreshed&lt;/span> &lt;span class="k">state&lt;/span> &lt;span class="k">will&lt;/span> &lt;span class="k">be&lt;/span> &lt;span class="k">used&lt;/span> &lt;span class="k">to&lt;/span> &lt;span class="k">calculate&lt;/span> &lt;span class="k">this&lt;/span> &lt;span class="k">plan&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">but&lt;/span> &lt;span class="k">will&lt;/span> &lt;span class="k">not&lt;/span> &lt;span class="k">be&lt;/span>
&lt;span class="k">persisted&lt;/span> &lt;span class="k">to&lt;/span> &lt;span class="k">local&lt;/span> &lt;span class="k">or&lt;/span> &lt;span class="k">remote&lt;/span> &lt;span class="k">state&lt;/span> &lt;span class="k">storage&lt;/span>&lt;span class="p">.&lt;/span>
&lt;span class="k">data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">archive_file&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">lambda&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="k">Refreshing&lt;/span> &lt;span class="k">state&lt;/span>&lt;span class="p">...&lt;/span>
&lt;span class="err">------------------------------------------------------------------------&lt;/span>
&lt;span class="k">An&lt;/span> &lt;span class="k">execution&lt;/span> &lt;span class="k">plan&lt;/span> &lt;span class="k">has&lt;/span> &lt;span class="k">been&lt;/span> &lt;span class="k">generated&lt;/span> &lt;span class="k">and&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="k">shown&lt;/span> &lt;span class="k">below&lt;/span>&lt;span class="p">.&lt;/span>
&lt;span class="k">Resource&lt;/span> &lt;span class="k">actions&lt;/span> &lt;span class="k">are&lt;/span> &lt;span class="k">indicated&lt;/span> &lt;span class="k">with&lt;/span> &lt;span class="k">the&lt;/span> &lt;span class="k">following&lt;/span> &lt;span class="k">symbols&lt;/span>&lt;span class="err">:&lt;/span>
&lt;span class="err">+&lt;/span> &lt;span class="k">create&lt;/span>
&lt;span class="k">Terraform&lt;/span> &lt;span class="k">will&lt;/span> &lt;span class="k">perform&lt;/span> &lt;span class="k">the&lt;/span> &lt;span class="k">following&lt;/span> &lt;span class="k">actions&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="c1">
&lt;/span>&lt;span class="c1">
&lt;/span>&lt;span class="c1"> # aws_cloudwatch_log_group.lambda_log_group will be created
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="err">+&lt;/span> &lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_cloudwatch_log_group&amp;#34; &amp;#34;lambda_log_group&amp;#34;&lt;/span> {
&lt;span class="n"> + arn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">known&lt;/span> &lt;span class="k">after&lt;/span> &lt;span class="k">apply&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n"> + id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">known&lt;/span> &lt;span class="k">after&lt;/span> &lt;span class="k">apply&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n"> + name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;/aws/lambda/simplest_lambda_function&amp;#34;&lt;/span>
&lt;span class="n"> + retention_in_days&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">30&lt;/span>
}&lt;span class="c1">
&lt;/span>&lt;span class="c1">
&lt;/span>&lt;span class="c1"> # aws_iam_role.lambda will be created
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="err">+&lt;/span> &lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_iam_role&amp;#34; &amp;#34;lambda&amp;#34;&lt;/span> {
&lt;span class="n"> + arn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">known&lt;/span> &lt;span class="k">after&lt;/span> &lt;span class="k">apply&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n"> + assume_role_policy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">jsonencode&lt;/span>&lt;span class="p">(&lt;/span>
{
&lt;span class="n"> + Statement&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;span class="err">+&lt;/span> {
&lt;span class="n"> + Action&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;sts:AssumeRole&amp;#34;&lt;/span>
&lt;span class="n"> + Effect&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Allow&amp;#34;&lt;/span>
&lt;span class="n"> + Principal&lt;/span> &lt;span class="o">=&lt;/span> {
&lt;span class="n"> + Service&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;lambda.amazonaws.com&amp;#34;&lt;/span>
}
}&lt;span class="p">,&lt;/span>
&lt;span class="p">]&lt;/span>
&lt;span class="n"> + Version&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;2012-10-17&amp;#34;&lt;/span>
}
&lt;span class="p">)&lt;/span>
&lt;span class="n"> + create_date&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">known&lt;/span> &lt;span class="k">after&lt;/span> &lt;span class="k">apply&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n"> + force_detach_policies&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kt">false&lt;/span>
&lt;span class="n"> + id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">known&lt;/span> &lt;span class="k">after&lt;/span> &lt;span class="k">apply&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n"> + max_session_duration&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">3600&lt;/span>
&lt;span class="n"> + name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;simplest_lambda_role&amp;#34;&lt;/span>
&lt;span class="n"> + path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;/&amp;#34;&lt;/span>
&lt;span class="n"> + unique_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">known&lt;/span> &lt;span class="k">after&lt;/span> &lt;span class="k">apply&lt;/span>&lt;span class="p">)&lt;/span>
}&lt;span class="c1">
&lt;/span>&lt;span class="c1">
&lt;/span>&lt;span class="c1"> # aws_iam_role_policy.logging will be created
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="err">+&lt;/span> &lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_iam_role_policy&amp;#34; &amp;#34;logging&amp;#34;&lt;/span> {
&lt;span class="n"> + id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">known&lt;/span> &lt;span class="k">after&lt;/span> &lt;span class="k">apply&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n"> + name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Cloudwatch-Logs&amp;#34;&lt;/span>
&lt;span class="n"> + policy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">jsonencode&lt;/span>&lt;span class="p">(&lt;/span>
{
&lt;span class="n"> + Statement&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;span class="err">+&lt;/span> {
&lt;span class="n"> + Action&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;span class="err">+&lt;/span> &lt;span class="s2">&amp;#34;logs:CreateLogGroup&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="err">+&lt;/span> &lt;span class="s2">&amp;#34;logs:CreateLogStream&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="err">+&lt;/span> &lt;span class="s2">&amp;#34;logs:PutLogEvents&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="p">]&lt;/span>
&lt;span class="n"> + Effect&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Allow&amp;#34;&lt;/span>
&lt;span class="n"> + Resource&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;arn:aws:logs:*:*:*&amp;#34;&lt;/span>
}&lt;span class="p">,&lt;/span>
&lt;span class="p">]&lt;/span>
}
&lt;span class="p">)&lt;/span>
&lt;span class="n"> + role&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;simplest_lambda_role&amp;#34;&lt;/span>
}&lt;span class="c1">
&lt;/span>&lt;span class="c1">
&lt;/span>&lt;span class="c1"> # aws_lambda_function.lambda will be created
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="err">+&lt;/span> &lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_lambda_function&amp;#34; &amp;#34;lambda&amp;#34;&lt;/span> {
&lt;span class="n"> + arn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">known&lt;/span> &lt;span class="k">after&lt;/span> &lt;span class="k">apply&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n"> + filename&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;./.terraform/lambda.zip&amp;#34;&lt;/span>
&lt;span class="n"> + function_name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;simplest_lambda_function&amp;#34;&lt;/span>
&lt;span class="n"> + handler&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;index.handler&amp;#34;&lt;/span>
&lt;span class="n"> + id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">known&lt;/span> &lt;span class="k">after&lt;/span> &lt;span class="k">apply&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n"> + invoke_arn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">known&lt;/span> &lt;span class="k">after&lt;/span> &lt;span class="k">apply&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n"> + last_modified&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">known&lt;/span> &lt;span class="k">after&lt;/span> &lt;span class="k">apply&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n"> + memory_size&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">128&lt;/span>
&lt;span class="n"> + publish&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kt">false&lt;/span>
&lt;span class="n"> + qualified_arn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">known&lt;/span> &lt;span class="k">after&lt;/span> &lt;span class="k">apply&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n"> + reserved_concurrent_executions&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="err">-&lt;/span>&lt;span class="m">1&lt;/span>
&lt;span class="n"> + role&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">known&lt;/span> &lt;span class="k">after&lt;/span> &lt;span class="k">apply&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n"> + runtime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;nodejs12.x&amp;#34;&lt;/span>
&lt;span class="n"> + source_code_hash&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="n"> &amp;#34;TFoUgCw/pW7Sy3/gY/TB3AtGzXoqRjN1L6YT066iPGg&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="err">&amp;#34;&lt;/span>
&lt;span class="n"> + source_code_size&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">known&lt;/span> &lt;span class="k">after&lt;/span> &lt;span class="k">apply&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="n"> + timeout&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">3&lt;/span>
&lt;span class="n"> + version&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">known&lt;/span> &lt;span class="k">after&lt;/span> &lt;span class="k">apply&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="err">+&lt;/span> &lt;span class="k">tracing_config&lt;/span> {
&lt;span class="n"> + mode&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">known&lt;/span> &lt;span class="k">after&lt;/span> &lt;span class="k">apply&lt;/span>&lt;span class="p">)&lt;/span>
}
}
&lt;span class="k">Plan&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="m">4&lt;/span> &lt;span class="k">to&lt;/span> &lt;span class="k">add&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="k">to&lt;/span> &lt;span class="k">change&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="k">to&lt;/span> &lt;span class="k">destroy&lt;/span>&lt;span class="p">.&lt;/span>
&lt;span class="err">------------------------------------------------------------------------&lt;/span>
&lt;span class="k">Note&lt;/span>&lt;span class="err">:&lt;/span> &lt;span class="k">You&lt;/span> &lt;span class="k">didn&lt;/span>&lt;span class="err">&amp;#39;&lt;/span>&lt;span class="k">t&lt;/span> &lt;span class="k">specify&lt;/span> &lt;span class="k">an&lt;/span> &lt;span class="s2">&amp;#34;-out&amp;#34;&lt;/span> &lt;span class="k">parameter&lt;/span> &lt;span class="k">to&lt;/span> &lt;span class="k">save&lt;/span> &lt;span class="k">this&lt;/span> &lt;span class="k">plan&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">so&lt;/span> &lt;span class="k">Terraform&lt;/span>
&lt;span class="k">can&lt;/span>&lt;span class="err">&amp;#39;&lt;/span>&lt;span class="k">t&lt;/span> &lt;span class="k">guarantee&lt;/span> &lt;span class="k">that&lt;/span> &lt;span class="k">exactly&lt;/span> &lt;span class="k">these&lt;/span> &lt;span class="k">actions&lt;/span> &lt;span class="k">will&lt;/span> &lt;span class="k">be&lt;/span> &lt;span class="k">performed&lt;/span> &lt;span class="k">if&lt;/span>
&lt;span class="s2">&amp;#34;terraform apply&amp;#34;&lt;/span> &lt;span class="k">is&lt;/span> &lt;span class="k">subsequently&lt;/span> &lt;span class="k">run&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This shows that Terraform will create 4 new resources:&lt;/p>
&lt;ul>
&lt;li>The Log Group&lt;/li>
&lt;li>An IAM Role&lt;/li>
&lt;li>An Inline IAM Policy attached to the Role&lt;/li>
&lt;li>Our Lambda Function&lt;/li>
&lt;/ul>
&lt;p>To actually create resources in AWS, execute:&lt;/p>
&lt;p>&lt;code>terraform apply&lt;/code>&lt;/p>
&lt;p>When prompted, enter &lt;code>yes&lt;/code>&lt;/p>
&lt;h2 id="testing-our-function">Testing our Function&lt;/h2>
&lt;p>To invoke our function from the command line, execute:&lt;/p>
&lt;pre>&lt;code>`aws lambda invoke --function-name simplest_lambda_function /dev/null --log-type Tail --query 'LogResult' --output text | base64 -d`
&lt;/code>&lt;/pre>&lt;p>which should respond with something similar to:&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="err">START&lt;/span> &lt;span class="err">RequestId:&lt;/span> &lt;span class="err">ab&lt;/span>&lt;span class="mi">934699-7094-4829-8&lt;/span>&lt;span class="err">cee-aecd&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="err">c&lt;/span>&lt;span class="mi">193583&lt;/span> &lt;span class="err">Version:&lt;/span> &lt;span class="err">$LATEST&lt;/span>
&lt;span class="mi">2020-06-16&lt;/span>&lt;span class="err">T&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="mi">58&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="mf">46.098&lt;/span>&lt;span class="err">Z&lt;/span> &lt;span class="err">ab&lt;/span>&lt;span class="mi">934699-7094-4829-8&lt;/span>&lt;span class="err">cee-aecd&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="err">c&lt;/span>&lt;span class="mi">193583&lt;/span> &lt;span class="err">INFO&lt;/span> &lt;span class="err">EVENT:&lt;/span>
&lt;span class="p">{}&lt;/span>
&lt;span class="mi">2020-06-16&lt;/span>&lt;span class="err">T&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="mi">58&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="mf">46.098&lt;/span>&lt;span class="err">Z&lt;/span> &lt;span class="err">ab&lt;/span>&lt;span class="mi">934699-7094-4829-8&lt;/span>&lt;span class="err">cee-aecd&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="err">c&lt;/span>&lt;span class="mi">193583&lt;/span> &lt;span class="err">INFO&lt;/span> &lt;span class="err">CONTEXT:&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="nt">&amp;#34;callbackWaitsForEmptyEventLoop&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;functionVersion&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;$LATEST&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;functionName&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;simplest_lambda_function&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;memoryLimitInMB&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;128&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;logGroupName&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;/aws/lambda/simplest_lambda_function&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;logStreamName&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2020/06/16/[$LATEST]759235165de84d3bbe7d3a87a7bd2db4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;invokedFunctionArn&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;arn:aws:lambda:eu-west-1:522711524578:function:simplest_lambda_function&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;awsRequestId&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;ab934699-7094-4829-8cee-aecd2c193583&amp;#34;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="err">END&lt;/span> &lt;span class="err">RequestId:&lt;/span> &lt;span class="err">ab&lt;/span>&lt;span class="mi">934699-7094-4829-8&lt;/span>&lt;span class="err">cee-aecd&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="err">c&lt;/span>&lt;span class="mi">193583&lt;/span>
&lt;span class="err">REPORT&lt;/span> &lt;span class="err">RequestId:&lt;/span> &lt;span class="err">ab&lt;/span>&lt;span class="mi">934699-7094-4829-8&lt;/span>&lt;span class="err">cee-aecd&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="err">c&lt;/span>&lt;span class="mi">193583&lt;/span> &lt;span class="err">Duration:&lt;/span> &lt;span class="mf">2.86&lt;/span> &lt;span class="err">ms&lt;/span> &lt;span class="err">Billed&lt;/span> &lt;span class="err">Duration:&lt;/span> &lt;span class="mi">100&lt;/span> &lt;span class="err">ms&lt;/span> &lt;span class="err">Memory&lt;/span> &lt;span class="err">Size:&lt;/span> &lt;span class="mi">128&lt;/span> &lt;span class="err">MB&lt;/span> &lt;span class="err">Max&lt;/span> &lt;span class="err">Memory&lt;/span> &lt;span class="err">Used:&lt;/span> &lt;span class="mi">63&lt;/span> &lt;span class="err">MB&lt;/span> &lt;span class="err">Init&lt;/span> &lt;span class="err">Duration:&lt;/span> &lt;span class="mf">129.60&lt;/span> &lt;span class="err">ms&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>While the command was a little long-winded, this is the log data you will also find in the CloudWatch Log Group. We can see the invocation id ab934699-7094-4829-8cee-aecd2c193583 and the content of the event and context objects. Finally we can see the &lt;code>REPORT&lt;/code> line with tells us&lt;/p>
&lt;ul>
&lt;li>The function took 2.86ms to run&lt;/li>
&lt;li>It will be billed at 100ms (always rounded up to the nearest 100ms)&lt;/li>
&lt;li>It was allocated 128MB of memory&lt;/li>
&lt;li>At its peak, it actually used a total of 63MB of memory&lt;/li>
&lt;li>It &amp;ldquo;Cold-Start&amp;rdquo; initialisation time was 129.6ms&lt;/li>
&lt;/ul>
&lt;p>You can use these values to tune the function which I will address in a future post.&lt;/p>
&lt;h2 id="cleaning-up">Cleaning Up&lt;/h2>
&lt;p>Since this was just a simple demo, you probably want to remove all these resources at some point. To do so execute:&lt;/p>
&lt;p>&lt;code>terraform destroy&lt;/code>&lt;/p>
&lt;h2 id="final-remarks">Final Remarks&lt;/h2>
&lt;p>This function doesn&amp;rsquo;t do a great deal, and right now we can only invoke it from either the command line or from the AWS Lambda console, but it covers the basics of using Terraform to get it, and all its supporting resources deployed.&lt;/p>
&lt;p>For more information see the official &lt;a href="https://www.terraform.io/">Terraform&lt;/a> site.&lt;/p></description></item></channel></rss>