Showing posts with label Lambda. Show all posts
Showing posts with label Lambda. Show all posts

Monday, July 26, 2021

Creating AWS Lambda Layer

Creating AWS Lambda Layer

How to create AWS Lambda Layer (for Python)

From Library

Be sure to work from Python Virtual Environment. Always work from venv. Follow this link. You can also see here.

1. Install Libraries that you want

python -m pip install requests

2. Show it's dependencies (you'll also see it being installed). You'll see Requires line with certifi, urllib3, charset-normalizer, idna

pip show requests

3. Open the folder /Lib/site-packages

4. Copy all the listed Requires packages into a separate directory called python

5. Zip up this folder. Resulting zip and content




6. Terraform Code to use 

locals{
    layer_file = "${path.module}/requests_2_26_0.zip"
}

resource "aws_lambda_layer_version" "requests" {
  filename            = local.layer_file
  layer_name          = "requests"
  compatible_runtimes = ["python3.8"]
  source_code_hash    = data.archive_file.requests.output_base64sha256
}


From Custom Code

1. Create a new folder called python
2. Drop your python code into this folder
3. Create a zip file where python is at root
4. Terraform Code to use (this code will handle the zipping). Your python code should be located at /layer_code/python/my_code.py
locals{
    layer_file = "${path.module}/zips/new_layer.zip"
}
data "archive_file" "requests" {
  type             = "zip"
  output_path      = local.layer_file
  source_dir       = "${path.module}/layer_code"
  output_file_mode = "0666"
}
resource "aws_lambda_layer_version" "new_layer" {
  filename            = local.layer_file
  layer_name          = "new_layer"
  compatible_runtimes = ["python3.8"]
  source_code_hash    = data.archive_file.requests.output_base64sha256
}

Monday, July 19, 2021

AWS Security Hub Auto Remediation

 AWS Security Hub Auto Remediation

In this guide, we configure Security Hub account to be able to take action on any other accounts in the Organization. This is all triggered from CloudWatch Event Pattern. The result looks like this.




Cloudwatch Event Pattern

Here's an example of the CW Event Pattern. 

resource "aws_cloudwatch_event_rule" "example" {
  name        = "Example"
  description = "Example"

  event_pattern = <<EOF
{
    "source" : [
      "aws.securityhub"
    ],
    "detail-type" : [
      "Security Hub Findings - Imported"
    ],
    "detail" : {
      "findings" : {
         "GeneratorId":["arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0/rule/4.3"],
         "Compliance":{
            "Status":["FAILED"]
        }
      }
    }
  }
EOF
}

Cloudwatch Event Targets

SNS

Pass in a Topic arn and enable this target. 

resource "aws_cloudwatch_event_target" "cloudwatch_event_target_sns" {
  count = local.sns_arn == null ? 0:1
  ## [\.\-_A-Za-z0-9]+, 64 characters max
  rule = aws_cloudwatch_event_rule.cloudwatch_event_rule.name
  ## some unique assignment ID, random will be assigned since not provided
  ## target_id
  ## This is the ARN of the target regardless of the type
  arn = local.sns_arn
}

Be sure the SNS topic permission include this 

{
 "Sid": "allow_from_events",
 "Effect": "Allow",
 "Principal": {
   "Service": "events.amazonaws.com"
 },
 "Action": "SNS:Publish",
 "Resource": "arn:aws:sns:us-east-1:99999999999:security-hub-topic"
}

Lambda

Point to Lambda Function arn and enable this target.

resource "aws_cloudwatch_event_target" "cloudwatch_event_target_lambda" {
  count = local.lambda_arn == null ? 0:1
  ## [\.\-_A-Za-z0-9]+, 64 characters max
  rule = aws_cloudwatch_event_rule.cloudwatch_event_rule.name
  ## some unique assignment ID, random will be assigned since not provided
  ## target_id
  ## This is the ARN of the target regardless of the type
  arn = local.lambda_arn
}

Lambda Function

Create the lambda function here

resource "aws_lambda_function" "default" {
  ## ([a-zA-Z0-9-_]+)
  function_name    = local.rule_name
  filename         = local.lambda_zip_file
  role             = local.lambda_role_arn
  source_code_hash = local.lambda_hash

  description = "blah" 
  handler     = "lambda_function.lambda_handler" 
  runtime     = "python3.8"
  timeout     = 60
  memory_size = 128
}

Lambda Role

Permission Policy

