From e142b44428b7c9039ab20140b309ecb6c6320919 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 | 146 +++++++++ .../dns/dns_tlsa_record_data_source_test.go | 44 +++ .../services/dns/dns_tlsa_record_resource.go | 310 ++++++++++++++++++ .../dns/dns_tlsa_record_resource_test.go | 306 +++++++++++++++++ internal/services/dns/registration.go | 2 + website/docs/d/dns_tlsa_record.html.markdown | 62 ++++ website/docs/r/dns_tlsa_record.html.markdown | 98 ++++++ 7 files changed, 968 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 000000000000..38c8fffb33b6 --- /dev/null +++ b/internal/services/dns/dns_tlsa_record_data_source.go @@ -0,0 +1,146 @@ +// 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/go-azure-sdk/resource-manager/dns/2023-07-01-preview/zones" + "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"` + ZoneId string `tfschema:"dns_zone_id"` + 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, + }, + + "dns_zone_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: zones.ValidateDnsZoneID, + }, + } +} + +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, + }, + + "certificate_association_data": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + + "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 { + client := metadata.Client.Dns.RecordSets + subscriptionId := metadata.Client.Account.SubscriptionId + + var state DnsTLSARecordDataSourceModel + if err := metadata.Decode(&state); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + zoneId, err := zones.ParseDnsZoneID(state.ZoneId) + if err != nil { + return fmt.Errorf("parsing dns_zone_id: %+v", err) + + } + id := recordsets.NewRecordTypeID(subscriptionId, zoneId.ResourceGroupName, zoneId.DnsZoneName, recordsets.RecordTypeTLSA, state.Name) + + resp, err := client.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was 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 = flattenDnsTLSARecords(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 000000000000..1c49c2bff052 --- /dev/null +++ b/internal/services/dns/dns_tlsa_record_data_source_test.go @@ -0,0 +1,44 @@ +// 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("dns_zone_id").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 + dns_zone_id = azurerm_dns_zone.test.id +} +`, 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 000000000000..b22f25764459 --- /dev/null +++ b/internal/services/dns/dns_tlsa_record_resource.go @@ -0,0 +1,310 @@ +// 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/go-azure-sdk/resource-manager/dns/2023-07-01-preview/zones" + "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"` + ZoneId string `tfschema:"dns_zone_id"` + 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:"certificate_association_data"` +} + +func (DnsTLSARecordResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + }, + + "dns_zone_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: zones.ValidateDnsZoneID, + }, + + "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, + }, + + "certificate_association_data": { + Type: pluginsdk.TypeString, + Required: true, + }, + }, + }, + }, + + "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 { + client := metadata.Client.Dns.RecordSets + subscriptionId := metadata.Client.Account.SubscriptionId + + var model DnsTLSARecordResourceModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + zoneId, err := zones.ParseDnsZoneID(model.ZoneId) + if err != nil { + return fmt.Errorf("parsing dns_zone_id: %+v", err) + + } + + id := recordsets.NewRecordTypeID(subscriptionId, zoneId.ResourceGroupName, zoneId.DnsZoneName, 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: expandDnsTLSARecords(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) + } + + zoneId := zones.NewDnsZoneID(id.SubscriptionId, id.ResourceGroupName, id.DnsZoneName) + state.Name = id.RelativeRecordSetName + state.ZoneId = zoneId.ID() + + 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 = flattenDnsTLSARecords(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 fmt.Errorf("decoding: %+v", 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 = expandDnsTLSARecords(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 flattenDnsTLSARecords(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 expandDnsTLSARecords(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 +} 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 000000000000..7f3f7810bfce --- /dev/null +++ b/internal/services/dns/dns_tlsa_record_resource_test.go @@ -0,0 +1,306 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dns_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "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" +) + +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), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +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"), + ), + }, + data.ImportStep(), + { + Config: r.updateRecords(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("record.#").HasValue("3"), + ), + }, + data.ImportStep(), + }) +} + +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"), + ), + }, + data.ImportStep(), + { + 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 pointer.To(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" + dns_zone_id = azurerm_dns_zone.test.id + ttl = 300 + + record { + matching_type = 1 + selector = 1 + usage = 3 + certificate_association_data = "370C66FD4A0673CE1B62E76B819835DABB20702E4497CB10AFFE46E8135381E7" + } + + record { + matching_type = 1 + selector = 0 + usage = 0 + certificate_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 + dns_zone_id = azurerm_dns_zone.test.id + ttl = 300 + + record { + matching_type = 1 + selector = 1 + usage = 3 + certificate_association_data = "370C66FD4A0673CE1B62E76B819835DABB20702E4497CB10AFFE46E8135381E7" + } + + record { + matching_type = 1 + selector = 0 + usage = 0 + certificate_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" + dns_zone_id = azurerm_dns_zone.test.id + ttl = 300 + + record { + matching_type = 1 + selector = 1 + usage = 3 + certificate_association_data = "370C66FD4A0673CE1B62E76B819835DABB20702E4497CB10AFFE46E8135381E7" + } + + record { + matching_type = 1 + selector = 0 + usage = 0 + certificate_association_data = "d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971" + } + + record { + matching_type = 1 + selector = 1 + usage = 3 + certificate_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" + dns_zone_id = azurerm_dns_zone.test.id + ttl = 300 + + record { + matching_type = 1 + selector = 1 + usage = 3 + certificate_association_data = "370C66FD4A0673CE1B62E76B819835DABB20702E4497CB10AFFE46E8135381E7" + } + + record { + matching_type = 1 + selector = 0 + usage = 0 + certificate_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" + dns_zone_id = azurerm_dns_zone.test.id + ttl = 300 + + record { + matching_type = 1 + selector = 1 + usage = 3 + certificate_association_data = "370C66FD4A0673CE1B62E76B819835DABB20702E4497CB10AFFE46E8135381E7" + } + + record { + matching_type = 1 + selector = 0 + usage = 0 + certificate_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 7055cbed6901..09a3f40930b0 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 000000000000..9d5a6e36bb02 --- /dev/null +++ b/website/docs/d/dns_tlsa_record.html.markdown @@ -0,0 +1,62 @@ +--- +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" + dns_zone_id = azurerm_dns_zone.example.id +} + +output "dns_tlsa_record_id" { + value = data.azurerm_dns_tlsa_record.example.id +} +``` + +## Argument Reference + +* `name` - The name of the DNS TLSA Record. + +* `dns_zone_id` - Specifies the DNS Zone ID 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. + +* `certificate_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 000000000000..47fdb9665ace --- /dev/null +++ b/website/docs/r/dns_tlsa_record.html.markdown @@ -0,0 +1,98 @@ +--- +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" + dns_zone_id = azurerm_dns_zone.example.id + ttl = 300 + + record { + matching_type = 1 + selector = 1 + usage = 3 + certificate_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. + +- `dns_zone_id` - (Required) Specifies the DNS Zone ID 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. + +- `certificate_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 +```