-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ba44089
commit bffec85
Showing
8 changed files
with
331 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
# terraform-aws-lambda-shell | ||
Runs an arbitrary shell command in a Lambda function. | ||
# Terraform Lambda Shell | ||
|
||
This module creates a Lambda function that can run arbitrary shell commands. It is intended to be used with the [Invicton-Labs/lambda-shell-data/aws](https://registry.terraform.io/modules/Invicton-Labs/lambda-shell-data/aws/latest) and [Invicton-Labs/lambda-shell-resource/aws](https://registry.terraform.io/modules/Invicton-Labs/lambda-shell-resource/aws/latest) modules. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import subprocess | ||
import uuid | ||
import os | ||
|
||
import logging | ||
|
||
logger = logging.getLogger() | ||
logger.setLevel(logging.DEBUG) | ||
|
||
|
||
def lambda_handler(event, context): | ||
logger.debug("Input event: {}".format(event)) | ||
|
||
# If a special flag is set, skip the execution | ||
if '__IL_TF_LS_SKIP_RUN' in event: | ||
return {} | ||
|
||
cmd = event['interpreter'] | ||
if event['command'] is not None: | ||
# Create a unique file for the script to be temporarily stored in | ||
scriptpath = "/tmp/botoform-{}".format(uuid.uuid1()) | ||
f = open(scriptpath, "x") | ||
# Write the script to the file | ||
f.write(event['command']) | ||
f.close() | ||
cmd.append(scriptpath) | ||
|
||
# Run the command as a subprocess | ||
logger.info("Running command: {}".format(cmd)) | ||
result = subprocess.run(cmd, shell=False, capture_output=True) | ||
|
||
logger.debug("Result: {}".format(result)) | ||
|
||
if event['command'] is not None: | ||
os.remove(scriptpath) | ||
|
||
stdout = result.stdout.decode('utf-8') | ||
stderr = result.stderr.decode('utf-8') | ||
if result.returncode != 0 and event['fail_on_error']: | ||
raise subprocess.SubprocessError( | ||
"Command returned non-zero exit code ({}) with stdout '{}' and stderr '{}'".format(result.returncode, stdout, stderr)) | ||
|
||
return { | ||
'exitstatus': result.returncode, | ||
'stdout': stdout, | ||
'stderr': stderr | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
resource "random_id" "lambda" { | ||
byte_length = 14 | ||
} | ||
|
||
locals { | ||
// If no role/policy info is given, try using the current caller's role | ||
lambda_role = var.lambda_role_arn == null && length(var.lambda_role_policies_json) == 0 && length(var.lambda_role_policy_arns) == 0 ? local.caller_role_arn : var.lambda_role_arn | ||
} | ||
|
||
// Create the Lambda that will execute the shell commands | ||
module "shell_lambda" { | ||
depends_on = [ | ||
module.assert_role_present.checked, | ||
module.assert_single_body.checked | ||
] | ||
source = "Invicton-Labs/lambda-set/aws" | ||
version = "0.4.1" | ||
edge = false | ||
source_directory = "${path.module}/lambda" | ||
archive_output_directory = "${path.module}/archives/" | ||
lambda_config = { | ||
function_name = "invicton-labs-aws-lambda-shell-${random_id.lambda.hex}" | ||
handler = "main.lambda_handler" | ||
runtime = "python3.8" | ||
timeout = var.lambda_timeout | ||
memory_size = var.lambda_memory_size | ||
role = local.lambda_role | ||
tags = { | ||
"ModuleAuthor" = "InvictonLabs" | ||
"ModuleUrl" = "https://registry.terraform.io/modules/Invicton-Labs/lambda-shell/aws" | ||
} | ||
} | ||
role_policies = var.lambda_role_policies_json | ||
role_policy_arns = var.lambda_role_policy_arns | ||
logs_lambda_subscriptions = var.lambda_logs_lambda_subscriptions | ||
logs_non_lambda_subscriptions = var.lambda_logs_non_lambda_subscriptions | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
output "invicton_labs_lambda_shell_arn" { | ||
value = module.shell_lambda.lambda.arn | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
variable "lambda_timeout" { | ||
description = "The timeout (in seconds) for the Lambda function that is running the shell command." | ||
type = number | ||
default = 30 | ||
} | ||
|
||
variable "lambda_memory_size" { | ||
description = "The memory size (in MB) for the Lambda function that is running the shell command." | ||
type = number | ||
default = 128 | ||
} | ||
|
||
variable "lambda_role_arn" { | ||
description = "The ARN of the role to use for the Lambda that runs shell commands. If this value is provided, a new role will not be created. Conflicts with `lambda_role_policy_json`. If neither is provided, the module will attempt to use the role that the Terraform caller has assumed (if a role has been assumed)." | ||
type = string | ||
default = null | ||
} | ||
|
||
variable "lambda_role_policies_json" { | ||
description = "A list of JSON-encoded policies to apply to a new role that will be created for the Lambda that runs shell commands. Conflicts with `lambda_role_arn`. If neither is provided, the module will attempt to use the role that the Terraform caller has assumed (if a role has been assumed)." | ||
type = list(string) | ||
default = [] | ||
} | ||
|
||
variable "lambda_role_policy_arns" { | ||
description = "A list of IAM policy ARNs to apply to a new role that will be created for the Lambda that runs shell commands. Conflicts with `lambda_role_arn`. If neither is provided, the module will attempt to use the role that the Terraform caller has assumed (if a role has been assumed)." | ||
type = list(string) | ||
default = [] | ||
} | ||
|
||
variable "lambda_logs_lambda_subscriptions" { | ||
description = "A list of configurations for Lambda subscriptions to the CloudWatch Logs Group for the Lambda function that runs shell commands. Each element should be a map with `destination_arn` (required), `name` (optional), `filter_pattern` (optional), and `distribution` (optional)." | ||
type = list(object({ | ||
destination_arn = string | ||
name = optional(string) | ||
filter_pattern = optional(string) | ||
distribution = optional(string) | ||
})) | ||
default = [] | ||
} | ||
|
||
variable "lambda_logs_non_lambda_subscriptions" { | ||
description = "A list of configurations for non-Lambda subscriptions to the CloudWatch Logs Group for the Lambda function that runs shell commands. Each element should be a map with `destination_arn` (required), `name` (optional), `filter_pattern` (optional), `role_arn` (optional), and `distribution` (optional)." | ||
type = list(object({ | ||
destination_arn = string | ||
name = optional(string) | ||
filter_pattern = optional(string) | ||
role_arn = optional(string) | ||
distribution = optional(string) | ||
})) | ||
default = [] | ||
} | ||
|
||
data "aws_caller_identity" "current" {} | ||
|
||
data "aws_arn" "role" { | ||
arn = data.aws_caller_identity.current.arn | ||
} | ||
|
||
locals { | ||
caller_role_arn = substr(data.aws_arn.role.resource, 0, 5) == "role/" ? data.aws_caller_identity.current.arn : (substr(data.aws_arn.role.resource, 0, 13) == "assumed-role/" ? "arn:${data.aws_arn.role.partition}:iam::${data.aws_arn.role.account}:role/${split("/", data.aws_arn.role.resource)[1]}" : null) | ||
} | ||
|
||
module "assert_role_present" { | ||
source = "Invicton-Labs/assertion/null" | ||
version = "0.2.1" | ||
condition = var.lambda_role_arn != null || length(var.lambda_role_policies_json) > 0 || length(var.lambda_role_policy_arns) > 0 || local.caller_role_arn != null | ||
error_message = "One of the `lambda_role_arn`, `lambda_role_policies_json`, or `lambda_role_policy_arns` input parameters must be provided, or this module must be called from a Terraform configuration that has assumed a role." | ||
} | ||
module "assert_single_body" { | ||
source = "Invicton-Labs/assertion/null" | ||
version = "0.2.1" | ||
condition = var.lambda_role_arn == null || (length(var.lambda_role_policies_json) == 0 && length(var.lambda_role_policy_arns) == 0) | ||
error_message = "The `lambda_role_arn` cannot be provided when either the `lambda_role_policies_json` or `lambda_role_policy_arns` input parameter is provided." | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
terraform { | ||
required_version = ">= 1.0.0" | ||
experiments = [module_variable_optional_attrs] | ||
} |