Skip to content

feat: Terraform example #1458

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,6 @@ example/HelloWorldFunction/build
/example/.gradle/
/example/.java-version
.gradle
build/
build/
.terraform*
terraform.tfstate*
4 changes: 1 addition & 3 deletions docs/stylesheets/extra.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
}

.highlight .hll {
[data-md-color-scheme="default"] {
background-color: lavender;
}
background-color: lavender
}

.md-typeset table:not([class]) {
Expand Down
1 change: 1 addition & 0 deletions examples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<module>powertools-examples-core/cdk/app</module>
<module>powertools-examples-core/cdk/infra</module>
<module>powertools-examples-core/serverless</module>
<module>powertools-examples-core/terraform</module>
<module>powertools-examples-idempotency</module>
<module>powertools-examples-parameters</module>
<module>powertools-examples-serialization</module>
Expand Down
1 change: 1 addition & 0 deletions examples/powertools-examples-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ We provide examples for the following infrastructure-as-code tools:
* [AWS SAM](sam/)
* [AWS CDK](cdk/)
* [Serverless framework](serverless/)
* [Terraform](terraform/)

We also provide an example showing the integration of SAM, Powertools, and Gradle:

Expand Down
27 changes: 27 additions & 0 deletions examples/powertools-examples-core/terraform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Powertools for AWS Lambda (Java) - Core Utilities Example with Terraform

This project demonstrates the Lambda for Powertools Java module deployed using [Terraform](https://www.terraform.io/).
For general information on the deployed example itself, you can refer to the parent [README](../README.md).
To install Terraform if you don't have it yet, you can follow the [Install Terraform Guide](https://developer.hashicorp.com/terraform/downloads?product_intent=terraform).

## Configuration
Terraform uses [main.tf](./main.tf) to define the application's AWS resources.
This file defines the Lambda function to be deployed as well as API Gateway for it.

It is a [Maven](https://maven.apache.org/) based project, so you can open this project with any Maven compatible Java IDE to build and run tests.


## Deploy the sample application

To deploy the app, simply run the following commands:
```bash
terraform init
mvn package && terraform apply
```

## Useful commands

To destroy the app
```bash
terraform destroy
```
94 changes: 94 additions & 0 deletions examples/powertools-examples-core/terraform/infra/api-gateway.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
resource "aws_api_gateway_rest_api" "hello_world_api" {
name = "hello_world_api"
description = "API Gateway endpoint URL for Prod stage for Hello World function"
}

resource "aws_api_gateway_resource" "hello_resource" {
rest_api_id = "${aws_api_gateway_rest_api.hello_world_api.id}"
parent_id = "${aws_api_gateway_rest_api.hello_world_api.root_resource_id}"
path_part = "hello"
}

resource "aws_api_gateway_resource" "hello_stream_resource" {
rest_api_id = "${aws_api_gateway_rest_api.hello_world_api.id}"
parent_id = "${aws_api_gateway_rest_api.hello_world_api.root_resource_id}"
path_part = "hellostream"
}

resource "aws_api_gateway_method" "hello_get_method" {
rest_api_id = "${aws_api_gateway_rest_api.hello_world_api.id}"
resource_id = "${aws_api_gateway_resource.hello_resource.id}"
http_method = "GET"
authorization = "NONE"
}

resource "aws_api_gateway_method" "hello_stream_get_method" {
rest_api_id = "${aws_api_gateway_rest_api.hello_world_api.id}"
resource_id = "${aws_api_gateway_resource.hello_stream_resource.id}"
http_method = "GET"
authorization = "NONE"
}

resource "aws_api_gateway_integration" "java_lambda_integration" {
rest_api_id = "${aws_api_gateway_rest_api.hello_world_api.id}"
resource_id = "${aws_api_gateway_resource.hello_resource.id}"
http_method = "${aws_api_gateway_method.hello_get_method.http_method}"

integration_http_method = "POST"
type = "AWS_PROXY"
uri = "${aws_lambda_function.hello_world_lambda.invoke_arn}"
}

resource "aws_api_gateway_integration" "java_stream_lambda_integration" {
rest_api_id = "${aws_api_gateway_rest_api.hello_world_api.id}"
resource_id = "${aws_api_gateway_resource.hello_stream_resource.id}"
http_method = "${aws_api_gateway_method.hello_stream_get_method.http_method}"

integration_http_method = "POST"
type = "AWS_PROXY"
uri = "${aws_lambda_function.hello_world_stream_lambda.invoke_arn}"
}

resource "aws_api_gateway_deployment" "prod_deployment" {
depends_on = [aws_api_gateway_integration.java_lambda_integration, aws_api_gateway_integration.java_stream_lambda_integration]
rest_api_id = "${aws_api_gateway_rest_api.hello_world_api.id}"
stage_name = "prod"
}

# Allows API gateway to invoke lambda
resource "aws_lambda_permission" "hello_world_lambda_invoke" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.hello_world_lambda.function_name}"
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.hello_world_api.execution_arn}/${aws_api_gateway_deployment.prod_deployment.stage_name}/GET/hello"
}