data "aws_iam_policy_document" "lambda_role_basic" {
  ## Create log group
  statement {
    sid       = "SidLogGroup"
    actions   = ["logs:CreateLogGroup"]
    resources = ["arn:aws:logs:*:${local.self_account_id}:*"]
  }

  ## Create log stream
  statement {
    sid = "SidStream"
    actions = [
      "logs:PutLogEvents",
      "logs:CreateLogStream"
    ]
    resources = ["arn:aws:logs:*:${local.self_account_id}:log-group:/aws/lambda/${var.rule_name_prefix}*:*"]
  }

  ## Allow this role to assume target roles in other accounts
  statement {
    sid       = "SidAssumeAccountRoles"
    actions   = ["sts:AssumeRole"]
    resources = ["arn:aws:iam::*:role/${var.target_role_name}"]
  }
}

resource "aws_iam_policy" "lambda_role_basic" {
  name   = "${var.target_role_name}-policy"
  policy = data.aws_iam_policy_document.lambda_role_basic.json
}

Assume Role Policy

data "aws_iam_policy_document" "lambda_role_assume_policy" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["lambda.amazonaws.com"]
    }
  }
}

Role

resource "aws_iam_role" "lambda_role" {
  ## Create a new role and start with Assume Role Policy to let it be used by Lambda
  ## This is the role that is added to Lambda above
  name               = var.lambda_role_name
  assume_role_policy = data.aws_iam_policy_document.lambda_role_assume_policy.json
}

resource "aws_iam_role_policy_attachment" "lambda_role_basic" {
  ## Attach the permission policy defined above
  role       = aws_iam_role.lambda_role.name
  policy_arn = aws_iam_policy.lambda_role_basic.arn
}

Target Account's Role

Assume Role Policy

data "aws_iam_policy_document" "security_hub_assume_role_policy" {
  ## Allow the role from Main Account to assume this role
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "AWS"
      identifiers = ["arn:aws:iam::${local.sechub_account_id}:role/${local.security_hub_lambda_role_name}"]
    }
  }
}

Role

resource "aws_iam_role" "security_hub_role" {
  ## Create a new role, start with Assume Role Policy
  name               = local.security_hub_role_name
  assume_role_policy = data.aws_iam_policy_document.security_hub_assume_role_policy.json
}

Permission Policies

  • Give SecHub account permission to take action inside this account when something triggers in SecHub
  • This role needs to be assumable from SecHub account
  • This role needs to do whatever we need to do to remediate any SecHub findings
  • Can only have up to 20 attached policies to a role (10 is default, 20 is when upped the limit to max), so be wise how you split this
  • Each policy can only be up to 5000 characters (1500 is default, 5000 is when upped the limit to max), so be wise how you word your policy
  • The roles names of assumed and assumer needs to match exactly

ReadOnly Policy

resource "aws_iam_role_policy_attachment" "security_hub_read_policy" {
  ## Use built-in policy for this
  role       = aws_iam_role.security_hub_role.id
  policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}

Ability to Update Security Hub Finding Policy

data "aws_iam_policy_document" "security_hub_edit_sechub_policy_doc" {
  statement {
    actions = [
      "securityhub:CreateActionTarget",
      "securityhub:UpdateFindings",
      "securityhub:BatchDisableStandards",
    ]
    resources = ["arn:aws:securityhub:*:${local.account_id}:hub/default"]
  }
}

resource "aws_iam_policy" "security_hub_edit_sechub_policy" {
  name   = "Security-hub-edit-sechub-policy"
  path   = "/"
  policy = data.aws_iam_policy_document.security_hub_edit_sechub_policy_doc.json
}

resource "aws_iam_role_policy_attachment" "security_hub_edit_sechub_policy_attach" {
  role       = aws_iam_role.security_hub_role.id
  policy_arn = aws_iam_policy.security_hub_edit_sechub_policy.arn
}

Ability to Edit IAM Policy

data "aws_iam_policy_document" "security_hub_edit_IAM_policy_doc" {
  statement {
    actions = [
      "IAM:DeleteAccessKey",
      "IAM:Detach*",
      "IAM:DeleteUserPolicy"
    ]
    resources = ["*"]
  }
}

resource "aws_iam_policy" "security_hub_edit_IAM_policy" {
  name   = "Security-hub-edit-IAM-policy"
  path   = "/"
  policy = data.aws_iam_policy_document.security_hub_edit_IAM_policy_doc.json
}

resource "aws_iam_role_policy_attachment" "security_hub_edit_IAM_policy_attach" {
  role       = aws_iam_role.security_hub_role.id
  policy_arn = aws_iam_policy.security_hub_edit_IAM_policy.arn
}





















