diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8d1795ea4..6986696ee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
### Added
- New resource `elasticstack_elasticsearch_data_stream` to manage Elasticsearch [data streams](https://www.elastic.co/guide/en/elasticsearch/reference/current/data-streams.html) ([#45](https://github.com/elastic/terraform-provider-elasticstack/pull/45))
- New resource `elasticstack_elasticsearch_ingest_pipeline` to manage Elasticsearch [ingest pipelines](https://www.elastic.co/guide/en/elasticsearch/reference/7.16/ingest.html) ([#56](https://github.com/elastic/terraform-provider-elasticstack/issues/56))
+- New resource `elasticstack_elasticsearch_component_template` to manage Elasticsearch [component templates](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-component-template.html) ([#39](https://github.com/elastic/terraform-provider-elasticstack/pull/39))
### Fixed
- Update only changed index settings ([#52](https://github.com/elastic/terraform-provider-elasticstack/issues/52))
diff --git a/docs/resources/elasticsearch_component_template.md b/docs/resources/elasticsearch_component_template.md
new file mode 100644
index 000000000..e7acc3ac0
--- /dev/null
+++ b/docs/resources/elasticsearch_component_template.md
@@ -0,0 +1,104 @@
+---
+subcategory: "Index"
+layout: ""
+page_title: "Elasticstack: elasticstack_elasticsearch_component_template Resource"
+description: |-
+ Creates or updates a component template.
+---
+
+# Resource: elasticstack_elasticsearch_component_template
+
+Creates or updates a component template. Component templates are building blocks for constructing index templates that specify index mappings, settings, and aliases. See, https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-component-template.html
+
+## Example Usage
+
+```terraform
+provider "elasticstack" {
+ elasticsearch {}
+}
+
+resource "elasticstack_elasticsearch_component_template" "my_template" {
+ name = "my_template"
+
+ template {
+ aliases {
+ name = "my_template_test"
+ }
+
+ settings = jsonencode({
+ number_of_shards = "3"
+ })
+ }
+}
+
+resource "elasticstack_elasticsearch_index_template" "my_template" {
+ name = "my_data_stream"
+
+ index_patterns = ["stream*"]
+ composed_of = [elasticstack_elasticsearch_component_template.my_template.name]
+}
+```
+
+
+## Schema
+
+### Required
+
+- **name** (String) Name of the component template to create.
+- **template** (Block List, Min: 1, Max: 1) Template to be applied. It may optionally include an aliases, mappings, or settings configuration. (see [below for nested schema](#nestedblock--template))
+
+### Optional
+
+- **elasticsearch_connection** (Block List, Max: 1) Used to establish connection to Elasticsearch server. Overrides environment variables if present. (see [below for nested schema](#nestedblock--elasticsearch_connection))
+- **metadata** (String) Optional user metadata about the component template.
+- **version** (Number) Version number used to manage component templates externally.
+
+### Read-Only
+
+- **id** (String) Internal identifier of the resource
+
+
+### Nested Schema for `template`
+
+Optional:
+
+- **alias** (Block Set) Alias to add. (see [below for nested schema](#nestedblock--template--alias))
+- **mappings** (String) Mapping for fields in the index.
+- **settings** (String) Configuration options for the index. See, https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html#index-modules-settings
+
+
+### Nested Schema for `template.alias`
+
+Required:
+
+- **name** (String) The alias name. Index alias names support date math. See, https://www.elastic.co/guide/en/elasticsearch/reference/current/date-math-index-names.html
+
+Optional:
+
+- **filter** (String) Query used to limit documents the alias can access.
+- **index_routing** (String) Value used to route indexing operations to a specific shard. If specified, this overwrites the routing value for indexing operations.
+- **is_hidden** (Boolean) If true, the alias is hidden.
+- **is_write_index** (Boolean) If true, the index is the write index for the alias.
+- **routing** (String) Value used to route indexing and search operations to a specific shard.
+- **search_routing** (String) Value used to route search operations to a specific shard. If specified, this overwrites the routing value for search operations.
+
+
+
+
+### Nested Schema for `elasticsearch_connection`
+
+Optional:
+
+- **ca_file** (String) Path to a custom Certificate Authority certificate
+- **endpoints** (List of String, Sensitive) A list of endpoints the Terraform provider will point to. They must include the http(s) schema and port number.
+- **insecure** (Boolean) Disable TLS certificate validation
+- **password** (String, Sensitive) A password to use for API authentication to Elasticsearch.
+- **username** (String) A username to use for API authentication to Elasticsearch.
+
+## Import
+
+Import is supported using the following syntax:
+
+```shell
+terraform import elasticstack_elasticsearch_component_template.my_template /
+```
diff --git a/examples/resources/elasticstack_elasticsearch_component_template/import.sh b/examples/resources/elasticstack_elasticsearch_component_template/import.sh
new file mode 100644
index 000000000..a68a2436c
--- /dev/null
+++ b/examples/resources/elasticstack_elasticsearch_component_template/import.sh
@@ -0,0 +1 @@
+terraform import elasticstack_elasticsearch_component_template.my_template /
diff --git a/examples/resources/elasticstack_elasticsearch_component_template/resource.tf b/examples/resources/elasticstack_elasticsearch_component_template/resource.tf
new file mode 100644
index 000000000..f3565a994
--- /dev/null
+++ b/examples/resources/elasticstack_elasticsearch_component_template/resource.tf
@@ -0,0 +1,24 @@
+provider "elasticstack" {
+ elasticsearch {}
+}
+
+resource "elasticstack_elasticsearch_component_template" "my_template" {
+ name = "my_template"
+
+ template {
+ aliases {
+ name = "my_template_test"
+ }
+
+ settings = jsonencode({
+ number_of_shards = "3"
+ })
+ }
+}
+
+resource "elasticstack_elasticsearch_index_template" "my_template" {
+ name = "my_data_stream"
+
+ index_patterns = ["stream*"]
+ composed_of = [elasticstack_elasticsearch_component_template.my_template.name]
+}
diff --git a/internal/clients/index.go b/internal/clients/index.go
index 2a0f50e29..15a42251d 100644
--- a/internal/clients/index.go
+++ b/internal/clients/index.go
@@ -79,6 +79,72 @@ func (a *ApiClient) DeleteElasticsearchIlm(policyName string) diag.Diagnostics {
return diags
}
+func (a *ApiClient) PutElasticsearchComponentTemplate(template *models.ComponentTemplate) diag.Diagnostics {
+ var diags diag.Diagnostics
+ templateBytes, err := json.Marshal(template)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ log.Printf("[TRACE] sending request to ES: %s to create component template '%s' ", templateBytes, template.Name)
+
+ res, err := a.es.Cluster.PutComponentTemplate(template.Name, bytes.NewReader(templateBytes))
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ defer res.Body.Close()
+ if diags := utils.CheckError(res, "Unable to create component template"); diags.HasError() {
+ return diags
+ }
+
+ return diags
+}
+
+func (a *ApiClient) GetElasticsearchComponentTemplate(templateName string) (*models.ComponentTemplateResponse, diag.Diagnostics) {
+ var diags diag.Diagnostics
+ req := a.es.Cluster.GetComponentTemplate.WithName(templateName)
+ res, err := a.es.Cluster.GetComponentTemplate(req)
+ if err != nil {
+ return nil, diag.FromErr(err)
+ }
+ defer res.Body.Close()
+ if res.StatusCode == http.StatusNotFound {
+ return nil, nil
+ }
+ if diags := utils.CheckError(res, "Unable to request index template."); diags.HasError() {
+ return nil, diags
+ }
+
+ var componentTemplates models.ComponentTemplatesResponse
+ if err := json.NewDecoder(res.Body).Decode(&componentTemplates); err != nil {
+ return nil, diag.FromErr(err)
+ }
+
+ // we requested only 1 template
+ if len(componentTemplates.ComponentTemplates) != 1 {
+ diags = append(diags, diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Wrong number of templates returned",
+ Detail: fmt.Sprintf("Elasticsearch API returned %d when requested '%s' component template.", len(componentTemplates.ComponentTemplates), templateName),
+ })
+ return nil, diags
+ }
+ tpl := componentTemplates.ComponentTemplates[0]
+ return &tpl, diags
+}
+
+func (a *ApiClient) DeleteElasticsearchComponentTemplate(templateName string) diag.Diagnostics {
+ var diags diag.Diagnostics
+ res, err := a.es.Cluster.DeleteComponentTemplate(templateName)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ defer res.Body.Close()
+ if diags := utils.CheckError(res, "Unable to delete component template"); diags.HasError() {
+ return diags
+ }
+ return diags
+}
+
func (a *ApiClient) PutElasticsearchIndexTemplate(template *models.IndexTemplate) diag.Diagnostics {
var diags diag.Diagnostics
templateBytes, err := json.Marshal(template)
diff --git a/internal/elasticsearch/index/component_template.go b/internal/elasticsearch/index/component_template.go
new file mode 100644
index 000000000..dbb34b906
--- /dev/null
+++ b/internal/elasticsearch/index/component_template.go
@@ -0,0 +1,298 @@
+package index
+
+import (
+ "context"
+ "encoding/json"
+ "strings"
+
+ "github.com/elastic/terraform-provider-elasticstack/internal/clients"
+ "github.com/elastic/terraform-provider-elasticstack/internal/models"
+ "github.com/elastic/terraform-provider-elasticstack/internal/utils"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+)
+
+func ResourceComponentTemplate() *schema.Resource {
+ // NOTE: component_template and index_template uses the same schema
+ componentTemplateSchema := map[string]*schema.Schema{
+ "id": {
+ Description: "Internal identifier of the resource",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "name": {
+ Description: "Name of the component template to create.",
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ },
+ "metadata": {
+ Description: "Optional user metadata about the component template.",
+ Type: schema.TypeString,
+ Optional: true,
+ ValidateFunc: validation.StringIsJSON,
+ DiffSuppressFunc: utils.DiffJsonSuppress,
+ },
+ "template": {
+ Description: "Template to be applied. It may optionally include an aliases, mappings, or settings configuration.",
+ Type: schema.TypeList,
+ Required: true,
+ MaxItems: 1,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "alias": {
+ Description: "Alias to add.",
+ Type: schema.TypeSet,
+ Optional: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Description: "The alias name. Index alias names support date math. See, https://www.elastic.co/guide/en/elasticsearch/reference/current/date-math-index-names.html",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "filter": {
+ Description: "Query used to limit documents the alias can access.",
+ Type: schema.TypeString,
+ Optional: true,
+ DiffSuppressFunc: utils.DiffJsonSuppress,
+ ValidateFunc: validation.StringIsJSON,
+ },
+ "index_routing": {
+ Description: "Value used to route indexing operations to a specific shard. If specified, this overwrites the routing value for indexing operations.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "is_hidden": {
+ Description: "If true, the alias is hidden.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ },
+ "is_write_index": {
+ Description: "If true, the index is the write index for the alias.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: false,
+ },
+ "routing": {
+ Description: "Value used to route indexing and search operations to a specific shard.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "search_routing": {
+ Description: "Value used to route search operations to a specific shard. If specified, this overwrites the routing value for search operations.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ },
+ },
+ },
+ "mappings": {
+ Description: "Mapping for fields in the index.",
+ Type: schema.TypeString,
+ Optional: true,
+ DiffSuppressFunc: utils.DiffJsonSuppress,
+ ValidateFunc: validation.StringIsJSON,
+ },
+ "settings": {
+ Description: "Configuration options for the index. See, https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html#index-modules-settings",
+ Type: schema.TypeString,
+ Optional: true,
+ DiffSuppressFunc: utils.DiffIndexSettingSuppress,
+ ValidateFunc: validation.StringIsJSON,
+ },
+ },
+ },
+ },
+ "version": {
+ Description: "Version number used to manage component templates externally.",
+ Type: schema.TypeInt,
+ Optional: true,
+ },
+ }
+
+ utils.AddConnectionSchema(componentTemplateSchema)
+
+ return &schema.Resource{
+ Description: "Creates or updates a component template. Component templates are building blocks for constructing index templates that specify index mappings, settings, and aliases. See, https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-component-template.html",
+
+ CreateContext: resourceComponentTemplatePut,
+ UpdateContext: resourceComponentTemplatePut,
+ ReadContext: resourceComponentTemplateRead,
+ DeleteContext: resourceComponentTemplateDelete,
+
+ Importer: &schema.ResourceImporter{
+ StateContext: schema.ImportStatePassthroughContext,
+ },
+
+ Schema: componentTemplateSchema,
+ }
+}
+
+func resourceComponentTemplatePut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ client, err := clients.NewApiClient(d, meta)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ componentId := d.Get("name").(string)
+ id, diags := client.ID(componentId)
+ if diags.HasError() {
+ return diags
+ }
+ var componentTemplate models.ComponentTemplate
+ componentTemplate.Name = componentId
+
+ if v, ok := d.GetOk("metadata"); ok {
+ metadata := make(map[string]interface{})
+ if err := json.NewDecoder(strings.NewReader(v.(string))).Decode(&metadata); err != nil {
+ return diag.FromErr(err)
+ }
+ componentTemplate.Meta = metadata
+ }
+
+ if v, ok := d.GetOk("template"); ok {
+ // only one template block allowed to be declared
+ definedTempl := v.([]interface{})[0].(map[string]interface{})
+ definedAliases := definedTempl["alias"].(*schema.Set)
+ templ := models.Template{}
+
+ aliases := make(map[string]models.IndexAlias, definedAliases.Len())
+ for _, a := range definedAliases.List() {
+ alias := a.(map[string]interface{})
+ templAlias := models.IndexAlias{}
+
+ if f, ok := alias["filter"]; ok {
+ if f.(string) != "" {
+ filterMap := make(map[string]interface{})
+ if err := json.Unmarshal([]byte(f.(string)), &filterMap); err != nil {
+ return diag.FromErr(err)
+ }
+ templAlias.Filter = filterMap
+ }
+ }
+ if ir, ok := alias["index_routing"]; ok {
+ templAlias.IndexRouting = ir.(string)
+ }
+ templAlias.IsHidden = alias["is_hidden"].(bool)
+ templAlias.IsWriteIndex = alias["is_write_index"].(bool)
+ if r, ok := alias["routing"]; ok {
+ templAlias.Routing = r.(string)
+ }
+ if sr, ok := alias["search_routing"]; ok {
+ templAlias.SearchRouting = sr.(string)
+ }
+
+ aliases[alias["name"].(string)] = templAlias
+ }
+ templ.Aliases = aliases
+
+ if mappings, ok := definedTempl["mappings"]; ok {
+ if mappings.(string) != "" {
+ maps := make(map[string]interface{})
+ if err := json.Unmarshal([]byte(mappings.(string)), &maps); err != nil {
+ return diag.FromErr(err)
+ }
+ templ.Mappings = maps
+ }
+ }
+
+ if settings, ok := definedTempl["settings"]; ok {
+ if settings.(string) != "" {
+ sets := make(map[string]interface{})
+ if err := json.Unmarshal([]byte(settings.(string)), &sets); err != nil {
+ return diag.FromErr(err)
+ }
+ templ.Settings = sets
+ }
+ }
+
+ componentTemplate.Template = &templ
+ }
+
+ if v, ok := d.GetOk("version"); ok {
+ definedVer := v.(int)
+ componentTemplate.Version = &definedVer
+ }
+
+ if diags := client.PutElasticsearchComponentTemplate(&componentTemplate); diags.HasError() {
+ return diags
+ }
+
+ d.SetId(id.String())
+ return resourceComponentTemplateRead(ctx, d, meta)
+}
+
+func resourceComponentTemplateRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ var diags diag.Diagnostics
+ client, err := clients.NewApiClient(d, meta)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ compId, diags := clients.CompositeIdFromStr(d.Id())
+ if diags.HasError() {
+ return diags
+ }
+ templateId := compId.ResourceId
+
+ tpl, diags := client.GetElasticsearchComponentTemplate(templateId)
+ if tpl == nil && diags == nil {
+ d.SetId("")
+ return diags
+ }
+ if diags.HasError() {
+ return diags
+ }
+
+ // set the fields
+ if err := d.Set("name", tpl.Name); err != nil {
+ return diag.FromErr(err)
+ }
+
+ if tpl.ComponentTemplate.Meta != nil {
+ metadata, err := json.Marshal(tpl.ComponentTemplate.Meta)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ if err := d.Set("metadata", string(metadata)); err != nil {
+ return diag.FromErr(err)
+ }
+ }
+
+ if tpl.ComponentTemplate.Template != nil {
+ template, diags := flattenTemplateData(tpl.ComponentTemplate.Template)
+ if diags.HasError() {
+ return diags
+ }
+
+ if err := d.Set("template", template); err != nil {
+ return diag.FromErr(err)
+ }
+ }
+
+ if err := d.Set("version", tpl.ComponentTemplate.Version); err != nil {
+ return diag.FromErr(err)
+ }
+
+ return diags
+}
+
+func resourceComponentTemplateDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ var diags diag.Diagnostics
+ client, err := clients.NewApiClient(d, meta)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ id := d.Id()
+ compId, diags := clients.CompositeIdFromStr(id)
+ if diags.HasError() {
+ return diags
+ }
+ if diags := client.DeleteElasticsearchComponentTemplate(compId.ResourceId); diags.HasError() {
+ return diags
+ }
+ d.SetId("")
+ return diags
+}
diff --git a/internal/elasticsearch/index/component_template_test.go b/internal/elasticsearch/index/component_template_test.go
new file mode 100644
index 000000000..5739ac3be
--- /dev/null
+++ b/internal/elasticsearch/index/component_template_test.go
@@ -0,0 +1,76 @@
+package index_test
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/elastic/terraform-provider-elasticstack/internal/acctest"
+ "github.com/elastic/terraform-provider-elasticstack/internal/clients"
+ sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+)
+
+func TestAccResourceComponentTemplate(t *testing.T) {
+ // generate a random username
+ templateName := sdkacctest.RandStringFromCharSet(10, sdkacctest.CharSetAlphaNum)
+
+ resource.UnitTest(t, resource.TestCase{
+ PreCheck: func() { acctest.PreCheck(t) },
+ CheckDestroy: checkResourceComponentTemplateDestroy,
+ ProviderFactories: acctest.Providers,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccResourceComponentTemplateCreate(templateName),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr("elasticstack_elasticsearch_component_template.test", "name", templateName),
+ resource.TestCheckResourceAttr("elasticstack_elasticsearch_component_template.test", "template.0.alias.0.name", "my_template_test"),
+ resource.TestCheckResourceAttr("elasticstack_elasticsearch_component_template.test", "template.0.settings", `{"index":{"number_of_shards":"3"}}`),
+ ),
+ },
+ },
+ })
+}
+
+func testAccResourceComponentTemplateCreate(name string) string {
+ return fmt.Sprintf(`
+provider "elasticstack" {
+ elasticsearch {}
+}
+
+resource "elasticstack_elasticsearch_component_template" "test" {
+ name = "%s"
+
+ template {
+ alias {
+ name = "my_template_test"
+ }
+
+ settings = jsonencode({
+ number_of_shards = "3"
+ })
+ }
+}`, name)
+}
+
+func checkResourceComponentTemplateDestroy(s *terraform.State) error {
+ client := acctest.Provider.Meta().(*clients.ApiClient)
+
+ for _, rs := range s.RootModule().Resources {
+ if rs.Type != "elasticstack_elasticsearch_component_template" {
+ continue
+ }
+ compId, _ := clients.CompositeIdFromStr(rs.Primary.ID)
+
+ req := client.GetESClient().Cluster.GetComponentTemplate.WithName(compId.ResourceId)
+ res, err := client.GetESClient().Cluster.GetComponentTemplate(req)
+ if err != nil {
+ return err
+ }
+
+ if res.StatusCode != 404 {
+ return fmt.Errorf("Component template (%s) still exists", compId.ResourceId)
+ }
+ }
+ return nil
+}
diff --git a/internal/models/models.go b/internal/models/models.go
index a75ac8254..2ad83ca95 100644
--- a/internal/models/models.go
+++ b/internal/models/models.go
@@ -67,6 +67,22 @@ type IndexTemplateResponse struct {
IndexTemplate IndexTemplate `json:"index_template"`
}
+type ComponentTemplate struct {
+ Name string `json:"-"`
+ Meta map[string]interface{} `json:"_meta,omitempty"`
+ Template *Template `json:"template,omitempty"`
+ Version *int `json:"version,omitempty"`
+}
+
+type ComponentTemplatesResponse struct {
+ ComponentTemplates []ComponentTemplateResponse `json:"component_templates"`
+}
+
+type ComponentTemplateResponse struct {
+ Name string `json:"name"`
+ ComponentTemplate ComponentTemplate `json:"component_template"`
+}
+
type PolicyDefinition struct {
Policy Policy `json:"policy"`
Modified string `json:"modified_date"`
diff --git a/internal/provider/provider.go b/internal/provider/provider.go
index f6ecb80d9..b693f4165 100644
--- a/internal/provider/provider.go
+++ b/internal/provider/provider.go
@@ -70,6 +70,7 @@ func New(version string) func() *schema.Provider {
},
ResourcesMap: map[string]*schema.Resource{
"elasticstack_elasticsearch_cluster_settings": cluster.ResourceSettings(),
+ "elasticstack_elasticsearch_component_template": index.ResourceComponentTemplate(),
"elasticstack_elasticsearch_data_stream": index.ResourceDataStream(),
"elasticstack_elasticsearch_index": index.ResourceIndex(),
"elasticstack_elasticsearch_index_lifecycle": index.ResourceIlm(),
diff --git a/templates/resources/elasticsearch_component_template.md.tmpl b/templates/resources/elasticsearch_component_template.md.tmpl
new file mode 100644
index 000000000..a1e9d7830
--- /dev/null
+++ b/templates/resources/elasticsearch_component_template.md.tmpl
@@ -0,0 +1,23 @@
+---
+subcategory: "Index"
+layout: ""
+page_title: "Elasticstack: elasticstack_elasticsearch_component_template Resource"
+description: |-
+ Creates or updates a component template.
+---
+
+# Resource: elasticstack_elasticsearch_component_template
+
+Creates or updates a component template. Component templates are building blocks for constructing index templates that specify index mappings, settings, and aliases. See, https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-component-template.html
+
+## Example Usage
+
+{{ tffile "examples/resources/elasticstack_elasticsearch_component_template/resource.tf" }}
+
+{{ .SchemaMarkdown | trimspace }}
+
+## Import
+
+Import is supported using the following syntax:
+
+{{ codefile "shell" "examples/resources/elasticstack_elasticsearch_component_template/import.sh" }}