From 5c5aac8eee230fada0e6a8bdbfdfba8738470b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=A1vio=20Oliveira?= Date: Thu, 28 Dec 2023 10:38:51 -0300 Subject: [PATCH] update module tag conformance pack --- aws/modules/tag-conformance-pack/README.md | 157 ++++++++-- ...onformance_pack.tf => conformance-pack.tf} | 0 .../multiple-accounts-deploy/backend.tf | 1 + .../conformance-pack.tf | 25 +- .../multiple-accounts-deploy/providers.tf | 1 + .../multiple-accounts-deploy/variables.tf | 32 +- ...agger.tf => lambda-event_bridge_tagger.tf} | 23 +- .../src/event_bridge_tagger.py | 277 +++++++++++++++--- aws/modules/tag-conformance-pack/ssm.tf | 2 +- aws/modules/tag-conformance-pack/variables.tf | 9 + aws/modules/tag-conformance-pack/versions.tf | 6 - 11 files changed, 445 insertions(+), 88 deletions(-) rename aws/modules/tag-conformance-pack/{conformance_pack.tf => conformance-pack.tf} (100%) rename aws/modules/tag-conformance-pack/{lambda_event_bridge_tagger.tf => lambda-event_bridge_tagger.tf} (87%) diff --git a/aws/modules/tag-conformance-pack/README.md b/aws/modules/tag-conformance-pack/README.md index 82122f3..9377001 100644 --- a/aws/modules/tag-conformance-pack/README.md +++ b/aws/modules/tag-conformance-pack/README.md @@ -4,6 +4,59 @@ It leverages AWS Config to enforce compliance with certain tagging standards acr This project integrates AWS Managed Config Rules, custom AWS Lambda functions, and AWS EventBridge for automated compliance checks and actions. +## TL;DR +Para utlizar o módulo, o exemplo `multiple-accounts-deploy` pode ser consultado. Um exemplo de variáveis de ambiente +para o módulo pode ser consultado abaixo: + +obs: utilizar a pasta scr no modulo para o terraform. Copiá-la e utilizar seus scripts. + +```terraform +profile = "DEMO" #account name, do not change +bucket_name = "tag-conformance-pack-files-demo-qa" +create_tf_backend = false +region = "us-east-1" +application_domain_path = "./accounts/demo_account/application-domain.json" +script_path = "./src" +custom_lambda_script = "custom-required-tags-evaluation.py" +create_event_bridge_tagger = true +event_bridge_tagger_script = "event_bridge_tagger.py" +ssm_script = "ssmDocumentAutomation.py" +tags = { + application = "config" + domain = "infrastructure" + board = "demo" + company = "rd" + shared = "no" + env = "qa" + tag_created = "iac" +} +RemediationExecutionControls = { + ExecutionControls = { + SsmControls = { + ConcurrentExecutionRatePercentage = 20 + ErrorPercentage = 40 + } + } + Automatic = true + MaximumAutomaticAttempts = 1 + RetryAttemptSeconds = 1200 +} + +# supported resources for required-tags managed config rule +# https://docs.aws.amazon.com/config/latest/developerguide/required-tags.html +resource_types = [ +... +] + +# all supporter resources +# https://docs.aws.amazon.com/config/latest/developerguide/resource-config-reference.html +custom_lambda_resource_types = [ +... +] + + +``` + ## Project Structure ``` @@ -13,6 +66,7 @@ compliance checks and actions. ├── conformance-pack.tf ├── custom_lambda_config ├── data.tf + ├── backend.tf ├── iam.tf ├── lambda_event_bridge_tagger.tf ├── locals.tf @@ -22,39 +76,77 @@ compliance checks and actions. ├── variables.tf └── versions.tf ``` +## Tag Conformance pack -## TagConformacePack +O módulo terraform tag conformance pack vai representar um aws conformance pack contendo 2 aws config rules (custom lambda e required-tags managed rule) e 1 função lambda com triggers a partir do event bridge para 4 recursos não suportados pelo aws config (cloudwatch log groups, elasticache, aws config rules e ssm parameters). -### Conformance pack +### Conformance Pack +O conformance pack não é um agregador do aws config, mas representa um conjunto de aws config rules e remediation actions em um único objeto. -#### config required-tags +### AWS Config - required tags -recurssos suportados +O aws config required-tags é uma regra gerenciada pela aws e apesar de ser um mecanismo pronto, possui limitação de recursos suportados. -script +input: -diagram +cloudformation: -#### custom lambda config +- config rule +- config rule remediation +- script do ssm document +- iam permissions +- catálogo de tags em formato json +- tags board, company, rd, shared e tag_created na variável tags = {} -recurssos suportados +```mermaid +graph TD + + config --> resource-tag-evaluation + resource-tag-evaluation --> config + config --> remediation-action + remediation-action --> ssm-document + ssm-document --> catalog-evaluation + catalog-evaluation --> ssm-document + ssm-document --> tag-resource +``` +Solução pronta da aws: aws config required-tags (managed rule). Possui quantidade limitada de recursos. Quando um recurso é criado ou modificado é avaliado sua conformidade com as tags. Para recusos fora de conformidade é disparada a ação de remediação e executado o script python via ssm document automation. O script tagueia o recurso conforme as tags do catálogo. -script +## Custom lambda required-tags config -diagram +O custom lambda required-tags é uma regra do aws config customizada. É implementada uma função lambda que realiza a validação das tags para cada recurso avaliado e para os recursos fora de compliance é disparada uma ação de remediação via ssm document automation da mesma forma que a regra gerenciada da aws. Ambos, esta regra do aws config e a regra gerenciada estão contidas no conformance pack. Este recurso abrange todos os [recursos suportados pelo aws config](Iniciativas%2046509273c2284bb0870fa758affd34c1/Raia%20Drogasil%20-%20Squad%20FinOps%205c1ceba1ca5845abb0d0d888682d4adf/Board%20Temp%20b2c1ec192de84bdbb5843872d4495fc8/RD%20MultiCloud%20Tagueamento%20-%20TMC%205866e19979f9419096b471f57a013a2e.md). -#### Event Bridge tagger +```mermaid +graph TD + custom-config --> lambda-resource-tag-evaluation + lambda-resource-tag-evaluation --> custom-config + custom-config --> remediation-action + remediation-action --> ssm-document + ssm-document --> catalog-evaluation + catalog-evaluation --> ssm-document + ssm-document --> tag-resource +``` +AWS custom lambda. Funciona para todos os recursos suportados para o aws config. A função lambda realiza a checagem de tags dos recursos quando são modificados. É preciso implementar as apis para taguear cada recurso no script. -recurssos suportados +# Event bridge tagger -script +O event bridge tagger é um conjunto de componentes. É essencialmente uma função lambda com event bridge triggers. A função lambda dispara a partir dos eventos registrados no cloud trail para config rules, ssm parameters, log groups e elasticache clusters. A rule do event bridge vai monitorar criação do recurso e modificação das tags. -diagram +```mermaid +graph TD -*cloud trail* + config-rules --> cloud-trail-change-log + ssm-parameters --> cloud-trail-change-log + log-groups --> cloud-trail-change-log + elasticache --> cloud-trail-change-log + cloud-trail-change-log --> event-bridge + event-bridge --> lambda + lambda --> catalog-evaluation + catalog-evaluation --> lambda + lambda --> tag-resource -#### iam permissions +``` +função lambda que funciona de forma parecida ao aws config. Contempla recursos não suportados pelo aws config. O eventBridge vai monitorar modificações nos recuros então dispara a função lambda para analisar e taguear o recurso. ## Debug @@ -67,7 +159,36 @@ O ssm document pode ter seus logs de execução observados em ssm > automations. application_value = matching_item.get("application", "") ``` 2. Erro de acesso +Se houver erro de acesso, será explicitado nos logs de execução 3. Erro na ação de tagueamento de recurso espećifico - -4. Erro ao tentar coletar as tags do recurso \ No newline at end of file +Se houver problema na implementação, para cada api terá um erro específico demonstrando o +problema. Pode ser necessário ajuste no python script. + +4. Erro ao tentar coletar as tags do recurso +Para cada api haverá uma resposta padrão indicando este problema. Pode ser necessário +ajuste no python script. + +| variáveis | tipo | descrição | default | +| --- | --- | --- | --- | +| profile | string | | | +| bucket_name | string | nome do bucket para o backend | | +| create_tf_backend | bool | se um bucket s3 deve ser criado para o backend | false | +| create_tf_backend_dynamo_table | bool | | false | +| region | string | | | +| application_domain_path | string | direotiro do catalogo application-domain.json | | +| script_path | string | diretorio onde estao todos os scripts | | +| custom_lambda_script | sring | nome do script do custom lambda | | +| create_event_bridge_tagger | bool | | false | +| event_bridge_tagger_script | string | nome do script da lambda do tagguer do event bridge | | +| ssm_script | string | nome do script ssm | | +| tags | map | mapa de tags, deve conter as tags de finops | | +| RemediationExecutionControls | object() | | | +| ConcurrentExecutionRatePercentage | string | | 20 | +| ErrorPercentage | string | % de erro tolerado para a remediação | 40 | +| Automatic | bool | se a ação de remediação é automatica | false | +| MaximumAutomaticAttempts | num | | 1 | +| RetryAttemptSeconds | num | | 1200 | +| resource_types | list | llista de recursos a serem avaliados pelo aws config managed rule required-tags | | +| custom_lambda_resource_types | list | lista de recursos a serem avaliados pelo custom lambda | | +| script_policy | json | policy permissionandoas actions de taguear e listar recursos para o aws config, ssm document e lambda. | | diff --git a/aws/modules/tag-conformance-pack/conformance_pack.tf b/aws/modules/tag-conformance-pack/conformance-pack.tf similarity index 100% rename from aws/modules/tag-conformance-pack/conformance_pack.tf rename to aws/modules/tag-conformance-pack/conformance-pack.tf diff --git a/aws/modules/tag-conformance-pack/examples/multiple-accounts-deploy/backend.tf b/aws/modules/tag-conformance-pack/examples/multiple-accounts-deploy/backend.tf index ec2b98d..502a55a 100644 --- a/aws/modules/tag-conformance-pack/examples/multiple-accounts-deploy/backend.tf +++ b/aws/modules/tag-conformance-pack/examples/multiple-accounts-deploy/backend.tf @@ -2,6 +2,7 @@ terraform { backend "s3" {} } + module "backend_bucket" { source = "terraform-aws-modules/s3-bucket/aws" version = "3.15.1" diff --git a/aws/modules/tag-conformance-pack/examples/multiple-accounts-deploy/conformance-pack.tf b/aws/modules/tag-conformance-pack/examples/multiple-accounts-deploy/conformance-pack.tf index d0e11da..dbf8238 100644 --- a/aws/modules/tag-conformance-pack/examples/multiple-accounts-deploy/conformance-pack.tf +++ b/aws/modules/tag-conformance-pack/examples/multiple-accounts-deploy/conformance-pack.tf @@ -1,16 +1,17 @@ module "ConformancePack" { source = "../module/tag-conformance-pack" - bucket_name = var.bucket_name - application_domain_path = var.application_domain_path - RemediationExecutionControls = var.RemediationExecutionControls - resource_types = var.resource_types - custom_lambda_resource_types = var.custom_lambda_resource_types - create_event_bridge_tagger = var.create_event_bridge_tagger - script_path = var.script_path - ssm_script = var.ssm_script - custom_lambda_script = var.custom_lambda_script - event_bridge_tagger_script = var.event_bridge_tagger_script - tags = var.tags - script_policy = data.aws_iam_policy_document.script_policy.json + profile = var.profile + bucket_name = var.bucket_name + application_domain_path = var.application_domain_path + RemediationExecutionControls = var.RemediationExecutionControls + resource_types = var.resource_types + custom_lambda_resource_types = var.custom_lambda_resource_types + create_event_bridge_tagger = var.create_event_bridge_tagger + script_path = var.script_path + ssm_script = var.ssm_script + custom_lambda_script = var.custom_lambda_script + event_bridge_tagger_script = var.event_bridge_tagger_script + tags = var.tags + script_policy = data.aws_iam_policy_document.script_policy.json } diff --git a/aws/modules/tag-conformance-pack/examples/multiple-accounts-deploy/providers.tf b/aws/modules/tag-conformance-pack/examples/multiple-accounts-deploy/providers.tf index b1f3890..9d71f22 100644 --- a/aws/modules/tag-conformance-pack/examples/multiple-accounts-deploy/providers.tf +++ b/aws/modules/tag-conformance-pack/examples/multiple-accounts-deploy/providers.tf @@ -3,6 +3,7 @@ provider "aws" { region = var.region default_tags { tags = { + Name = "TagConformancePack" git-location = "https://gitlab.com/raiadrogasil/rd/devops-rd/finopstools/tagconformacepack" application = var.tags["application"] domain = var.tags["domain"] diff --git a/aws/modules/tag-conformance-pack/examples/multiple-accounts-deploy/variables.tf b/aws/modules/tag-conformance-pack/examples/multiple-accounts-deploy/variables.tf index ac6ab24..59d2d53 100644 --- a/aws/modules/tag-conformance-pack/examples/multiple-accounts-deploy/variables.tf +++ b/aws/modules/tag-conformance-pack/examples/multiple-accounts-deploy/variables.tf @@ -6,7 +6,15 @@ variable "profile" { } variable "tags" { - type = map(string) + type = object({ + application = string + domain = string + board = string + company = string + shared = string + env = string + tag_created = string + }) } variable "application_domain_path" { @@ -25,7 +33,7 @@ variable "custom_lambda_resource_types" { variable "custom_lambda_script" { type = string - description = "script para a função lambda do lambda custom config" + description = "script para a função landa do lambda custom config" } variable "script_path" { @@ -55,7 +63,17 @@ variable "RemediationExecutionControls" { MaximumAutomaticAttempts = number RetryAttemptSeconds = number }) - + default = { + ExecutionControls = { + SsmControls = { + ConcurrentExecutionRatePercentage = 20 + ErrorPercentage = 40 + } + } + Automatic = false + MaximumAutomaticAttempts = 1 + RetryAttemptSeconds = 1200 + } description = "Config Remediation ExecutionControls yaml block" } @@ -77,4 +95,12 @@ variable "event_bridge_tagger_script" { variable "create_event_bridge_tagger" { type = bool +} + +# a função lambda event_bridge_tagger vai dispara apenas quando um recurso for modificado. +# esse scheduler tem como função executar a função lambda periodicamente para todos os recursos suportados por ela. +variable "schedule_expression" { + type = string + default = "rate(10 day)" + description = "taxa de execução do event bridge schedule que dispara a função lambda event_bridge_tagger." } \ No newline at end of file diff --git a/aws/modules/tag-conformance-pack/lambda_event_bridge_tagger.tf b/aws/modules/tag-conformance-pack/lambda-event_bridge_tagger.tf similarity index 87% rename from aws/modules/tag-conformance-pack/lambda_event_bridge_tagger.tf rename to aws/modules/tag-conformance-pack/lambda-event_bridge_tagger.tf index facd3a5..7df0ce3 100644 --- a/aws/modules/tag-conformance-pack/lambda_event_bridge_tagger.tf +++ b/aws/modules/tag-conformance-pack/lambda-event_bridge_tagger.tf @@ -7,7 +7,7 @@ module "event_bridge_tagger" { handler = "${trimsuffix(var.event_bridge_tagger_script, ".py")}.lambda_handler" runtime = "python3.10" create = var.create_event_bridge_tagger - source_path = "../module/tag-conformance-pack/src/event_bridge_tagger.py" # #"${var.script_path}/${var.event_bridge_tagger_script}" + source_path = "${var.script_path}/${var.event_bridge_tagger_script}" attach_create_log_group_permission = true cloudwatch_logs_retention_in_days = 1 @@ -145,6 +145,7 @@ PATTERN } + resource "aws_cloudwatch_event_target" "cloudwatch_logs_trigger" { count = var.create_event_bridge_tagger ? 1 : 0 rule = aws_cloudwatch_event_rule.cloudwatch_logs_trigger[0].name @@ -168,3 +169,23 @@ resource "aws_cloudwatch_event_target" "config_rule_trigger" { rule = aws_cloudwatch_event_rule.config_rule_trigger[0].name arn = module.event_bridge_tagger.lambda_function_arn } + +module "event_bridge_schedule" { + source = "terraform-aws-modules/eventbridge/aws" + create = var.create_event_bridge_tagger + + bus_name = "event_bridge_tagger_schedule" + + attach_lambda_policy = true + lambda_target_arns = [module.event_bridge_tagger.lambda_function_arn] + + schedules = { + event-bridge-tagger-cron = { + description = "Trigger for a Lambda" + schedule_expression = var.schedule_expression + timezone = "America/Sao_Paulo" + arn = module.event_bridge_tagger.lambda_function_arn + input = jsonencode({ "event_bridge_tagger_schedule": "schedule" }) # utilizado no script para distinguir o evento de schedule de outros. + } + } +} \ No newline at end of file diff --git a/aws/modules/tag-conformance-pack/src/event_bridge_tagger.py b/aws/modules/tag-conformance-pack/src/event_bridge_tagger.py index ae2d80e..3efe289 100644 --- a/aws/modules/tag-conformance-pack/src/event_bridge_tagger.py +++ b/aws/modules/tag-conformance-pack/src/event_bridge_tagger.py @@ -32,7 +32,7 @@ def load_application_domain_json_catalog_from_s3(self): )['Body'].read().decode('utf-8') return json.loads(response) - except Exception as e: + except ClientError as e: print(f"Error reading JSON from S3: {e}") raise @@ -90,7 +90,7 @@ def get_application_domain_values_from_json(self, json_data, search_key): domain_value = matching_item.get("domain", "") return application_value, domain_value - except Exception as e: + except ClientError as e: print( f"\nNo match found for search key (application and domain values): {search_key}\n" ) @@ -253,6 +253,7 @@ def get_logs_tags(self, resource_arn) -> dict: return response['tags'] + def get_ssm_parameter_tags(self, resource_arn) -> list: response = self.ssm.list_tags_for_resource( ResourceType = 'Parameter', @@ -267,62 +268,191 @@ def get_config_rule_tags(self, resource_arn) -> list: return response['Tags'] + def get_log_groups(self) -> list: + ''' + + :return: lista de arns de todos os log groups + ''' + next_token = None + arn_list = [] + + while True: + if next_token: + response = self.logs.describe_log_groups( + limit = 50, + NextToken = next_token + ) + + else: + response = self.logs.describe_log_groups( + limit = 50, + ) + + resources = response['logGroups'] + + for resource in resources: + resource_arn = f"arn:aws:logs:{region}:{account}:log-group:{resource['logGroupName']}" + arn_list.append(resource_arn) + next_token = response.get('nextToken') + + if not next_token: + break + + return arn_list + + def get_config_rules(self) -> list: + ''' + + :return: lista de arns de todos os log groups + ''' + next_token = None + arn_list = [] + + while True: + if next_token: + response = self.config.describe_config_rules( + NextToken = next_token + ) + else: + response = self.config.describe_config_rules( + ) + + resources = response['ConfigRules'] + + for resource in resources: + resource_arn = resource['ConfigRuleArn'] + arn_list.append(resource_arn) + + next_token = response.get('NextToken') + + if not next_token: + break + + return arn_list + + def get_ssm_parameters(self) -> list: + ''' + + :return: lista de arns de todos os ssm parameters + ''' + next_token = None + arn_list = [] + + while True: + if next_token: + response = self.ssm.describe_parameters( + MaxResults = 50, + NextToken = next_token + ) + else: + response = self.ssm.describe_parameters( + MaxResults = 50 + ) + + resources = response['Parameters'] + + for resource in resources: + resource_arn = resource['Name'] + arn_list.append(resource_arn) + + next_token = response.get('NextToken') + + if not next_token: + break + + return arn_list + + + def get_elasticache_clusters(self) -> list: + ''' + + :return: lista de arns de todos os ssm parameters + ''' + next_token = None + arn_list = [] + + while True: + if next_token: + response = self.elasticache.describe_cache_clusters( + MaxRecords = 100, + Marker = next_token + ) + else: + response = self.elasticache.describe_cache_clusters( + MaxRecords = 100 + ) + + resources = response['CacheClusters'] + + for resource in resources: + resource_arn = resource['ARN'] + arn_list.append(resource_arn) + + next_token = response.get('Marker') + + if not next_token: + break + + return arn_list + def check_defined(reference, reference_name): if not reference: - raise Exception('Error: ', reference_name, 'is not defined') + raise ClientError('Error: ', reference_name, 'is not defined') return reference -def lambda_handler(event, context): - ''' - Cada evento represena 1 recurso. Para cada recurso é comparado suas tags um catálogo de tags de referência. - Se fora de conformidade o recurso é tagueado. - :param event: payload do eventbridge - :return: resposta da api de tagueamento ou que o recurso já está em compliance com o catálogo. - ''' - event_source = event["source"] +def resource_tagger(event, lambda_action): print(f"Event: {event}\n") + event_source = event["source"] print(f"EVENT SOURCE: {event_source}\n") resource_key_words = [] resource_arn = "" - # cada source (logs, elasticache, ssm, config) possui uma estrutura de payload diferente. - if 'logs' in event_source: - if "resourceArn" in event["detail"]["requestParameters"].keys(): - resource_arn = event["detail"]["requestParameters"]["resourceArn"] + if 'event_bridge_tagger_schedule' not in event: + # cada source (logs, elasticache, ssm, config) possui uma estrutura de payload diferente. + if 'logs' in event_source: + if "resourceArn" in event["detail"]["requestParameters"].keys(): + resource_arn = event["detail"]["requestParameters"]["resourceArn"] - elif "logGroupName" in event["detail"]["requestParameters"].keys(): - log_group = event["detail"]["requestParameters"]["logGroupName"] - resource_arn = f"arn:aws:logs:{region}:{account}:log-group:{log_group}" - resource_key_words.extend(["logs"]) + elif "logGroupName" in event["detail"]["requestParameters"].keys(): + log_group = event["detail"]["requestParameters"]["logGroupName"] + resource_arn = f"arn:aws:logs:{region}:{account}:log-group:{log_group}" + resource_key_words.extend(["logs"]) - elif 'elasticache' in event_source: - resource_arn = event["detail"]["requestParameters"]["resourceName"] - resource_key_words.extend(["elasticache"]) + elif 'elasticache' in event_source: + resource_arn = event["detail"]["requestParameters"]["resourceName"] + resource_key_words.extend(["elasticache"]) - elif 'config' in event_source: - if "resourceArn" in event["detail"]["requestParameters"].keys(): - resource_arn = event["detail"]["requestParameters"]["resourceArn"] + elif 'config' in event_source: + if "resourceArn" in event["detail"]["requestParameters"].keys(): + resource_arn = event["detail"]["requestParameters"]["resourceArn"] - elif "configRuleArn" in event["detail"]["requestParameters"]["configRule"].keys(): - resource_arn = event["detail"]["requestParameters"]["configRule"]["configRuleArn"] + elif "configRuleArn" in event["detail"]["requestParameters"]["configRule"].keys(): + resource_arn = event["detail"]["requestParameters"]["configRule"]["configRuleArn"] - elif 'ssm' in event_source: - resource_arn = event["detail"]["requestParameters"]["resourceId"] - resource_key_words.extend(["ssm"]) + elif 'ssm' in event_source: + resource_arn = event["detail"]["requestParameters"]["resourceId"] + # o ssm é identificado via resource id que não contem a palavra ssm como no arn de outros recursos + # portanto é necessario add a palavra à lista de comparações + resource_key_words.extend(["ssm"]) + else: + raise ClientError('event source não identificado. Recrusosos suportados: SSM, CONFIG, ELASTICACHE, LOGS') else: - raise Exception('event source não identificado. Recrusosos suportados: SSM, CONFIG, ELASTICACHE, LOGS') + resource_arn = event['resourceID'] + if 'ssm' in event_source: + # o ssm é identificado via resource id que não contem a palavra 'ssm',por exemplo, como no arn de outros recursos + # portanto é necessario add a palavra à lista de comparações + resource_key_words.extend(["ssm"]) required_tags = {} - lambda_action = EventBridgeTagger( - bucket_name = os.environ.get("BUCKET_NAME"), - application_domain_json_key = os.environ.get("BUCKET_KEY"), - ) - - resource_tags = lambda_action.get_tags(event_source, resource_arn) + try: + resource_tags = lambda_action.get_tags(event_source, resource_arn) + except ClientError as e: + print(f'Erro ao listar tags do recurso: {e}') + raise # normalizar a estrutura de tags para formato lista if isinstance(resource_tags, dict): @@ -361,7 +491,7 @@ def lambda_handler(event, context): resource_key_words.extend([item['eks:nodegroup-name']]) print(f"Lista de palavras + TagName e Tags EKS, se houver -> {resource_key_words}\n") - except Exception as e: + except ClientError as e: print(f'\n exception No tag Name found-> {e}') print(f'continuing...') @@ -370,7 +500,7 @@ def lambda_handler(event, context): json_data = json_data, resource_words_list = resource_key_words ) print(f'\nsearch_key -> {search_key}') - except Exception as e: + except ClientError as e: print(f'\nNão foi possivel encontrar search_key\n') raise @@ -393,7 +523,7 @@ def lambda_handler(event, context): required_tags['env'] = os.environ.get("TAG_ENV") - except Exception as e: + except ClientError as e: print( f'\nNão foi possivel gerar tags application e/ou domain -> \n{e}\n' ) @@ -403,14 +533,67 @@ def lambda_handler(event, context): if required_tags == lambda_action.array_to_dict(resource_tags): print(f'\nREQUIRED TAGS: \n {required_tags}') print(f'\nRESOURCE TAGS: \n {resource_tags}') - print("NOTHING TO DO, TAG IS COMPLIANT") - return "Nothing to do, tag is COMPLIANT" + return "NOTHING TO DO, TAG IS COMPLIANT" + + else: + # add info tags + # required_tags['lambda_automation'] = "event_bridge_tagger" + + try: + response = lambda_action.tag_resource(event_source, resource_arn, required_tags) + except ClientError as e: + print(f'Erro ao taguear o recurso: {e}') + raise + + print(f'RESPONSE -> {response}') + return response + + +def schedule_trigger(event, lambda_action): + resources_arns = [] + lambda_response = {} + lambda_action = lambda_action + + event_sources = ["aws.logs", "aws.config", "" + ".ssm", "aws.elasticache"] + + for event_source in event_sources: + event['source'] = event_source + + if 'logs' in event_source: + resources_arns = lambda_action.get_log_groups() + lambda_response['logGroups'] = len(resources_arns) + elif 'config' in event_source: + resources_arns = lambda_action.get_config_rules() + lambda_response['configRules'] = len(resources_arns) + elif 'elasticache' in event_source: + resources_arns = lambda_action.get_elasticache_clusters() + lambda_response['elasticacheClusters'] = len(resources_arns) + elif 'ssm' in event_source: + resources_arns = lambda_action.get_ssm_parameters() + lambda_response['ssmParameters'] = len(resources_arns) - # add info tags - # required_tags['lambda_automation'] = "event_bridge_tagger" + print(f'TAGGING BY SCHEDULE EVENT') + print(f'TAGGING {len(resources_arns)} {event_source} resources') + for arn in resources_arns: + event['resourceID'] = arn + print(f'TAGGING {event_source}: {arn}') + response = resource_tagger(event, lambda_action) + print(f"RESPONSE -> {response}") - response = lambda_action.tag_resource(event_source, resource_arn, required_tags) + return lambda_response - print(f'\n ### response \n {response} \n########') - return response + +def lambda_handler(event, context): + lambda_action = EventBridgeTagger( + bucket_name = os.environ.get("BUCKET_NAME"), + application_domain_json_key = os.environ.get("BUCKET_KEY"), + ) + + if 'event_bridge_tagger_schedule' in event: + return schedule_trigger(event, lambda_action) + + else: + return resource_tagger(event, lambda_action) + diff --git a/aws/modules/tag-conformance-pack/ssm.tf b/aws/modules/tag-conformance-pack/ssm.tf index e7db0d7..5b735e8 100644 --- a/aws/modules/tag-conformance-pack/ssm.tf +++ b/aws/modules/tag-conformance-pack/ssm.tf @@ -1,7 +1,7 @@ # automation documents must use version 0.3 # https://docs.aws.amazon.com/systems-manager/latest/userguide/documents-schemas-features.html resource "aws_ssm_document" "SsmDocumentTagRemediation" { - name = "SsmDocumentTagRemediation" + name = "SsmDocumentTagRemediationAutomation" document_format = "YAML" document_type = "Automation" content = local.ssm_content diff --git a/aws/modules/tag-conformance-pack/variables.tf b/aws/modules/tag-conformance-pack/variables.tf index bc72989..390f900 100644 --- a/aws/modules/tag-conformance-pack/variables.tf +++ b/aws/modules/tag-conformance-pack/variables.tf @@ -62,3 +62,12 @@ variable "create_event_bridge_tagger" { variable "script_policy" { type = any } + +variable "profile" { + type = string +} + +variable "schedule_expression" { + type = string + default = "rate(10 day)" +} \ No newline at end of file diff --git a/aws/modules/tag-conformance-pack/versions.tf b/aws/modules/tag-conformance-pack/versions.tf index d0004ec..fd4bfe5 100644 --- a/aws/modules/tag-conformance-pack/versions.tf +++ b/aws/modules/tag-conformance-pack/versions.tf @@ -1,10 +1,4 @@ terraform { required_version = "~> 1.6.0" - required_providers { - aws = { - source = "hashicorp/aws" - version = "5.25.0" - } - } }