Thursday, April 2, 2020

AWS Custom Config Rule from Org

Deploy AWS Config Rule from Org

If you have AWS Org configured, you can deploy Config Rule from a single location out to all the accounts in the same Org. You can do this for both AWS Managed and your Custom Rules. 

References:

Details:
  • Max 150 rules
  • All necessary permissions and roles must be pre-configured
Advantages:
  • Deploy from central location
  • Does not allow accounts from editing the rules

Setup

Setup Root Account

  • Configure Aggregator
  • Configure S3 Logging Bucket for Config* 
  • Configure SNS Topic*
  • Configure Lambda Role
    • Your usual Lambda Role permissions
    • Ability to Assume Role to the Recorder Role name in all accounts (this can be extracted from "executionRoleArn" of the Event object)
  • Configure Lambda Function
    • Need to allow this function to be executed by all accounts in this Org

Setup All Accounts

  • Configure Recorder Role 
    • use same name across all accounts
    • need necessary permission to 
      • write to Config 
      • write to S3 Logging Bucket in Root Account*
      • write to SNS Topic in Root Account*
      • Trust Lambda Role in Root Account to AssumeRole
      • and read/edit necessary resources for it to do what you want
  • Turn on Recording
*The S3 and SNS does not have to be in Root Account, just put it somewhere common

Execution



Wednesday, March 25, 2020

AWS Custom Config Cross-Account

How to AWS Custom Config Cross-Account

Lambda - Python

Account 999999999
  • This is where you'll host the Lambda code
Account 888888888
  • This is where you'll host the Config Rule

Account 999999999

Follow the previous instruction for the Python code, update the lambda_handler with following:

def lambda_handler(event, context):
    evaluations = []
    test_mode = False
    thisRegion = 'us-east-1'
    print('Event: ',event)
    invoking_event = json.loads(event["invokingEvent"])
    print('Invoking Event: ',invoking_event)
    notification_time = invoking_event["notificationCreationTime"]
    print('Invoked Time: ', notification_time)
    result_token = event["resultToken"]
    print('Result Token: ', result_token)
    # In our test event, we have defined the result_token to be XYZ
    if result_token == 'XYZ':
        test_mode = True
        
    # this is passed to us from Config Rule, if this exists, then Assume Role
    ruleParameters = json.loads(event["ruleParameters"])
    if 'executionRole' in ruleParameters.keys():
        print('Assume Role')
        executionRole = ruleParameters["executionRole"]
        print(executionRole)
        sts_client = boto3.client('sts')
        assume_role_response = sts_client.assume_role(RoleArn=executionRole, RoleSessionName="configLambdaExecution")
        credentials = assume_role_response['Credentials']
        config = boto3.client("config", region_name=thisRegion,
                        aws_access_key_id=credentials['AccessKeyId'],
                        aws_secret_access_key=credentials['SecretAccessKey'],
                        aws_session_token=credentials['SessionToken']
                       )
        
    else:
        credentials = []
        config = boto3.client("config")
        print('Self Run')
    # pass in credential and time stamp and get back eval result
    evaluations = evaluate_compliance(notification_time, credentials)
    print(evaluations)
    print(result_token)
    result = config.put_evaluations(
            Evaluations = evaluations,
            ResultToken = result_token,
            TestMode = test_mode
            )
    
    metaData = result["ResponseMetadata"]
    statusCode = metaData["HTTPStatusCode"]
    return {
        'statusCode': statusCode,
        'body': json.dumps(result)
    }

Be sure your execution role for this function has at least the following. The first statement allows the function to send logs to cloudwatch. The second statement allows this function to assume the role of Config_Role from another account.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:us-east-1:999999999:log-group:/aws/lambda/config_test:*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:us-east-1:999999999:*"
        }
    ]
}

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::888888888:role/config_role
        }
    ]
}

Add a Resource-based policy to your Lambda function so that it can be used by another account. This can only be done via CLI or API call.
Run this CLI command using credential of account 99999999

aws lambda add-permission 
  --function-name config_test
  --region us-east-1 
  --statement-id 1001 
  --action "lambda:InvokeFunction" 
  --principal config.amazonaws.com 
  --source-account 888888888 


Account 888888888

Create a new role in this account with at least following permission. Be sure to have specific permission so that you can evaluate your resources.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "config:PutEvaluations"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

