elastic / terraform-provider-elasticstack

Terraform provider for Elastic Stack
https://registry.terraform.io/providers/elastic/elasticstack/latest/docs
Apache License 2.0
164 stars 87 forks source link

[Feature] Support Synthetics APIs as Terraform resources #610

Open BenB196 opened 5 months ago

BenB196 commented 5 months ago

Is your feature request related to a problem? Please describe.

I would like to be able to programmatically manage Elastic Synthetics via Terraform.

Describe the resource you would like to have implemented.

https://github.com/elastic/kibana/issues/169547 recently introduced public APIs for these resources.

Describe the solution you'd like

This Terraform provider to be able to manage Synthetics resources.

Describe alternatives you've considered

Hacking something together via https://registry.terraform.io/providers/Mastercard/restapi/latest/docs

Additional context

https://github.com/elastic/cloud-on-k8s/issues/7724

russellproud commented 3 months ago

Coming here to add some support to this request.

Utilising the mastercard/restapi provider works for create and update, but is unable to manage the delete lifecycle as a result of the DELETE endpoint for Synthetics requires the Id to be included in the request body, not the uri.

@BenB196 have you found a method to achieve the delete via mastercard/restapi?

tobio commented 1 month ago

Duplicates https://github.com/elastic/terraform-provider-elasticstack/issues/335

mholttech commented 1 month ago

love the progress here. Any plans to expand for icmp and browser monitor types?

doom160 commented 3 weeks ago

@russellproud @mholttech I used this code (quite an amount of transformation) for elastic cloud 8.12.2, so far it works for most of the monitors (even for deletion), i think this endpoint is different for new version

resource "restapi_object" "main" {
  for_each  = { for idx, synthetic in local.synthetic_request_body : synthetic.name => synthetic }
  path      = "/internal/synthetics/service/monitors"
  read_path = "/internal/synthetics/service/monitor/{id}?decrypted=true"
  data      = jsonencode(each.value)
}

locals {
  synthetic_request_body = [
    for synthetic_url in var.synthetic_monitors : merge({
      name : (var.prefix_name == "") ? "${synthetic_url.url}-${synthetic_url.env}" : "${var.prefix_name}-${synthetic_url.url}-${synthetic_url.env}",
      urls : synthetic_url.url
      type : can(synthetic_url.form_monitor_type) ? ((synthetic_url.form_monitor_type == "single" || synthetic_url.form_monitor_type == "multistep") ? "browser" : synthetic_url.form_monitor_type) : "http",
      form_monitor_type : can(synthetic_url.form_monitor_type) ? synthetic_url.form_monitor_type : "http",

      enabled : can(synthetic_url.enable) ? synthetic_url.enable : true,
      alert : {
        status : {
          enabled : can(synthetic_url.enable) ? synthetic_url.enable : true
        },
        tls : {
          enabled : can(synthetic_url.enable) ? synthetic_url.enable : true
        }
      },
      schedule : {
        number : can(synthetic_url.schedule_number) ? synthetic_url.schedule_number : "1",
        unit : can(synthetic_url.schedule_unit) ? synthetic_url.schedule_unit : "m"
      },
      config_id : uuidv5("url", synthetic_url.url),
      tags : concat(var.default_tags, can(synthetic_url.tags) ? synthetic_url.tags : []),
      timeout : can(synthetic_url.timeout) ? synthetic_url.timeout : "5",
      locations : [(lower(synthetic_url.env) == "sg") ?
        {
          id : "asia-southeast1-a",
          label : "Asia/Pacific - Singapore",
          geo : {
            lat : 1.29027,
            lon : 103.851959
          },
          isServiceManaged : true
          } : {
          id : var.agent_policies[synthetic_url.env].policy_id,
          label : (var.suffix == "") ? "${var.prefix_name}-${synthetic_url.env}" : "${var.prefix_name}-${synthetic_url.env}-${var.suffix}"
          isServiceManaged : false
          geo : {
            lat : 0,
            lon : 0
          }
        }
      ],
      max_attempts : can(synthetic_url.max_attempts) ? synthetic_url.max_attempts : 2,
      max_redirects : can(synthetic_url.max_redirects) ? synthetic_url.max_attempts : "1",
      },
      // Add synthetic script
      can(synthetic_url.form_monitor_type) ? ((synthetic_url.form_monitor_type == "single") ?
        { "source.inline.script" : templatefile("${path.module}/single/single.js.tftpl", { url = synthetic_url.url }) } :
        (synthetic_url.form_monitor_type == "multistep") ?
      { "source.inline.script" : file(synthetic_url.file) } : {}) : {}
    )
  ]
}

synthetic_monitors = [
        {   
            // Simple (HTTP)
            url = "https://www.elastic.co"
            env = "prd"
        }, {
            // Complex
            url = "https://www.microsoft.com"
            env = "dev"
            form_monitor_type = "http" //default
            enable = false
            schedule_number = "5"
            schedule_unit = "m"
            timeout = 10
            max_attempts = 2
            max_redirects = 1
            tags = ["additional_tags"]
        }, {
            //Multi-step
            url = "https://www.microsoft.com/en-sg"
            env = "sg"
            // mandatory to point to a playwright .js file
            file = "synthetic/example.js"
            form_monitor_type = "multistep"
            enable = true
        }, {
            //Single-step
            url = "https://www.elastic.co"
            env = "sg"
            form_monitor_type = "single"
            enable = true
        },  {
            // TCP
            url = "google.com.sg:443"
            env = "dev"
            form_monitor_type = "tcp"
            enable = false
        }, {
            // ICMP
            url = "google.com.sg"
            env = "dev"
            form_monitor_type = "icmp"
            enable = false
        }
    ]