Showing posts with label Terraform. Show all posts
Showing posts with label Terraform. Show all posts

Wednesday, 31 July 2024

How to use HashiCorp Cloud as a remote storage for Terraform state file



Terraform state file keeps track of the infrastructure which is under Terraform's control. Terraform compares resource configuration files against it in order to find out which resource needs to be added, edited or deleted. If state file gets lost, Terraform will try to re-create all resources. 

By default Terraform state file (terraform.tfstate) is stored locally, on the machine where we initialize Terraform. But this carries the risk of adding this file (which may contain sensitive data) to the repository and pushing it to remote which can be a security risk or deleting it by chance which can be painful experience - see Lessons learned after losing the Terraform state file | Trying things.

To minimize chances of losing the Terraform state file and enable multiple contributors to work on the same infrastructure in parallel we should define a remote storage for it. We can store it in AWS S3 bucket, Google Cloud etc...but one of the totally free options, which also includes the shared state file locking mechanism, is Terraform Cloud.

Here are the steps which explain how to do it.

Sign Up for HashiCorp Cloud Platform (HCP):
  • Go to Terraform Cloud (https://app.terraform.io/) and create an account.
  • Create an organization (e.g. terraform-states) and a workspace (e.g. remote-state-demo) within Terraform Cloud. Workspaces are where state files are stored and managed.
 Configure Terraform Cloud Backend:
  • Add the following backend configuration to e.g. terraform.tf file:

terraform {
  backend "remote" {
    organization = "terraform-states"

    workspaces {
      name = "remote-state-demo"
    }
  }
}

Login to Terraform Cloud:

$ terraform login
Terraform will request an API token for app.terraform.io using your browser.

If login is successful, Terraform will store the token in plain text in
the following file for use by subsequent commands:
    /home/<user>/.terraform.d/credentials.tfrc.json

Do you want to proceed?
  Only 'yes' will be accepted to confirm.

  Enter a value: yes


---------------------------------------------------------------------------------

Terraform must now open a web browser to the tokens page for app.terraform.io.

If a browser does not open this automatically, open the following URL to proceed:
    https://app.terraform.io/app/settings/tokens?source=terraform-login


---------------------------------------------------------------------------------

Generate a token using your browser, and copy-paste it into this prompt.

Terraform will store the token in plain text in the following file
for use by subsequent commands:
    /home/<user>/.terraform.d/credentials.tfrc.json

Token for app.terraform.io:
  Enter a value: Opening in existing browser session.



Retrieved token for user <tf_user>


---------------------------------------------------------------------------------

                                          -                                
                                          -----                           -
                                          ---------                      --
                                          ---------  -                -----
                                           ---------  ------        -------
                                             -------  ---------  ----------
                                                ----  ---------- ----------
                                                  --  ---------- ----------
   Welcome to HCP Terraform!                       -  ---------- -------
                                                      ---  ----- ---
   Documentation: terraform.io/docs/cloud             --------   -
                                                      ----------
                                                      ----------
                                                       ---------
                                                           -----
                                                               -


   New to HCP Terraform? Follow these steps to instantly apply an example configuration:

   $ git clone https://github.com/hashicorp/tfc-getting-started.git
   $ cd tfc-getting-started
   $ scripts/setup.sh

During this process a Terraform Cloud token generation page opens in browser:


terraform login should automatically pick the token and save it but in case this fails, you can copy the token and paste it here:

/home/<user>/.terraform.d/credentials.tfrc.json:

{
  "credentials": {
    "app.terraform.io": {
      "token": "1kLiQ....h3A"
    }
  }
}


This authentication is necessary for the next step:

Initialize the Backend:
  • Run terraform init to initialize the backend configuration
If we don't login to Terraform first we'll get:

$ terraform init
Initializing HCP Terraform...
│ Error: Required token could not be found
│ 
│ Run the following command to generate a token for app.terraform.io:
│     terraform login

If we're authenticated with Terraform:

$ terraform init
Initializing the backend...

Successfully configured the backend "remote"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
- Finding latest version of hashicorp/local...
- Installing hashicorp/local v2.5.1...
- Installed hashicorp/local v2.5.1 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

Let's assume we have the following resource:

main.tf:

resource "local_file" "foo" {
  filename = "${path.cwd}/temp/foo.txt"
  content = "This is a text content of the foo file!"
}


We can now see the plan:

$ terraform plan
Running plan in the remote backend. Output will stream here. Pressing Ctrl-C
will stop streaming the logs, but will not stop the plan running remotely.

Preparing the remote plan...

To view this run in a browser, visit:
https://app.terraform.io/app/terraform-states/remote-state-demo/runs/run-nbxxG2TBxSYGEgCm

Waiting for the plan to start...

Terraform v1.9.3
on linux_amd64
Initializing plugins and modules...

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # local_file.foo will be created
  + resource "local_file" "foo" {
      + content              = "This is a text content of the foo file!"
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "/home/tfc-agent/.tfc-agent/component/terraform/runs/run-nbxxG2TBxSYGEgCm/config/temp/foo.txt"
      + id                   = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.


Notice that plan is running in remote backend and file path is also the one on the remote Terraform cloud machine. This is because we left our workspace to use organisation's Execution Mode which is Remote - all resources will be created on the remote machine. But this is not what we want, we want remote to contain only state file. Therefore we need to change the setting:




We can now apply the configuration (after executing terraform init so the new Execution Mode gets picked):


$ terraform plan
local_file.foo: Refreshing state... [id=db5ca40b5588d44e9ec6c1b4005e11a6fd0c910e]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # local_file.foo will be created
  + resource "local_file" "foo" {
      + content              = "This is a text content of the foo file!"
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "/home/<user>/...hcp-cloud-state-storage-demo/temp/foo.txt"
      + id                   = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.


We can now execute terraform apply and changes will be done on the local machine.

If we create a resource on the remote (cloud), we can see it in web console:




If we by mistake create a resource on the remote (cloud), we can delete it by removing it from the state:

$ terraform state list
local_file.foo

$ terraform state rm local_file.foo
Removed local_file.foo
Successfully removed 1 resource instance(s).


All revisions of the state file are listed in Terraform Cloud. 






We can also roll back to some of the previous versions:










After this we need to unlock the state file:








Monday, 17 June 2024

How to organize Terraform project




.
├── data.tf
├── files
│   ├── xyz.yaml
│   ├── xyz.json
│   ├── ...
│   └── templates
│       ├── xyz.json.tftpl
│       ├──...
│       └── xyz.yaml.tftpl
├── ...
├── ...
├── locals.tf
├── main.tf
├── outputs.tf
├── providers.tf
├── README.md
├── variables.tf
└── versions.tf




A relatively common convention:

versions.tf contains a terraform block that contains a required_providers block that specifies the providers that the module uses and the earliest version of each provider the module is known to be compatible with. This information is scoped to one module at a time, so when writing it you should only think about what the current module needs and not consider what any other modules are compatible with.

providers.tf contains one or more provider blocks that actually instantiate the providers, specifying the configuration to use for each.

Provider configurations have global scope, so the root module configures these on behalf of all downstream modules and so must consider the needs of the entire effective configuration.

If you are intending to follow that convention, then every one of your modules would have a versions.tf file, and each one should describe only what that one module needs. For example, if one of your modules uses a provider feature that is relatively new then it would probably specify a different minimum version than another module that uses provider features that have been available for a long time.

You should have providers.tf only in modules that you intend to use as root modules. The others should inherit or be passed configurations from their callers.

Some exceptions to these guidelines:
  • If you have a module that you know has been broken by a later major version of a provider and you aren’t yet ready to upgrade it, you would typically specify an upper limit on the version constraint in required_providers for that particular module, so Terraform won’t try to select a newer version that’s incompatible. 
  • Some legacy modules include provider blocks even though they aren’t root modules. I would not recommend writing any new modules like that, but it is technically still allowed – with various caveats – for backward compatibility.
When you run terraform init in a root module you will get one more file generated automatically: .terraform.lock.hcl. This file tracks Terraform’s decisions about which version of each provider to use to satisfy all of the modules’ version constraints, and so you should also add this to your version control to ensure that later provider releases with breaking changes can’t break your setup. Terraform will select a new version only if you explicitly ask it to by running terraform init -upgrade.

Here is another strong reason why NOT to put provider configuration in a non-root module: Terraform - refactoring modules: Error: Provider configuration not present - Stack Overflow


References:


Tuesday, 4 June 2024

Securing AWS EKS with GuardDuty and Terraform

Amazon GuardDuty is a threat detection service which continuously monitors, profiles and analyses events across AWS accounts and resources. In Introduction to Amazon GuardDuty I wrote about its general features.




Its support for Amazon Elastic Kubernetes Service (Amazon EKS) comes in two ways:
  • GuardDuty EKS Protection (EKS Audit Log Monitoring)
    • GuardDuty feature that monitors cluster control plane activity by analyzing EKS audit logs
    • Helps detect potentially suspicious activities in EKS clusters 
    • Enabled by default when GuardDuty is enabled
  • GuardDuty EKS Runtime Monitoring
    • mechanism to detect runtime threats from over 30 security findings to protect our EKS clusters
    • can  identify specific containers within your EKS clusters that are potentially compromised and detect attempts to escalate privileges from an individual container to the underlying Amazon EC2 host and the broader AWS environment
    • fully managed EKS add-on 
    • managed as a part of GuardDuty Runtime Monitoring
    • visibility into individual container runtime activities (on-host operating system-level behavior) such as:
      • file access
      • process execution
      • network connections
    • lightweight security agent is deployed as a Kubernetes Daemonset which has a pod running on every node
    • Detection of a potential threat triggers creation of a security finding that pinpoints the specific container, and includes details such as:
      • pod ID
      • image ID
      • EKS cluster tags
      • executable path
      • process lineage
    • can be enabled both on existing and new EKS clusters 
    • available from March 2023 [Amazon GuardDuty now monitors runtime activity from containers running on Amazon EKS]
    • Needs to be enabled explicitly, after GuardDuty is enabled

In Introduction to Amazon GuardDuty we saw that to enable EKS Audit Log Monitoring we only need to provision and enable GuardDuty detector (aws_guardduty_detector | Resources | hashicorp/aws | Terraform | Terraform Registry):


resource "aws_guardduty_detector" "this" {
  enable = true
}

To enable GuardDuty EKS Runtime Monitoring (which is a prerequisite for activating GuardDuty EKS add-on) we need to use resource aws_guardduty_detector_feature.

To add and activate GuardDuty EKS add-on we have two choices:


We can enable EKS Runtime Monitoring AND automatic GuardDuty add-on management (automatic deployment and updates of the security agent in accounts where Runtime Monitoring is enabled) with the following:

resource "aws_guardduty_detector_feature" "eks_runtime_monitoring" {
  detector_id = aws_guardduty_detector.this.id
  name        = "EKS_RUNTIME_MONITORING"
  status      = "ENABLED"

  additional_configuration {
    name   = "EKS_ADDON_MANAGEMENT"
    status = "ENABLED"
  }
}

Note that if current account does not have required privileges, provisioning this resource will throw an error:

Error: updating GuardDuty Detector (92bXXXXXXXd1) Feature (EKS_RUNTIME_MONITORING): BadRequestException: The request is rejected because member accounts cannot manage specified resources or properties.

In AWS Console we can now see that EKS Runtime monitoring is now also enabled:






EKS monitoring is now covered in full (EKS Audit Logs monitoring + EKS Runtime monitoring + automatic GuardDuty EKS addon management).

After provisioning the EKS cluster we can see that GuardDuty picked it up:



We can check out the addons section in the EKS cluster:





Amazon GuardDuty EKS runtime monitoring

Amazon GuardDuty is a security monitoring service that analyzes and processes multiple supported data sources to identify any unexpected and potentially unauthorized suspicious or malicious activity within your AWS environment.

EKS Runtime Monitoring in Amazon GuardDuty provides runtime threat detection to protect EKS clusters in your AWS environment. EKS Runtime Monitoring uses a fully-managed EKS add-on (GuardDuty security agent) that adds visibility into individual Kubernetes container runtime activities, such as file access, process execution, and network connections.

Once you enable EKS Runtime Monitoring within Amazon GuardDuty and install the EKS add-on within your cluster, GuardDuty starts to consume runtime activity events from all EC2 hosts and containers in the cluster. These events are analyzed by GuardDuty for potential threats. As GuardDuty detects a potential threat, it generates a security finding. Navigate to the GuardDuty console to view the generated findings in your account.


Disabling EKS_ADDON_MANAGEMENT will keep the GurdDuty addon active but management will switch from Automatic to Manual:

resource "aws_guardduty_detector_feature" "eks_runtime_monitoring" {
  detector_id = aws_guardduty_detector.this.id
  name        = "EKS_RUNTIME_MONITORING"
  status      = "ENABLED"

  additional_configuration {
    name   = "EKS_ADDON_MANAGEMENT"
    status = "DISABLED"
  }
}

terraform plan output:

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_guardduty_detector_feature.eks_runtime_monitoring will be updated in-place
  ~ resource "aws_guardduty_detector_feature" "eks_runtime_monitoring" {
        id          = "e6c7f038a9682cf6ff6bb514c110a66f/EKS_RUNTIME_MONITORING"
        name        = "EKS_RUNTIME_MONITORING"
        # (2 unchanged attributes hidden)

      ~ additional_configuration {
            name   = "EKS_ADDON_MANAGEMENT"
          ~ status = "ENABLED" -> "DISABLED"
        }
    }

Plan: 0 to add, 1 to change, 0 to destroy.







resource "aws_guardduty_detector_feature" "eks_runtime_monitoring" {
  detector_id = aws_guardduty_detector.this.id
  name        = "EKS_RUNTIME_MONITORING"
  status      = "ENABLED"
}

terraform plan output:

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # aws_guardduty_detector_feature.eks_runtime_monitoring must be replaced
-/+ resource "aws_guardduty_detector_feature" "eks_runtime_monitoring" {
      ~ id          = "e6c7f038a9682cf6ff6bb514c110a66f/EKS_RUNTIME_MONITORING" -> (known after apply)
        name        = "EKS_RUNTIME_MONITORING"
        # (2 unchanged attributes hidden)

      - additional_configuration { # forces replacement
          - name   = "EKS_ADDON_MANAGEMENT" -> null # forces replacement
          - status = "DISABLED" -> null
        }
    }

Plan: 1 to add, 0 to change, 1 to destroy.

Nothing changed in the AWS Console - addon is active but manually managed.

Let's now disable EKS_RUNTIME_MONITORING feature completely:

resource "aws_guardduty_detector_feature" "eks_runtime_monitoring" {
  detector_id = aws_guardduty_detector.this.id
  name        = "EKS_RUNTIME_MONITORING"
  status      = "DISABLED"
}

terraform plan output:

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # aws_guardduty_detector_feature.eks_runtime_monitoring must be replaced
-/+ resource "aws_guardduty_detector_feature" "eks_runtime_monitoring" {
      ~ id          = "e6c7f038a9682cf6ff6bb514c110a66f/EKS_RUNTIME_MONITORING" -> (known after apply)
        name        = "EKS_RUNTIME_MONITORING"
      ~ status      = "ENABLED" -> "DISABLED"
        # (1 unchanged attribute hidden)

      - additional_configuration { # forces replacement
          - name   = "EKS_ADDON_MANAGEMENT" -> null # forces replacement
          - status = "DISABLED" -> null
        }
    }

Plan: 1 to add, 0 to change, 1 to destroy.

Addon is not present anymore:





Let's see what happens if we add GuardDuty addon to EKS cluster via resource aws_eks_addon.

For it we need to find out the correct name of the addon and also its version. We can use aws eks describe-addon-versions for that:

$ aws eks describe-addon-versions --output table --query 'addons[].{Name: addonName, Publisher: publisher}'
------------------------------------------------------------------------
|                         DescribeAddonVersions                        |
+---------------------------------------------+------------------------+
|                    Name                     |       Publisher        |
+---------------------------------------------+------------------------+
|  vpc-cni                                    |  eks                   |
|  upwind-security_upwind-operator            |  Upwind Security       |
|  upbound_universal-crossplane               |  upbound               |
|  tetrate-io_istio-distro                    |  tetrate-io            |
|  teleport_teleport                          |  teleport              |
|  stormforge_optimize-live                   |  StormForge            |
|  splunk_splunk-otel-collector-chart         |  Splunk                |
|  solo-io_istio-distro                       |  Solo.io               |
|  solo-io_gloo-mesh-starter-pack             |  Solo.io               |
|  solarwinds_swo-k8s-collector-addon         |  SolarWinds            |
|  snapshot-controller                        |  eks                   |
|  rafay-systems_rafay-operator               |  rafay-systems         |
|  new-relic_kubernetes-operator              |  New Relic             |
|  netapp_trident-operator                    |  NetApp Inc.           |
|  leaksignal_leakagent                       |  leaksignal            |
|  kubecost_kubecost                          |  kubecost              |
|  kube-proxy                                 |  eks                   |
|  kong_konnect-ri                            |  kong                  |
|  kasten_k10                                 |  Kasten by Veeam       |
|  haproxy-technologies_kubernetes-ingress-ee |  HAProxy Technologies  |
|  groundcover_agent                          |  groundcover           |
|  grafana-labs_kubernetes-monitoring         |  Grafana Labs          |
|  factorhouse_kpow                           |  factorhouse           |
|  eks-pod-identity-agent                     |  eks                   |
|  dynatrace_dynatrace-operator               |  dynatrace             |
|  datree_engine-pro                          |  datree                |
|  datadog_operator                           |  Datadog               |
|  cribl_cribledge                            |  Cribl                 |
|  coredns                                    |  eks                   |
|  cisco_cisco-cloud-observability-operators  |  Cisco Systems, Inc.   |
|  cisco_cisco-cloud-observability-collectors |  Cisco Systems, Inc.   |
|  calyptia_fluent-bit                        |  Calyptia Inc          |
|  aws-mountpoint-s3-csi-driver               |  s3                    |
aws-guardduty-agent                        |  eks                   |
|  aws-efs-csi-driver                         |  eks                   |
|  aws-ebs-csi-driver                         |  eks                   |
|  amazon-cloudwatch-observability            |  eks                   |
|  akuity_agent                               |  akuity                |
|  adot                                       |  eks                   |
|  accuknox_kubearmor                         |  AccuKnox              |
+---------------------------------------------+------------------------+

Now when we know the name of the addon we're interesting in (aws-guardduty-agent) we can check its latest version:

$ aws eks describe-addon-versions --output table --addon-name=aws-guardduty-agent --query 'addons[].{Name: addonName, Publisher: publisher, Version: addonVersions[0].addonVersion}'
-----------------------------------------------------------
|                  DescribeAddonVersions                  |
+----------------------+------------+---------------------+
|         Name         | Publisher  |       Version       |
+----------------------+------------+---------------------+
|  aws-guardduty-agent |  eks       |  v1.6.1-eksbuild.1  |
+----------------------+------------+---------------------+

Let's now add this resource to our eks cluster (Terraform module) and provision it:

resource "aws_eks_addon" "guardduty" {
  cluster_name                = aws_eks_cluster.this.name
  addon_name                  = "aws-guardduty-agent"
  addon_version               = "v1.6.1-eksbuild.1" 
  resolve_conflicts_on_update = "OVERWRITE"
}


terraform plan output:


Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
  ~ update in-place

Terraform will perform the following actions:

  # module.voting-app-eks-cluster.aws_eks_addon.guardduty will be created
  + resource "aws_eks_addon" "guardduty" {
      + addon_name                  = "aws-guardduty-agent"
      + addon_version               = "v1.6.1-eksbuild.1"
      + arn                         = (known after apply)
      + cluster_name                = "example-voting-app"
      + configuration_values        = (known after apply)
      + created_at                  = (known after apply)
      + id                          = (known after apply)
      + modified_at                 = (known after apply)
      + resolve_conflicts_on_update = "OVERWRITE"
      + tags_all                    = (known after apply)
    }

During the provisioning, AWS Console shows the accurate status:



Provisioning the addon was not successful after 20 minutes timeout:

module.voting-app-eks-cluster.aws_eks_addon.guardduty: Still creating... [19m30s elapsed]
module.voting-app-eks-cluster.aws_eks_addon.guardduty: Still creating... [19m40s elapsed]
module.voting-app-eks-cluster.aws_eks_addon.guardduty: Still creating... [19m50s elapsed]
module.voting-app-eks-cluster.aws_eks_addon.guardduty: Still creating... [20m0s elapsed]
│ Warning: Running terraform apply again will remove the kubernetes add-on and attempt to create it again effectively purging previous add-on configuration
│ 
│   with module.voting-app-eks-cluster.aws_eks_addon.guardduty,
│   on ../../modules/eks-cluster/main.tf line 174, in resource "aws_eks_addon" "guardduty":
│  174: resource "aws_eks_addon" "guardduty" {
│ 
│ Error: waiting for EKS Add-On (example-voting-app:aws-guardduty-agent) create: timeout while waiting for state to become 'ACTIVE' (last state: 'CREATING', timeout: 20m0s)
│ 
│   with module.voting-app-eks-cluster.aws_eks_addon.guardduty,
│   on ../../modules/eks-cluster/main.tf line 174, in resource "aws_eks_addon" "guardduty":
│  174: resource "aws_eks_addon" "guardduty" {

After this I commented out resource "aws_eks_addon" "guardduty" {...} and executed terraform apply so this resource was removed.

The reason for failing to install this add-on might be the fact that GuardDuty Runtime monitoring is not enabled for EKS. 

Let's try to manually add this add-on, without enabling the EKS Runtime monitoring:











I was not able to stop creation of this add-on in AWS Console but it worked via AWS CLI:

$ aws eks delete-addon --cluster-name example-voting-app --addon-name aws-guardduty-agent --profile terraform --region eu-west-2
{
    "addon": {
        "addonName": "aws-guardduty-agent",
        "clusterName": "example-voting-app",
        "status": "DELETING",
        "addonVersion": "v1.6.1-eksbuild.1",
        "health": {
            "issues": []
        },
        "addonArn": "arn:aws:eks:eu-west-2:471112786618:addon/example-voting-app/aws-guardduty-agent/cec7f182-1052-421b-76db-9fa8e353af80",
        "createdAt": "2024-06-04T12:30:49.852000+01:00",
        "modifiedAt": "2024-06-04T12:42:29.170000+01:00",
        "tags": {}
    }
}

When I was adding this add-on I didn't enable EKS Runtime Monitoring although its activation is required for an optimal operating experience:

Ensure to enable EKS Runtime Monitoring within Amazon GuardDuty.


Let's enable EKS Runtime Monitoring via: 

resource "aws_guardduty_detector_feature" "eks_runtime_monitoring" {
  detector_id = aws_guardduty_detector.this.id
  name        = "EKS_RUNTIME_MONITORING"
  status      = "ENABLED"
}



After enabling  EKS Runtime Monitoring I tried to provision resource "aws_eks_addon" "guardduty" {...} and again it started creating it only to error out after 20 minutes.




Let's try to enable RUNTIME_MONITORING feature (which includes EKS_RUNTIME_MONITORING):

resource "aws_guardduty_detector_feature" "eks_runtime_monitoring" {
  detector_id = aws_guardduty_detector.this.id
  name        = "RUNTIME_MONITORING"
  status      = "ENABLED"
}

We can now see in AWS Console that runtime monitoring is enabled for EKS, ECS and EC2:





Provisioning resource "aws_eks_addon" "guardduty" {...} again error out after 20 minutes.




I will try to find out what prevents successful provisioning of the aws_eks_addon resource.


After Disabling GuardDuty Detector


Resource aws_guardduty_detector_feature can be provisioned only if aws_guardduty_detector is enabled. If we try to provision it while aws_guardduty_detector is disabled, we'll get an error:

Error: updating GuardDuty Detector Feature (RUNTIME_MONITORING): BadRequestException: The request failed because you cannot enable a data source while the detector is disabled.