diff --git a/.gitignore b/.gitignore index 50955f8..1d20e8f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ crash.*.log # to change depending on the environment. # *.tfvars +!terraform.*example*.tfvars # Ignore override files as they are usually used to override resources locally and so # are not checked in diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fe078fb..91f6626 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: args: - "--args=--only=terraform_deprecated_interpolation" - "--args=--only=terraform_deprecated_index" - # - "--args=--only=terraform_unused_declarations" + - "--args=--only=terraform_unused_declarations" - "--args=--only=terraform_comment_syntax" - "--args=--only=terraform_documented_outputs" - "--args=--only=terraform_documented_variables" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1c49cd8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,46 @@ +# Change Log + +All notable changes to this module will be documented in this file. + +## [1.1.0] - 2022-07-22 + +### Changed + +- Remove upload code from s3 + - S3 source code is used for versioning +- Change `additional_lambda_role_policy_arn` to map from list + +### Added + +- Enable Tracing + +## [v1.0.2] - 2022-07-01 + +### Added + +- Add default log retention 90 days, KMS encryption support + +### Fixed + +- Fix kms security issue by @xshot9011 in #9 + +## [v1.0.1] - 2022-06-08 + +### Added + +- Add resource base policy for lambda + +## [v1.0.0] - 2022-05-17 + +### Added + +- Since Lambdas are uploaded via zip files, we generate a zip file from the path specified. +- Upload the zip file containing the build artifacts to S3. +- Allow access to this lambda function from AWS. +- Allow lambda to generate logs. +- Construct a role that AWS services can adopt in order to invoke our function. +- This policy also has the capability to write logs to CloudWatch. +- Create the secret SSM parameters that can be retrieved and decoded by the lambda function. +- Create an IAM policy document granting the ability to read and retrieve SSM parameter values. +- Develop a policy based on the SSM policy paper +- Custom policies to attach to this role diff --git a/README.md b/README.md index 4a36f9f..743da50 100644 --- a/README.md +++ b/README.md @@ -2,102 +2,34 @@ ## Usage -### Source code form bucket - ```terraform -module "lambda_from_bucket" { - source = "" +module "lambda" { + source = "git@github.com:oozou/terraform-aws-lambda.git?ref=v1.1.0" prefix = "oozou" - environment = "test" - name = "bakara" - - is_edge = false # Defautl is `fault`, If you want to publish to the edge don't forget to override aws's provider to virgina + environment = "dev" + name = "demo" - # Source code configuration. If is_upload_form_s3 is `true` - is_upload_form_s3 = true # Default is `true` - bucket_name = "oozou-test-loal-lambda-bucket-557291035693-48fexi" - file_name = "oozou-test-loal.zip" - - # Lambda Env - runtime = "nodejs12.x" - handler = "index.handler" # Default `"index.handler"` + is_edge = true # Defautl is `fault`, If you want to publish to the edge don't forget to override aws's provider to virgina - # Lambda Specification - timeout = 3 # Default is `3` seconds - memory_size = 128 # Default is `128` MB, the more mem size increase, the performance is better - reserved_concurrent_executions = -1 - ## Optional to connect Lambda to VPC - vpc_config = { - security_group_ids = ["sg-028f637312eea735e"] - subnet_ids_to_associate = ["subnet-0b853f8c85796d72d", "subnet-07c068b4b51262793", "subnet-0362f68c559ef7716"] - } - dead_letter_target_arn = "arn:aws:sns:ap-southeast-1:557291035693:demo" # To send failed processing to target, Default is `""` + # If is_edge is `false`, ignore this config + is_create_lambda_bucket = true # Default is `false`; plz use false, if not 1 lambda: 1 bucket + bucket_name = "" # If `is_create_lambda_bucket` is `false`; specified this, default is `""` - # IAM - is_create_lambda_role = true # Default is `true` - lambda_role_arn = "" # If `is_create_lambda_role` is `false` - additional_lambda_role_policy_arns = ["arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess", ] # The policies that you want to attach to IAM Role created by only this module - - # Resource policy - lambda_permission_configuration = { - lambda_on_my_account = { - pricipal = "apigateway.amazonaws.com" - source_arn = "arn:aws:execute-api:ap-southeast-1:557291035693:lk36vflbha/*/*/" - } - lambda_on_my_another_account_wrong = { - pricipal = "apigateway.amazonaws.com" - source_arn = "arn:aws:execute-api:ap-southeast-1:562563527952:q6pwa6wgr6/*/*/" - source_account = "557291035693" # Optional just to restrict the permission - } - lambda_on_my_another_account_correct = { - pricipal = "apigateway.amazonaws.com" - source_arn = "arn:aws:execute-api:ap-southeast-1:557291035693:wpj4t3scmb/*/*/" - } - } - - # Logging - is_create_cloudwatch_log_group = true # Default is `true` - cloudwatch_log_retention_in_days = 90 # Default is 90 days - cloudwatch_log_kms_key_id = null # Specify the kms to encrypt cloudwatch log - - # Secret for lambda function - ssm_params = {} - - tags = { "Workspace" = "xxx-yyy-zzz" } -} - -``` - -### Source code from local - -```terraform -module "lambda_from_local" { - source = "" - - prefix = "oozou" - environment = "test" - name = "local" - - is_edge = false # Defautl is `fault`, If you want to publish to the edge don't forget to override aws's provider to virgina - - # Source code configuration. If is_upload_form_s3 is `false` - is_upload_form_s3 = false # Default is `true` + # Source code source_code_dir = "./src" - file_globs = ["index.js"] + file_globs = ["main.py"] compressed_local_file_dir = "./outputs" - is_create_lambda_bucket = true # Default is `false`; plz use false, if not 1 lambda: 1 bucket - bucket_name = "" # If `is_create_lambda_bucket` is `false`; specified this, default is `""` # Lambda Env - runtime = "nodejs12.x" - handler = "index.handler" # Default `"index.handler"` + runtime = "python3.9" + handler = "main.lambda_handler" # Lambda Specification timeout = 3 # Default is `3` seconds memory_size = 128 # Default is `128` MB, the more mem size increase, the performance is better reserved_concurrent_executions = -1 - ## Optional to connect Lambda to VPC + # Optional to connect Lambda to VPC vpc_config = { security_group_ids = ["sg-028f637312eea735e"] subnet_ids_to_associate = ["subnet-0b853f8c85796d72d", "subnet-07c068b4b51262793", "subnet-0362f68c559ef7716"] @@ -105,12 +37,15 @@ module "lambda_from_local" { dead_letter_target_arn = "arn:aws:sns:ap-southeast-1:557291035693:demo" # To send failed processing to target, Default is `""` # IAM - is_create_lambda_role = true # Default is `true` - lambda_role_arn = "" # If `is_create_lambda_role` is `false` - additional_lambda_role_policy_arns = ["arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess", ] # The policies that you want to attach to IAM Role created by only this module + is_create_lambda_role = true # Default is `true` + lambda_role_arn = "" # If `is_create_lambda_role` is `false` + # The policies that you want to attach to IAM Role created by only this module + additional_lambda_role_policy_arns = { + allow_lambda_to_read_s3 = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" + } # Resource policy - lambda_permission_configuration = { + lambda_permission_configurations = { lambda_on_my_account = { pricipal = "apigateway.amazonaws.com" source_arn = "arn:aws:execute-api:ap-southeast-1:557291035693:lk36vflbha/*/*/" @@ -127,16 +62,21 @@ module "lambda_from_local" { } # Logging - is_create_cloudwatch_log_group = true # Default is `true` - retention_in_days = 30 # Default is `30` + is_create_cloudwatch_log_group = true # Default is `true` + cloudwatch_log_retention_in_days = 30 # Default is `90` - # Secret for lambda function - ssm_params = { - "DATABASE_PASSWORD" = "abdhegcg2365daA" - "DATABASE_HOST" = "www.google.com" + # Env + ssm_params = {} + plaintext_params = { + region = "ap-southeast-1" + cluster_name = "oozou-dev-test-schedule-cluster" + nodegroup_name = "oozou-dev-test-schedule-custom-nodegroup" + min = 1, + max = 1, + desired = 1 } - tags = { "Workspace" = "pc" } + tags = var.generics_info["custom_tags"] } ``` @@ -154,13 +94,13 @@ module "lambda_from_local" { | Name | Version | |---------------------------------------------------------------|---------| | [archive](#provider\_archive) | 2.2.0 | -| [aws](#provider\_aws) | 4.13.0 | +| [aws](#provider\_aws) | 4.23.0 | ## Modules | Name | Source | Version | |--------------------------------------------|-------------------------------------------|---------| -| [s3](#module\_s3) | git@github.com:oozou/terraform-aws-s3.git | v1.0.2 | +| [s3](#module\_s3) | git@github.com:oozou/terraform-aws-s3.git | v1.0.4 | ## Resources @@ -176,19 +116,18 @@ module "lambda_from_local" { | [aws_lambda_permission.allow_serivce](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource | | [aws_s3_object.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource | | [aws_ssm_parameter.params](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource | -| [archive_file.zip_file](https://registry.terraform.io/providers/hashicorp/archive/2.2.0/docs/data-sources/file) | data source | +| [archive_file.this](https://registry.terraform.io/providers/hashicorp/archive/2.2.0/docs/data-sources/file) | data source | | [aws_iam_policy_document.assume_role_policy_doc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.lambda_access_vpc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.lambda_logs_policy_doc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.lambda_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.secret_access_policy_doc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_s3_object.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/s3_object) | data source | ## Inputs | Name | Description | Type | Default | Required | |--------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|:--------:| -| [additional\_lambda\_role\_policy\_arns](#input\_additional\_lambda\_role\_policy\_arns) | List of policies ARNs to attach to the lambda | `list(string)` | `[]` | no | +| [additional\_lambda\_role\_policy\_arns](#input\_additional\_lambda\_role\_policy\_arns) | Map of policies ARNs to attach to the lambda | `map(string)` | `{}` | no | | [bucket\_name](#input\_bucket\_name) | Name of the bucket to put the file in. Alternatively, an S3 access point ARN can be specified. | `string` | `""` | no | | [cloudwatch\_log\_kms\_key\_id](#input\_cloudwatch\_log\_kms\_key\_id) | The ARN for the KMS encryption key. | `string` | `null` | no | | [cloudwatch\_log\_retention\_in\_days](#input\_cloudwatch\_log\_retention\_in\_days) | Retention day for cloudwatch log group | `number` | `90` | no | @@ -197,14 +136,12 @@ module "lambda_from_local" { | [dead\_letter\_target\_arn](#input\_dead\_letter\_target\_arn) | Dead letter queue configuration that specifies the queue or topic where Lambda sends asynchronous events when they fail processing. | `string` | `null` | no | | [environment](#input\_environment) | Environment Variable used as a prefix | `string` | n/a | yes | | [file\_globs](#input\_file\_globs) | list of files or globs that you want included from the source\_code\_dir | `list(string)` | `[]` | no | -| [file\_name](#input\_file\_name) | The compressed file name used to upload to lambda use when is\_upload\_form\_s3 is true | `string` | `""` | no | -| [handler](#input\_handler) | Function entrypoint in your code. | `string` | `"index.handler"` | no | +| [handler](#input\_handler) | Function entrypoint in your code. | `string` | n/a | yes | | [is\_create\_cloudwatch\_log\_group](#input\_is\_create\_cloudwatch\_log\_group) | Whether to create cloudwatch log group or not | `bool` | `true` | no | | [is\_create\_lambda\_bucket](#input\_is\_create\_lambda\_bucket) | Whether to create lambda bucket or not | `bool` | `false` | no | | [is\_create\_lambda\_role](#input\_is\_create\_lambda\_role) | Whether to create lamda role or not | `bool` | `true` | no | | [is\_edge](#input\_is\_edge) | Whether lambda is lambda@Edge or not | `bool` | `false` | no | -| [is\_upload\_form\_s3](#input\_is\_upload\_form\_s3) | Whether to upload the source code from s3 or not | `bool` | `true` | no | -| [lambda\_permission\_configuration](#input\_lambda\_permission\_configuration) | principal - (Required) The principal who is getting this permission e.g., s3.amazonaws.com, an AWS account ID, or any valid AWS service principal such as events.amazonaws.com or sns.amazonaws.com.
source\_arn - (Optional) When the principal is an AWS service, the ARN of the specific resource within that service to grant permission to. Without this, any resource from
source\_account - (Optional) This parameter is used for S3 and SES. The AWS account ID (without a hyphen) of the source owner. | `any` | `{}` | no | +| [lambda\_permission\_configurations](#input\_lambda\_permission\_configurations) | principal - (Required) The principal who is getting this permission e.g., s3.amazonaws.com, an AWS account ID, or any valid AWS service principal such as events.amazonaws.com or sns.amazonaws.com.
source\_arn - (Optional) When the principal is an AWS service, the ARN of the specific resource within that service to grant permission to. Without this, any resource from
source\_account - (Optional) This parameter is used for S3 and SES. The AWS account ID (without a hyphen) of the source owner. | `any` | `{}` | no | | [lambda\_role\_arn](#input\_lambda\_role\_arn) | The arn of role that already created by something to asso with lambda | `string` | `""` | no | | [memory\_size](#input\_memory\_size) | (Optional) Amount of memory in MB your Lambda Function can use at runtime. Defaults to 128. | `number` | `128` | no | | [name](#input\_name) | Name of the ECS cluster to create | `string` | n/a | yes | @@ -216,6 +153,7 @@ module "lambda_from_local" { | [ssm\_params](#input\_ssm\_params) | Lambda@Edge does not support env vars, so it is a common pattern to exchange Env vars for SSM params.
! SECRET

you would have lookups in SSM, like:
`const someEnvValue = await ssmClient.getParameter({ Name: 'SOME_SSM_PARAM_NAME', WithDecryption: true })` | `map(string)` | `{}` | no | | [tags](#input\_tags) | Custom tags which can be passed on to the AWS resources. They should be key value pairs having distinct keys | `map(any)` | `{}` | no | | [timeout](#input\_timeout) | (Optional) Amount of time your Lambda Function has to run in seconds. Defaults to 3. | `number` | `3` | no | +| [tracing\_mode](#input\_tracing\_mode) | Tracing mode of the Lambda Function. Valid value can be either PassThrough or Active. | `string` | `"PassThrough"` | no | | [vpc\_config](#input\_vpc\_config) | For network connectivity to AWS resources in a VPC, specify a list of security groups and subnets in the VPC.
When you connect a function to a VPC, it can only access resources and the internet through that VPC. See VPC Settings.

security\_group\_ids - (Required) List of security group IDs associated with the Lambda function.
subnet\_ids\_to\_associate - (Required) List of subnet IDs associated with the Lambda function. |
object({
security_group_ids = list(string)
subnet_ids_to_associate = list(string)
})
|
{
"security_group_ids": [],
"subnet_ids_to_associate": []
}
| no | ## Outputs diff --git a/examples/complete/README.md b/examples/complete/README.md new file mode 100644 index 0000000..324d415 --- /dev/null +++ b/examples/complete/README.md @@ -0,0 +1,35 @@ + +## Requirements + +| Name | Version | +|---------------------------------------------------------------------------|----------| +| [terraform](#requirement\_terraform) | >= 1.0.0 | +| [aws](#requirement\_aws) | >= 4.0.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|--------------------------------------------------------|--------|---------| +| [lambda](#module\_lambda) | ../../ | n/a | + +## Resources + +No resources. + +## Inputs + +| Name | Description | Type | Default | Required | +|-----------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|------------|---------|:--------:| +| [custom\_tags](#input\_custom\_tags) | Custom tags which can be passed on to the AWS resources. They should be key value pairs having distinct keys | `map(any)` | `{}` | no | +| [environment](#input\_environment) | Environment Variable used as a prefix | `string` | n/a | yes | +| [name](#input\_name) | Name of the ECS cluster and s3 also redis to create | `string` | n/a | yes | +| [prefix](#input\_prefix) | The prefix name of customer to be displayed in AWS console and resource | `string` | n/a | yes | + +## Outputs + +No outputs. + diff --git a/examples/complete/main.tf b/examples/complete/main.tf new file mode 100644 index 0000000..fad5e14 --- /dev/null +++ b/examples/complete/main.tf @@ -0,0 +1,76 @@ +module "lambda" { + source = "../../" + + prefix = var.prefix + environment = var.environment + name = var.name + + is_edge = false # Defautl is `false`, If you want to publish to the edge don't forget to override aws's provider to virgina + + # If is_edge is `false`, ignore this config + is_create_lambda_bucket = true # Default is `false`; plz use false, if not 1 lambda: 1 bucket + bucket_name = "" # If `is_create_lambda_bucket` is `false`; specified this, default is `""` + + # Source code + source_code_dir = "./src" + file_globs = ["index.js"] + compressed_local_file_dir = "./outputs" + + # Lambda Env + runtime = "nodejs12.x" + handler = "index.handler" + + # Lambda Specification + timeout = 3 + memory_size = 128 + reserved_concurrent_executions = -1 + + # Optional to connect Lambda to VPC + vpc_config = { + security_group_ids = ["sg-028f637312eea735e"] + subnet_ids_to_associate = ["subnet-0b853f8c85796d72d", "subnet-07c068b4b51262793", "subnet-0362f68c559ef7716"] + } + dead_letter_target_arn = "arn:aws:sns:ap-southeast-1:557291035693:demo" # To send failed processing to target, Default is `""` + + # IAM + is_create_lambda_role = true # Default is `true` + lambda_role_arn = "" # If `is_create_lambda_role` is `false` + # The policies that you want to attach to IAM Role created by only this module # If `is_create_lambda_role` is `false` + additional_lambda_role_policy_arns = { + allow_lambda_to_read_s3 = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" + } + + # Resource policy + lambda_permission_configurations = { + lambda_on_my_account = { + pricipal = "apigateway.amazonaws.com" + source_arn = "arn:aws:execute-api:ap-southeast-1:557291035112:lk36vflbha/*/*/" + } + lambda_on_my_another_account_wrong = { + pricipal = "apigateway.amazonaws.com" + source_arn = "arn:aws:execute-api:ap-southeast-1:224563527112:q6pwa6wgr6/*/*/" + source_account = "557291035112" + } + lambda_on_my_another_account_correct = { + pricipal = "apigateway.amazonaws.com" + source_arn = "arn:aws:execute-api:ap-southeast-1:557291035112:wpj4t3scmb/*/*/" + } + } + + # Logging + is_create_cloudwatch_log_group = true # Default is `true` + cloudwatch_log_retention_in_days = 90 # Default is `90` + + # Env + ssm_params = {} + plaintext_params = { + region = "ap-southeast-1" + cluster_name = "oozou-dev-test-schedule-cluster" + nodegroup_name = "oozou-dev-test-schedule-custom-nodegroup" + min = 1, + max = 1, + desired = 1 + } + + tags = var.custom_tags +} diff --git a/examples/complete/outputs.tf b/examples/complete/outputs.tf new file mode 100644 index 0000000..e69de29 diff --git a/examples/complete/src/index.js b/examples/complete/src/index.js new file mode 100644 index 0000000..afe164e --- /dev/null +++ b/examples/complete/src/index.js @@ -0,0 +1,18 @@ +var http = require('http') + +exports.handler = (event, context, callback) => { + const options = { + hostname: event.Host, + port: event.Port + } + + const response = {}; + + http.get(options, (res) => { + response.httpStatus = res.statusCode + callback(null, response) + }).on('error', (err) => { + callback(null, err.message); + }) + +}; diff --git a/examples/complete/terraform.example.tfvars b/examples/complete/terraform.example.tfvars new file mode 100644 index 0000000..d47f9fc --- /dev/null +++ b/examples/complete/terraform.example.tfvars @@ -0,0 +1,6 @@ +prefix = "example" +environment = "devops" +name = "cms" +custom_tags = { + "Remark" = "terraform-aws-lambda-example" +} diff --git a/examples/complete/variables.tf b/examples/complete/variables.tf new file mode 100644 index 0000000..648758a --- /dev/null +++ b/examples/complete/variables.tf @@ -0,0 +1,23 @@ +/* -------------------------------------------------------------------------- */ +/* Generics */ +/* -------------------------------------------------------------------------- */ +variable "prefix" { + description = "The prefix name of customer to be displayed in AWS console and resource" + type = string +} + +variable "environment" { + description = "Environment Variable used as a prefix" + type = string +} + +variable "name" { + description = "Name of the ECS cluster and s3 also redis to create" + type = string +} + +variable "custom_tags" { + description = "Custom tags which can be passed on to the AWS resources. They should be key value pairs having distinct keys" + type = map(any) + default = {} +} diff --git a/examples/complete/version.tf b/examples/complete/version.tf new file mode 100644 index 0000000..97f0cf5 --- /dev/null +++ b/examples/complete/version.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.0.0" + } + } +} diff --git a/examples/simple/README.md b/examples/simple/README.md new file mode 100644 index 0000000..324d415 --- /dev/null +++ b/examples/simple/README.md @@ -0,0 +1,35 @@ + +## Requirements + +| Name | Version | +|---------------------------------------------------------------------------|----------| +| [terraform](#requirement\_terraform) | >= 1.0.0 | +| [aws](#requirement\_aws) | >= 4.0.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|--------------------------------------------------------|--------|---------| +| [lambda](#module\_lambda) | ../../ | n/a | + +## Resources + +No resources. + +## Inputs + +| Name | Description | Type | Default | Required | +|-----------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|------------|---------|:--------:| +| [custom\_tags](#input\_custom\_tags) | Custom tags which can be passed on to the AWS resources. They should be key value pairs having distinct keys | `map(any)` | `{}` | no | +| [environment](#input\_environment) | Environment Variable used as a prefix | `string` | n/a | yes | +| [name](#input\_name) | Name of the ECS cluster and s3 also redis to create | `string` | n/a | yes | +| [prefix](#input\_prefix) | The prefix name of customer to be displayed in AWS console and resource | `string` | n/a | yes | + +## Outputs + +No outputs. + diff --git a/examples/simple/main.tf b/examples/simple/main.tf new file mode 100644 index 0000000..998974d --- /dev/null +++ b/examples/simple/main.tf @@ -0,0 +1,45 @@ +module "lambda" { + source = "../../" + + prefix = var.prefix + environment = var.environment + name = var.name + + source_code_dir = "./src" + file_globs = ["index.js"] + compressed_local_file_dir = "./outputs" + + runtime = "nodejs12.x" + handler = "index.handler" + + additional_lambda_role_policy_arns = { + allow_lambda_to_read_s3 = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" + } + lambda_permission_configurations = { + lambda_on_my_account = { + pricipal = "apigateway.amazonaws.com" + source_arn = "arn:aws:execute-api:ap-southeast-1:557291035112:lk36vflbha/*/*/" + } + lambda_on_my_another_account_wrong = { + pricipal = "apigateway.amazonaws.com" + source_arn = "arn:aws:execute-api:ap-southeast-1:224563527112:q6pwa6wgr6/*/*/" + source_account = "557291035112" + } + lambda_on_my_another_account_correct = { + pricipal = "apigateway.amazonaws.com" + source_arn = "arn:aws:execute-api:ap-southeast-1:557291035112:wpj4t3scmb/*/*/" + } + } + + ssm_params = {} + plaintext_params = { + region = "ap-southeast-1" + cluster_name = "oozou-dev-test-schedule-cluster" + nodegroup_name = "oozou-dev-test-schedule-custom-nodegroup" + min = 1, + max = 1, + desired = 1 + } + + tags = var.custom_tags +} diff --git a/examples/simple/outputs.tf b/examples/simple/outputs.tf new file mode 100644 index 0000000..e69de29 diff --git a/examples/simple/src/index.js b/examples/simple/src/index.js new file mode 100644 index 0000000..afe164e --- /dev/null +++ b/examples/simple/src/index.js @@ -0,0 +1,18 @@ +var http = require('http') + +exports.handler = (event, context, callback) => { + const options = { + hostname: event.Host, + port: event.Port + } + + const response = {}; + + http.get(options, (res) => { + response.httpStatus = res.statusCode + callback(null, response) + }).on('error', (err) => { + callback(null, err.message); + }) + +}; diff --git a/examples/simple/terraform.example.tfvars b/examples/simple/terraform.example.tfvars new file mode 100644 index 0000000..d47f9fc --- /dev/null +++ b/examples/simple/terraform.example.tfvars @@ -0,0 +1,6 @@ +prefix = "example" +environment = "devops" +name = "cms" +custom_tags = { + "Remark" = "terraform-aws-lambda-example" +} diff --git a/examples/simple/variables.tf b/examples/simple/variables.tf new file mode 100644 index 0000000..648758a --- /dev/null +++ b/examples/simple/variables.tf @@ -0,0 +1,23 @@ +/* -------------------------------------------------------------------------- */ +/* Generics */ +/* -------------------------------------------------------------------------- */ +variable "prefix" { + description = "The prefix name of customer to be displayed in AWS console and resource" + type = string +} + +variable "environment" { + description = "Environment Variable used as a prefix" + type = string +} + +variable "name" { + description = "Name of the ECS cluster and s3 also redis to create" + type = string +} + +variable "custom_tags" { + description = "Custom tags which can be passed on to the AWS resources. They should be key value pairs having distinct keys" + type = map(any) + default = {} +} diff --git a/examples/simple/version.tf b/examples/simple/version.tf new file mode 100644 index 0000000..97f0cf5 --- /dev/null +++ b/examples/simple/version.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.0.0" + } + } +} diff --git a/main.tf b/main.tf index 9a4d9da..40f4626 100644 --- a/main.tf +++ b/main.tf @@ -4,10 +4,12 @@ locals { name = format("%s-%s-%s", var.prefix, var.environment, var.name) - lambda_role_arn = var.is_create_lambda_role ? aws_iam_role.this[0].arn : var.lambda_role_arn - bucket_name = var.is_upload_form_s3 ? var.bucket_name : var.is_create_lambda_bucket ? element(module.s3[*].bucket_name, 0) : var.bucket_name - object_key = var.is_upload_form_s3 ? data.aws_s3_object.this[0].key : aws_s3_object.this[0].id - object_version_id = var.is_upload_form_s3 ? data.aws_s3_object.this[0].version_id : aws_s3_object.this[0].version_id + lambda_role_arn = var.is_create_lambda_role ? aws_iam_role.this[0].arn : var.lambda_role_arn + + file_name = var.is_edge ? null : data.archive_file.this.output_path + bucket_name = var.is_edge ? var.is_create_lambda_bucket ? module.s3[0].bucket_name : var.bucket_name : null + object_key = var.is_edge ? aws_s3_object.this[0].id : null + object_version_id = var.is_edge ? aws_s3_object.this[0].version_id : null tags = merge( { @@ -21,20 +23,15 @@ locals { locals { raise_is_lambda_role_arn_empty = var.is_create_lambda_role == false && var.lambda_role_arn == "" ? file("Variable `lambda_role_arn` is required when `is_create_lambda_role` is false") : "pass" - raise_bucket_name_empty = var.is_upload_form_s3 && length(var.bucket_name) == 0 ? file("Variable `bucket_name` is required when `is_upload_form_s3` is true") : "pass" - raise_file_name_empty = var.is_upload_form_s3 && length(var.file_name) == 0 ? file("Variable `file_name` is required when `is_upload_form_s3` is true") : "pass" - - raise_compressed_local_file_dir_empty = var.is_upload_form_s3 == false && length(var.compressed_local_file_dir) == 0 ? file("Variable `compressed_local_file_dir` is required when `is_upload_form_s3` is false") : "pass" - raise_file_globs_empty = var.is_upload_form_s3 == false && length(var.file_globs) == 0 ? file("Variable `file_globs` is required when `is_upload_form_s3` is false") : "pass" + raise_bucket_name_empty = var.is_edge && var.is_create_lambda_bucket == false && length(var.bucket_name) == 0 ? file("Variable `bucket_name` is required when `is_create_lambda_bucket` is false") : "pass" + raise_local_file_dir_empty = length(var.compressed_local_file_dir) == 0 ? file("Variable `compressed_local_file_dir` is required") : "pass" + raise_file_globs_empty = length(var.file_globs) == 0 ? file("Variable `file_globs` is required") : "pass" } /* -------------------------------------------------------------------------- */ -/* S3 */ +/* Zip File */ /* -------------------------------------------------------------------------- */ -/* -------------------------------- ZIP File -------------------------------- */ -data "archive_file" "zip_file" { - count = var.is_upload_form_s3 == false ? 1 : 0 - +data "archive_file" "this" { type = "zip" output_path = format("%s/%s.zip", var.compressed_local_file_dir, local.name) @@ -63,10 +60,13 @@ data "archive_file" "zip_file" { } } +/* -------------------------------------------------------------------------- */ +/* S3 */ +/* -------------------------------------------------------------------------- */ module "s3" { - count = var.is_create_lambda_bucket && var.is_upload_form_s3 == false ? 1 : 0 + count = var.is_edge && var.is_create_lambda_bucket ? 1 : 0 - source = "git@github.com:oozou/terraform-aws-s3.git?ref=v1.0.2" + source = "git@github.com:oozou/terraform-aws-s3.git?ref=v1.0.4" prefix = var.prefix environment = var.environment @@ -80,20 +80,13 @@ module "s3" { tags = var.tags } -data "aws_s3_object" "this" { - count = var.is_upload_form_s3 ? 1 : 0 - - bucket = local.bucket_name - key = var.file_name -} - resource "aws_s3_object" "this" { - count = var.is_upload_form_s3 == false ? 1 : 0 + count = var.is_edge && var.is_create_lambda_bucket ? 1 : 0 - bucket = var.is_create_lambda_bucket ? element(module.s3[*].bucket_name, 0) : var.bucket_name + bucket = element(module.s3[*].bucket_name, 0) key = format("%s.zip", local.name) - source = data.archive_file.zip_file[0].output_path - etag = data.archive_file.zip_file[0].output_md5 + source = data.archive_file.this.output_path + etag = data.archive_file.this.output_md5 tags = merge(local.tags, { "Name" = format("%s.zip", local.name) }) } @@ -102,7 +95,7 @@ resource "aws_s3_object" "this" { /* Resource Based Policy */ /* -------------------------------------------------------------------------- */ resource "aws_lambda_permission" "allow_serivce" { - for_each = var.lambda_permission_configuration + for_each = var.lambda_permission_configurations statement_id = format("AllowExecutionFrom-%s", each.key) action = "lambda:InvokeFunction" @@ -199,7 +192,7 @@ resource "aws_iam_role_policy" "logs_role_policy" { } resource "aws_iam_role_policy_attachment" "this" { - for_each = var.is_create_lambda_role ? toset(var.additional_lambda_role_policy_arns) : toset([]) + for_each = var.is_create_lambda_role ? var.additional_lambda_role_policy_arns : {} role = aws_iam_role.this[0].name policy_arn = each.value @@ -219,7 +212,7 @@ resource "aws_ssm_parameter" "params" { type = "SecureString" tier = length(each.value) > 4096 ? "Advanced" : "Standard" - tags = var.tags + tags = local.tags } data "aws_iam_policy_document" "secret_access_policy_doc" { @@ -261,16 +254,27 @@ resource "aws_lambda_function" "this" { function_name = format("%s-function", local.name) description = format("Lambda function: %s", local.name) - # Read the file from s3 + # Read source code from s3 s3_bucket = local.bucket_name s3_key = local.object_key s3_object_version = local.object_version_id + # Read source code from local + filename = local.file_name + source_code_hash = filebase64sha256(data.archive_file.this.output_path) + # Specification timeout = var.timeout memory_size = var.memory_size reserved_concurrent_executions = var.reserved_concurrent_executions + # Code Env + publish = true # Force public new version + runtime = var.runtime + handler = var.handler + + role = local.lambda_role_arn + vpc_config { security_group_ids = var.vpc_config.security_group_ids subnet_ids = var.vpc_config.subnet_ids_to_associate @@ -284,17 +288,11 @@ resource "aws_lambda_function" "this" { } } - # Code Env - publish = true # Force public new version - runtime = var.runtime - handler = var.handler - - role = local.lambda_role_arn - - lifecycle { - ignore_changes = [ - last_modified, - ] + dynamic "tracing_config" { + for_each = var.tracing_mode == null ? [] : [true] + content { + mode = var.tracing_mode + } } tags = merge(local.tags, { "Name" = format("%s-function", local.name) }) diff --git a/variables.tf b/variables.tf index 72f716f..b2101b8 100644 --- a/variables.tf +++ b/variables.tf @@ -1,8 +1,8 @@ /* -------------------------------------------------------------------------- */ /* Generic */ /* -------------------------------------------------------------------------- */ -variable "name" { - description = "Name of the ECS cluster to create" +variable "prefix" { + description = "The prefix name of customer to be displayed in AWS console and resource" type = string } @@ -11,8 +11,8 @@ variable "environment" { type = string } -variable "prefix" { - description = "The prefix name of customer to be displayed in AWS console and resource" +variable "name" { + description = "Name of the ECS cluster to create" type = string } @@ -66,7 +66,7 @@ variable "config_file_name" { /* -------------------------------------------------------------------------- */ /* Resource Based Policy */ /* -------------------------------------------------------------------------- */ -variable "lambda_permission_configuration" { +variable "lambda_permission_configurations" { description = <