The new role also needs trust relationship to ALLOW it be assumed by the Lambda function

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::999999999:role/service-role/lambdaConfigRole"
      },
      "Action": "sts:AssumeRole",
      "Condition": {}
    }
  ]
}

Now create the new Config Rule
Provide the Lambda function that you created in account 99999999:


Set your trigger type and resource type(s).

Fill out the Rule parameter with the role that you want the Lambda function to use:

Done. Now try running the config rule manually by clicking on the blue Re-evaluate button in account 8888888888 and watch the Cloudwatch logs in account 999999999.

Monday, March 23, 2020

AWS Config Custom Rule

How to AWS Custom Config 

Lambda - Python

Want to know how to setup your lambda Python function for AWS Custom Config? Here's what you need to know to get started. 

Here's a really simplified version of the Python code that you need to get started. You should add some try and catch errors and parse whatever input you are looking for.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def lambda_handler(event, context):
    # you can check what invoked it
    invoking_event = json.loads(event["invokingEvent"])
    # you only need this for resource change trigger
    configuration_item = invoking_event["configurationItem"]
    # you can check what rule parameters were passed in
    rule_parameters = json.loads(event["ruleParameters"])
 
    compliant, annotation = evaluate_compliance(some_argument)

    config = boto3.client("config")
    # this example assumes resource trigger, for time trigger, you can have the above function spit out a list of resources
    config.put_evaluations(
    Evaluations=[
            {
                "ComplianceResourceType": configuration_item["resourceType"],
                "ComplianceResourceId": configuration_item["resourceId"],
                "ComplianceType": compliant, 
                "Annotation": annotation,
                "OrderingTimestamp": configuration_item["configurationItemCaptureTime"]
            },
        ],
        ResultToken=result_token,
    )    
 
 
def evaluate_compliance(argument):
    compliant = "COMPLIANT"
    annotations = []
 if blah: 
  compliant = "COMPLIANT"
  annotations.append("blah")
 else:
  compliant = "NON_COMPLIANT"
  annotations.append("different blah")
 return compliant, " ".join(annotations)

To understand how to fill in the logic of your code, you can look at what you get in the event payload.

Inputs


Sample scheduled trigger:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
   "version":"1.0",
   "invokingEvent":"{\"awsAccountId\":\"999999999\",\"notificationCreationTime\":\"2020-03-23T02:42:51.511Z\",\"messageType\":\"ScheduledNotification\",\"recordVersion\":\"1.0\"}",
   "ruleParameters":"{\"executionRole\":\"arn:aws:iam::9999:role/blah\"}",
   "resultToken":"XYZ",
   "eventLeftScope":false,
   "executionRoleArn":"arn:aws:iam::9999999:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig",
   "configRuleArn":"arn:aws:config:us-east-2:999999999:config-rule/config-rule-kfabou",
   "configRuleName":"my-test-rule1",
   "configRuleId":"config-rule-kfabou",
   "accountId":"9999999999"
}
Above's invoking event:
1
2
3
4
5
6
{
   "awsAccountId":"999999999",
   "notificationCreationTime":"2020-03-23T02:42:51.511Z",
   "messageType":"ScheduledNotification",
   "recordVersion":"1.0"
}
Rule parameter is the values you provide in Key:Value pair under your Config rule setup



