From ef770c1ea4ee633489323a8ab321b1214b51a770 Mon Sep 17 00:00:00 2001 From: Ellie Date: Mon, 12 Aug 2024 16:57:11 +0100 Subject: chore: add aws_iam_role --- terraform/iam.tf | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 terraform/iam.tf diff --git a/terraform/iam.tf b/terraform/iam.tf new file mode 100644 index 0000000..7501373 --- /dev/null +++ b/terraform/iam.tf @@ -0,0 +1,29 @@ +# define + +resource "aws_iam_role" "bentley_service_role" { + assume_role_policy = < Date: Tue, 13 Aug 2024 11:25:33 +0100 Subject: infra(tf): add s3 policy for list & write --- terraform/iam.tf | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/terraform/iam.tf b/terraform/iam.tf index 7501373..b9919a5 100644 --- a/terraform/iam.tf +++ b/terraform/iam.tf @@ -1,5 +1,3 @@ -# define - resource "aws_iam_role" "bentley_service_role" { assume_role_policy = < Date: Tue, 13 Aug 2024 11:30:28 +0100 Subject: database connection added to func --- src/extract_lambda.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/extract_lambda.py b/src/extract_lambda.py index e69de29..7d56c66 100644 --- a/src/extract_lambda.py +++ b/src/extract_lambda.py @@ -0,0 +1,32 @@ +from pg8000.native import Connection, Error, DatabaseError, InterfaceError +from dotenv import load_dotenv +import os + +load_dotenv() + +def extract(): + +# temporary credentials for dev- will not have access when uploaded + + database = os.getenv('database') + user = os.getenv('user') + password = os.getenv('password') + host = os.getenv('host') + port = os.getenv('port') + + + try: + db = Connection.run( + database=database, + user=user, + password=password, + host=host, + port=port + ) + except DatabaseError as e: + print(e) + except InterfaceError as i: + print(i) + + + \ No newline at end of file -- cgit v1.2.3 From 65e470c0bce51381da8f401f0ba07bd20a76071f Mon Sep 17 00:00:00 2001 From: Ellie Date: Tue, 13 Aug 2024 11:55:00 +0100 Subject: infra(tf): add wip write policy and attach policy --- terraform/iam.tf | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/terraform/iam.tf b/terraform/iam.tf index b9919a5..dda4d74 100644 --- a/terraform/iam.tf +++ b/terraform/iam.tf @@ -22,17 +22,12 @@ resource "aws_iam_role" "bentley_service_role" { EOF } +# lambda setup + + # s3 setup -# allows to list and retrieve s3 buckets, and allows retention/tagging/access control settings +# allows allows retention/tagging/access control settings data "aws_iam_policy_document" "s3_data_policy_doc" { - statement { - actions = [ - "s3:ListAllMyBuckets", - "s3:GetBucketLocation" - ] - resources = ["arn:aws:s3:::*"] - } - statement { actions = [ "s3:PutObject", @@ -41,8 +36,22 @@ data "aws_iam_policy_document" "s3_data_policy_doc" { "s3:PutObjectAcl" ] resources = [ - "${aws_s3_bucket.data_bucket.arn}/*", - "${aws_s3_bucket.code_bucket.arn}/*" + "${aws_s3_bucket.extract_bucket.arn}/*", + "${aws_s3_bucket.transform_bucket.arn}/*", + "${aws_s3_bucket.lambda_bucket.arn}/*", ] } -} \ No newline at end of file +} + +# write policy +resource "aws_iam_policy" "s3_policy" { + policy = data.aws_iam_policy_document.s3_data_policy_doc.json +} + +# attach policy to role +resource "aws_iam_role_policy_attachment" "s3_policy_attachment" { + role = aws_iam_role.bentley_service_role.name + policy_arn = aws_iam_policy.s3_policy.arn +} + +# lambda setup -- cgit v1.2.3 From 936eee1eb44d8bfdbd148d22b749966e9606fb46 Mon Sep 17 00:00:00 2001 From: Ellie Date: Tue, 13 Aug 2024 11:58:02 +0100 Subject: infra(tf): add wip lambda role --- terraform/iam.tf | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/terraform/iam.tf b/terraform/iam.tf index dda4d74..10b8749 100644 --- a/terraform/iam.tf +++ b/terraform/iam.tf @@ -23,6 +23,9 @@ resource "aws_iam_role" "bentley_service_role" { } # lambda setup +resource "aws_iam_role" "lambda_role" { + assume_role_policy = data.aws_iam_policy_document.bentley_service_role.json +} # s3 setup @@ -44,14 +47,14 @@ data "aws_iam_policy_document" "s3_data_policy_doc" { } # write policy -resource "aws_iam_policy" "s3_policy" { +resource "aws_iam_policy" "s3_write_policy" { policy = data.aws_iam_policy_document.s3_data_policy_doc.json } # attach policy to role resource "aws_iam_role_policy_attachment" "s3_policy_attachment" { - role = aws_iam_role.bentley_service_role.name - policy_arn = aws_iam_policy.s3_policy.arn + role = aws_iam_role.lambda_role.name + policy_arn = aws_iam_policy.s3_write_policy.arn } # lambda setup -- cgit v1.2.3 From eb09f0f6a42e2a2ce9529492a47a34f782ffad53 Mon Sep 17 00:00:00 2001 From: Ellie Date: Tue, 13 Aug 2024 12:17:52 +0100 Subject: infra(tf): clean-up code & init lambda iam setup --- terraform/iam.tf | 123 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 74 insertions(+), 49 deletions(-) diff --git a/terraform/iam.tf b/terraform/iam.tf index 10b8749..ecc63b1 100644 --- a/terraform/iam.tf +++ b/terraform/iam.tf @@ -1,60 +1,85 @@ -resource "aws_iam_role" "bentley_service_role" { - assume_role_policy = < Date: Tue, 13 Aug 2024 12:30:20 +0100 Subject: replacement for events.tf variable placeholders, s3 bucket notifications, lambda.tf set up (function, zip, permissions), code bucket to store zipped lambdas --- events - TBD?.tf | 52 +++++++++++++++++++++++++++++++++++++ events.tf | 52 ------------------------------------- terraform/events.tf | 23 ++++++++--------- terraform/lambda.tf | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++ terraform/s3.tf | 39 ++++++++++++++++++++++------ terraform/vars.tf | 5 ++++ 6 files changed, 173 insertions(+), 72 deletions(-) create mode 100644 events - TBD?.tf delete mode 100644 events.tf create mode 100644 terraform/lambda.tf diff --git a/events - TBD?.tf b/events - TBD?.tf new file mode 100644 index 0000000..25fb35b --- /dev/null +++ b/events - TBD?.tf @@ -0,0 +1,52 @@ +resource "aws_cloudwatch_event_rule" "lambda_trigger" { + name = "lambda-scheduled-trigger" + description = "Schedule to trigger the Lambda function" + schedule_expression = "rate(30 minutes)" + +# event_pattern = jsonencode({ +# detail-type = [ +# "AWS Console Sign In via CloudTrail" +# ] +# }) +} + + +resource "aws_cloudwatch_event_target" "lambda" { + rule = aws_cloudwatch_event_rule.lambda_trigger.name + target_id = "TargetFunctionV1" + arn = aws_lambda_function.my_lambda_function.arn +} + + + +resource "aws_lambda_permission" "allow_eventbridge" { + statement_id = "AllowExecutionFromEventBridge" + action = "lambda:InvokeFunction" + function_name = aws_lambda_function.my_lambda_function.function_name + principal = "events.amazonaws.com" + source_arn = aws_cloudwatch_event_rule.lambda_trigger.arn +} + + +# below is step function 1 +resource "aws_lambda_permission" "allow_s3_ingestion" { + statement_id = "AllowS3InvokeLambdaTransform" + action = "lambda:InvokeFunction" + function_name = aws_lambda_function.lambda_transform.function_name + principal = "s3.amazonaws.com" + source_arn = aws_s3_bucket.extract.arn +} + + +resource "aws_s3_bucket_notification" "extract_bucket_notification" { + bucket = aws_s3_bucket.extract.id + + lambda_function { + events = ["s3:ObjectCreated:*"] + lambda_function_arn = aws_lambda_function.lambda_transform.arn + } + + depends_on = [aws_lambda_permission.allow_s3_ingestion] +} + +# need to duplicate and replace "2" with "3" \ No newline at end of file diff --git a/events.tf b/events.tf deleted file mode 100644 index 25fb35b..0000000 --- a/events.tf +++ /dev/null @@ -1,52 +0,0 @@ -resource "aws_cloudwatch_event_rule" "lambda_trigger" { - name = "lambda-scheduled-trigger" - description = "Schedule to trigger the Lambda function" - schedule_expression = "rate(30 minutes)" - -# event_pattern = jsonencode({ -# detail-type = [ -# "AWS Console Sign In via CloudTrail" -# ] -# }) -} - - -resource "aws_cloudwatch_event_target" "lambda" { - rule = aws_cloudwatch_event_rule.lambda_trigger.name - target_id = "TargetFunctionV1" - arn = aws_lambda_function.my_lambda_function.arn -} - - - -resource "aws_lambda_permission" "allow_eventbridge" { - statement_id = "AllowExecutionFromEventBridge" - action = "lambda:InvokeFunction" - function_name = aws_lambda_function.my_lambda_function.function_name - principal = "events.amazonaws.com" - source_arn = aws_cloudwatch_event_rule.lambda_trigger.arn -} - - -# below is step function 1 -resource "aws_lambda_permission" "allow_s3_ingestion" { - statement_id = "AllowS3InvokeLambdaTransform" - action = "lambda:InvokeFunction" - function_name = aws_lambda_function.lambda_transform.function_name - principal = "s3.amazonaws.com" - source_arn = aws_s3_bucket.extract.arn -} - - -resource "aws_s3_bucket_notification" "extract_bucket_notification" { - bucket = aws_s3_bucket.extract.id - - lambda_function { - events = ["s3:ObjectCreated:*"] - lambda_function_arn = aws_lambda_function.lambda_transform.arn - } - - depends_on = [aws_lambda_permission.allow_s3_ingestion] -} - -# need to duplicate and replace "2" with "3" \ No newline at end of file diff --git a/terraform/events.tf b/terraform/events.tf index 4d68a23..0196dc3 100644 --- a/terraform/events.tf +++ b/terraform/events.tf @@ -11,18 +11,17 @@ resource "aws_cloudwatch_event_rule" "lambda_trigger" { } -resource "aws_cloudwatch_event_target" "lambda" { +resource "aws_cloudwatch_event_target" "extract_lambda_cw_event" { rule = aws_cloudwatch_event_rule.lambda_trigger.name target_id = "TargetFunctionV1" - arn = aws_lambda_function.my_lambda_function.arn + arn = aws_lambda_function.extract_lambda.arn #replaced lambda name placeholder } - resource "aws_lambda_permission" "allow_eventbridge" { statement_id = "AllowExecutionFromEventBridge" action = "lambda:InvokeFunction" - function_name = aws_lambda_function.my_lambda_function.function_name + function_name = aws_lambda_function.extract_lambda.function_name #replaced lambda name placeholder principal = "events.amazonaws.com" source_arn = aws_cloudwatch_event_rule.lambda_trigger.arn } @@ -32,18 +31,18 @@ resource "aws_lambda_permission" "allow_eventbridge" { resource "aws_lambda_permission" "allow_s3_ingestion" { statement_id = "AllowS3InvokeLambdaTransform" action = "lambda:InvokeFunction" - function_name = aws_lambda_function.lambda_transform.function_name + function_name = aws_lambda_function.transform_lambda.function_name #replaced lambda name placeholder principal = "s3.amazonaws.com" - source_arn = aws_s3_bucket.extract.arn + source_arn = aws_s3_bucket.extract_bucket.arn #replaced bucket name placeholder } resource "aws_s3_bucket_notification" "extract_bucket_notification" { - bucket = aws_s3_bucket.extract.id + bucket = aws_s3_bucket.extract_bucket.id #replaced bucket name placeholder lambda_function { events = ["s3:ObjectCreated:*"] - lambda_function_arn = aws_lambda_function.lambda_transform.arn + lambda_function_arn = aws_lambda_function.transform_lambda.arn #replaced lambda name placeholder } depends_on = [aws_lambda_permission.allow_s3_ingestion] @@ -54,18 +53,18 @@ resource "aws_s3_bucket_notification" "extract_bucket_notification" { resource "aws_lambda_permission" "allow_s3_transfrom_bucket" { statement_id = "AllowS3InvokeLambdaTransform" action = "lambda:InvokeFunction" - function_name = aws_lambda_function.lambda_transform.function_name + function_name = aws_lambda_function.transform_lambda.function_name #replaced lambda name placeholder principal = "s3.amazonaws.com" - source_arn = aws_s3_bucket.transform.arn + source_arn = aws_s3_bucket.transform_bucket.arn #replaced bucket name placeholder } resource "aws_s3_bucket_notification" "transform_bucket_notification" { - bucket = aws_s3_bucket.transform.id + bucket = aws_s3_bucket.transform_bucket.id #replaced bucket name placeholder lambda_function { events = ["s3:ObjectCreated:*"] - lambda_function_arn = aws_lambda_function.lambda_transform.arn + lambda_function_arn = aws_lambda_function.transform_lambda.arn #replaced lambda name placeholder } depends_on = [aws_lambda_permission.allow_s3_transform] diff --git a/terraform/lambda.tf b/terraform/lambda.tf new file mode 100644 index 0000000..09d6697 --- /dev/null +++ b/terraform/lambda.tf @@ -0,0 +1,74 @@ +### EXTRACT LAMBDA SET UP +data "archive_file" "extract_lambda_zip" { + type = "zip" + source_file = "${path.module}/../src/extract_lambda.py" + output_path = "${path.module}/../extract_function.zip" +} + +resource "aws_lambda_function" "extract_lambda" { + function_name = "${var.extract_lambda_name}" + s3_bucket = aws_s3_bucket.lambda_bucket.bucket + s3_key = "extract_lambda/extract_function.zip" + role = aws_iam_role.PLACEHOLDER_extract_lambda_role.arn # << lambda role placehodler + handler = "extract_lambda.lambda_handler" # << check that the function is called lambda handler + runtime = "python3.11" + environment { + variables = { + output = aws_s3_bucket.extract_bucket.bucket + } + } +} + +resource "aws_lambda_permission" "allow_to_write_to_s3_extract_bucket" { + action = "lambda:InvokeFunction" + function_name = aws_lambda_function.extract_lambda.function_name + principal = "s3.amazonaws.com" + source_arn = aws_s3_bucket.extract_bucket.arn +} + + +### TRANSFORM LAMBDA SET UP +data "archive_file" "transform_lambda_zip" { + type = "zip" + source_file = "${path.module}/../src/transform_lambda.py" + output_path = "${path.module}/../transform_function.zip" +} + +resource "aws_lambda_function" "transform_lambda" { + function_name = "${var.transform_lambda_name}" + s3_bucket = aws_s3_bucket.lambda_bucket.bucket + s3_key = "transform_lambda/transform_function.zip" + role = aws_iam_role.PLACEHOLDER_transform_lambda_role.arn # << lambda role placehodler + handler = "transform_lambda.lambda_handler" # << check that the function is called lambda handler + runtime = "python3.11" + environment { + variables = { + output = aws_s3_bucket.transform_bucket.bucket + } + } +} + +resource "aws_lambda_permission" "allow_to_write_to_s3_transform_bucket" { + action = "lambda:InvokeFunction" + function_name = aws_lambda_function.transform_lambda.function_name + principal = "s3.amazonaws.com" + source_arn = aws_s3_bucket.transform_bucket.arn +} + + +### LOAD LAMBDA SET UP +data "archive_file" "load_lambda_zip" { + type = "zip" + source_file = "${path.module}/../src/load_lambda.py" + output_path = "${path.module}/../load_function.zip" +} + +resource "aws_lambda_function" "load_lambda" { + function_name = "${var.load_lambda_name}" + s3_bucket = aws_s3_bucket.lambda_bucket.bucket + s3_key = "load_lambda/load_function.zip" + role = aws_iam_role.PLACEHOLDER_load_lambda_role.arn # << lambda role placehodler + handler = "load_lambda.lambda_handler" # << check that the function is called lambda handler + runtime = "python3.11" +} + diff --git a/terraform/s3.tf b/terraform/s3.tf index bfe891e..8cb65ef 100644 --- a/terraform/s3.tf +++ b/terraform/s3.tf @@ -1,17 +1,40 @@ +### EXTRACT BUCKET SET-UP resource "aws_s3_bucket" "extract_bucket" { bucket = "${var.s3_extract_bucket_name}" } +resource "aws_s3_object" "extract_lambda_code" { + bucket = aws_s3_bucket.s3_code_bucket_name.bucket + key = "${var.extract_lambda_name}/extract_function.zip" + source = "${path.module}/../extract_function.zip" +} # << can't figure out how this is being used but we seem to need it + +resource "aws_s3_bucket_notification" "extract_bucket_notification" { + bucket = aws_s3_bucket.extract_bucket.id + lambda_function { + lambda_function_arn = aws_lambda_function.extract_lambda.arn + events = ["s3:ObjectCreated:*"] + } + depends_on = [aws_lambda_permission.allow_to_write_to_s3_extract_bucket] +} # << is this the correct permission dependency? + + +### TRANSFORM BUCKET SET-UP resource "aws_s3_bucket" "transform_bucket" { bucket = "${var.s3_transform_bucket_name}" } -resource "aws_s3_bucket" "lambda_bucket" { - bucket = "${var.s3_code_bucket_name}" -} - -resource "aws_s3_object" "extract_lambda_code" { +resource "aws_s3_object" "transform_lambda_code" { bucket = aws_s3_bucket.s3_code_bucket_name.bucket - key = "${var.extract_lambda_name}/function_e.zip" - source = "${path.module}/../function_e.zip" -} \ No newline at end of file + key = "${var.transform_lambda_name}/transform_function.zip" + source = "${path.module}/../transform_function.zip" +} # << can't figure out how this is being used but we seem to need it + +resource "aws_s3_bucket_notification" "transform_bucket_notification" { + bucket = aws_s3_bucket.transform_bucket.id + lambda_function { + lambda_function_arn = aws_lambda_function.transform_lambda.arn + events = ["s3:ObjectCreated:*"] + } + depends_on = [aws_lambda_permission.allow_to_write_to_s3_transform_bucket] +} # << is this the correct permission dependency? diff --git a/terraform/vars.tf b/terraform/vars.tf index fa84222..cc9348a 100644 --- a/terraform/vars.tf +++ b/terraform/vars.tf @@ -23,6 +23,11 @@ variable "transform_lambda_name" { default = "transform-lambda" } +variable "load_lambda_name" { + type = string + default = "load-lambda" +} + data "aws_caller_identity" "current" {} data "aws_region" "current" {} \ No newline at end of file -- cgit v1.2.3 From 3c824df60374380d044cb9181672fa76b610d84f Mon Sep 17 00:00:00 2001 From: Ellie Date: Tue, 13 Aug 2024 12:30:53 +0100 Subject: infra(tf): clean-up code --- terraform/iam.tf | 69 +++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/terraform/iam.tf b/terraform/iam.tf index ecc63b1..bb8d932 100644 --- a/terraform/iam.tf +++ b/terraform/iam.tf @@ -4,7 +4,7 @@ ######################################################################## # DEFINE MULTI-SERVICE ROLE (lambda, s3, cloudwatch, events) -resource "aws_iam_role" "multi_service_role" { +resource "aws_iam_role" "bentley_multi_service_role" { name = "multi_service_role" assume_role_policy = jsonencode({ @@ -61,6 +61,61 @@ resource "aws_iam_policy" "s3_access_policy" { ) } +######################################################################## +# LAMBDA SETUP +# Description: Allows Lambda permission to write to Cloudwatch logs +######################################################################## + +resource "aws_iam_policy" "lambda_execution_policy" { + name = "lambda_execution_policy" + path = "/" + description = "IAM policy for Lambda execution" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "lambda:InvokeFunction", + "lambda:GetFunction" + ] + Resource = "*" + } + ] + } + ) +} + +######################################################################## +# CLOUDWATCH SETUP +# Description: Give permission for Lambda to write to CloudWatch logs +######################################################################## + +data "aws_iam_policy_document" "cw_document" { + statement { + actions = ["logs:CreateLogGroup"] + resources = [ + "arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*" + ] + } + + statement { + actions = [ + "logs:CreateLogStream", + "logs:CreateLogGroup", + "logs:PutLogEvents" + ] + resources = [ + "arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:/aws/lambda/*" + ] + } +} + +######################################################################## +# POLICY WRITE & ATTACH +######################################################################## + # S3 WRITE POLICY resource "aws_iam_policy" "s3_write_policy" { policy = data.aws_iam_policy_document.s3_data_policy_doc.json @@ -70,16 +125,4 @@ resource "aws_iam_policy" "s3_write_policy" { resource "aws_iam_role_policy_attachment" "lambda_s3_policy_attachment" { role = aws_iam_role.lambda_role.name policy_arn = aws_iam_policy.s3_write_policy.arn -} - -######################################################################## -# LAMBDA SETUP -# Description: Allows Lambda permission to write to Cloudwatch logs -######################################################################## - - - -# Uses Iam policy document to assume role for lambda functions -resource "aws_iam_role" "lambda_role" { - assume_role_policy = data.aws_iam_policy_document.bentley_service_role.json } \ No newline at end of file -- cgit v1.2.3