diff --git a/.all-contributorsrc b/.all-contributorsrc index 91233385..2d1ae6a2 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -218,6 +218,15 @@ "contributions": [ "code" ] + }, + { + "login": "smaftoul", + "name": "smaftoul", + "avatar_url": "https://avatars.githubusercontent.com/u/239203?v=4", + "profile": "https://github.com/smaftoul", + "contributions": [ + "code" + ] } ] } diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index a3cf7729..467c1575 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -31,6 +31,7 @@ Thank you to all the people who have already contributed to driftctl.
Craig Furman

💻
Christopher Bowman

💻
Karni Wolf

💻 +
smaftoul

💻 diff --git a/README.md b/README.md index f398e5c2..ac2f0112 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ To learn more about compiling driftctl and contributing, please refer to the [co This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification and is brought to you by these [awesome contributors](CONTRIBUTORS.md). -Build with ❤️️ from 🇫🇷 🇬🇧 🇯🇵 🇬🇷 🇸🇪 🇺🇸 🇷🇪 🇨🇦 +Build with ❤️️ from 🇫🇷 🇬🇧 🇯🇵 🇬🇷 🇸🇪 🇺🇸 🇷🇪 🇨🇦 🇮🇱 --- diff --git a/pkg/analyser/analysis.go b/pkg/analyser/analysis.go index 88136f71..4aa5cc21 100644 --- a/pkg/analyser/analysis.go +++ b/pkg/analyser/analysis.go @@ -39,6 +39,7 @@ type Analysis struct { managed []*resource.Resource deleted []*resource.Resource differences []Difference + options AnalyzerOptions summary Summary alerts alerter.Alerts Duration time.Duration @@ -72,6 +73,10 @@ type GenDriftIgnoreOptions struct { OutputPath string } +func NewAnalysis(options AnalyzerOptions) *Analysis { + return &Analysis{options: options} +} + func (a Analysis) MarshalJSON() ([]byte, error) { bla := serializableAnalysis{} for _, m := range a.managed { @@ -156,6 +161,10 @@ func (a *Analysis) IsSync() bool { return a.summary.TotalDrifted == 0 && a.summary.TotalUnmanaged == 0 && a.summary.TotalDeleted == 0 } +func (a *Analysis) Options() AnalyzerOptions { + return a.options +} + func (a *Analysis) AddDeleted(resources ...*resource.Resource) { a.deleted = append(a.deleted, resources...) a.summary.TotalResources += len(resources) diff --git a/pkg/analyser/analyzer.go b/pkg/analyser/analyzer.go index 7134550f..f968b6fb 100644 --- a/pkg/analyser/analyzer.go +++ b/pkg/analyser/analyzer.go @@ -52,7 +52,7 @@ func NewAnalyzer(alerter *alerter.Alerter, options AnalyzerOptions, filter filte } func (a Analyzer) Analyze(remoteResources, resourcesFromState []*resource.Resource) (Analysis, error) { - analysis := Analysis{} + analysis := Analysis{options: a.options} // Iterate on remote resources and filter ignored resources filteredRemoteResource := make([]*resource.Resource, 0, len(remoteResources)) diff --git a/pkg/cmd/scan/output/console.go b/pkg/cmd/scan/output/console.go index 2158f155..2ea6e92b 100644 --- a/pkg/cmd/scan/output/console.go +++ b/pkg/cmd/scan/output/console.go @@ -207,7 +207,9 @@ func (c Console) writeSummary(analysis *analyser.Analysis) { if analysis.Summary().TotalDrifted > 0 { drifted = errorWriter.Sprintf("%d", analysis.Summary().TotalDrifted) } - fmt.Printf(" - %s resource(s) out of sync with Terraform state\n", boldWriter.Sprintf("%s/%d", drifted, analysis.Summary().TotalManaged)) + if analysis.Options().Deep { + fmt.Printf(" - %s resource(s) out of sync with Terraform state\n", boldWriter.Sprintf("%s/%d", drifted, analysis.Summary().TotalManaged)) + } unmanaged := successWriter.Sprintf("0") if analysis.Summary().TotalUnmanaged > 0 { diff --git a/pkg/cmd/scan/output/console_test.go b/pkg/cmd/scan/output/console_test.go index 09a6fd58..fa3656cb 100644 --- a/pkg/cmd/scan/output/console_test.go +++ b/pkg/cmd/scan/output/console_test.go @@ -31,7 +31,7 @@ func TestConsole_Write(t *testing.T) { name: "test console output", goldenfile: "output.txt", args: args{analysis: func() *analyser.Analysis { - a := fakeAnalysis() + a := fakeAnalysis(analyser.AnalyzerOptions{}) a.AddDeleted( &resource.Resource{ Id: "test-id-1", @@ -108,6 +108,12 @@ func TestConsole_Write(t *testing.T) { args: args{analysis: fakeAnalysisWithGithubEnumerationError()}, wantErr: false, }, + { + name: "test console output without deep mode", + goldenfile: "output_without_deep.txt", + args: args{analysis: fakeAnalysisWithoutDeep()}, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/cmd/scan/output/json_test.go b/pkg/cmd/scan/output/json_test.go index 3ee0482c..32915388 100644 --- a/pkg/cmd/scan/output/json_test.go +++ b/pkg/cmd/scan/output/json_test.go @@ -28,7 +28,7 @@ func TestJSON_Write(t *testing.T) { name: "test json output", goldenfile: "output.json", args: args{ - analysis: fakeAnalysis(), + analysis: fakeAnalysis(analyser.AnalyzerOptions{}), }, wantErr: false, }, @@ -103,7 +103,7 @@ func TestJSON_Write_stdout(t *testing.T) { goldenfile: "output.json", path: "stdout", args: args{ - analysis: fakeAnalysis(), + analysis: fakeAnalysis(analyser.AnalyzerOptions{}), }, wantErr: false, }, @@ -113,7 +113,7 @@ func TestJSON_Write_stdout(t *testing.T) { goldenfile: "output.json", path: "/dev/stdout", args: args{ - analysis: fakeAnalysis(), + analysis: fakeAnalysis(analyser.AnalyzerOptions{}), }, wantErr: false, }, @@ -160,7 +160,7 @@ func TestJSON_Write_stdout(t *testing.T) { func TestJSON_WriteMultiplesTimesInSameFile(t *testing.T) { emptyAnalysis := &analyser.Analysis{} - longerAnalysis := fakeAnalysis() + longerAnalysis := fakeAnalysis(analyser.AnalyzerOptions{}) tempDir := t.TempDir() tempFile, err := ioutil.TempFile(tempDir, "result") if err != nil { diff --git a/pkg/cmd/scan/output/output_test.go b/pkg/cmd/scan/output/output_test.go index 1c1d296b..fd739017 100644 --- a/pkg/cmd/scan/output/output_test.go +++ b/pkg/cmd/scan/output/output_test.go @@ -16,8 +16,11 @@ import ( "github.com/r3labs/diff/v2" ) -func fakeAnalysis() *analyser.Analysis { - a := analyser.Analysis{} +func fakeAnalysis(opts analyser.AnalyzerOptions) *analyser.Analysis { + if opts == (analyser.AnalyzerOptions{}) { + opts = analyser.AnalyzerOptions{Deep: true} + } + a := analyser.NewAnalysis(opts) a.AddUnmanaged( &resource.Resource{ Id: "unmanaged-id-1", @@ -106,11 +109,11 @@ func fakeAnalysis() *analyser.Analysis { }}) a.ProviderName = "AWS" a.ProviderVersion = "3.19.0" - return &a + return a } func fakeAnalysisWithAlerts() *analyser.Analysis { - a := fakeAnalysis() + a := fakeAnalysis(analyser.AnalyzerOptions{}) a.SetAlerts(alerter.Alerts{ "": []alerter.Alert{ alerts.NewRemoteAccessDeniedAlert(common.RemoteAWSTerraform, remoteerr.NewResourceListingErrorWithType(errors.New("dummy error"), "aws_vpc", "aws_vpc"), alerts.EnumerationPhase), @@ -136,7 +139,7 @@ func fakeAnalysisNoDrift() *analyser.Analysis { } func fakeAnalysisWithJsonFields() *analyser.Analysis { - a := analyser.Analysis{} + a := analyser.NewAnalysis(analyser.AnalyzerOptions{Deep: true}) a.AddManaged( &resource.Resource{ Id: "diff-id-1", @@ -193,11 +196,11 @@ func fakeAnalysisWithJsonFields() *analyser.Analysis { }}) a.ProviderName = "AWS" a.ProviderVersion = "3.19.0" - return &a + return a } func fakeAnalysisWithoutAttrs() *analyser.Analysis { - a := analyser.Analysis{} + a := analyser.NewAnalysis(analyser.AnalyzerOptions{Deep: true}) a.AddDeleted( &resource.Resource{ Id: "dfjkgnbsgj", @@ -226,11 +229,11 @@ func fakeAnalysisWithoutAttrs() *analyser.Analysis { ) a.ProviderName = "AWS" a.ProviderVersion = "3.19.0" - return &a + return a } func fakeAnalysisWithStringerResources() *analyser.Analysis { - a := analyser.Analysis{} + a := analyser.NewAnalysis(analyser.AnalyzerOptions{Deep: true}) schema := &resource.Schema{HumanReadableAttributesFunc: func(res *resource.Resource) map[string]string { return map[string]string{ "Name": (*res.Attrs)["name"].(string), @@ -295,11 +298,11 @@ func fakeAnalysisWithStringerResources() *analyser.Analysis { }}) a.ProviderName = "AWS" a.ProviderVersion = "3.19.0" - return &a + return a } func fakeAnalysisWithComputedFields() *analyser.Analysis { - a := analyser.Analysis{} + a := analyser.NewAnalysis(analyser.AnalyzerOptions{Deep: true}) a.AddManaged( &resource.Resource{ Id: "diff-id-1", @@ -377,7 +380,7 @@ func fakeAnalysisWithComputedFields() *analyser.Analysis { }) a.ProviderName = "AWS" a.ProviderVersion = "3.19.0" - return &a + return a } func fakeAnalysisWithAWSEnumerationError() *analyser.Analysis { @@ -446,6 +449,22 @@ func fakeAnalysisForJSONPlan() *analyser.Analysis { return &a } +func fakeAnalysisWithoutDeep() *analyser.Analysis { + a := analyser.Analysis{} + a.AddUnmanaged( + &resource.Resource{ + Id: "unmanaged-id-1", + Type: "aws_unmanaged_resource", + Attrs: &resource.Attributes{ + "name": "First unmanaged resource", + }, + }, + ) + a.ProviderName = "AWS" + a.ProviderVersion = "3.19.0" + return &a +} + func TestGetPrinter(t *testing.T) { tests := []struct { name string diff --git a/pkg/cmd/scan/output/testdata/output_without_deep.txt b/pkg/cmd/scan/output/testdata/output_without_deep.txt new file mode 100644 index 00000000..f84cd0be --- /dev/null +++ b/pkg/cmd/scan/output/testdata/output_without_deep.txt @@ -0,0 +1,8 @@ +Found resources not covered by IaC: + aws_unmanaged_resource: + - unmanaged-id-1 +Found 1 resource(s) + - 0% coverage + - 0 resource(s) managed by terraform + - 1 resource(s) not managed by Terraform + - 0 resource(s) found in a Terraform state but missing on the cloud provider diff --git a/pkg/iac/terraform/state/terraform_state_reader_test.go b/pkg/iac/terraform/state/terraform_state_reader_test.go index 5488863f..acf5b4c5 100644 --- a/pkg/iac/terraform/state/terraform_state_reader_test.go +++ b/pkg/iac/terraform/state/terraform_state_reader_test.go @@ -450,6 +450,7 @@ func TestTerraformStateReader_Azure_Resources(t *testing.T) { {name: "private dns ptr record", dirName: "azurerm_private_dns_ptr_record", wantErr: false}, {name: "private dns mx record", dirName: "azurerm_private_dns_mx_record", wantErr: false}, {name: "private dns srv record", dirName: "azurerm_private_dns_srv_record", wantErr: false}, + {name: "private dns txt record", dirName: "azurerm_private_dns_txt_record", wantErr: false}, {name: "images", dirName: "azurerm_image", wantErr: false}, {name: "ssh public key", dirName: "azurerm_ssh_public_key", wantErr: false}, } diff --git a/pkg/iac/terraform/state/test/azurerm_private_dns_txt_record/results.golden.json b/pkg/iac/terraform/state/test/azurerm_private_dns_txt_record/results.golden.json new file mode 100755 index 00000000..84d39ee7 --- /dev/null +++ b/pkg/iac/terraform/state/test/azurerm_private_dns_txt_record/results.golden.json @@ -0,0 +1,39 @@ +[ + { + "Id": "/subscriptions/8cb43347-a79f-4bb2-a8b4-c838b41fa5a5/resourceGroups/martin-dev/providers/Microsoft.Network/privateDnsZones/thisisatestusingtf.com/TXT/othertesttxt", + "Type": "azurerm_private_dns_txt_record", + "Attrs": { + "fqdn": "othertesttxt.thisisatestusingtf.com.", + "id": "/subscriptions/8cb43347-a79f-4bb2-a8b4-c838b41fa5a5/resourceGroups/martin-dev/providers/Microsoft.Network/privateDnsZones/thisisatestusingtf.com/TXT/othertesttxt", + "name": "othertesttxt", + "record": [ + { + "value": "this is value line 1" + }, + { + "value": "this is value line 2" + } + ], + "resource_group_name": "martin-dev", + "ttl": 300, + "zone_name": "thisisatestusingtf.com" + } + }, + { + "Id": "/subscriptions/8cb43347-a79f-4bb2-a8b4-c838b41fa5a5/resourceGroups/martin-dev/providers/Microsoft.Network/privateDnsZones/thisisatestusingtf.com/TXT/testtxt", + "Type": "azurerm_private_dns_txt_record", + "Attrs": { + "fqdn": "testtxt.thisisatestusingtf.com.", + "id": "/subscriptions/8cb43347-a79f-4bb2-a8b4-c838b41fa5a5/resourceGroups/martin-dev/providers/Microsoft.Network/privateDnsZones/thisisatestusingtf.com/TXT/testtxt", + "name": "testtxt", + "record": [ + { + "value": "this is value line 3" + } + ], + "resource_group_name": "martin-dev", + "ttl": 300, + "zone_name": "thisisatestusingtf.com" + } + } +] \ No newline at end of file diff --git a/pkg/iac/terraform/state/test/azurerm_private_dns_txt_record/terraform.tfstate b/pkg/iac/terraform/state/test/azurerm_private_dns_txt_record/terraform.tfstate new file mode 100644 index 00000000..9baa6905 --- /dev/null +++ b/pkg/iac/terraform/state/test/azurerm_private_dns_txt_record/terraform.tfstate @@ -0,0 +1,76 @@ +{ + "version": 4, + "terraform_version": "0.14.5", + "serial": 19, + "lineage": "7f22faf7-4655-0f68-3b00-9099a46450a7", + "outputs": {}, + "resources": [ + { + "mode": "managed", + "type": "azurerm_private_dns_txt_record", + "name": "othertesttxt", + "provider": "provider[\"registry.terraform.io/hashicorp/azurerm\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "fqdn": "othertesttxt.thisisatestusingtf.com.", + "id": "/subscriptions/8cb43347-a79f-4bb2-a8b4-c838b41fa5a5/resourceGroups/martin-dev/providers/Microsoft.Network/privateDnsZones/thisisatestusingtf.com/TXT/othertesttxt", + "name": "othertesttxt", + "record": [ + { + "value": "this is value line 1" + }, + { + "value": "this is value line 2" + } + ], + "resource_group_name": "martin-dev", + "tags": null, + "timeouts": null, + "ttl": 300, + "zone_name": "thisisatestusingtf.com" + }, + "sensitive_attributes": [], + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxODAwMDAwMDAwMDAwLCJkZWxldGUiOjE4MDAwMDAwMDAwMDAsInJlYWQiOjMwMDAwMDAwMDAwMCwidXBkYXRlIjoxODAwMDAwMDAwMDAwfX0=", + "dependencies": [ + "azurerm_private_dns_zone.testzone", + "data.azurerm_resource_group.martin" + ] + } + ] + }, + { + "mode": "managed", + "type": "azurerm_private_dns_txt_record", + "name": "testtxt", + "provider": "provider[\"registry.terraform.io/hashicorp/azurerm\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "fqdn": "testtxt.thisisatestusingtf.com.", + "id": "/subscriptions/8cb43347-a79f-4bb2-a8b4-c838b41fa5a5/resourceGroups/martin-dev/providers/Microsoft.Network/privateDnsZones/thisisatestusingtf.com/TXT/testtxt", + "name": "testtxt", + "record": [ + { + "value": "this is value line 3" + } + ], + "resource_group_name": "martin-dev", + "tags": null, + "timeouts": null, + "ttl": 300, + "zone_name": "thisisatestusingtf.com" + }, + "sensitive_attributes": [], + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxODAwMDAwMDAwMDAwLCJkZWxldGUiOjE4MDAwMDAwMDAwMDAsInJlYWQiOjMwMDAwMDAwMDAwMCwidXBkYXRlIjoxODAwMDAwMDAwMDAwfX0=", + "dependencies": [ + "azurerm_private_dns_zone.testzone", + "data.azurerm_resource_group.martin" + ] + } + ] + } + ] +} diff --git a/pkg/remote/azurerm/azurerm_privatedns_txt_record_enumerator.go b/pkg/remote/azurerm/azurerm_privatedns_txt_record_enumerator.go new file mode 100644 index 00000000..1af419d4 --- /dev/null +++ b/pkg/remote/azurerm/azurerm_privatedns_txt_record_enumerator.go @@ -0,0 +1,57 @@ +package azurerm + +import ( + "github.com/cloudskiff/driftctl/pkg/remote/azurerm/repository" + remoteerror "github.com/cloudskiff/driftctl/pkg/remote/error" + "github.com/cloudskiff/driftctl/pkg/resource" + "github.com/cloudskiff/driftctl/pkg/resource/azurerm" +) + +type AzurermPrivateDNSTXTRecordEnumerator struct { + repository repository.PrivateDNSRepository + factory resource.ResourceFactory +} + +func NewAzurermPrivateDNSTXTRecordEnumerator(repo repository.PrivateDNSRepository, factory resource.ResourceFactory) *AzurermPrivateDNSTXTRecordEnumerator { + return &AzurermPrivateDNSTXTRecordEnumerator{ + repository: repo, + factory: factory, + } +} + +func (e *AzurermPrivateDNSTXTRecordEnumerator) SupportedType() resource.ResourceType { + return azurerm.AzurePrivateDNSTXTRecordResourceType +} + +func (e *AzurermPrivateDNSTXTRecordEnumerator) Enumerate() ([]*resource.Resource, error) { + + zones, err := e.repository.ListAllPrivateZones() + if err != nil { + return nil, remoteerror.NewResourceListingErrorWithType(err, string(e.SupportedType()), azurerm.AzurePrivateDNSZoneResourceType) + } + + results := make([]*resource.Resource, 0) + + for _, zone := range zones { + records, err := e.repository.ListAllTXTRecords(zone) + if err != nil { + return nil, remoteerror.NewResourceListingError(err, string(e.SupportedType())) + } + for _, record := range records { + results = append( + results, + e.factory.CreateAbstractResource( + string(e.SupportedType()), + *record.ID, + map[string]interface{}{ + "name": *record.Name, + "zone_name": *zone.Name, + }, + ), + ) + } + + } + + return results, err +} diff --git a/pkg/remote/azurerm/init.go b/pkg/remote/azurerm/init.go index 061bf5b7..01176785 100644 --- a/pkg/remote/azurerm/init.go +++ b/pkg/remote/azurerm/init.go @@ -81,6 +81,8 @@ func Init( remoteLibrary.AddDetailsFetcher(azurerm.AzurePrivateDNSPTRRecordResourceType, common.NewGenericDetailsFetcher(azurerm.AzurePrivateDNSPTRRecordResourceType, provider, deserializer)) remoteLibrary.AddEnumerator(NewAzurermPrivateDNSSRVRecordEnumerator(privateDNSRepo, factory)) remoteLibrary.AddDetailsFetcher(azurerm.AzurePrivateDNSSRVRecordResourceType, common.NewGenericDetailsFetcher(azurerm.AzurePrivateDNSSRVRecordResourceType, provider, deserializer)) + remoteLibrary.AddEnumerator(NewAzurermPrivateDNSTXTRecordEnumerator(privateDNSRepo, factory)) + remoteLibrary.AddDetailsFetcher(azurerm.AzurePrivateDNSTXTRecordResourceType, common.NewGenericDetailsFetcher(azurerm.AzurePrivateDNSTXTRecordResourceType, provider, deserializer)) remoteLibrary.AddEnumerator(NewAzurermImageEnumerator(computeRepo, factory)) remoteLibrary.AddEnumerator(NewAzurermSSHPublicKeyEnumerator(computeRepo, factory)) diff --git a/pkg/remote/azurerm/repository/mock_PrivateDNSRepository.go b/pkg/remote/azurerm/repository/mock_PrivateDNSRepository.go index 9324b147..f4227efc 100644 --- a/pkg/remote/azurerm/repository/mock_PrivateDNSRepository.go +++ b/pkg/remote/azurerm/repository/mock_PrivateDNSRepository.go @@ -172,3 +172,26 @@ func (_m *MockPrivateDNSRepository) ListAllSRVRecords(zone *armprivatedns.Privat return r0, r1 } + +// ListAllTXTRecords provides a mock function with given fields: zone +func (_m *MockPrivateDNSRepository) ListAllTXTRecords(zone *armprivatedns.PrivateZone) ([]*armprivatedns.RecordSet, error) { + ret := _m.Called(zone) + + var r0 []*armprivatedns.RecordSet + if rf, ok := ret.Get(0).(func(*armprivatedns.PrivateZone) []*armprivatedns.RecordSet); ok { + r0 = rf(zone) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*armprivatedns.RecordSet) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(*armprivatedns.PrivateZone) error); ok { + r1 = rf(zone) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/pkg/remote/azurerm/repository/privatedns.go b/pkg/remote/azurerm/repository/privatedns.go index 41a14def..267a005f 100644 --- a/pkg/remote/azurerm/repository/privatedns.go +++ b/pkg/remote/azurerm/repository/privatedns.go @@ -19,6 +19,7 @@ type PrivateDNSRepository interface { ListAllPTRRecords(zone *armprivatedns.PrivateZone) ([]*armprivatedns.RecordSet, error) ListAllMXRecords(zone *armprivatedns.PrivateZone) ([]*armprivatedns.RecordSet, error) ListAllSRVRecords(zone *armprivatedns.PrivateZone) ([]*armprivatedns.RecordSet, error) + ListAllTXTRecords(zone *armprivatedns.PrivateZone) ([]*armprivatedns.RecordSet, error) } type privateDNSZoneListPager interface { @@ -197,6 +198,22 @@ func (s *privateDNSRepository) ListAllSRVRecords(zone *armprivatedns.PrivateZone return results, nil } +func (s *privateDNSRepository) ListAllTXTRecords(zone *armprivatedns.PrivateZone) ([]*armprivatedns.RecordSet, error) { + records, err := s.listAllRecords(zone) + if err != nil { + return nil, err + } + results := make([]*armprivatedns.RecordSet, 0) + for _, record := range records { + if record.Properties.TxtRecords == nil { + continue + } + results = append(results, record) + + } + return results, nil +} + func (s *privateDNSRepository) ListAllPrivateZones() ([]*armprivatedns.PrivateZone, error) { cacheKey := "privateDNSListAllPrivateZones" v := s.cache.GetAndLock(cacheKey) diff --git a/pkg/remote/azurerm/repository/privatedns_test.go b/pkg/remote/azurerm/repository/privatedns_test.go index 57d20672..aafcb61c 100644 --- a/pkg/remote/azurerm/repository/privatedns_test.go +++ b/pkg/remote/azurerm/repository/privatedns_test.go @@ -1430,3 +1430,210 @@ func Test_ListAllSRVRecords_Error(t *testing.T) { } // endregion + +// region TXTRecord +func Test_ListAllTXTRecords_MultiplesResults(t *testing.T) { + + expected := []*armprivatedns.RecordSet{ + { + ProxyResource: armprivatedns.ProxyResource{ + Resource: armprivatedns.Resource{ + ID: to.StringPtr("record1"), + }, + }, + Properties: &armprivatedns.RecordSetProperties{ + TxtRecords: []*armprivatedns.TxtRecord{ + {Value: []*string{to.StringPtr("value")}}, + }, + }, + }, + { + ProxyResource: armprivatedns.ProxyResource{ + Resource: armprivatedns.Resource{ + ID: to.StringPtr("record3"), + }, + }, + Properties: &armprivatedns.RecordSetProperties{ + TxtRecords: []*armprivatedns.TxtRecord{ + {Value: []*string{to.StringPtr("value")}}, + }, + }, + }, + } + + fakeRecordSetClient := &mockPrivateRecordSetClient{} + + mockPager := &mockPrivateDNSRecordSetListPager{} + mockPager.On("Err").Return(nil).Times(3) + mockPager.On("NextPage", mock.Anything).Return(true).Times(2) + mockPager.On("NextPage", mock.Anything).Return(false).Times(1) + mockPager.On("PageResponse").Return(armprivatedns.RecordSetsListResponse{ + RecordSetsListResult: armprivatedns.RecordSetsListResult{ + RecordSetListResult: armprivatedns.RecordSetListResult{ + Value: []*armprivatedns.RecordSet{ + { + ProxyResource: armprivatedns.ProxyResource{ + Resource: armprivatedns.Resource{ + ID: to.StringPtr("record1"), + }, + }, + Properties: &armprivatedns.RecordSetProperties{ + TxtRecords: []*armprivatedns.TxtRecord{ + {Value: []*string{to.StringPtr("value")}}, + }, + }, + }, + { + ProxyResource: armprivatedns.ProxyResource{ + Resource: armprivatedns.Resource{ + ID: to.StringPtr("record2"), + }, + }, + Properties: &armprivatedns.RecordSetProperties{}, + }, + }, + }, + }, + }).Times(1) + mockPager.On("PageResponse").Return(armprivatedns.RecordSetsListResponse{ + RecordSetsListResult: armprivatedns.RecordSetsListResult{ + RecordSetListResult: armprivatedns.RecordSetListResult{ + Value: []*armprivatedns.RecordSet{ + { + ProxyResource: armprivatedns.ProxyResource{ + Resource: armprivatedns.Resource{ + ID: to.StringPtr("record3"), + }, + }, + Properties: &armprivatedns.RecordSetProperties{ + TxtRecords: []*armprivatedns.TxtRecord{ + {Value: []*string{to.StringPtr("value")}}, + }, + }, + }, + { + ProxyResource: armprivatedns.ProxyResource{ + Resource: armprivatedns.Resource{ + ID: to.StringPtr("record4"), + }, + }, + Properties: &armprivatedns.RecordSetProperties{}, + }, + }, + }, + }, + }).Times(1) + + fakeRecordSetClient.On("List", "rgid", "zone", (*armprivatedns.RecordSetsListOptions)(nil)).Return(mockPager) + + c := &cache.MockCache{} + c.On("GetAndLock", "privateDNSlistAllRecords-/subscriptions/subid/resourceGroups/rgid/providers/Microsoft.Network/privateDnsZones/zone.com").Return(nil).Times(1) + c.On("Unlock", "privateDNSlistAllRecords-/subscriptions/subid/resourceGroups/rgid/providers/Microsoft.Network/privateDnsZones/zone.com").Return().Times(1) + c.On("Put", "privateDNSlistAllRecords-/subscriptions/subid/resourceGroups/rgid/providers/Microsoft.Network/privateDnsZones/zone.com", mock.Anything).Return(true).Times(1) + s := &privateDNSRepository{ + recordClient: fakeRecordSetClient, + cache: c, + } + got, err := s.ListAllTXTRecords(&armprivatedns.PrivateZone{ + TrackedResource: armprivatedns.TrackedResource{ + Resource: armprivatedns.Resource{ + ID: to.StringPtr("/subscriptions/subid/resourceGroups/rgid/providers/Microsoft.Network/privateDnsZones/zone.com"), + Name: to.StringPtr("zone"), + }, + }, + }) + if err != nil { + t.Errorf("ListAllTXTRecords() error = %v", err) + return + } + + mockPager.AssertExpectations(t) + fakeRecordSetClient.AssertExpectations(t) + c.AssertExpectations(t) + + if !reflect.DeepEqual(got, expected) { + t.Errorf("ListAllTXTRecords() got = %v, want %v", got, expected) + } +} + +func Test_ListAllTXTRecords_MultiplesResults_WithCache(t *testing.T) { + + expected := []*armprivatedns.RecordSet{ + { + ProxyResource: armprivatedns.ProxyResource{ + Resource: armprivatedns.Resource{ + ID: to.StringPtr("record1"), + }, + }, + Properties: &armprivatedns.RecordSetProperties{ + TxtRecords: []*armprivatedns.TxtRecord{ + {Value: []*string{to.StringPtr("value")}}, + }, + }, + }, + } + + fakeRecordSetClient := &mockPrivateRecordSetClient{} + + c := &cache.MockCache{} + c.On("GetAndLock", "privateDNSlistAllRecords-/subscriptions/subid/resourceGroups/rgid/providers/Microsoft.Network/privateDnsZones/zone.com").Return(expected).Times(1) + c.On("Unlock", "privateDNSlistAllRecords-/subscriptions/subid/resourceGroups/rgid/providers/Microsoft.Network/privateDnsZones/zone.com").Times(1) + s := &privateDNSRepository{ + recordClient: fakeRecordSetClient, + cache: c, + } + got, err := s.ListAllTXTRecords(&armprivatedns.PrivateZone{ + TrackedResource: armprivatedns.TrackedResource{ + Resource: armprivatedns.Resource{ + ID: to.StringPtr("/subscriptions/subid/resourceGroups/rgid/providers/Microsoft.Network/privateDnsZones/zone.com"), + Name: to.StringPtr("zone"), + }, + }, + }) + if err != nil { + t.Errorf("ListAllTXTRecords() error = %v", err) + return + } + + fakeRecordSetClient.AssertExpectations(t) + c.AssertExpectations(t) + + if !reflect.DeepEqual(got, expected) { + t.Errorf("ListAllTXTRecords() got = %v, want %v", got, expected) + } +} + +func Test_ListAllTXTRecords_Error(t *testing.T) { + + fakeClient := &mockPrivateRecordSetClient{} + + expectedErr := errors.New("unexpected error") + + mockPager := &mockPrivateDNSRecordSetListPager{} + mockPager.On("Err").Return(expectedErr).Times(1) + mockPager.On("NextPage", mock.Anything).Return(true).Times(1) + mockPager.On("PageResponse").Return(armprivatedns.RecordSetsListResponse{}).Times(1) + + fakeClient.On("List", "rgid", "zone", (*armprivatedns.RecordSetsListOptions)(nil)).Return(mockPager) + + s := &privateDNSRepository{ + recordClient: fakeClient, + cache: cache.New(0), + } + got, err := s.ListAllTXTRecords(&armprivatedns.PrivateZone{ + TrackedResource: armprivatedns.TrackedResource{ + Resource: armprivatedns.Resource{ + ID: to.StringPtr("/subscriptions/subid/resourceGroups/rgid/providers/Microsoft.Network/privateDnsZones/zone.com"), + Name: to.StringPtr("zone"), + }, + }, + }) + + mockPager.AssertExpectations(t) + fakeClient.AssertExpectations(t) + + assert.Equal(t, expectedErr, err) + assert.Nil(t, got) +} + +// endregion diff --git a/pkg/remote/azurerm_privatedns_scanner_test.go b/pkg/remote/azurerm_privatedns_scanner_test.go index ca425b5d..1c47a20f 100644 --- a/pkg/remote/azurerm_privatedns_scanner_test.go +++ b/pkg/remote/azurerm_privatedns_scanner_test.go @@ -1062,3 +1062,157 @@ func TestAzurermPrivateDNSSRVRecord(t *testing.T) { }) } } + +func TestAzurermPrivateDNSTXTRecord(t *testing.T) { + + dummyError := errors.New("this is an error") + + tests := []struct { + test string + dirName string + mocks func(*repository.MockPrivateDNSRepository, *mocks.AlerterInterface) + wantErr error + }{ + { + test: "no private txt record", + dirName: "azurerm_private_dns_txt_record_empty", + mocks: func(repository *repository.MockPrivateDNSRepository, alerter *mocks.AlerterInterface) { + repository.On("ListAllPrivateZones").Return([]*armprivatedns.PrivateZone{}, nil) + }, + }, + { + test: "error listing private zone", + dirName: "azurerm_private_dns_txt_record_empty", + mocks: func(repository *repository.MockPrivateDNSRepository, alerter *mocks.AlerterInterface) { + repository.On("ListAllPrivateZones").Return(nil, dummyError) + }, + wantErr: remoteerr.NewResourceListingErrorWithType(dummyError, resourceazure.AzurePrivateDNSTXTRecordResourceType, resourceazure.AzurePrivateDNSZoneResourceType), + }, + { + test: "error listing private txt records", + dirName: "azurerm_private_dns_txt_record_empty", + mocks: func(repository *repository.MockPrivateDNSRepository, alerter *mocks.AlerterInterface) { + repository.On("ListAllPrivateZones").Return([]*armprivatedns.PrivateZone{ + { + TrackedResource: armprivatedns.TrackedResource{ + Resource: armprivatedns.Resource{ + ID: to.StringPtr("/subscriptions/7bfb2c5c-7308-46ed-8ae4-fffa356eb406/resourceGroups/martin-dev/providers/Microsoft.Network/privateDnsZones/thisisatestusingtf.com"), + Name: to.StringPtr("thisisatestusingtf.com"), + }, + }, + }, + }, nil) + repository.On("ListAllTXTRecords", mock.Anything).Return(nil, dummyError) + }, + wantErr: remoteerr.NewResourceListingError(dummyError, resourceazure.AzurePrivateDNSTXTRecordResourceType), + }, + { + test: "multiple private txt records", + dirName: "azurerm_private_dns_txt_record_multiple", + mocks: func(repository *repository.MockPrivateDNSRepository, alerter *mocks.AlerterInterface) { + repository.On("ListAllPrivateZones").Return([]*armprivatedns.PrivateZone{ + { + TrackedResource: armprivatedns.TrackedResource{ + Resource: armprivatedns.Resource{ + ID: to.StringPtr("/subscriptions/7bfb2c5c-7308-46ed-8ae4-fffa356eb406/resourceGroups/martin-dev/providers/Microsoft.Network/privateDnsZones/thisisatestusingtf.com"), + Name: to.StringPtr("thisisatestusingtf.com"), + }, + }, + }, + }, nil) + + repository.On("ListAllTXTRecords", mock.Anything).Return([]*armprivatedns.RecordSet{ + { + ProxyResource: armprivatedns.ProxyResource{ + Resource: armprivatedns.Resource{ + ID: to.StringPtr("/subscriptions/8cb43347-a79f-4bb2-a8b4-c838b41fa5a5/resourceGroups/martin-dev/providers/Microsoft.Network/privateDnsZones/thisisatestusingtf.com/TXT/othertesttxt"), + Name: to.StringPtr("othertesttxt"), + }, + }, + Properties: &armprivatedns.RecordSetProperties{ + TxtRecords: []*armprivatedns.TxtRecord{ + {Value: []*string{to.StringPtr("this is value line 1")}}, + {Value: []*string{to.StringPtr("this is value line 2")}}, + }, + }, + }, + { + ProxyResource: armprivatedns.ProxyResource{ + Resource: armprivatedns.Resource{ + ID: to.StringPtr("/subscriptions/8cb43347-a79f-4bb2-a8b4-c838b41fa5a5/resourceGroups/martin-dev/providers/Microsoft.Network/privateDnsZones/thisisatestusingtf.com/TXT/testtxt"), + Name: to.StringPtr("testtxt"), + }, + }, + Properties: &armprivatedns.RecordSetProperties{ + PtrRecords: []*armprivatedns.PtrRecord{ + {Ptrdname: to.StringPtr("this is value line 3")}, + }, + }, + }, + }, nil).Once() + }, + }, + } + + providerVersion := "2.71.0" + schemaRepository := testresource.InitFakeSchemaRepository("azurerm", providerVersion) + resourceazure.InitResourcesMetadata(schemaRepository) + factory := terraform.NewTerraformResourceFactory(schemaRepository) + deserializer := resource.NewDeserializer(factory) + + for _, c := range tests { + t.Run(c.test, func(tt *testing.T) { + shouldUpdate := c.dirName == *goldenfile.Update + + scanOptions := ScannerOptions{Deep: true} + providerLibrary := terraform.NewProviderLibrary() + remoteLibrary := common.NewRemoteLibrary() + + // Initialize mocks + alerter := &mocks.AlerterInterface{} + fakeRepo := &repository.MockPrivateDNSRepository{} + c.mocks(fakeRepo, alerter) + + var repo repository.PrivateDNSRepository = fakeRepo + providerVersion := "2.71.0" + realProvider, err := terraformtest.InitTestAzureProvider(providerLibrary, providerVersion) + if err != nil { + t.Fatal(err) + } + provider := terraformtest.NewFakeTerraformProvider(realProvider) + provider.WithResponse(c.dirName) + + // Replace mock by real resources if we are in update mode + if shouldUpdate { + err := realProvider.Init() + if err != nil { + t.Fatal(err) + } + provider.ShouldUpdate() + cred, err := azidentity.NewDefaultAzureCredential(&azidentity.DefaultAzureCredentialOptions{}) + if err != nil { + t.Fatal(err) + } + con := arm.NewDefaultConnection(cred, nil) + repo = repository.NewPrivateDNSRepository(con, realProvider.GetConfig(), cache.New(0)) + } + + remoteLibrary.AddEnumerator(azurerm.NewAzurermPrivateDNSTXTRecordEnumerator(repo, factory)) + remoteLibrary.AddDetailsFetcher(resourceazure.AzurePrivateDNSTXTRecordResourceType, common.NewGenericDetailsFetcher(resourceazure.AzurePrivateDNSTXTRecordResourceType, provider, deserializer)) + + testFilter := &filter.MockFilter{} + testFilter.On("IsTypeIgnored", mock.Anything).Return(false) + + s := NewScanner(remoteLibrary, alerter, scanOptions, testFilter) + got, err := s.Resources() + assert.Equal(tt, c.wantErr, err) + + if err != nil { + return + } + test.TestAgainstGoldenFile(got, resourceazure.AzurePrivateDNSTXTRecordResourceType, c.dirName, provider, deserializer, shouldUpdate, tt) + alerter.AssertExpectations(tt) + fakeRepo.AssertExpectations(tt) + }) + } +} diff --git a/pkg/remote/test/azurerm_private_dns_txt_record_multiple/azurerm_private_dns_txt_record-_subscriptions_8cb43347-a79f-4bb2-a8b4-c838b41fa5a5_resourceGroups_martin-dev_providers_Microsoft.Network_privateDnsZones_thisisatestusingtf.com_TXT_othertesttxt.res.golden.json b/pkg/remote/test/azurerm_private_dns_txt_record_multiple/azurerm_private_dns_txt_record-_subscriptions_8cb43347-a79f-4bb2-a8b4-c838b41fa5a5_resourceGroups_martin-dev_providers_Microsoft.Network_privateDnsZones_thisisatestusingtf.com_TXT_othertesttxt.res.golden.json new file mode 100755 index 00000000..5c03e341 --- /dev/null +++ b/pkg/remote/test/azurerm_private_dns_txt_record_multiple/azurerm_private_dns_txt_record-_subscriptions_8cb43347-a79f-4bb2-a8b4-c838b41fa5a5_resourceGroups_martin-dev_providers_Microsoft.Network_privateDnsZones_thisisatestusingtf.com_TXT_othertesttxt.res.golden.json @@ -0,0 +1,5 @@ +{ + "Typ": "WyJvYmplY3QiLHsiZnFkbiI6InN0cmluZyIsImlkIjoic3RyaW5nIiwibmFtZSI6InN0cmluZyIsInJlY29yZCI6WyJzZXQiLFsib2JqZWN0Iix7InZhbHVlIjoic3RyaW5nIn1dXSwicmVzb3VyY2VfZ3JvdXBfbmFtZSI6InN0cmluZyIsInRhZ3MiOlsibWFwIiwic3RyaW5nIl0sInRpbWVvdXRzIjpbIm9iamVjdCIseyJjcmVhdGUiOiJzdHJpbmciLCJkZWxldGUiOiJzdHJpbmciLCJyZWFkIjoic3RyaW5nIiwidXBkYXRlIjoic3RyaW5nIn1dLCJ0dGwiOiJudW1iZXIiLCJ6b25lX25hbWUiOiJzdHJpbmcifV0=", + "Val": "eyJmcWRuIjoib3RoZXJ0ZXN0dHh0LnRoaXNpc2F0ZXN0dXNpbmd0Zi5jb20uIiwiaWQiOiIvc3Vic2NyaXB0aW9ucy84Y2I0MzM0Ny1hNzlmLTRiYjItYThiNC1jODM4YjQxZmE1YTUvcmVzb3VyY2VHcm91cHMvbWFydGluLWRldi9wcm92aWRlcnMvTWljcm9zb2Z0Lk5ldHdvcmsvcHJpdmF0ZURuc1pvbmVzL3RoaXNpc2F0ZXN0dXNpbmd0Zi5jb20vVFhUL290aGVydGVzdHR4dCIsIm5hbWUiOiJvdGhlcnRlc3R0eHQiLCJyZWNvcmQiOlt7InZhbHVlIjoidGhpcyBpcyB2YWx1ZSBsaW5lIDEifSx7InZhbHVlIjoidGhpcyBpcyB2YWx1ZSBsaW5lIDIifV0sInJlc291cmNlX2dyb3VwX25hbWUiOiJtYXJ0aW4tZGV2IiwidGFncyI6e30sInRpbWVvdXRzIjp7ImNyZWF0ZSI6bnVsbCwiZGVsZXRlIjpudWxsLCJyZWFkIjpudWxsLCJ1cGRhdGUiOm51bGx9LCJ0dGwiOjMwMCwiem9uZV9uYW1lIjoidGhpc2lzYXRlc3R1c2luZ3RmLmNvbSJ9", + "Err": null +} \ No newline at end of file diff --git a/pkg/remote/test/azurerm_private_dns_txt_record_multiple/azurerm_private_dns_txt_record-_subscriptions_8cb43347-a79f-4bb2-a8b4-c838b41fa5a5_resourceGroups_martin-dev_providers_Microsoft.Network_privateDnsZones_thisisatestusingtf.com_TXT_testtxt.res.golden.json b/pkg/remote/test/azurerm_private_dns_txt_record_multiple/azurerm_private_dns_txt_record-_subscriptions_8cb43347-a79f-4bb2-a8b4-c838b41fa5a5_resourceGroups_martin-dev_providers_Microsoft.Network_privateDnsZones_thisisatestusingtf.com_TXT_testtxt.res.golden.json new file mode 100755 index 00000000..ab5af9bf --- /dev/null +++ b/pkg/remote/test/azurerm_private_dns_txt_record_multiple/azurerm_private_dns_txt_record-_subscriptions_8cb43347-a79f-4bb2-a8b4-c838b41fa5a5_resourceGroups_martin-dev_providers_Microsoft.Network_privateDnsZones_thisisatestusingtf.com_TXT_testtxt.res.golden.json @@ -0,0 +1,5 @@ +{ + "Typ": "WyJvYmplY3QiLHsiZnFkbiI6InN0cmluZyIsImlkIjoic3RyaW5nIiwibmFtZSI6InN0cmluZyIsInJlY29yZCI6WyJzZXQiLFsib2JqZWN0Iix7InZhbHVlIjoic3RyaW5nIn1dXSwicmVzb3VyY2VfZ3JvdXBfbmFtZSI6InN0cmluZyIsInRhZ3MiOlsibWFwIiwic3RyaW5nIl0sInRpbWVvdXRzIjpbIm9iamVjdCIseyJjcmVhdGUiOiJzdHJpbmciLCJkZWxldGUiOiJzdHJpbmciLCJyZWFkIjoic3RyaW5nIiwidXBkYXRlIjoic3RyaW5nIn1dLCJ0dGwiOiJudW1iZXIiLCJ6b25lX25hbWUiOiJzdHJpbmcifV0=", + "Val": "eyJmcWRuIjoidGVzdHR4dC50aGlzaXNhdGVzdHVzaW5ndGYuY29tLiIsImlkIjoiL3N1YnNjcmlwdGlvbnMvOGNiNDMzNDctYTc5Zi00YmIyLWE4YjQtYzgzOGI0MWZhNWE1L3Jlc291cmNlR3JvdXBzL21hcnRpbi1kZXYvcHJvdmlkZXJzL01pY3Jvc29mdC5OZXR3b3JrL3ByaXZhdGVEbnNab25lcy90aGlzaXNhdGVzdHVzaW5ndGYuY29tL1RYVC90ZXN0dHh0IiwibmFtZSI6InRlc3R0eHQiLCJyZWNvcmQiOlt7InZhbHVlIjoidGhpcyBpcyB2YWx1ZSBsaW5lIDMifV0sInJlc291cmNlX2dyb3VwX25hbWUiOiJtYXJ0aW4tZGV2IiwidGFncyI6e30sInRpbWVvdXRzIjp7ImNyZWF0ZSI6bnVsbCwiZGVsZXRlIjpudWxsLCJyZWFkIjpudWxsLCJ1cGRhdGUiOm51bGx9LCJ0dGwiOjMwMCwiem9uZV9uYW1lIjoidGhpc2lzYXRlc3R1c2luZ3RmLmNvbSJ9", + "Err": null +} \ No newline at end of file diff --git a/pkg/remote/test/azurerm_private_dns_txt_record_multiple/results.golden.json b/pkg/remote/test/azurerm_private_dns_txt_record_multiple/results.golden.json new file mode 100755 index 00000000..a52f6bf1 --- /dev/null +++ b/pkg/remote/test/azurerm_private_dns_txt_record_multiple/results.golden.json @@ -0,0 +1,35 @@ +[ + { + "fqdn": "testtxt.thisisatestusingtf.com.", + "id": "/subscriptions/8cb43347-a79f-4bb2-a8b4-c838b41fa5a5/resourceGroups/martin-dev/providers/Microsoft.Network/privateDnsZones/thisisatestusingtf.com/TXT/testtxt", + "name": "testtxt", + "record": [ + { + "value": "this is value line 3" + } + ], + "resource_group_name": "martin-dev", + "tags": null, + "timeouts": null, + "ttl": 300, + "zone_name": "thisisatestusingtf.com" + }, + { + "fqdn": "othertesttxt.thisisatestusingtf.com.", + "id": "/subscriptions/8cb43347-a79f-4bb2-a8b4-c838b41fa5a5/resourceGroups/martin-dev/providers/Microsoft.Network/privateDnsZones/thisisatestusingtf.com/TXT/othertesttxt", + "name": "othertesttxt", + "record": [ + { + "value": "this is value line 1" + }, + { + "value": "this is value line 2" + } + ], + "resource_group_name": "martin-dev", + "tags": null, + "timeouts": null, + "ttl": 300, + "zone_name": "thisisatestusingtf.com" + } +] \ No newline at end of file diff --git a/pkg/resource/azurerm/azurerm_private_dns_txt_record.go b/pkg/resource/azurerm/azurerm_private_dns_txt_record.go new file mode 100644 index 00000000..ef830f59 --- /dev/null +++ b/pkg/resource/azurerm/azurerm_private_dns_txt_record.go @@ -0,0 +1,25 @@ +package azurerm + +import ( + "github.com/cloudskiff/driftctl/pkg/resource" +) + +const AzurePrivateDNSTXTRecordResourceType = "azurerm_private_dns_txt_record" + +func initAzurePrivateDNSTXTRecordMetaData(resourceSchemaRepository resource.SchemaRepositoryInterface) { + resourceSchemaRepository.SetNormalizeFunc(AzurePrivateDNSTXTRecordResourceType, func(res *resource.Resource) { + res.Attributes().SafeDelete([]string{"timeouts"}) + }) + resourceSchemaRepository.SetHumanReadableAttributesFunc(AzurePrivateDNSTXTRecordResourceType, func(res *resource.Resource) map[string]string { + val := res.Attrs + attrs := make(map[string]string) + if name := val.GetString("name"); name != nil && *name != "" { + attrs["Name"] = *name + } + if zone := val.GetString("zone_name"); zone != nil && *zone != "" { + attrs["Zone"] = *zone + } + return attrs + }) + resourceSchemaRepository.SetFlags(AzurePrivateDNSTXTRecordResourceType, resource.FlagDeepMode) +} diff --git a/pkg/resource/azurerm/azurerm_private_dns_txt_record_test.go b/pkg/resource/azurerm/azurerm_private_dns_txt_record_test.go new file mode 100644 index 00000000..7c9b2244 --- /dev/null +++ b/pkg/resource/azurerm/azurerm_private_dns_txt_record_test.go @@ -0,0 +1,30 @@ +package azurerm_test + +import ( + "testing" + + "github.com/cloudskiff/driftctl/test" + "github.com/cloudskiff/driftctl/test/acceptance" +) + +func TestAcc_Azure_PrivateDNSTXTRecord(t *testing.T) { + acceptance.Run(t, acceptance.AccTestCase{ + TerraformVersion: "0.15.5", + Paths: []string{"./testdata/acc/azurerm_private_dns_txt_record"}, + Args: []string{ + "scan", + "--to", "azure+tf", "--deep", + }, + Checks: []acceptance.AccCheck{ + { + Check: func(result *test.ScanResult, stdout string, err error) { + if err != nil { + t.Fatal(err) + } + result.AssertInfrastructureIsInSync() + result.AssertManagedCount(2) + }, + }, + }, + }) +} diff --git a/pkg/resource/azurerm/metadata.go b/pkg/resource/azurerm/metadata.go index aee9a1cc..84aa1c85 100644 --- a/pkg/resource/azurerm/metadata.go +++ b/pkg/resource/azurerm/metadata.go @@ -20,6 +20,7 @@ func InitResourcesMetadata(resourceSchemaRepository resource.SchemaRepositoryInt initAzurePrivateDNSPTRRecordMetaData(resourceSchemaRepository) initAzurePrivateDNSSRVRecordMetaData(resourceSchemaRepository) initAzurePrivateDNSMXRecordMetaData(resourceSchemaRepository) + initAzurePrivateDNSTXTRecordMetaData(resourceSchemaRepository) initAzureImageMetaData(resourceSchemaRepository) initAzureSSHPublicKeyMetaData(resourceSchemaRepository) initAzurePrivateDNSCNameRecordMetaData(resourceSchemaRepository) diff --git a/pkg/resource/azurerm/metadata_test.go b/pkg/resource/azurerm/metadata_test.go index 793dec67..e778b6c8 100644 --- a/pkg/resource/azurerm/metadata_test.go +++ b/pkg/resource/azurerm/metadata_test.go @@ -32,6 +32,7 @@ func TestAzureMetadata_Flags(t *testing.T) { AzurePrivateDNSPTRRecordResourceType: {resource.FlagDeepMode}, AzurePrivateDNSMXRecordResourceType: {resource.FlagDeepMode}, AzurePrivateDNSSRVRecordResourceType: {resource.FlagDeepMode}, + AzurePrivateDNSTXTRecordResourceType: {resource.FlagDeepMode}, AzureImageResourceType: {}, AzureSSHPublicKeyResourceType: {resource.FlagDeepMode}, } diff --git a/pkg/resource/azurerm/testdata/acc/azurerm_private_dns_txt_record/.driftignore b/pkg/resource/azurerm/testdata/acc/azurerm_private_dns_txt_record/.driftignore new file mode 100644 index 00000000..00a5a082 --- /dev/null +++ b/pkg/resource/azurerm/testdata/acc/azurerm_private_dns_txt_record/.driftignore @@ -0,0 +1,2 @@ +* +!azurerm_private_dns_txt_record diff --git a/pkg/resource/azurerm/testdata/acc/azurerm_private_dns_txt_record/terraform.tf b/pkg/resource/azurerm/testdata/acc/azurerm_private_dns_txt_record/terraform.tf new file mode 100644 index 00000000..0f0e27a1 --- /dev/null +++ b/pkg/resource/azurerm/testdata/acc/azurerm_private_dns_txt_record/terraform.tf @@ -0,0 +1,46 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~> 2.71.0" + } + } +} + +provider "azurerm" { + features {} +} + +data "azurerm_resource_group" "example" { + name = "driftctl-qa-1" +} + +resource "azurerm_private_dns_zone" "testzone" { + name = "this-zone-is-a-test-for-driftctl.com" + resource_group_name = data.azurerm_resource_group.example.name +} + +resource "azurerm_private_dns_txt_record" "othertesttxt" { + name = "othertesttxt" + zone_name = azurerm_private_dns_zone.testzone.name + resource_group_name = data.azurerm_resource_group.example.name + ttl = 300 + record { + value = "this is value line 1" + } + + record { + value = "this is value line 2" + } +} + +resource "azurerm_private_dns_txt_record" "testtxt" { + name = "testtxt" + zone_name = azurerm_private_dns_zone.testzone.name + resource_group_name = data.azurerm_resource_group.example.name + ttl = 300 + record { + value = "this is value line 3" + } +} + diff --git a/pkg/resource/resource_types.go b/pkg/resource/resource_types.go index 3cc05893..4e3054bb 100644 --- a/pkg/resource/resource_types.go +++ b/pkg/resource/resource_types.go @@ -207,6 +207,7 @@ var supportedTypes = map[string]ResourceTypeMeta{ "azurerm_private_dns_ptr_record": {}, "azurerm_private_dns_srv_record": {}, "azurerm_private_dns_mx_record": {}, + "azurerm_private_dns_txt_record": {}, "azurerm_image": {}, "azurerm_ssh_public_key": {}, }