Monday, February 24, 2020

AWS Role Assumption

AWS Role Assumption

Perpetual Self Role Assumption

The below script shows you how to daisy-chain role assumption from a single role. I do this from my environment that has ADFS enabled, so my initial role assumption happens using my network credential. Because any role that has STS:RoleAssume is allowed to assume role in the same account, this is permitted. If you don't want this to be possible, then DENY this permission.

1. Create a role with AdministratorAccess AWS managed policy attached.
2. Set the Trust Relationship to trust your SAML-Provider arn: arn:aws:iam::9999999999999:saml-provider/my_adfs

Setup to get all the available credentials

$endPoint= "https://sts.mynet.net/adfs/ls/IdpInitiatedSignOn.aspx?loginToRp=urn:amazon:webservices"
$storeAs="ADFS"
$region = "us-east-1"
$accountNum = "999999999999"
$roleName = "STS-Administrator"
$LongprofileName = $accountNum + ":role/" + $roleName
$roleArn = "arn:aws:iam::" + $LongprofileName
$mysessionName = "itsme"
## provide my network credential
$Cred = Get-Credential -Message "AWS Session credentials (do not include domain)"
## Set endpoint ##
Set-AWSSamlEndpoint -Endpoint $Endpoint -StoreAs $storeAs -AuthenticationType Negotiate 
## Retrieves ALL roles that I am allowed to use and store them as NetSDKCredentialsFile ##
Set-AWSSamlRoleProfile -EndpointName $storeAs -StoreAllRoles -networkCredential $cred 
## view my NetSDKCredentialsFile list
Get-AWSCredential -ListProfileDetail

