## Stock Lambda는 리소스를 이용해서 생성함.
# 현재 호출자, ID 값을 가져오기 위한 데이터
data "aws_caller_identity" "current" {}
# 현재 사용중인 Region 정보를 가져오기 위한 데이터
data "aws_region" "current" {}
# Stock Lambda 생성을 위한 리소스
resource "aws_lambda_function" "stock_lambda" {
function_name = "stock"
filename = data.archive_file.lambda_zip_file.output_path
# filename = "${path.module}/stock_lambda.zip"
source_code_hash = data.archive_file.lambda_zip_file.output_base64sha256
handler = "index.handler"
role = aws_iam_role.stock_lambda_role.arn
runtime = "nodejs14.x"
environment {
variables = {
factory_ENDPOINT = var.factory_ENDPOINT,
callback_ENDPOINT = module.api_gateway_increase.apigatewayv2_api_api_endpoint
}
}
}
# 소스파일 zip 압축
data "archive_file" "lambda_zip_file" {
type = "zip"
source_dir = "${path.module}/aws-node-project"
output_path = "${path.module}/stock_lambda.zip"
}
# Role to execute lambda
resource "aws_iam_role" "stock_lambda_role" {
name = "stock_lambda_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
# CloudWatch Log group to store Lambda logs
resource "aws_cloudwatch_log_group" "stock_lambda_loggroup" {
name = "/aws/lambda/${aws_lambda_function.stock_lambda.function_name}"
retention_in_days = 14
}
# Custom policy to read SQS queue and write to CloudWatch Logs with least privileges
resource "aws_iam_policy" "stock_lambda_policy" {
name = "stock_lambda_policy"
path = "/"
description = "Policy for sqs to lambda demo"
policy = <<EOF
{
"Version" : "2012-10-17",
"Statement" : [
{
"Effect": "Allow",
"Action": [
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
"sqs:GetQueueAttributes"
],
"Resource": "${aws_sqs_queue.stock_queue.arn}"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:/aws/lambda/${aws_lambda_function.stock_lambda.function_name}:*:*"
}
]
}
EOF
}
# 위에서 작성된 IAM ROLE을 정책에 연결
resource "aws_iam_role_policy_attachment" "lambda_policy_attachment" {
role = aws_iam_role.stock_lambda_role.name
policy_arn = aws_iam_policy.stock_lambda_policy.arn
}
# 이벤트 소스 매핑
resource "aws_lambda_event_source_mapping" "sqs_lambda_source_mapping" {
event_source_arn = aws_sqs_queue.stock_queue.arn
function_name = aws_lambda_function.stock_lambda.function_name
}
# sales Lambda 생성을 위한 리소스
resource "aws_lambda_function" "sales_api" {
function_name = "sales-api"
filename = data.archive_file.lambda_zip_file2.output_path
source_code_hash = data.archive_file.lambda_zip_file2.output_base64sha256
handler = "handler.handler"
role = aws_iam_role.sales_lambda_role.arn
runtime = "nodejs14.x"
environment {
variables = {
TOPIC_ARN = module.sns_topic.sns_topic_arn,
DB_HOSTNAME = var.DB_HOSTNAME,
DB_USERNAME = var.DB_USERNAME,
DB_PASSWORD = var.DB_PASSWORD,
DB_DATABASE = var.DB_DATABASE,
DB_PORT = var.DB_PORT
}
}
}
# 소스파일 zip 압축
data "archive_file" "lambda_zip_file2" {
type = "zip"
source_dir = "${path.module}/sales-api"
output_path = "${path.module}/sales-api.zip"
}
# API_GATEWAY
# 모듈로 생성
module "api_gateway" {
source = "terraform-aws-modules/apigateway-v2/aws"
name = "${aws_lambda_function.sales_api.function_name}"
description = "My awesome HTTP API Gateway"
protocol_type = "HTTP"
integrations = {
"$default" = {
lambda_arn = aws_lambda_function.sales_api.arn
payload_format_version = "2.0" # Payload
}
}
create_api_domain_name = false # to control creation of API Gateway Domain Name
# create_default_stage = false # to control creation of "$default" stage
# create_default_stage_api_mapping = false # to control creation of "$default" stage and API mapping
tags = {
Name = "http-apigateway"
}
}
# 람다 트리거 연결하는 리소스
# Attach API Gateway to Lambda
resource "aws_lambda_permission" "api_gw" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.sales_api.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${module.api_gateway.apigatewayv2_api_execution_arn}/*"
}
# 람다 ROLE 생성
resource "aws_iam_role" "sales_lambda_role" {
name = "sales_lambda_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
# 람다에 넣을 SNS 정책 생성
# Create Policy Lambda to SNS
data "aws_iam_policy_document" "lambda_policy_document" {
statement {
effect = "Allow"
actions = [
"sns:Publish"
]
resources = [
module.sns_topic.sns_topic_arn
]
}
}
# 위에서 작성한 정책을 기반으로 정책 resource생성
# Policy Convert to JSON
resource "aws_iam_policy" "lambda_SNS_policy" {
name = "lambda_SNS_policy"
path = "/"
policy = data.aws_iam_policy_document.lambda_policy_document.json
}
# 위에서 작성된 IAM ROLE을 정책에 연결
resource "aws_iam_role_policy_attachment" "lambda_policy_attachment1" {
role = aws_iam_role.sales_lambda_role.name
policy_arn = aws_iam_policy.lambda_SNS_policy.arn
}
# 람다에 연결할 정책 리소스 생성
resource "aws_iam_policy" "sales_lambda_policy" {
name = "sales_lambda_policy"
path = "/"
description = "Policy for sqs to lambda demo"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement" : [
{
"Effect": "Allow",
"Action": [
"logs:PutLogEvents",
"logs:CreateLogStream",
"logs:CreateLogGroup"
],
"Resource": [
"arn:aws:logs:ap-northeast-2:725601756882:log-group:/aws/lambda/sales-apis:*:*",
"arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:/aws/lambda/${aws_lambda_function.sales_api.function_name}:*"
]
}
]
}
EOF
}
# 위에서 작성된 IAM ROLE을 정책에 연결
resource "aws_iam_role_policy_attachment" "lambda_policy_attachment2" {
role = aws_iam_role.sales_lambda_role.name
policy_arn = aws_iam_policy.sales_lambda_policy.arn
}
# CloudWatch Log group 생성
# CloudWatch Log group to store Lambda logs
resource "aws_cloudwatch_log_group" "sales_lambda_loggroup" {
name = "/aws/lambda/${aws_lambda_function.sales_api.function_name}"
retention_in_days = 14
}
## SNS 리소스 생성
## SNS 토픽 발행
module "sns_topic" {
source = "terraform-aws-modules/sns/aws"
version = "~> 3.0"
name = "stock_empty2"
}
resource "aws_sqs_queue" "stock_queue" {
name = "stock_queue_2"
delay_seconds = 0
max_message_size = 256000
message_retention_seconds = 345600
receive_wait_time_seconds = 0
redrive_policy = jsonencode({
deadLetterTargetArn = aws_sqs_queue.stock_queue_deadletter.arn
maxReceiveCount = 4
})
}
# SQS DLQ 생성
resource "aws_sqs_queue" "stock_queue_deadletter" {
name = "stock_queue_2_dlq"
delay_seconds = 0
max_message_size = 256000
message_retention_seconds = 345600
receive_wait_time_seconds = 0
}
# SNS 구독 SNS -> SQS
resource "aws_sns_topic_subscription" "sqs_target" {
topic_arn = module.sns_topic.sns_topic_arn
protocol = "sqs"
endpoint = aws_sqs_queue.stock_queue.arn
}
# SNS to SQS 정책 생성
resource "aws_sqs_queue_policy" "sns_sqs_sqspolicy" {
queue_url = aws_sqs_queue.stock_queue.id
policy = <<EOF
{
"Version": "2012-10-17",
"Id": "sns_sqs_policy",
"Statement": [
{
"Sid": "Allow SNS publish to SQS",
"Effect": "Allow",
"Principal": {
"Service": "sns.amazonaws.com"
},
"Action": "sqs:SendMessage",
"Resource": "${aws_sqs_queue.stock_queue.arn}",
"Condition": {
"ArnEquals": {
"aws:SourceArn": "${module.sns_topic.sns_topic_arn}"
}
}
}
]
}
EOF
}
# sales Lambda 생성을 위한 리소스
resource "aws_lambda_function" "increase_lambda" {
function_name = "increase"
filename = data.archive_file.lambda_zip_file3.output_path
source_code_hash = data.archive_file.lambda_zip_file3.output_base64sha256
handler = "handler.handler"
role = aws_iam_role.increase_lambda_role.arn
runtime = "nodejs14.x"
environment {
variables = {
DB_HOSTNAME = var.DB_HOSTNAME,
DB_USERNAME = var.DB_USERNAME,
DB_PASSWORD = var.DB_PASSWORD,
DB_DATABASE = var.DB_DATABASE,
DB_PORT = var.DB_PORT
}
}
}
# 소스파일 zip 압축
data "archive_file" "lambda_zip_file3" {
type = "zip"
source_dir = "${path.module}/stock-increase-lambda"
output_path = "${path.module}/stock-increase-lambda.zip"
}
# API_GATEWAY
# 모듈로 생성
module "api_gateway_increase" {
source = "terraform-aws-modules/apigateway-v2/aws"
name = "${aws_lambda_function.increase_lambda.function_name}"
description = "My awesome HTTP API Gateway"
protocol_type = "HTTP"
integrations = {
"$default" = {
lambda_arn = aws_lambda_function.increase_lambda.arn
payload_format_version = "2.0" # Payload
}
}
create_api_domain_name = false # to control creation of API Gateway Domain Name
# create_default_stage = false # to control creation of "$default" stage
# create_default_stage_api_mapping = false # to control creation of "$default" stage and API mapping
tags = {
Name = "http-apigateway"
}
}
# 람다 트리거 연결하는 리소스
# Attach API Gateway to Lambda
resource "aws_lambda_permission" "api_gw_increase" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.increase_lambda.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${module.api_gateway_increase.apigatewayv2_api_execution_arn}/*"
}
# 람다 ROLE 생성
resource "aws_iam_role" "increase_lambda_role" {
name = "increase_lambda_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
# 람다에 연결할 정책 리소스 생성
resource "aws_iam_policy" "increase_lambda_policy" {
name = "increase_lambda_policy"
path = "/"
description = "Policy for sqs to lambda demo"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement" : [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup",
"logs:TagResource"
],
"Resource": [
"arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:/aws/lambda/${aws_lambda_function.sales_api.function_name}:*"
]
},
{
"Effect": "Allow",
"Action": [
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:/aws/lambda/${aws_lambda_function.sales_api.function_name}:*"
]
}
]
}
EOF
}
# 위에서 작성된 IAM ROLE을 정책에 연결
resource "aws_iam_role_policy_attachment" "lambda_policy_attachment3" {
role = aws_iam_role.increase_lambda_role.name
policy_arn = aws_iam_policy.increase_lambda_policy.arn
}
# CloudWatch Log group 생성
# CloudWatch Log group to store Lambda logs
resource "aws_cloudwatch_log_group" "increase_lambda_loggroup" {
name = "/aws/lambda/${aws_lambda_function.increase_lambda.function_name}"
retention_in_days = 14
}
# Sales Lambda
variable "DB_DATABASE" {
type = string
}
variable "DB_HOSTNAME" {
type = string
}
variable "DB_PASSWORD" {
type = string
}
variable "DB_USERNAME" {
type = string
}
variable "DB_PORT" {
type = string
}
variable "factory_ENDPOINT" {
type = string
}
scret.tfvars로 파일이름을 작성하면 terraform 명령어를 사용할 때 마다
-var-file=scret.tfvars 옵션을 붙여줘야하지만 .auto를 중간에 넣으면 자동으로 모든 명령어에 scret.auto.tfvars파일이 사용된다.
factory_ENDPOINT =
DB_HOSTNAME =
DB_USERNAME =
DB_PASSWORD =
DB_DATABASE =
DB_PORT =
## Sales Lambda는 module을 이용해서 생성함.
# Lambda 선언
module "lambda" {
source = "terraform-aws-modules/lambda/aws"
version = "3.3.1"
# insert the 32 required variables here
function_name = "sales-apis"
description = "My awesome lambda function"
handler = "handler.handler"
runtime = "nodejs14.x"
timeout = 6
environment_variables = {
TOPIC_ARN = module.sns_topic.sns_topic_arn
HOSTNAME = var.DB_HOSTNAME
USERNAME = var.DB_USERNAME
PASSWORD = var.DB_PASSWORD
DATABASE = var.DB_DATABASE
}
source_path = "${path.module}/sales-api"
attach_cloudwatch_logs_policy = true
tags = {
Name = "sales-api"
}
}
# API_GATEWAY
# 모듈로 생성
module "api_gateway" {
source = "terraform-aws-modules/apigateway-v2/aws"
name = "${module.lambda.lambda_function_name}-API"
description = "My awesome HTTP API Gateway"
protocol_type = "HTTP"
integrations = {
"$default" = {
lambda_arn = module.lambda.lambda_function_arn
payload_format_version = "2.0" # Payload
}
}
create_api_domain_name = false # to control creation of API Gateway Domain Name
# create_default_stage = false # to control creation of "$default" stage
# create_default_stage_api_mapping = false # to control creation of "$default" stage and API mapping
tags = {
Name = "http-apigateway"
}
}
# 람다 트리거 연결하는 리소스
# Attach API Gateway to Lambda
resource "aws_lambda_permission" "api_gw" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = module.lambda.lambda_function_name
principal = "apigateway.amazonaws.com"
source_arn = "${module.api_gateway.apigatewayv2_api_execution_arn}/*"
}
# Create Policy Lambda to SNS
data "aws_iam_policy_document" "lambda_policy_document" {
statement {
effect = "Allow"
actions = [
"sns:Publish"
]
resources = [
module.sns_topic.sns_topic_arn
]
}
}
# Policy Convert to JSON
resource "aws_iam_policy" "lambda_SNS_policy" {
name = "lambda_SNS_policy"
path = "/"
policy = data.aws_iam_policy_document.lambda_policy_document.json
}
# Policy Connect
resource "aws_iam_policy_attachment" "attach_lambda_iam_policy" {
name = "lambda-policy-attachment"
roles = [module.lambda.lambda_role_name]
policy_arn = aws_iam_policy.lambda_SNS_policy.arn
}
## SNS 리소스 생성
## SNS 토픽 발행
module "sns_topic" {
source = "terraform-aws-modules/sns/aws"
version = "~> 3.0"
name = "stock_empty2"
}
resource "aws_sqs_queue" "stock_queue" {
name = "stock_queue_2"
delay_seconds = 0
max_message_size = 256000
message_retention_seconds = 345600
receive_wait_time_seconds = 0
redrive_policy = jsonencode({
deadLetterTargetArn = aws_sqs_queue.stock_queue_deadletter.arn
maxReceiveCount = 4
})
}
# SQS DLQ 생성
resource "aws_sqs_queue" "stock_queue_deadletter" {
name = "stock_queue_2_dlq"
delay_seconds = 0
max_message_size = 256000
message_retention_seconds = 345600
receive_wait_time_seconds = 0
}
# SNS 구독 SNS -> SQS
resource "aws_sns_topic_subscription" "sqs_target" {
topic_arn = module.sns_topic.sns_topic_arn
protocol = "sqs"
endpoint = aws_sqs_queue.stock_queue.arn
}
# SNS to SQS 정책 생성
resource "aws_sqs_queue_policy" "sns_sqs_sqspolicy" {
queue_url = aws_sqs_queue.stock_queue.id
policy = <<EOF
{
"Version": "2012-10-17",
"Id": "sns_sqs_policy",
"Statement": [
{
"Sid": "Allow SNS publish to SQS",
"Effect": "Allow",
"Principal": {
"Service": "sns.amazonaws.com"
},
"Action": "sqs:SendMessage",
"Resource": "${aws_sqs_queue.stock_queue.arn}",
"Condition": {
"ArnEquals": {
"aws:SourceArn": "${module.sns_topic.sns_topic_arn}"
}
}
}
]
}
EOF
}
lambda함수 구현부분을 terraform module을 사용해서 구현을 해보았으나
lambda 소스 코드를 zip파일로 만드는 과정에서 에러가 발생하였다.
Error: local-exec provisioner error
│
│ with module.lambda.null_resource.archive[0],
│ on .terraform\modules\lambda\package.tf line 65, in resource "null_resource" "archive":
│ 65: provisioner "local-exec" {
│
│ Error running command 'builds\550594252e08f92313c2125245723a80278c1ac94b8fae934e0562c09b1bcbe4.plan.json': exit status 1. Output: zip: creating
│ 'builds\550594252e08f92313c2125245723a80278c1ac94b8fae934e0562c09b1bcbe4.zip' archive
│ Installing npm requirements: .\sales-api\package.json
│ > mktemp -d terraform-aws-lambda-XXXXXXXX # 'C:\Users\arnol\AppData\Local\Temp\terraform-aws-lambda-kizea77r'
│ > npm install
│ zip: Error during zip archive creation
│ Traceback (most recent call last):
│ File "C:\Users\arnol\OneDrive\Desktop\bootcamp\bootcamp_fork\project3-msa\.terraform\modules\lambda\package.py", line 1041, in install_npm_requirements
│ check_call(npm_command, env=subproc_env)
│ File "C:\Users\arnol\AppData\Local\Programs\Python\Python311\Lib\subprocess.py", line 408, in check_call
│ retcode = call(*popenargs, **kwargs)
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^
│ File "C:\Users\arnol\AppData\Local\Programs\Python\Python311\Lib\subprocess.py", line 389, in call
│ with Popen(*popenargs, **kwargs) as p:
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ File "C:\Users\arnol\AppData\Local\Programs\Python\Python311\Lib\subprocess.py", line 1024, in __init__
│ self._execute_child(args, executable, preexec_fn, close_fds,
│ File "C:\Users\arnol\AppData\Local\Programs\Python\Python311\Lib\subprocess.py", line 1509, in _execute_child
│ hp, ht, pid, tid = _winapi.CreateProcess(executable, args,
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ FileNotFoundError: [WinError 2] ������ ������ ã�� �� �����ϴ�
│
│ The above exception was the direct cause of the following exception:
│
│ Traceback (most recent call last):
│ File "C:\Users\arnol\OneDrive\Desktop\bootcamp\bootcamp_fork\project3-msa\.terraform\modules\lambda\package.py", line 1277, in build_command
│ bpm.execute(build_plan, zs, query)
│ File "C:\Users\arnol\OneDrive\Desktop\bootcamp\bootcamp_fork\project3-msa\.terraform\modules\lambda\package.py", line 821, in execute
│ with install_npm_requirements(query, npm_requirements, tmp_dir) as rd:
│ File "C:\Users\arnol\AppData\Local\Programs\Python\Python311\Lib\contextlib.py", line 137, in __enter__
│ return next(self.gen)
│ ^^^^^^^^^^^^^^
│ File "C:\Users\arnol\OneDrive\Desktop\bootcamp\bootcamp_fork\project3-msa\.terraform\modules\lambda\package.py", line 1043, in install_npm_requirements
│ raise RuntimeError(
│ RuntimeError: Nodejs interpreter version equal to defined lambda runtime (nodejs14.x) should be available in system PATH
나는 윈도우 환경에서 terraform을 실행 시키고 싶었기에 terraform module로 lambda를 구현하는것을 포기하고 terraform 코드를 사용하여 구현하였다.
한가지 방법이 더 존재한다. 자동으로 zip파일을 만드는 부분에서 에러가 발생하기에 zip파일을 만들어놓고 그냥 읽어서 쓰는방식으로 수정하면 에러가 발생하지 않았다.
## Sales Lambda는 module을 이용해서 생성함.
# Lambda 선언
module "lambda" {
source = "terraform-aws-modules/lambda/aws"
version = "3.3.1"
# insert the 32 required variables here
function_name = "sales-apis"
description = "My awesome lambda function"
handler = "handler.handler"
runtime = "nodejs14.x"
timeout = 6
environment_variables = {
TOPIC_ARN = module.sns_topic.sns_topic_arn
HOSTNAME = var.DB_HOSTNAME
USERNAME = var.DB_USERNAME
PASSWORD = var.DB_PASSWORD
DATABASE = var.DB_DATABASE
}
attach_cloudwatch_logs_policy = true
create_package = false
local_existing_package = "./sales-api.zip"
tags = {
Name = "sales-api"
}
}