# Allows API gateway to invoke lambda
resource "aws_lambda_permission" "hello_world_lambda_testinvoke" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this test-invoke-stage ?

Copy link
Contributor Author

@skal111 skal111 Oct 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Required stage to test the lambda invocation from the API Gateway console. Can be removed, but CDK example deploys similar permissions, including that test-invoke-stage.
https://awscli.amazonaws.com/v2/documentation/api/latest/reference/apigateway/test-invoke-method.html

statement_id = "AllowAPIGatewayTestInvoke"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.hello_world_lambda.function_name}"
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.hello_world_api.execution_arn}/test-invoke-stage/GET/hello"
}

# Allows API gateway to invoke lambda
resource "aws_lambda_permission" "hello_world_stream_lambda_invoke" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.hello_world_stream_lambda.function_name}"
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.hello_world_api.execution_arn}/${aws_api_gateway_deployment.prod_deployment.stage_name}/GET/hellostream"
}

# Allows API gateway to invoke lambda
resource "aws_lambda_permission" "hello_world_stream_lambda_testinvoke" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here?

statement_id = "AllowAPIGatewayTestInvoke"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.hello_world_stream_lambda.function_name}"
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.hello_world_api.execution_arn}/test-invoke-stage/GET/hellostream"
}

output "invoke" {value=aws_api_gateway_deployment.prod_deployment.invoke_url}
95 changes: 95 additions & 0 deletions examples/powertools-examples-core/terraform/infra/lambda.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
resource "aws_lambda_function" "hello_world_lambda" {
runtime = "java11"
filename = "target/helloworld-lambda.jar"
source_code_hash = filebase64sha256("target/helloworld-lambda.jar")
function_name = "hello_world_lambda"

handler = "helloworld.App"
description = "Powertools example, deployed by Terraform"
timeout = 20
memory_size = 512
role = "${aws_iam_role.iam_role_for_lambda.arn}"
tracing_config {
mode = "Active"
}
depends_on = [aws_cloudwatch_log_group.log_group]
}

resource "aws_lambda_function" "hello_world_stream_lambda" {
runtime = "java11"
filename = "target/helloworld-lambda.jar"
source_code_hash = filebase64sha256("target/helloworld-lambda.jar")
function_name = "hello_world_stream_lambda"

handler = "helloworld.AppStream"
description = "Powertools example, deployed by Terraform"
timeout = 20
memory_size = 512
role = "${aws_iam_role.iam_role_for_lambda.arn}"
tracing_config {
mode = "Active"
}
depends_on = [aws_cloudwatch_log_group.log_group]
}

# Create a log group for the lambda
resource "aws_cloudwatch_log_group" "log_group" {
name = "/aws/lambda/hello_world_lambda"
}

# Create a log group for the lambda
resource "aws_cloudwatch_log_group" "log_group_stream" {
name = "/aws/lambda/hello_world_stream_lambda"
}

# lambda role
resource "aws_iam_role" "iam_role_for_lambda" {
name = "lambda-invoke-role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}

# lambda policy, allow logs to be published to CloudWatch, and traces to Xray
resource "aws_iam_policy" "iam_policy_for_lambda" {
name = "lambda-invoke-policy"
path = "/"

policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "LambdaPolicy",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"xray:PutTelemetryRecords",
"xray:PutTraceSegments"
],
"Resource": "*"
}
]
}
EOF
}

# Attach the policy to the role
resource "aws_iam_role_policy_attachment" "aws_iam_role_policy_attachment" {
role = "${aws_iam_role.iam_role_for_lambda.name}"
policy_arn = "${aws_iam_policy.iam_policy_for_lambda.arn}"
}
5 changes: 5 additions & 0 deletions examples/powertools-examples-core/terraform/infra/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
provider "aws" {
access_key = "${var.aws_access_key}"
secret_key = "${var.aws_secret_key}"
region = "${var.region}"
}
14 changes: 14 additions & 0 deletions examples/powertools-examples-core/terraform/infra/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
variable "aws_access_key" {
# set aws access key
default = ""
}

variable "aws_secret_key" {
# set aws secret key
default = ""
}

variable "region" {
# set aws region
default = ""
}
9 changes: 9 additions & 0 deletions examples/powertools-examples-core/terraform/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# terraform modules
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we know that we haven't broken this - can we lint it as part of the build?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we do a terraform validate at least ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is also this: https://github.com/terraform-linters/setup-tflint. Should we add it?

Copy link
Contributor

@jeromevdl jeromevdl Oct 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@scottgerring we should something similar for SAM and the others... sam build / sam validate --lint --template template.yml
(for another issue/pr)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok I will add some form of linting/validation

module "powertools_for_java_lambda" {
source = "./infra/"
}

output "api_url" {
value = module.powertools_for_java_lambda.invoke
description = "URL where the API gateway can be invoked"
}
Loading