Get keys by assuming itself by calling "Use-STSRole"
$tempadmin = Use-STSRole `
               -RoleArn $roleArn `
               -ProfileName $LongprofileName `
               -RoleSessionName $mysessionName `
               -Region $region `
               -DurationInSeconds (900)

Set-AWSCredential -StoreAs $mysessionName `
                  -SecretKey $tempadmin.Credentials.SecretAccessKey `
                  -AccessKey $tempadmin.Credentials.AccessKeyId `
                  -SessionToken $tempadmin.Credentials.SessionToken `
                  -ProfileLocation $env:userprofile\.aws\credentials

Using the above $tempadmin credential, we loop and continue to generate more temporary credential

for($i=0;$i -lt 59;$i++){
    $skey = $tempadmin.Credentials.SecretAccessKey
    $akey = $tempadmin.Credentials.AccessKeyId
    $stoken = $tempadmin.Credentials.SessionToken
    $tempadmin = Use-STSRole `
               -RoleArn $roleArn `
               -ProfileName $LongprofileName `
               -RoleSessionName $mysessionName"_"$i `
               -Region $region `
               -SecretKey $skey `
               -AccessKey $akey `
               -SessionToken $stoken `
               -DurationInSeconds 900
    write-host $mysessionName"_"$i" will expire at" $tempadmin.Credentials.Expiration    
    Set-AWSCredential -StoreAs $mysessionName"_"$i `
                -SecretKey $tempadmin.Credentials.SecretAccessKey `
                -AccessKey $tempadmin.Credentials.AccessKeyId `
                -SessionToken $tempadmin.Credentials.SessionToken `
                -ProfileLocation $env:userprofile\.aws\credentials

    ## sleep for 1 mins
    start-sleep -Seconds (60 * 1)
}

In theory, you could run the above "for" loop indefinitely and gain perpetual temp credential.

Wednesday, February 12, 2020

Terraform Errors

Terraform Notes

Terraform Errors

The given key does not identify an element in this collection value.

Error: Invalid index

  on main.tf line 89, in resource "aws_organizations_organizational_unit" "level2_ou":
  89:       parent_id = "${[for thisLevel in local.level1_ou_flat: thisLevel.id if thisLevel.path == each.value["parent_path"]][0]}"
    |----------------
    | each.value["parent_path"] is "/Root/Non-Production"
    | local.level1_ou_flat is tuple with 3 elements

The given key does not identify an element in this collection value.

This means that there were nothing that matches the "/Root/Non-Production"
You need to account for this happening by checking for element size as such:

parent_id = length([for thisLevel in local.level1_ou_flat: thisLevel.id if thisLevel.path == each.value["parent_path"]]) > 0 ? "${[for thisLevel in local.level1_ou_flat: thisLevel.id if thisLevel.path == each.value["parent_path"]][0]}" : aws_organizations_organization.stifel-master.roots[0].id

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Index brackets must contain either a literal number or a literal string.

Error: Index value required

  on <import-address> line 1:
   1: aws_organizations_account.account[TEST-Master]

Index brackets must contain either a literal number or a literal string.

This error is probably because of your shell is stripping the double quotes.
Try this instead: wrap the whole module name in single quotes and escape the double quotes inside for the index string.

terraform import 'module.accounts.aws_organizations_account.account[\"TEST-Master\"]' 1111111111111

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Self-referential block

You can't reference itself in the for_each block inside resource block. I thought maybe Terraform would put a placeholder such as "(Known after apply)" like it usually does during plan. It seems that the entire resource's dependencies are evaluated as a whole. This blog does a good job of explaining this limitation.  

Error Output
Error: Self-referential block

  on modules\ous\m_org_ou.tf line 142, in resource "aws_organizations_organizational_unit" "levelX_ou":
 142:       parent_id = each.value["name"] == "Alpha" ? "r-f999" : aws_organizations_organizational_unit.levelX_ou["X1"].id

Configuration for aws_organizations_organizational_unit.levelX_ou["X1"] may
not refer to itself.

Resource Block
resource "aws_organizations_organizational_unit" "levelX_ou"{
    for_each = var.ou_map_x
      name = each.value["name"]
      parent_id = each.value["name"] == "Alpha" ? "r-f999" : aws_organizations_organizational_unit.levelX_ou["X1"].id 
}

Variable

variable "ou_map_x"{
    default = {
        "X1" = {
            "depth" = "1"
            "name" = "Alpha"
            "path" = "/"
            "parent_name" = "Root"
            "parent_path" = "/Root"
        },
        "X2" = {
            "depth" = "2"
            "name" = "Bravo"
            "path" = "/"
            "parent_name" = "Alpha"
            "parent_path" = "/Root/Alpha"
        },
        "X3" = {
            "depth" = "3"
            "name" = "Charlie"
            "path" = "/"
            "parent_name" = "Bravo"
            "parent_path" = "/Root/Alpha/Bravo"
        }
    }
}

This is using same Terraform code as above, except I tried to loop through the entire resource block and find the matching parent_name, which returned a more ambiguous error, but same issue with cyclic self-reference issue.


Error: Cycle: module.ous.aws_organizations_organizational_unit.levelX_ou["X3"], module.ous.aws_organizations_organizational_unit.levelX_ou["X2"], module.ous.aws_organizations_organizational_unit.levelX_ou["X1"]

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

No valid credential sources found for AWS Provider

If you changed the profile used to access your S3 backend, you won't have access to it anymore. You'll see this error during terraform init

Initializing the backend...

Error: No valid credential sources found for AWS Provider.
        Please see https://terraform.io/docs/providers/aws/index.html for more information on
        providing credentials for the AWS Provider

In order to fix this, you can either copy the new profile name so you have both new and old profile credential (although same credential). Or you can edit the local terraform.tfstate so that it uses the new profile name. 

"backend": {
        "type": "s3",
        "config": {
            "profile": "oldProfile"

If you see this error during terraform plan/apply then in your provider block, you have wrong or invalid profile name.



=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Wednesday, February 5, 2020

PowerShell Challenge - In-line filter and present

PowerShell challenge

One liner to filter and present a data

Get data from AWS, filter it, then present a subset of returned data one of which includes a specific tag key. The challenging part? Do it as a single command. There'll be lots of pipes and pipeline variables.


1
2
3
4
5
6
7
get-ec2NetworkInterface | 
   ?{$_.vpcid -like "vpc-12345"} |
       select privateipaddress, description, tagset | 
           format-table 
              privateipaddress, 
              description, 
              @{Label="Name"; Expression = {($_.tagset | ?{$_.key -eq "Name"}).value}} 

Explanation:
Line 1: AWS PowerShell command to return all nic in an account
Line 2: Filter the previous output to only include this one VPC
Line 3: Filter the previous output to only show these three properties
Line 4: Filter the previous out in a table to include...
Line 5: privateipaddress
Line 6: description
Line 7: We can use hash table with two keys Label and Expression that format-table will use. In the expression, the first pipeline variable ($_.tagset) is from line 3 and the second pipeline variable ($_.key) is from $_.tagset.



Thursday, January 30, 2020

Terraform working with Collection Functions

Terraform Notes

Working with Collection Functions


Here's a little challenge: Gather the structure of existing OU of AWS Organization. We are not going to get all the OU IDs and import then into our Terraform State. We are also not going to just [destroy and] rebuild them. Instead we are going to get it using Data call.

One hurdle was that each call to retrieve OU only returns its immediate child.
https://www.terraform.io/docs/providers/aws/d/organizations_organizational_units.html

So I had to hardcode how deep I wanted to retrieve (AWS limits you to 5 deep for OU). But this is a good illustration of working with Map, List, flatten, and concat.

Few highlights:


When you do a data call as a count loop, Terraform will create a list of 3 attributes
  • Terraform ID
  • Attribute used as an argument reference (in the case of Org, this is Parent_ID)
  • List of the expected attribute reference (in the case of Org, this is Children)
When you flatten a list of attributes that contains another list of attributes, your end result is list of attribute. This is how I picture it:

Finding a attribute's value in a list and then returning another attribute's value from that index is pretty straight forward:

parent_name = [for x in local.level1_ou_flat: x.name if x.id == ou.parent_id][0]

This code says, loop through all the items in local.level1_ou_flat and only keep name where id is  ou.parent_id. The above code snippet works because I know that all the names returned will be the same, so I just keep the first item in the list. 

When you output something from module, this will be returned to main call, so from main you have to explicitly output this value which can be referenced using the module's name that you defined in the main. No, you don't have to call this output, main. Also, output is ONLY invoked when you do Terraform Apply. Terraform Plan, unfortunately does not provide output.

In Module:

output "ou_tree" {
  value = local.all_ou
}

In Main:

output "main"{
  value = module.org_scp.ou_tree
}

main.tf
provider "aws" {
  alias    = "master_east_2"
  version = "~> 2.0"
  region  = "us-east-2"
  access_key = "AAAAAAAAAAAAAAAAAAA"
  secret_key = "zzzzzzzzzzzzzzzzzzz"
}

module "org_scp" {
  source = "./module"
  providers = {
    aws = aws.master_east_2
  }  
}

output "main"{
  value = module.org_scp.ou_tree
}

module/m_org_scp.tf

// this return information about org: https://www.terraform.io/docs/providers/aws/d/organizations_organization.html
data "aws_organizations_organization" "myRoot"{
}

/*
data.aws_organizations_organization.myRoot.roots[0].id
data.aws_organizations_organization.myRoot.roots[0].name
data.aws_organizations_organization.myRoot.roots[0].arn
*/

// root will only have 1 element, but we leave this open-ended for consistency with the remaining levels
data "aws_organizations_organizational_units" "level1_ou"{
    count = length(data.aws_organizations_organization.myRoot.roots)
    parent_id = data.aws_organizations_organization.myRoot.roots[count.index].id
}

/* 
data.aws_organizations_organizational_units.level1_ou[*].parent_id
data.aws_organizations_organizational_units.level1_ou[*].id  <-- don't use this
data.aws_organizations_organizational_units.level1_ou[*].children[*].id
data.aws_organizations_organizational_units.level1_ou[*].children[*].arn
data.aws_organizations_organizational_units.level1_ou[*].children[*].name 
*/

locals {
    level1_ou_flat = flatten([
        for ou in data.aws_organizations_organizational_units.level1_ou : [
            for child in ou.children : {
                parent_id = ou.parent_id
                parent_name = data.aws_organizations_organization.myRoot.roots[0].name
                id = child.id
                name = child.name
                arn = child.arn
                path = "${data.aws_organizations_organization.myRoot.roots[0].name}/${child.name}"
            }
        ]
    ])        
}

/*
local.level1_ou_flat[*].parent_id
local.level1_ou_flat[*].parent_name
local.level1_ou_flat[*].id
local.level1_ou_flat[*].name
local.level1_ou_flat[*].arn
local.level1_ou_flat[*].path
*/

// this returns a list of list of OUs on layer 2
data "aws_organizations_organizational_units" "level2_ou"{
    count = length(local.level1_ou_flat)
    parent_id = local.level1_ou_flat[count.index].id 
}

/* 
data.aws_organizations_organizational_units.level2_ou[*].parent_id
data.aws_organizations_organizational_units.level2_ou[*].id  <-- don't use this
data.aws_organizations_organizational_units.level2_ou[*].children[*].id
data.aws_organizations_organizational_units.level2_ou[*].children[*].arn
data.aws_organizations_organizational_units.level2_ou[*].children[*].name 
*/

// flatten the list of list layer 2
locals {
    level2_ou_flat = flatten([
        for ou in data.aws_organizations_organizational_units.level2_ou : [
            for child in ou.children : {
                parent_id = ou.parent_id
                parent_name = [for x in local.level1_ou_flat: x.name if x.id == ou.parent_id][0]
                id = child.id
                name = child.name
                arn = child.arn
                path = "${[for x in local.level1_ou_flat: x.path if x.id == ou.parent_id][0]}/${child.name}"
            }
        ]
    ])        
}

/*
local.level2_ou_flat[*].parent_id
local.level2_ou_flat[*].parent_name
local.level2_ou_flat[*].id
local.level2_ou_flat[*].name
local.level2_ou_flat[*].arn
local.level2_ou_flat[*].path
*/

// this returns a list of list of OUs on layer 3
data "aws_organizations_organizational_units" "level3_ou"{
    count = length(local.level2_ou_flat)
    parent_id = local.level2_ou_flat[count.index].id
}

// flatten the list of list layer 3
locals {
    level3_ou_flat = flatten([
        for ou in data.aws_organizations_organizational_units.level3_ou : [
            for child in ou.children : {
                parent_id = ou.parent_id
                parent_name = [for x in local.level2_ou_flat: x.name if x.id == ou.parent_id][0]
                id = child.id
                name = child.name
                arn = child.arn
                path = "${[for x in local.level2_ou_flat: x.path if x.id == ou.parent_id][0]}/${child.name}"
            }
        ]
    ])        
}

// this returns a list of list of OUs on layer 4
data "aws_organizations_organizational_units" "level4_ou"{
    count = length(local.level3_ou_flat)
    parent_id = local.level3_ou_flat[count.index].id
}

// flatten the list of list layer 4
locals {
    level4_ou_flat = flatten([
        for ou in data.aws_organizations_organizational_units.level4_ou : [
            for child in ou.children : {
                parent_id = ou.parent_id
                parent_name = [for x in local.level3_ou_flat: x.name if x.id == ou.parent_id][0]
                id = child.id
                name = child.name
                arn = child.arn
                path = "${[for x in local.level3_ou_flat: x.path if x.id == ou.parent_id][0]}/${child.name}"
            }
        ]
    ])        
}

// this returns a list of list of OUs on layer 5
data "aws_organizations_organizational_units" "level5_ou"{
    count = length(local.level4_ou_flat)
    parent_id = local.level4_ou_flat[count.index].id
}

// flatten the list of list layer 5
locals {
    level5_ou_flat = flatten([
        for ou in data.aws_organizations_organizational_units.level5_ou : [
            for child in ou.children : {
                parent_id = ou.parent_id
                parent_name = [for x in local.level4_ou_flat: x.name if x.id == ou.parent_id][0]
                id = child.id
                name = child.name
                arn = child.arn
                path = "${[for x in local.level4_ou_flat: x.path if x.id == ou.parent_id][0]}/${child.name}"
            }
        ]
    ])        
}


locals {
    // AWS Organization currently only supports 1 root
    root_id = data.aws_organizations_organization.myRoot.roots[0].id
    root_accounts = data.aws_organizations_organization.myRoot.accounts[*].id
    all_ou = concat(local.level1_ou_flat,local.level2_ou_flat,local.level3_ou_flat,local.level4_ou_flat,local.level5_ou_flat)

    /*
    local.all_ou[*].parent_id
    local.all_ou[*].parent_name
    local.all_ou[*].id
    local.all_ou[*].name
    local.all_ou[*].arn
    local.all_ou[*].path
    */
}


output "ou_tree" {
  value = local.all_ou
}

output:

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

main = [
  {
    "arn" = "arn:aws:organizations::111111111111:ou/o-fyteu3rxnm/ou-abcd-wdbh6dlt"
    "id" = "ou-abcd-wdbh6dlt"
    "name" = "QA"
    "parent_id" = "r-abcd"
    "parent_name" = "Root"
    "path" = "Root/QA"
  },
  {
    "arn" = "arn:aws:organizations::111111111111:ou/o-fyteu3rxnm/ou-abcd-zuidgyzl"
    "id" = "ou-abcd-zuidgyzl"
    "name" = "Test"
    "parent_id" = "r-abcd"
    "parent_name" = "Root"
    "path" = "Root/Test"
  },
  {
    "arn" = "arn:aws:organizations::111111111111:ou/o-fyteu3rxnm/ou-abcd-dpde5cp9"
    "id" = "ou-abcd-dpde5cp9"
    "name" = "Prod"
    "parent_id" = "r-abcd"
    "parent_name" = "Root"
    "path" = "Root/Prod"
  },
  {
    "arn" = "arn:aws:organizations::111111111111:ou/o-fyteu3rxnm/ou-abcd-bi34zvmq"
    "id" = "ou-abcd-bi34zvmq"
    "name" = "QA_App"
    "parent_id" = "ou-abcd-wdbh6dlt"
    "parent_name" = "QA"
    "path" = "Root/QA/QA_App"
  },
  {
    "arn" = "arn:aws:organizations::111111111111:ou/o-fyteu3rxnm/ou-abcd-fbj0cnnr"
    "id" = "ou-abcd-fbj0cnnr"
    "name" = "App1"
    "parent_id" = "ou-abcd-zuidgyzl"
    "parent_name" = "Test"
    "path" = "Root/Test/App1"
  },
  {
    "arn" = "arn:aws:organizations::111111111111:ou/o-fyteu3rxnm/ou-abcd-t3ngdv40"
    "id" = "ou-abcd-t3ngdv40"
    "name" = "Prod_App2"
    "parent_id" = "ou-abcd-dpde5cp9"
    "parent_name" = "Prod"
    "path" = "Root/Prod/Prod_App2"
  },
  {
    "arn" = "arn:aws:organizations::111111111111:ou/o-fyteu3rxnm/ou-abcd-qubfzrrr"
    "id" = "ou-abcd-qubfzrrr"
    "name" = "Prod_App"
    "parent_id" = "ou-abcd-dpde5cp9"
    "parent_name" = "Prod"
    "path" = "Root/Prod/Prod_App"
  },
  {
    "arn" = "arn:aws:organizations::111111111111:ou/o-fyteu3rxnm/ou-abcd-x4csr627"
    "id" = "ou-abcd-x4csr627"
    "name" = "SubDeptA"
    "parent_id" = "ou-abcd-fbj0cnnr"
    "parent_name" = "App1"
    "path" = "Root/Test/App1/SubDeptA"
  },
  {
    "arn" = "arn:aws:organizations::111111111111:ou/o-fyteu3rxnm/ou-abcd-cii1t2vv"
    "id" = "ou-abcd-cii1t2vv"
    "name" = "SubDeptB"
    "parent_id" = "ou-abcd-fbj0cnnr"
    "parent_name" = "App1"
    "path" = "Root/Test/App1/SubDeptB"
  },
]





Tuesday, January 28, 2020

Terraform working with variables

Terraform Notes

Working with Variables

In this example, I am populating a dummy Service Control Policy (SCP) in my Org. As such, I need to provide target ID that is the root's ID.

main.tf
provider "aws" {
  alias    = "master_east_2"
  version = "~> 2.0"
  region  = "us-east-2"
  access_key = "AAAAAAAAAAAAAAAAA"
  secret_key = "ffffffffffffffffffffffffffffffffff"
}

variable "main_target_id" {
    type = string
    default = "r-ppg1"
}

module "org_scp" {
  source = "./module"
  module_target_id = var.main_target_id
  providers = {
    aws = aws.master_east_2
  }  
}

From main.tf, I feed in a local variable called main_target_id with the value "r-ppg1" this is the id of the org root. The module is using a variable called "module_target_id," we define this in the module file in the variable block. The variable block is used to define the type and default, if applicable.


module/module_scp.tf

resource "aws_organizations_policy" "example" {
  name = "example"

  content = <<CONTENT
{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": "*",
    "Resource": "*"
  }
}
CONTENT
}

variable "module_target_id" {
    type = string
}

resource "aws_organizations_policy_attachment" "root" {
  policy_id = aws_organizations_policy.example.id
  target_id = var.module_target_id
}

The resource "aws_organizations_policy_attachment" is expecting two arguments policy_id and target_id. These we don't control.

Thursday, January 16, 2020

Terraform loop - count vs for_each

Terraform Notes

Using Count Loop vs For_Each Loop

In my previous example, Terraform Cloudwatch Event Rules, I used Count to loop through a List of CloudWatch rules. One downside of doing it this way is that if you have to remove 1 resource in the array, it causes a cascade of delete and recreates to ensure the indices are reordered.
So if you have 5 resources and you delete [2], following will occur.

[1]   -->   [1]
[2]   -->    X
[3]   -->    X    -->   [2]
[4]   -->    X    -->   [3]
[5]   -->    X    -->   [4]

The better way to do this (supported as of Terraform version 0.12) is to use for_each. Here is my example.

This is the my variable
variable "event_rules" {
    description = "Create event rules with following name, description, and rule JSON"
    type = map(map(string))
    default = {
        "CIS3_1" = {
            "name"="CIS-UnauthorizedActivityAttempt",
            "description"="3.1 UnauthorizedActivityAttempt",
            "filename"="CIS-UnauthorizedActivityAttempt.json"
        },
        "CIS3_2" = {
            "name"="CIS-ConsoleLoginNoMFA",
            "description"="3.2 ConsoleLoginNoMFA",
            "filename"="CIS-ConsoleLoginNoMFA.json"
        }
    }
}

This is where resources are created

resource "aws_cloudwatch_event_rule" "all_events_map" {
    for_each = var.event_rules_map
      name = each.value["name"]
      description = each.value["description"]
      event_pattern = file("${path.module}/jsons/${each.value["filename"]}")
}

Here is another example, where we nest the for loop so that we evaluate the aws_cloudwatch_event_rule before we take action on it. The inner for loop will create a map that uses key as the key and value as its value (key => value) only if the following if statement resolves to true. Then the outer for loop will run against that resulting map. So it's possible that no resource gets created.


resource "aws_lambda_permission" "all_events_map" {
  for_each = {
    for key, value in aws_cloudwatch_event_rule.all_events_map:
    key => value
    if length(var.lambda_function_name) > 0
  }
    statement_id  = each.value["name"]
    action        = "lambda:InvokeFunction"
    function_name = var.lambda_function_name
    principal     = "events.amazonaws.com"
    source_arn    = each.value["arn"]
}








Terraform AWS Assume Role


Using AWS Assume Role in Terraform

You built a Terraform build server or TFE server on AWS EC2.  You want to be able to deploy resources to multiple accounts in your organization. How do you do this?

References:
https://www.terraform.io/docs/providers/aws/index.html
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#w329aac23c19c25c21b9b3
https://docs.aws.amazon.com/cli/latest/reference/sts/assume-role.html
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_permissions-to-switch.html
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user.html


Be sure you have added an Instance Profile loaded onto your instance, you can extract the temporary credential for testing.

This is the aws cli command to look up your own identity:

aws sts get-caller-identity


This is how you extract the temp cred :
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/SOURCE_ROLE_NAME

Let's say from this instance you want to execute Terraform commands against a different role than the Instance Profile.

Be sure the Source Role is given necessary permission to assume the Target Role

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

And the Target Role must have the necessary Trust relationship to the Source Role. In the below example, I also limit this action to happen from some known IP range.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::999999999999:role/SOURCE_ROLE_NAME"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": "10.10.10.0/24"
        }
      }
    }
  ]
}

You can test your policy via this command
aws sts assume-role --role-arn arn:aws:iam::888888888888:role/TARGET_ROLE_NAME --role-session-name MySession

Once your test works, then you can use the assume_role block inside "aws" provider.

Assume Role Usage:

provider "aws" {
  assume_role {
    role_arn     = "arn:aws:iam::88888888888/TARGET_ROLE_NAME"
    session_name = "MY_SESSION_NAME"
    external_id  = "SOME_OPTIONAL_EXTERNAL_ID"
  }
}

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...