From 0b5eb5003ac15d8885c6dfb76189e19c11e67d0c Mon Sep 17 00:00:00 2001 From: Alex Wilcox Date: Thu, 12 Dec 2024 22:29:54 +0000 Subject: [PATCH] Add TLSA records --- .../dns/dns_tlsa_record_data_source.go | 147 ++++++++ .../dns/dns_tlsa_record_data_source_test.go | 46 +++ .../services/dns/dns_tlsa_record_resource.go | 324 ++++++++++++++++++ .../dns/dns_tlsa_record_resource_test.go | 311 +++++++++++++++++ internal/services/dns/registration.go | 2 + website/docs/d/dns_tlsa_record.html.markdown | 65 ++++ website/docs/r/dns_tlsa_record.html.markdown | 101 ++++++ 7 files changed, 996 insertions(+) create mode 100644 internal/services/dns/dns_tlsa_record_data_source.go create mode 100644 internal/services/dns/dns_tlsa_record_data_source_test.go create mode 100644 internal/services/dns/dns_tlsa_record_resource.go create mode 100644 internal/services/dns/dns_tlsa_record_resource_test.go create mode 100644 website/docs/d/dns_tlsa_record.html.markdown create mode 100644 website/docs/r/dns_tlsa_record.html.markdown diff --git a/internal/services/dns/dns_tlsa_record_data_source.go b/internal/services/dns/dns_tlsa_record_data_source.go new file mode 100644 index 0000000000000..69c2346624442 --- /dev/null +++ b/internal/services/dns/dns_tlsa_record_data_source.go @@ -0,0 +1,147 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dns + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2023-07-01-preview/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +var ( + _ sdk.DataSource = DnsTLSARecordDataResource{} +) + +type DnsTLSARecordDataResource struct{} + +func (DnsTLSARecordDataResource) ModelObject() interface{} { + return &DnsTLSARecordDataSourceModel{} +} + +func (d DnsTLSARecordDataResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return validate.ValidateRecordTypeID(recordsets.RecordTypeTLSA) +} + +func (DnsTLSARecordDataResource) ResourceType() string { + return "azurerm_dns_tlsa_record" +} + +type DnsTLSARecordDataSourceModel struct { + Name string `tfschema:"name"` + ResourceGroupName string `tfschema:"resource_group_name"` + ZoneName string `tfschema:"zone_name"` + Ttl int64 `tfschema:"ttl"` + Record []DnsTLSARecordResourceRecord `tfschema:"record"` + Tags map[string]string `tfschema:"tags"` + Fqdn string `tfschema:"fqdn"` +} + +func (DnsTLSARecordDataResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + } +} + +func (DnsTLSARecordDataResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "record": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "matching_type": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "selector": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "usage": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "cert_association_data": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + Set: resourceDnsTLSARecordHash, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + } +} + +func (DnsTLSARecordDataResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + var state DnsTLSARecordDataSourceModel + if err := metadata.Decode(&state); err != nil { + return err + } + + client := metadata.Client.Dns.RecordSets + subscriptionId := metadata.Client.Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, state.ResourceGroupName, state.ZoneName, recordsets.RecordTypeTLSA, state.Name) + + resp, err := client.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("record %s not found", id) + } + return fmt.Errorf("retrieving %s: %+v", id, err) + } + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + state.Ttl = pointer.From(props.TTL) + state.Fqdn = pointer.From(props.Fqdn) + + state.Record = flattenAzureRmDnsTLSARecords(props.TLSARecords) + + state.Tags = pointer.From(props.Metadata) + + } + } + metadata.SetID(id) + + return metadata.Encode(&state) + }, + } +} diff --git a/internal/services/dns/dns_tlsa_record_data_source_test.go b/internal/services/dns/dns_tlsa_record_data_source_test.go new file mode 100644 index 0000000000000..cb17443f5d1e5 --- /dev/null +++ b/internal/services/dns/dns_tlsa_record_data_source_test.go @@ -0,0 +1,46 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type DnsTLSARecordDataSource struct{} + +func TestAccDataSourceDnsTLSARecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_dns_tlsa_record", "test") + r := DnsTLSARecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("zone_name").Exists(), + check.That(data.ResourceName).Key("record.#").HasValue("2"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (DnsTLSARecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_dns_tlsa_record" "test" { + name = azurerm_dns_tlsa_record.test.name + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_dns_zone.test.name +} +`, DnsTLSARecordResource{}.basic(data)) +} diff --git a/internal/services/dns/dns_tlsa_record_resource.go b/internal/services/dns/dns_tlsa_record_resource.go new file mode 100644 index 0000000000000..06a7920e7c89e --- /dev/null +++ b/internal/services/dns/dns_tlsa_record_resource.go @@ -0,0 +1,324 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dns + +import ( + "bytes" + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2023-07-01-preview/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +var ( + _ sdk.Resource = DnsTLSARecordResource{} + _ sdk.ResourceWithUpdate = DnsTLSARecordResource{} +) + +type DnsTLSARecordResource struct{} + +func (DnsTLSARecordResource) ModelObject() interface{} { + return &DnsTLSARecordResourceModel{} +} + +func (DnsTLSARecordResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return validate.ValidateRecordTypeID(recordsets.RecordTypeTLSA) +} + +func (DnsTLSARecordResource) ResourceType() string { + return "azurerm_dns_tlsa_record" +} + +type DnsTLSARecordResourceModel struct { + Name string `tfschema:"name"` + ResourceGroupName string `tfschema:"resource_group_name"` + ZoneName string `tfschema:"zone_name"` + Ttl int64 `tfschema:"ttl"` + Record []DnsTLSARecordResourceRecord `tfschema:"record"` + Tags map[string]string `tfschema:"tags"` + Fqdn string `tfschema:"fqdn"` +} + +type DnsTLSARecordResourceRecord struct { + MatchingType int64 `tfschema:"matching_type"` + Selector int64 `tfschema:"selector"` + Usage int64 `tfschema:"usage"` + CertAssociationData string `tfschema:"cert_association_data"` +} + +func (DnsTLSARecordResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + }, + + "resource_group_name": commonschema.ResourceGroupName(), + + "zone_name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + }, + + "record": { + Type: pluginsdk.TypeSet, + Required: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "matching_type": { + Type: pluginsdk.TypeInt, + Required: true, + }, + + "selector": { + Type: pluginsdk.TypeInt, + Required: true, + }, + + "usage": { + Type: pluginsdk.TypeInt, + Required: true, + }, + + "cert_association_data": { + Type: pluginsdk.TypeString, + Required: true, + }, + }, + }, + Set: resourceDnsTLSARecordHash, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Required: true, + }, + + "tags": commonschema.Tags(), + } +} + +func (DnsTLSARecordResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + } +} + +func (r DnsTLSARecordResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + var model DnsTLSARecordResourceModel + if err := metadata.Decode(&model); err != nil { + return err + } + + client := metadata.Client.Dns.RecordSets + subscriptionId := metadata.Client.Account.SubscriptionId + + id := recordsets.NewRecordTypeID(subscriptionId, model.ResourceGroupName, model.ZoneName, recordsets.RecordTypeTLSA, model.Name) + + existing, err := client.Get(ctx, id) + if err != nil { + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + } + } + + if !response.WasNotFound(existing.HttpResponse) { + return metadata.ResourceRequiresImport(r.ResourceType(), id) + } + + parameters := recordsets.RecordSet{ + Name: pointer.To(model.Name), + Properties: &recordsets.RecordSetProperties{ + Metadata: pointer.To(model.Tags), + TTL: pointer.To(model.Ttl), + TLSARecords: expandAzureRmDnsTLSARecords(model.Record), + }, + } + + if _, err := client.CreateOrUpdate(ctx, id, parameters, recordsets.DefaultCreateOrUpdateOperationOptions()); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + + return nil + }, + } +} + +func (DnsTLSARecordResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Dns.RecordSets + + state := DnsTLSARecordResourceModel{} + + id, err := recordsets.ParseRecordTypeID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(id) + } + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + state.Name = id.RelativeRecordSetName + state.ResourceGroupName = id.ResourceGroupName + state.ZoneName = id.DnsZoneName + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + state.Ttl = pointer.From(props.TTL) + state.Fqdn = pointer.From(props.Fqdn) + + state.Record = flattenAzureRmDnsTLSARecords(props.TLSARecords) + + state.Tags = pointer.From(props.Metadata) + + } + } + + return metadata.Encode(&state) + + }, + } +} + +func (DnsTLSARecordResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Dns.RecordSets + + var model DnsTLSARecordResourceModel + + if err := metadata.Decode(&model); err != nil { + return err + } + + id, err := recordsets.ParseRecordTypeID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + existing, err := client.Get(ctx, *id) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + if existing.Model == nil { + return fmt.Errorf("retrieving %s: `model` was nil", id) + } + + if existing.Model.Properties == nil { + return fmt.Errorf("retrieving %s: `properties` was nil", id) + } + + payload := *existing.Model + + if metadata.ResourceData.HasChange("record") { + payload.Properties.TLSARecords = expandAzureRmDnsTLSARecords(model.Record) + } + + if metadata.ResourceData.HasChange("ttl") { + payload.Properties.TTL = pointer.To(model.Ttl) + } + + if metadata.ResourceData.HasChange("tags") { + payload.Properties.Metadata = pointer.To(model.Tags) + } + + if _, err := client.CreateOrUpdate(ctx, *id, payload, recordsets.DefaultCreateOrUpdateOperationOptions()); err != nil { + return fmt.Errorf("updating %s: %+v", id, err) + } + + return nil + }, + } +} + +func (DnsTLSARecordResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Dns.RecordSets + + id, err := recordsets.ParseRecordTypeID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + if _, err := client.Delete(ctx, *id, recordsets.DefaultDeleteOperationOptions()); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + + return nil + }, + } +} + +func flattenAzureRmDnsTLSARecords(records *[]recordsets.TlsaRecord) []DnsTLSARecordResourceRecord { + results := make([]DnsTLSARecordResourceRecord, 0) + + if records != nil { + for _, record := range *records { + results = append(results, DnsTLSARecordResourceRecord{ + MatchingType: pointer.From(record.MatchingType), + Selector: pointer.From(record.Selector), + Usage: pointer.From(record.Usage), + CertAssociationData: pointer.From(record.CertAssociationData), + }) + } + } + + return results +} + +func expandAzureRmDnsTLSARecords(d []DnsTLSARecordResourceRecord) *[]recordsets.TlsaRecord { + records := make([]recordsets.TlsaRecord, 0) + + for _, v := range d { + records = append(records, recordsets.TlsaRecord{ + MatchingType: pointer.To(v.MatchingType), + Selector: pointer.To(v.Selector), + Usage: pointer.To(v.Usage), + CertAssociationData: pointer.To(v.CertAssociationData), + }) + } + + return &records +} + +func resourceDnsTLSARecordHash(v interface{}) int { + var buf bytes.Buffer + + if m, ok := v.(map[string]interface{}); ok { + buf.WriteString(fmt.Sprintf("%d-", m["matching_type"].(int))) + buf.WriteString(fmt.Sprintf("%d-", m["selector"].(int))) + buf.WriteString(fmt.Sprintf("%d-", m["usage"].(int))) + buf.WriteString(fmt.Sprintf("%s-", m["cert_association_data"].(string))) + } + + return pluginsdk.HashString(buf.String()) +} diff --git a/internal/services/dns/dns_tlsa_record_resource_test.go b/internal/services/dns/dns_tlsa_record_resource_test.go new file mode 100644 index 0000000000000..ab87db31974cf --- /dev/null +++ b/internal/services/dns/dns_tlsa_record_resource_test.go @@ -0,0 +1,311 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dns_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2023-07-01-preview/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type DnsTLSARecordResource struct{} + +func TestAccDnsTLSARecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_dns_tlsa_record", "test") + r := DnsTLSARecordResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("fqdn").Exists(), + ), + }, + data.ImportStep(), + }) +} + +func TestAccDnsTLSARecord_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_dns_tlsa_record", "test") + r := DnsTLSARecordResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + { + Config: r.requiresImport(data), + ExpectError: acceptance.RequiresImportError("azurerm_dns_tlsa_record"), + }, + }) +} + +func TestAccDnsTLSARecord_updateRecords(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_dns_tlsa_record", "test") + r := DnsTLSARecordResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("record.#").HasValue("2"), + ), + }, + { + Config: r.updateRecords(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("record.#").HasValue("3"), + ), + }, + }) +} + +func TestAccDnsTLSARecord_withTags(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_dns_tlsa_record", "test") + r := DnsTLSARecordResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withTags(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("tags.%").HasValue("2"), + ), + }, + { + Config: r.withTagsUpdate(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("tags.%").HasValue("1"), + ), + }, + data.ImportStep(), + }) +} + +func (DnsTLSARecordResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := recordsets.ParseRecordTypeID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.Dns.RecordSets.Get(ctx, *id) + if err != nil { + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) + } + + return utils.Bool(resp.Model != nil), nil +} + +func (DnsTLSARecordResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_dns_tlsa_record" "test" { + name = "myarecord%d" + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_dns_zone.test.name + ttl = 300 + + record { + matching_type = 1 + selector = 1 + usage = 3 + cert_association_data = "370C66FD4A0673CE1B62E76B819835DABB20702E4497CB10AFFE46E8135381E7" + } + + record { + matching_type = 1 + selector = 0 + usage = 0 + cert_association_data = "d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (r DnsTLSARecordResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_dns_tlsa_record" "import" { + name = azurerm_dns_tlsa_record.test.name + resource_group_name = azurerm_dns_tlsa_record.test.resource_group_name + zone_name = azurerm_dns_tlsa_record.test.zone_name + ttl = 300 + + record { + matching_type = 1 + selector = 1 + usage = 3 + cert_association_data = "370C66FD4A0673CE1B62E76B819835DABB20702E4497CB10AFFE46E8135381E7" + } + + record { + matching_type = 1 + selector = 0 + usage = 0 + cert_association_data = "d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971" + } +} +`, r.basic(data)) +} + +func (DnsTLSARecordResource) updateRecords(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_dns_tlsa_record" "test" { + name = "myarecord%d" + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_dns_zone.test.name + ttl = 300 + + record { + matching_type = 1 + selector = 1 + usage = 3 + cert_association_data = "370C66FD4A0673CE1B62E76B819835DABB20702E4497CB10AFFE46E8135381E7" + } + + record { + matching_type = 1 + selector = 0 + usage = 0 + cert_association_data = "d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971" + } + + record { + matching_type = 1 + selector = 1 + usage = 3 + cert_association_data = "0C72AC70B745AC19998811B131D662C9AC69DBDBE7CB23E5B514B56664C5D3D6" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (DnsTLSARecordResource) withTags(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_dns_tlsa_record" "test" { + name = "myarecord%d" + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_dns_zone.test.name + ttl = 300 + + record { + matching_type = 1 + selector = 1 + usage = 3 + cert_association_data = "370C66FD4A0673CE1B62E76B819835DABB20702E4497CB10AFFE46E8135381E7" + } + + record { + matching_type = 1 + selector = 0 + usage = 0 + cert_association_data = "d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971" + } + + tags = { + environment = "Production" + cost_center = "MSFT" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (DnsTLSARecordResource) withTagsUpdate(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_dns_tlsa_record" "test" { + name = "myarecord%d" + resource_group_name = azurerm_resource_group.test.name + zone_name = azurerm_dns_zone.test.name + ttl = 300 + + record { + matching_type = 1 + selector = 1 + usage = 3 + cert_association_data = "370C66FD4A0673CE1B62E76B819835DABB20702E4497CB10AFFE46E8135381E7" + } + + record { + matching_type = 1 + selector = 0 + usage = 0 + cert_association_data = "d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971" + } + + tags = { + environment = "staging" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} diff --git a/internal/services/dns/registration.go b/internal/services/dns/registration.go index 7055cbed69015..09a3f40930b06 100644 --- a/internal/services/dns/registration.go +++ b/internal/services/dns/registration.go @@ -68,6 +68,7 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { func (r Registration) DataSources() []sdk.DataSource { return []sdk.DataSource{ DnsDSRecordDataResource{}, + DnsTLSARecordDataResource{}, } } @@ -75,5 +76,6 @@ func (r Registration) DataSources() []sdk.DataSource { func (r Registration) Resources() []sdk.Resource { return []sdk.Resource{ DnsDSRecordResource{}, + DnsTLSARecordResource{}, } } diff --git a/website/docs/d/dns_tlsa_record.html.markdown b/website/docs/d/dns_tlsa_record.html.markdown new file mode 100644 index 0000000000000..a7083c2e916e9 --- /dev/null +++ b/website/docs/d/dns_tlsa_record.html.markdown @@ -0,0 +1,65 @@ +--- +subcategory: "DNS" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_dns_tlsa_record" +description: |- + Gets information about an existing DNS TLSA Record. +--- + +# Data Source: azurerm_dns_tlsa_record + +Use this data source to access information about an existing DNS TLSA Record within Azure DNS. + +~> **Note:** [The Azure DNS API has a throttle limit of 500 read (GET) operations per 5 minutes](https://docs.microsoft.com/azure/azure-resource-manager/management/request-limits-and-throttling#network-throttling) - whilst the default read timeouts will work for most cases - in larger configurations you may need to set a larger [read timeout](https://www.terraform.io/language/resources/syntax#operation-timeouts) then the default 5min. Although, we'd generally recommend that you split the resources out into smaller Terraform configurations to avoid the problem entirely. + +## Example Usage + +```hcl +data "azurerm_dns_tlsa_record" "example" { + name = "test" + zone_name = "test-zone" + resource_group_name = "test-rg" +} + +output "dns_tlsa_record_id" { + value = data.azurerm_dns_tlsa_record.example.id +} +``` + +## Argument Reference + +* `name` - The name of the DNS TLSA Record. + +* `resource_group_name` - Specifies the resource group where the DNS Zone (parent resource) exists. + +* `zone_name` - Specifies the DNS Zone where the resource exists. + +## Attributes Reference + +* `id` - The DNS TLSA Record ID. + +* `fqdn` - The FQDN of the DNS TLSA Record. + +* `ttl` - The Time To Live (TTL) of the DNS record in seconds. + +* `record` - A list of values that make up the TLSA record. Each `record` block supports fields documented below. + +* `tags` - A mapping of tags assigned to the resource. + +--- + +The `record` block supports: + +* `matching_type` - Matching Type of the TLSA record. + +* `selector` - Selector of the TLSA record. + +* `usage` - Usage of the TLSA record. + +* `cert_association_data` - Certificate data to be matched. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +* `read` - (Defaults to 5 minutes) Used when retrieving the DNS SRV Record. diff --git a/website/docs/r/dns_tlsa_record.html.markdown b/website/docs/r/dns_tlsa_record.html.markdown new file mode 100644 index 0000000000000..788a2ae083511 --- /dev/null +++ b/website/docs/r/dns_tlsa_record.html.markdown @@ -0,0 +1,101 @@ +--- +subcategory: "DNS" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_dns_tlsa_record" +description: |- + Manages a DNS TLSA Record. +--- + +# azurerm_dns_tlsa_record + +Enables you to manage DNS TLSA Records within Azure DNS. + +~> **Note:** [The Azure DNS API has a throttle limit of 500 read (GET) operations per 5 minutes](https://docs.microsoft.com/azure/azure-resource-manager/management/request-limits-and-throttling#network-throttling) - whilst the default read timeouts will work for most cases - in larger configurations you may need to set a larger [read timeout](https://www.terraform.io/language/resources/syntax#operation-timeouts) then the default 5min. Although, we'd generally recommend that you split the resources out into smaller Terraform configurations to avoid the problem entirely. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_dns_zone" "example" { + name = "mydomain.com" + resource_group_name = azurerm_resource_group.example.name +} + +resource "azurerm_dns_tlsa_record" "example" { + name = "test" + zone_name = azurerm_dns_zone.example.name + resource_group_name = azurerm_resource_group.example.name + ttl = 300 + + record { + matching_type = 1 + selector = 1 + usage = 3 + cert_association_data = "370c66fd4a0673ce1b62e76b819835dabb20702e4497cb10affe46e8135381e7" + } + + tags = { + Environment = "Production" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +- `name` - (Required) The name of the DNS TLSA Record. Changing this forces a new resource to be created. + +- `resource_group_name` - (Required) Specifies the resource group where the DNS Zone (parent resource) exists. Changing this forces a new resource to be created. + +- `zone_name` - (Required) Specifies the DNS Zone where the resource exists. Changing this forces a new resource to be created. + +- `ttl` - (Required) The Time To Live (TTL) of the DNS record in seconds. + +- `record` - (Required) A list of values that make up the TLSA record. Each `record` block supports fields documented below. + +- `tags` - (Optional) A mapping of tags to assign to the resource. + +--- + +The `record` block supports: + +- `matching_type` - (Required) Matching Type of the TLSA record. + +- `selector` - (Required) Selector of the TLSA record. + +- `usage` - (Required) Usage of the TLSA record. + +- `cert_association_data` - (Required) Certificate data to be matched. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +- `id` - The DNS TLSA Record ID. + +- `fqdn` - The FQDN of the DNS TLSA Record. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +- `create` - (Defaults to 30 minutes) Used when creating the DNS TLSA Record. + +- `update` - (Defaults to 30 minutes) Used when updating the DNS TLSA Record. + +- `read` - (Defaults to 5 minutes) Used when retrieving the DNS TLSA Record. + +- `delete` - (Defaults to 30 minutes) Used when deleting the DNS TLSA Record. + +## Import + +TLSA records can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_dns_tlsa_record.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Network/dnsZones/zone1/TLSA/myrecord1 +```