Sample resource change trigger:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
   "version":"1.0",
   "invokingEvent":"{\"configurationItemDiff\":null,\"configurationItem\":{\"relatedEvents\":[],\"relationships\":[{\"resourceId\":\"ANPASP666ZVAXGDCE5SDN\",\"resourceName\":\"Read_Only_EC2\",\"resourceType\":\"AWS::IAM::Policy\",\"name\":\"Is attached to CustomerManagedPolicy\"}],\"configuration\":{\"path\":\"/\",\"userName\":\"FoxySugar\",\"userId\":\"AIDASP666ZVA36XQGO6DD\",\"arn\":\"arn:aws:iam::9999999999:user/FoxySugar\",\"createDate\":\"2020-01-07T14:06:13.000Z\",\"userPolicyList\":[],\"groupList\":[],\"attachedManagedPolicies\":[{\"policyName\":\"Read_Only_EC2\",\"policyArn\":\"arn:aws:iam::9999999:policy/Read_Only_EC2\"}],\"permissionsBoundary\":null,\"tags\":[]},\"supplementaryConfiguration\":{},\"tags\":{},\"configurationItemVersion\":\"1.3\",\"configurationItemCaptureTime\":\"2020-03-23T02:02:28.440Z\",\"configurationStateId\":1584928948999,\"awsAccountId\":\"999999999\",\"configurationItemStatus\":\"ResourceDiscovered\",\"resourceType\":\"AWS::IAM::User\",\"resourceId\":\"AIDASP666ZVA36XQGO6DD\",\"resourceName\":\"FoxySugar\",\"ARN\":\"arn:aws:iam::9999999:user/FoxySugar\",\"awsRegion\":\"global\",\"availabilityZone\":\"Not Applicable\",\"configurationStateMd5Hash\":\"\",\"resourceCreationTime\":\"2020-01-07T14:06:13.000Z\"},\"notificationCreationTime\":\"2020-03-23T02:40:01.999Z\",\"messageType\":\"ConfigurationItemChangeNotification\",\"recordVersion\":\"1.3\"}",
   "ruleParameters":"{}",
   "resultToken":"XYZ==",
   "eventLeftScope":false,
   "executionRoleArn":"arn:aws:iam::99999999:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig",
   "configRuleArn":"arn:aws:config:us-east-2:999999999:config-rule/config-rule-kfabou",
   "configRuleName":"my-config-rule1",
   "configRuleId":"config-rule-kfabou",
   "accountId":"99999999999999"
}
Above's invoking event:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
{
   "configurationItemDiff":null,
   "configurationItem":{
      "relatedEvents":[

      
      ],
      "relationships":[
         {
            "resourceId":"ANPASP666ZVAXGDCE5SDN",
            "resourceName":"Read_Only_EC2",
            "resourceType":"AWS::IAM::Policy",
            "name":"Is attached to CustomerManagedPolicy"
        
         }
      
      ],
      "configuration":{
         "path":"/",
         "userName":"FoxySugar",
         "userId":"AIDASP666ZVA36XQGO6DD",
         "arn":"arn:aws:iam::9999999999:user/FoxySugar",
         "createDate":"2020-01-07T14:06:13.000Z",
         "userPolicyList":[

         
         ],
         "groupList":[

         
         ],
         "attachedManagedPolicies":[
            {
               "policyName":"Read_Only_EC2",
               "policyArn":"arn:aws:iam::9999999:policy/Read_Only_EC2"
            
            }
         
         ],
         "permissionsBoundary":null,
         "tags":[

         
         ]
      
      },
      "supplementaryConfiguration":{

      
      },
      "tags":{

      
      },
      "configurationItemVersion":"1.3",
      "configurationItemCaptureTime":"2020-03-23T02:02:28.440Z",
      "configurationStateId":1584928948999,
      "awsAccountId":"999999999",
      "configurationItemStatus":"ResourceDiscovered",
      "resourceType":"AWS::IAM::User",
      "resourceId":"AIDASP666ZVA36XQGO6DD",
      "resourceName":"FoxySugar",
      "ARN":"arn:aws:iam::9999999:user/FoxySugar",
      "awsRegion":"global",
      "availabilityZone":"Not Applicable",
      "configurationStateMd5Hash":"",
      "resourceCreationTime":"2020-01-07T14:06:13.000Z"
   
   },
   "notificationCreationTime":"2020-03-23T02:40:01.999Z",
   "messageType":"ConfigurationItemChangeNotification",
   "recordVersion":"1.3"
}



Output:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
   "Evaluations": [ 
      { 
         "Annotation": "string",
         "ComplianceResourceId": "string",
         "ComplianceResourceType": "string",
         "ComplianceType": "string",
         "OrderingTimestamp": number
      }
   ],
   "ResultToken": "string",
   "TestMode": boolean
}
This is from this documentation.

Explanation (Lines):
2: This is array of compliance report. You can include as many as you like.
4. This is optional description
5. This is the ID of the resource
6. This is the resource type, this must be one supported by Config. Click here.
7. This must be one of following:  
  • COMPLIANT
  • NON_COMPLIANT
  • NOT_APPLICABLE
  • INSUFFICIENT_DATA
8. This is timestamp
11. This must be the same ResultToken that function received from Config.
12. If this is set to true, then nothing is actually reported back to Config.  


With this in mind, you should be able to make sense of these sample AWS Config Lambda codes such as one found here.

Wednesday, March 11, 2020

Terraform Lambda resource lifecycle

Terraform Notes

Lambda resource lifecycle and conditional tags

This example will create Lambda function and initially tag the resource with creation_date and modified_date. But will ONLY update the modified_date IF the python file's hash has changed.
- To prevent creation_date from updating each run, just add this to lambda resource under lifecycle's ignore_changes list.
- To prevent modified_date from updating each run, see the logic in locals below

  • If file does not exist, then set new modified_date
  • If file exists, but hash of the file has changed then set new modified_date


