초기 코드
vi main.tf
provider "aws" {
profile = "default"
region = "ap-northeast-2"
}
resource "aws_dynamodb_table" "product_table" {
name = "PRODUCT"
billing_mode = "PAY_PER_REQUEST"
hash_key = "product_id"
attribute {
name = "product_id"
type = "S"
}
attribute {
name = "category"
type = "S"
}
attribute {
name = "product_rating"
type = "N"
}
global_secondary_index {
name = "ProductCategoryRatingIndex"
hash_key = "category"
range_key = "product_rating"
projection_type = "ALL"
}
}
resource "aws_api_gateway_rest_api" "product_apigw" {
name = "product_apigw"
description = "Product API Gateway"
endpoint_configuration {
types = ["REGIONAL"]
}
}
resource "aws_api_gateway_resource" "product" {
rest_api_id = aws_api_gateway_rest_api.product_apigw.id
parent_id = aws_api_gateway_rest_api.product_apigw.root_resource_id
path_part = "product"
}
resource "aws_api_gateway_method" "createproduct" {
rest_api_id = aws_api_gateway_rest_api.product_apigw.id
resource_id = aws_api_gateway_resource.product.id
http_method = "POST"
authorization = "NONE"
}
resource "aws_iam_role" "ProductLambdaRole" {
name = "ProductLambdaRole"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
data "template_file" "productlambdapolicy" {
template = "${file("${path.module}/policy.json")}"
}
resource "aws_iam_policy" "ProductLambdaPolicy" {
name = "ProductLambdaPolicy"
path = "/"
description = "IAM policy for Product lambda functions"
policy = data.template_file.productlambdapolicy.rendered
}
resource "aws_iam_role_policy_attachment" "ProductLambdaRolePolicy" {
role = aws_iam_role.ProductLambdaRole.name
policy_arn = aws_iam_policy.ProductLambdaPolicy.arn
}
resource "aws_lambda_function" "CreateProductHandler" {
function_name = "CreateProductHandler"
filename = "../lambda/product_lambda.zip"
handler = "createproduct.lambda_handler"
runtime = "python3.8"
environment {
variables = {
REGION = "ap-northeast-2"
PRODUCT_TABLE = aws_dynamodb_table.product_table.name
}
}
source_code_hash = filebase64sha256("../lambda/product_lambda.zip")
role = aws_iam_role.ProductLambdaRole.arn
timeout = "5"
memory_size = "128"
}
resource "aws_api_gateway_integration" "createproduct-lambda" {
rest_api_id = aws_api_gateway_rest_api.product_apigw.id
resource_id = aws_api_gateway_method.createproduct.resource_id
http_method = aws_api_gateway_method.createproduct.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.CreateProductHandler.invoke_arn
}
resource "aws_lambda_permission" "apigw-CreateProductHandler" {
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.CreateProductHandler.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.product_apigw.execution_arn}/*/POST/product"
}
resource "aws_api_gateway_deployment" "productapistageprod" {
depends_on = [
aws_api_gateway_integration.createproduct-lambda
]
rest_api_id = aws_api_gateway_rest_api.product_apigw.id
stage_name = "prod"
}
vi policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"dynamodb:PutItem"
],
"Resource": "arn:aws:dynamodb:*:*:table/PRODUCT"
}
]
}
~
vi createproduct.py
zip ./product_lambda.zip *.py
import logging
import boto3
import json
import os
session = boto3.Session(region_name=os.environ['REGION'])
dynamodb_client = session.client('dynamodb')
def lambda_handler(event, context):
try:
print("event ->" + str(event))
payload = json.loads(event["body"])
print("payload ->" + str(payload))
dynamodb_response = dynamodb_client.put_item(
TableName=os.environ["PRODUCT_TABLE"],
Item={
"product_id": {
"S": payload["productId"]
},
"category": {
"S": payload["category"]
},
"product_rating": {
"N": str(payload["productRating"])
},
"product_name": {
"S": payload["productName"]
},
"product_price": {
"N": str(payload["productPrice"])
},
"product_description": {
"S": payload["productDescription"]
}
}
)
print(dynamodb_response)
return {
'statusCode': 201,
'body': '{"status":"Product created"}'
}
except Exception as e:
logging.error(e)
return {
'statusCode': 500,
'body': '{"status":"Server error"}'
}
curl -X POST "https://zdqpdl1wmd.execute-api.ap-northeast-2.amazonaws.com/prod/product" -H 'Content-Type: application/json' -d'
{
"productId": "1",
"category": "Category",
"productName": "<<Product Name>>",
"productPrice": 12,
"productDescription": "Product description",
"productRating": 2
}
'
최종 코드
vi main.tf
route53 zone_id 수정 필요
provider "aws" {
profile = "default"
region = "ap-northeast-2"
}
resource "aws_dynamodb_table" "product_table" {
name = "UserTable"
billing_mode = "PAY_PER_REQUEST"
hash_key = "user_id"
attribute {
name = "user_id"
type = "S"
}
# attribute {
# name = "category"
# type = "S"
# }
# attribute {
# name = "product_rating"
# type = "N"
# }
# global_secondary_index {
# name = "ProductCategoryRatingIndex"
# hash_key = "category"
# range_key = "product_rating"
# projection_type = "ALL"
# }
}
resource "aws_api_gateway_rest_api" "product_apigw" {
name = "product_apigw"
description = "Product API Gateway"
endpoint_configuration {
types = ["REGIONAL"]
}
}
#resource "aws_api_gateway_resource" "product" {
# rest_api_id = aws_api_gateway_rest_api.product_apigw.id
# parent_id = aws_api_gateway_rest_api.product_apigw.root_resource_id
# path_part = "product"
#}
resource "aws_api_gateway_method" "createproduct" {
rest_api_id = aws_api_gateway_rest_api.product_apigw.id
resource_id = aws_api_gateway_rest_api.product_apigw.root_resource_id
http_method = "POST"
authorization = "NONE"
}
resource "aws_api_gateway_method" "test_options" {
rest_api_id = aws_api_gateway_rest_api.product_apigw.id
resource_id = aws_api_gateway_rest_api.product_apigw.root_resource_id
http_method = "OPTIONS"
authorization = "NONE"
}
resource "aws_api_gateway_method_response" "test_options_200" {
rest_api_id = aws_api_gateway_rest_api.product_apigw.id
resource_id = aws_api_gateway_rest_api.product_apigw.root_resource_id
http_method = aws_api_gateway_method.test_options.http_method
status_code = "200"
response_models = {
"application/json" = "Empty"
}
response_parameters = {
"method.response.header.Access-Control-Allow-Headers" = true,
"method.response.header.Access-Control-Allow-Methods" = true,
"method.response.header.Access-Control-Allow-Origin" = true
}
}
resource "aws_api_gateway_integration_response" "test_options" {
rest_api_id = aws_api_gateway_rest_api.product_apigw.id
resource_id = aws_api_gateway_rest_api.product_apigw.root_resource_id
http_method = aws_api_gateway_method.test_options.http_method
status_code = aws_api_gateway_method_response.test_options_200.status_code
response_parameters = {
"method.response.header.Access-Control-Allow-Headers" = "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
"method.response.header.Access-Control-Allow-Methods" = "'GET,OPTIONS,POST,PUT'",
"method.response.header.Access-Control-Allow-Origin" = "'*'"
}
}
resource "aws_api_gateway_integration" "test_options_mock" {
rest_api_id = aws_api_gateway_rest_api.product_apigw.id
resource_id = aws_api_gateway_rest_api.product_apigw.root_resource_id
http_method = aws_api_gateway_method.test_options.http_method
type = "MOCK"
request_templates = {
"application/json" = <<EOF
{
"statusCode": 200
}
EOF
}
}
#resource "aws_api_gateway_integration" "createproduct-lambda" {
#rest_api_id = aws_api_gateway_rest_api.product_apigw.id
# resource_id = aws_api_gateway_method.createproduct.resource_id
# http_method = aws_api_gateway_method.createproduct.http_method
# integration_http_method = "POST"
# type = "AWS"
#uri = aws_lambda_function.CreateProductHandler.invoke_arn
#}
resource "aws_api_gateway_integration" "createproduct-lambda" {
rest_api_id = aws_api_gateway_rest_api.product_apigw.id
resource_id = aws_api_gateway_rest_api.product_apigw.root_resource_id
http_method = aws_api_gateway_method.createproduct.http_method
integration_http_method = "POST" # Lambda function can only be invoked via POST
type = "AWS"
uri = aws_lambda_function.CreateProductHandler.invoke_arn
content_handling = "CONVERT_TO_TEXT"
}
resource "aws_api_gateway_method_response" "lambda" {
rest_api_id = aws_api_gateway_rest_api.product_apigw.id
resource_id = aws_api_gateway_rest_api.product_apigw.root_resource_id
http_method = aws_api_gateway_method.createproduct.http_method
status_code = "200"
response_models = {
"application/json" = "Empty"
}
}
resource "aws_api_gateway_integration_response" "lambda" {
rest_api_id = aws_api_gateway_rest_api.product_apigw.id
resource_id = aws_api_gateway_rest_api.product_apigw.root_resource_id
http_method = aws_api_gateway_method.createproduct.http_method
status_code = aws_api_gateway_method_response.lambda.status_code
depends_on = [aws_api_gateway_integration.createproduct-lambda]
}
resource "aws_api_gateway_deployment" "lambda" {
rest_api_id = aws_api_gateway_rest_api.product_apigw.id
triggers = {
redeployment = sha1(jsonencode([
aws_api_gateway_method.createproduct.id,
aws_api_gateway_integration.createproduct-lambda.id,
aws_api_gateway_method_response.lambda,
aws_api_gateway_integration_response.lambda,
]))
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_api_gateway_stage" "lambda" {
deployment_id = aws_api_gateway_deployment.lambda.id
rest_api_id = aws_api_gateway_rest_api.product_apigw.id
stage_name = "run" # Any Name you wish
}
output "deployment_invoke_url" {
description = "Deployment invoke url"
value = aws_api_gateway_stage.lambda.invoke_url
}
resource "aws_iam_role" "ProductLambdaRole" {
name = "ProductLambdaRole"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
data "template_file" "productlambdapolicy" {
template = "${file("${path.module}/policy.json")}"
}
resource "aws_iam_policy" "ProductLambdaPolicy" {
name = "ProductLambdaPolicy"
path = "/"
description = "IAM policy for Product lambda functions"
policy = data.template_file.productlambdapolicy.rendered
}
resource "aws_iam_role_policy_attachment" "ProductLambdaRolePolicy" {
role = aws_iam_role.ProductLambdaRole.name
policy_arn = aws_iam_policy.ProductLambdaPolicy.arn
}
resource "aws_lambda_function" "CreateProductHandler" {
function_name = "CreateProductHandler"
filename = "./product_lambda.zip"
handler = "createproduct.lambda_handler"
runtime = "python3.8"
environment {
variables = {
REGION = "ap-northeast-2"
PRODUCT_TABLE = aws_dynamodb_table.product_table.name
}
}
source_code_hash = filebase64sha256("./product_lambda.zip")
role = aws_iam_role.ProductLambdaRole.arn
timeout = "5"
memory_size = "128"
}
#resource "aws_api_gateway_integration" "createproduct-lambda" {
#rest_api_id = aws_api_gateway_rest_api.product_apigw.id
# resource_id = aws_api_gateway_method.createproduct.resource_id
# http_method = aws_api_gateway_method.createproduct.http_method
# integration_http_method = "POST"
# type = "AWS"
#uri = aws_lambda_function.CreateProductHandler.invoke_arn
#}
resource "aws_lambda_permission" "apigw-CreateProductHandler" {
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.CreateProductHandler.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.product_apigw.execution_arn}/*"
}
#resource "aws_api_gateway_deployment" "productapistageprod" {
# depends_on = [
# aws_api_gateway_integration.createproduct-lambda
# ]
# rest_api_id = aws_api_gateway_rest_api.product_apigw.id
# stage_name = "prod"
#}
vi policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"dynamodb:*"
],
"Resource": [
"*"
]
}
]
}
~
zip ./product_lambda.zip *.py
# import the json utility package since we will be working with a JSON object
import json
# import the AWS SDK (for Python the package name is boto3)
import boto3
# import two packages to help us with dates and date formatting
from time import gmtime, strftime
import uuid
PARTITION_KEY = 'user_id'
def lambda_handler(event, context):
# AWS SDK의 DynamoDB 클라이언트를 생성합니다.
user_id=uuid.uuid4()
now = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())
dynamodb = boto3.client('dynamodb')
# user_id = event['user_id']
name = event['Name']
phone = event['phone']
email = event['email']
try:
# PutItem 작업에 필요한 매개변수를 구성합니다.
params = {
'TableName': 'UserTable', # 테이블 이름을 적절히 변경하세요.
'Item': {
'user_id': {'S': str(user_id)}, # 테이블의 키 필드와 값을 적절히 변경하세요.
'Name': {'S': name},
'Phone': {'S': phone},
'Email': {'S': email},
'Time': {'S': now},
# 추가적인 속성 및 값을 필요에 따라 정의할 수 있습니다.
}
}
# PutItem 작업을 수행합니다.
response = dynamodb.put_item(**params)
print('PutItem 성공:', response)
return {
'statusCode': 200,
'body': 'PutItem 작업이 성공적으로 완료되었습니다.'
}
except Exception as e:
print('PutItem 실패:', e)
return {
'statusCode': 500,
'body': 'PutItem 작업이 실패했습니다.'
}
S3
iam identifier ID에 맞게 수정 필요
provider "aws" {
alias = "virginia"
region = "us-east-1"
default_tags {
tags = {
project = "serverless-demo",
type = "web",
iac = "terraform"
}
}
}
######### S3 ##########
resource "aws_s3_bucket" "website_bucket" {
bucket = "my-unique-bucket-name-peach1102"
}
#resource "aws_s3_object" "website_bucket" {
# bucket = aws_s3_bucket.website_bucket.id
# key = "index.html"
# source = "index.html"
# content_type = "text/html"
#}
### 버킷 정책
resource "aws_s3_bucket_ownership_controls" "example" {
bucket = aws_s3_bucket.website_bucket.id
rule {
object_ownership = "BucketOwnerPreferred"
}
}
resource "aws_s3_bucket_public_access_block" "website_bucket" {
bucket = aws_s3_bucket.website_bucket.id
block_public_acls = false
block_public_policy = false
ignore_public_acls = false
restrict_public_buckets = false
}
resource "aws_s3_bucket_acl" "example" {
depends_on = [
aws_s3_bucket_ownership_controls.example,
aws_s3_bucket_public_access_block.website_bucket,
]
bucket = aws_s3_bucket.website_bucket.id
acl = "private"
}
resource "aws_s3_bucket_policy" "allow_access_from_another_account" {
bucket = aws_s3_bucket.website_bucket.id
policy = data.aws_iam_policy_document.allow_access_from_another_account.json
}
data "aws_iam_policy_document" "allow_access_from_another_account" {
statement {
principals {
type = "AWS"
identifiers = ["606203148720"]
}
actions = [
"s3:GetObject",
"s3:ListBucket",
]
resources = [
aws_s3_bucket.website_bucket.arn,
"${aws_s3_bucket.website_bucket.arn}/*",
]
}
}
resource "aws_s3_bucket_website_configuration" "website_bucket" {
bucket = aws_s3_bucket.website_bucket.id
index_document {
suffix = "index.html"
}
}
Cloudfront
######## cloudfront######
resource "aws_cloudfront_origin_access_identity" "example" {
comment = "Some comment"
}
resource "aws_cloudfront_distribution" "cdn_static_site" {
enabled = true
is_ipv6_enabled = true
default_root_object = "index.html"
comment = "my cloudfront in front of the s3 bucket"
origin {
domain_name = aws_s3_bucket.website_bucket.bucket_regional_domain_name
origin_id = "my-s3-origin"
origin_access_control_id = aws_cloudfront_origin_access_control.default.id
}
default_cache_behavior {
min_ttl = 0
default_ttl = 0
max_ttl = 0
viewer_protocol_policy = "redirect-to-https"
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "my-s3-origin"
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
}
restrictions {
geo_restriction {
locations = []
restriction_type = "none"
}
}
aliases = ["blog4.changhoon.shop"]
viewer_certificate {
cloudfront_default_certificate = true
acm_certificate_arn = aws_acm_certificate.acm_certificate.arn
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1.2_2021"
}
}
resource "aws_cloudfront_origin_access_control" "default" {
name = "cloudfront OAC"
description = "description of OAC"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}
output "cloudfront_url" {
value = aws_cloudfront_distribution.cdn_static_site.domain_name
}
ACM+Route53
Z0209195I0PT3MTRG9LY zone_id 변경 필요
######### ACM, Route53
# request public certificates from the amazon certificate manager.
resource "aws_acm_certificate" "acm_certificate" {
domain_name = "*.changhoon.shop"
# subject_alternative_names = ["changhoon.shop"]
validation_method = "DNS"
provider = aws.virginia
lifecycle {
create_before_destroy = true
}
}
# get details about a route 53 hosted zone
data "aws_route53_zone" "route53_zone" {
zone_id = "Z065245838U84TYWCZQFH"
# name = "changhoon.shop"
private_zone = false
}
# create a record set in route 53 for domain validatation
resource "aws_route53_record" "route53_record" {
for_each = {
for dvo in aws_acm_certificate.acm_certificate.domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
}
}
allow_overwrite = true
name = each.value.name
records = [each.value.record]
ttl = 60
type = each.value.type
zone_id = data.aws_route53_zone.route53_zone.zone_id
}
# validate acm certificates
resource "aws_acm_certificate_validation" "acm_certificate_validation" {
provider = aws.virginia
certificate_arn = aws_acm_certificate.acm_certificate.arn
validation_record_fqdns = [for record in aws_route53_record.route53_record : record.fqdn]
}
resource "aws_route53_record" "blog" {
zone_id = data.aws_route53_zone.route53_zone.zone_id
name = "blog4.changhoon.shop"
type = "A"
alias {
name = aws_cloudfront_distribution.cdn_static_site.domain_name
zone_id = aws_cloudfront_distribution.cdn_static_site.hosted_zone_id
evaluate_target_health = false
}
}
댓글