lambda resource

resource "aws_lambda_function" "test_lambda_function" {
  filename = "${path.module}/${var.zip_filename}"
  ## this is the standard handler for python function
  handler = "lambda_function.lambda_handler"
  ## this is just a function name
  function_name = var.function_name
  ## the role arn is obtained from below
  role = aws_iam_role.test_lambda_role.arn
  runtime = "python3.8"
  ## Need this source_code_hash to ensure function is updated when the zip is updated
  source_code_hash = local.new_file_hash
  ## these are the variables that can be used by the code
  environment {
    variables = {
      myvar = var.test
    }
  }
  tags = local.tags
  # Only attributes defined by the resource type can be ignored.
  #  last_modified and source_code_size is only here for illustration purposes.
  #  for tags, any NEW tag creation can't be ignored. 
  # if you create a tag from AWS console that isn't listed, then it will cause update to occur
  # if you set to ignore any tag, this will NOT update the tag after the first run
  lifecycle {
    ignore_changes = [
      last_modified,
      source_code_size,
      tags["creation_date"]
    ]
  }
}

readme file resource

to manage trigger of modified_date tag
resource "local_file" "readme"{
  content = jsonencode({"name"=var.function_name,"lastmodified"=local.new_modified_date,"hash"=local.new_file_hash})
  filename = local.readme_file
}

data call 

to create the zip file from .py file
data "archive_file" "init"{
  type = "zip"
  output_path = local.zip_file_path
  source_dir = "${path.module}/${var.function_filepath}/"
}

variables

variable "region" {
  default = "us-east-1"
}

variable "function_name"{
  default = "test"
}

variable "function_filepath"{
  default = "files"
}

variable "zip_filename"{
  default = "test.zip"
}

variable "test"{
  default = "hello world"
}

locals


locals{
    ## Readme file should reside with the invoking root, because this file can be deleted
    readme_file = "${path.root}/readme.json"
    zip_file_path = "${path.module}/${var.zip_filename}"
    new_file_hash = data.archive_file.init.output_base64sha256
    current_time_stamp = formatdate("YYYY-MM-DD hh:mm:ssZZZZZ", timestamp())
}

## If readme files exists, use existing data
locals{
    ## if this is a new file, then old_mod_date will be current date
    old_modified_date = fileexists(local.readme_file) ? jsondecode(file(local.readme_file)).lastmodified : local.current_time_stamp
    ## if this is a new file, then old_file_hash will be the current hash
    old_file_hash     = fileexists(local.readme_file) ? jsondecode(file(local.readme_file)).hash : local.new_file_hash
}

locals{
    ## if old and new hash are equal then new_mod_date will be old_mod_date, otherwise use the current date, 
    new_modified_date = local.new_file_hash == local.old_file_hash ? local.old_modified_date : local.current_time_stamp
}

## Tags
locals{
  tags = {
    "creation_date" = local.current_time_stamp
    "modified_date" = local.new_modified_date
    "keep_until"    = "0"
    "hash"          = local.new_file_hash
  }
}


Monday, March 9, 2020

AWS Lambda Python url request

Web call in Lambda Python 

...without using requests from botocore.vendored module


Recently, it became not possible to use request module from botocore.vendored. So here is a workaround and a handy little ditty to test your network configuration from inside your Lambda function.

I use a Lambda variable called 'hostname'


import json
import socket
import os
import urllib.request


def lambda_handler(event, context):
    # TODO implement
    ABOUTME = socket.gethostname()    
    MYIP = socket.gethostbyname(ABOUTME)   
    print("About me...")
    print(ABOUTME, "=>", MYIP)
    
    print("Check outside connection...")
    HOSTNAME = os.environ['hostname']
    IP = socket.gethostbyname(HOSTNAME)
    print(HOSTNAME, "=>",IP)
    
    URL = "https://api.ipify.org?format=json"
    req = urllib.request.Request(URL)
    response = urllib.request.urlopen(req)
    output = response.read().decode('utf8')
    fromOutsideIP = json.loads(output)["ip"]
    print('Your advertised source IP is',fromOutsideIP)
    
    return {
        'statusCode': 200,
        'body': ('Done')
    }

AWS WAF log4j query

How to query AWS WAF log for log4j attacks 1. Setup your Athena table using this instruction https://docs.aws.amazon.com/athena/latest/ug/wa...