From 9fcd2f4011697621115baf7e3c7f41c92eb321b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fan=20Can=20Bak=C4=B1r?= Date: Thu, 8 Oct 2026 16:36:46 +0300 Subject: [PATCH 1/4] azure: add managed database endpoints --- go.mod | 15 +- go.sum | 42 ++-- pkg/providers/azure/azure.go | 15 +- pkg/providers/azure/databases.go | 276 ++++++++++++++++++++++++++ pkg/providers/azure/databases_test.go | 147 ++++++++++++++ 5 files changed, 475 insertions(+), 20 deletions(-) create mode 100644 pkg/providers/azure/databases.go create mode 100644 pkg/providers/azure/databases_test.go diff --git a/go.mod b/go.mod index f8b9b6bf..8a0a37cb 100644 --- a/go.mod +++ b/go.mod @@ -154,8 +154,8 @@ require ( require ( cloud.google.com/go/asset v1.21.1 - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/apimanagement/armapimanagement v1.1.1 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appcontainers/armappcontainers v1.1.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice v1.0.0 @@ -163,11 +163,16 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance v1.0.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cosmos/armcosmos/v4 v4.0.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/frontdoor/armfrontdoor v1.4.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/mysql/armmysqlflexibleservers/v2 v2.0.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/postgresql/armpostgresqlflexibleservers/v5 v5.0.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redis/armredis/v4 v4.0.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/sql/armsql v1.2.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager v1.3.0 github.com/alitto/pond/v2 v2.3.2 @@ -186,8 +191,8 @@ require ( cloud.google.com/go/longrunning v0.6.7 // indirect cloud.google.com/go/orgpolicy v1.15.0 // indirect cloud.google.com/go/osconfig v1.14.6 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2 // indirect github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057 // indirect github.com/Mzack9999/go-http-digest-auth-client v0.6.1-0.20220414142836-eb8883508809 // indirect github.com/akrylysov/pogreb v0.10.1 // indirect @@ -201,7 +206,7 @@ require ( github.com/gaissmai/bart v0.29.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/gofrs/flock v0.8.1 // indirect - github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/logrusorgru/aurora/v4 v4.0.0 // indirect diff --git a/go.sum b/go.sum index ce243d1b..eb9e170b 100644 --- a/go.sum +++ b/go.sum @@ -23,14 +23,14 @@ cloud.google.com/go/osconfig v1.14.6 h1:4uJrA1obzMBp1I+DF15y/MvsXKIODevuANpq3Qhv cloud.google.com/go/osconfig v1.14.6/go.mod h1:LS39HDBH0IJDFgOUkhSZUHFQzmcWaCpYXLrc3A4CVzI= git.arvancloud.ir/arvancloud/cdn-go-sdk v0.12.1 h1:yYxJ5KSIcL8YJrQrKlcPZqvQQc4nK8VVrVkEK8J0NwY= git.arvancloud.ir/arvancloud/cdn-go-sdk v0.12.1/go.mod h1:ujHWzPDF3SdsJNwN2tBYZQyQJE9h0obkR7TYJRe8KO4= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 h1:5YTBM8QDVIBN3sxBil89WfdAAqDZbyJTgh688DSxX5w= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0 h1:KpMC6LFL7mqpExyMC9jVOYRiVhLmamjeZfRsUpB7l4s= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0/go.mod h1:J7MUC/wtRpfGVbQ5sIItY5/FuVWmvzlY21WAOfQnq/I= -github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= -github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0 h1:aokoqcHvaGjiM3VpjKDfMMnF/8epJ+Q1HLJ7CudztqE= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0/go.mod h1:/WYEx9pcM9Y+Dd/APJaNlSvVSvzl54rrMdZT5+Oi2LM= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0 h1:CU4+EJeJi3TKYWEcYuSdWsjzw0nVsK/H0MSQOiPcymU= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0/go.mod h1:q0+UTSRvShwUCrR/s5HtyInYphN7Wvxb7snFM3u+SLA= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.4.0 h1:xFaZZ+IubdftrDHnGGwZ6QvQ3KHTtWl2MCK+GMt2vxs= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.4.0/go.mod h1:mCBhUhlMjLLJKr5aqw2TNS/VqJOie8MzWq3DAMJeKso= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 h1:fhqpLE3UEXi9lPaBRpQ6XuRW0nU7hgg4zlmZZa+a9q4= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0/go.mod h1:7dCRMLwisfRH3dBupKeNCioWYUZ4SS09Z14H+7i8ZoY= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/apimanagement/armapimanagement v1.1.1 h1:jCkNVNpsEevyic4bmjgVjzVA4tMGSJpXNGirf+S+mDI= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/apimanagement/armapimanagement v1.1.1/go.mod h1:a0Ug1l73Il7EhrCJEEt2dGjlNjvphppZq5KqJdgnwuw= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appcontainers/armappcontainers v1.1.0 h1:fdAOz6TFldGDoEcRa975i5L5QvWU8ptut+SJAIfuWUY= @@ -45,6 +45,8 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armconta github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance v1.0.0/go.mod h1:V0F1UD2J+8nx/DQEfxZCXnLCKVLFlYUG8lrjrxFKU8w= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0 h1:figxyQZXzZQIcP3njhC68bYUiTw45J8/SsHaLW8Ax0M= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0/go.mod h1:TmlMW4W5OvXOmOyKNnor8nlMMiO1ctIyzmHme/VHsrA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cosmos/armcosmos/v4 v4.0.0 h1:PbV55iju1R5VSjwDjILVeCBF0+N5XFzUGPUb/yC0G08= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cosmos/armcosmos/v4 v4.0.0/go.mod h1:QrLT4jHN38ZKphjiVZfdl3iTadWextUIzPvMOj68hz4= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/frontdoor/armfrontdoor v1.4.0 h1:dz5II+dFuMkrdpIkO9f/Ht3f8hnRUURiQdLj1hwKO5Q= @@ -53,24 +55,36 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNL github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2/go.mod h1:FbdwsQ2EzwvXxOPcMFYO8ogEc9uMMIj3YkmCdXdAFmk= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0/go.mod h1:AW8VEadnhw9xox+VaVd9sP7NjzOAnaZBLRH6Tq3cJ38= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.2.0 h1:+lnLQhKh3cgSOIOVH61UZ3s/l9d+bAZp5d/spt1+7UI= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.2.0/go.mod h1:tStOHrivWUrcBolspvKV70Us1ckESYGYSHdG4LX8zyY= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0 h1:pPvTJ1dY0sA35JOeFq6TsY2xj6Z85Yo23Pj4wCCvu4o= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0/go.mod h1:mLfWfj8v3jfWKsL9G4eoBoXVcsqcIUTapmdKy7uGOp0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/mysql/armmysqlflexibleservers/v2 v2.0.0 h1:iyx6jFFyRo8/9FQiyHRTjoUluopwt+9lvOtRBhfB7Y8= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/mysql/armmysqlflexibleservers/v2 v2.0.0/go.mod h1:LSdxfHhUzKllvM+sYDf1wrcQrbInrF4iXJ/NMk7fK90= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 h1:QM6sE5k2ZT/vI5BEe0r7mqjsUSnhVBFbOsVkEuaEfiA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0/go.mod h1:243D9iHbcQXoFUtgHJwL7gl2zx1aDuDMjvBZVGr2uW0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/postgresql/armpostgresqlflexibleservers/v5 v5.0.0 h1:S7K+MLPEYe+g9AX9dLKldBpYV03bPl7zeDaWhiNDqqs= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/postgresql/armpostgresqlflexibleservers/v5 v5.0.0/go.mod h1:EHRrmrnS2Q8fB3+DE30TTk04JLqjui5ZJEF7eMVQ2/M= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redis/armredis/v4 v4.0.0 h1:xXVDKm0Nd3I9Sz2AcRm/L4+fm7xZjAWI/AkU73m0Ato= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redis/armredis/v4 v4.0.0/go.mod h1:SF4DGSRInvFhQq0ndZ+nLrsiIKhJ23pacoVtdrmc6Pg= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armdeployments v1.0.0 h1:67nFqWXpo0x5Nz0XEb1yI7s8D+EHy8NsTinYw9sZnLk= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armdeployments v1.0.0/go.mod h1:fewgRjNVE84QVVh798sIMFb7gPXPp7NmnekGnboSnXk= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v3 v3.0.1 h1:guyQA4b8XB2sbJZXzUnOF9mn0WDBv/ZT7me9wTipKtE= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v3 v3.0.1/go.mod h1:8h8yhzh9o+0HeSIhUxYny+rEQajScrfIpNktvgYG3Q8= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0 h1:wxQx2Bt4xzPIKvW59WQf1tJNx/ZZKPfN+EhPX3Z6CYY= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0/go.mod h1:TpiwjwnW/khS0LKs4vW5UmmT9OWcxaveS8U7+tlknzo= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/sql/armsql v1.2.0 h1:S087deZ0kP1RUg4pU7w9U9xpUedTCbOtz+mnd0+hrkQ= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/sql/armsql v1.2.0/go.mod h1:B4cEyXrWBmbfMDAPnpJ1di7MAt5DKP57jPEObAvZChg= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1/go.mod h1:Ng3urmn6dYe8gnbCMoHHVl5APYz2txho3koEkV2o2HA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager v1.3.0 h1:e3kTG23M5ps+DjvPolK4dcgohDY8sHsXU7zrdHj1WzY= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager v1.3.0/go.mod h1:Os5dq8Cvvz97rJauZhZJAfKHN+OEvF/0nVmHzF4aVys= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= -github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 h1:XkkQbfMyuH2jTSjQjSoihryI8GINRcs4xp8lNawg0FI= -github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= +github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2 h1:RHK7bS+HQMslb1sZpAokUt+zTVmue0hKSs2C791hhzU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= @@ -217,8 +231,8 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= -github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= -github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= diff --git a/pkg/providers/azure/azure.go b/pkg/providers/azure/azure.go index 96221225..b813fad8 100644 --- a/pkg/providers/azure/azure.go +++ b/pkg/providers/azure/azure.go @@ -21,7 +21,7 @@ const ( providerName = "azure" ) -var Services = []string{"vm", "publicip", "trafficmanager", "cdn", "dns", "loadbalancer", "applicationgateway", "aks", "storage", "containerinstances", "appservice", "functions", "apimanagement", "frontdoor", "containerapps", "staticwebapps"} +var Services = []string{"vm", "publicip", "trafficmanager", "cdn", "dns", "loadbalancer", "applicationgateway", "aks", "storage", "containerinstances", "appservice", "functions", "apimanagement", "frontdoor", "containerapps", "staticwebapps", "redis", "mysql", "postgresql", "sql", "cosmosdb"} // Provider is a data provider for Azure API using Track 2 SDK type Provider struct { @@ -273,6 +273,19 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { } resources.Merge(frontDoors) } + + dbp := &databaseProvider{Credential: p.Credential, SubscriptionID: subscriptionID, id: p.id, extendedMetadata: p.extendedMetadata} + for _, fetcher := range dbp.fetchers() { + if !p.services.Has(fetcher.service) { + continue + } + databases, err := fetcher.fetch(ctx) + if err != nil { + gologger.Warning().Msgf("Error listing %s for subscription %s: %s", fetcher.service, subscriptionID, err) + continue + } + resources.Merge(databases) + } } return resources, nil } diff --git a/pkg/providers/azure/databases.go b/pkg/providers/azure/databases.go new file mode 100644 index 00000000..6355d3f6 --- /dev/null +++ b/pkg/providers/azure/databases.go @@ -0,0 +1,276 @@ +package azure + +import ( + "context" + "fmt" + "slices" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cosmos/armcosmos/v4" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/mysql/armmysqlflexibleservers/v2" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/postgresql/armpostgresqlflexibleservers/v5" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redis/armredis/v4" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/sql/armsql" + "github.com/projectdiscovery/cloudlist/pkg/schema" +) + +// databaseProvider is a provider for Azure managed database endpoints +// (Azure Cache for Redis, MySQL and PostgreSQL flexible servers, SQL servers, Cosmos DB) +type databaseProvider struct { + id string + SubscriptionID string + Credential azcore.TokenCredential + extendedMetadata bool + // clientOptions is nil outside tests, which point it at a fake ARM endpoint. + clientOptions *arm.ClientOptions +} + +type databaseFetcher struct { + service string + fetch func(context.Context) (*schema.Resources, error) +} + +// fetchers pairs each database service name with its lister +func (dp *databaseProvider) fetchers() []databaseFetcher { + return []databaseFetcher{ + {"redis", dp.redis}, + {"mysql", dp.mysql}, + {"postgresql", dp.postgresql}, + {"sql", dp.sql}, + {"cosmosdb", dp.cosmosdb}, + } +} + +// add appends a hostname. Public network access is recorded in metadata +// rather than skipping the host, since a private one still resolves through +// its privatelink CNAME and belongs in the inventory. +func (dp *databaseProvider) add(list *schema.Resources, service, host string, metadata map[string]string) { + if host == "" { + return + } + list.Append(&schema.Resource{ + Provider: providerName, + ID: dp.id, + DNSName: host, + Service: service, + Metadata: metadata, + }) +} + +func (dp *databaseProvider) redis(ctx context.Context) (*schema.Resources, error) { + client, err := armredis.NewClient(dp.SubscriptionID, dp.Credential, dp.clientOptions) + if err != nil { + return nil, fmt.Errorf("failed to create redis client: %w", err) + } + + list := schema.NewResources() + pager := client.NewListBySubscriptionPager(nil) + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("failed to list redis caches: %w", err) + } + for _, cache := range page.Value { + if cache == nil || cache.Properties == nil || cache.Properties.HostName == nil { + continue + } + var metadata map[string]string + if dp.extendedMetadata { + props := cache.Properties + metadata = dp.metadata(cache.ID, cache.Name, cache.Location, cache.Tags) + addEnumMetadata(metadata, "public_network_access", props.PublicNetworkAccess) + addEnumMetadata(metadata, "provisioning_state", props.ProvisioningState) + schema.AddMetadata(metadata, "redis_version", props.RedisVersion) + addInt32Metadata(metadata, "port", props.Port) + addInt32Metadata(metadata, "ssl_port", props.SSLPort) + if props.EnableNonSSLPort != nil { + metadata["non_ssl_port_enabled"] = fmt.Sprintf("%v", *props.EnableNonSSLPort) + } + } + dp.add(list, "redis", *cache.Properties.HostName, metadata) + } + } + return list, nil +} + +func (dp *databaseProvider) mysql(ctx context.Context) (*schema.Resources, error) { + client, err := armmysqlflexibleservers.NewServersClient(dp.SubscriptionID, dp.Credential, dp.clientOptions) + if err != nil { + return nil, fmt.Errorf("failed to create mysql servers client: %w", err) + } + + list := schema.NewResources() + pager := client.NewListPager(nil) + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("failed to list mysql flexible servers: %w", err) + } + for _, server := range page.Value { + if server == nil || server.Properties == nil || server.Properties.FullyQualifiedDomainName == nil { + continue + } + var metadata map[string]string + if dp.extendedMetadata { + props := server.Properties + metadata = dp.metadata(server.ID, server.Name, server.Location, server.Tags) + if props.Network != nil { + addEnumMetadata(metadata, "public_network_access", props.Network.PublicNetworkAccess) + } + addEnumMetadata(metadata, "version", props.Version) + addEnumMetadata(metadata, "state", props.State) + schema.AddMetadata(metadata, "administrator_login", props.AdministratorLogin) + if server.SKU != nil { + schema.AddMetadata(metadata, "sku_name", server.SKU.Name) + } + } + dp.add(list, "mysql", *server.Properties.FullyQualifiedDomainName, metadata) + } + } + return list, nil +} + +func (dp *databaseProvider) postgresql(ctx context.Context) (*schema.Resources, error) { + client, err := armpostgresqlflexibleservers.NewServersClient(dp.SubscriptionID, dp.Credential, dp.clientOptions) + if err != nil { + return nil, fmt.Errorf("failed to create postgresql servers client: %w", err) + } + + list := schema.NewResources() + pager := client.NewListBySubscriptionPager(nil) + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("failed to list postgresql flexible servers: %w", err) + } + for _, server := range page.Value { + if server == nil || server.Properties == nil || server.Properties.FullyQualifiedDomainName == nil { + continue + } + var metadata map[string]string + if dp.extendedMetadata { + props := server.Properties + metadata = dp.metadata(server.ID, server.Name, server.Location, server.Tags) + if props.Network != nil { + addEnumMetadata(metadata, "public_network_access", props.Network.PublicNetworkAccess) + } + addEnumMetadata(metadata, "version", props.Version) + addEnumMetadata(metadata, "state", props.State) + schema.AddMetadata(metadata, "administrator_login", props.AdministratorLogin) + if server.SKU != nil { + schema.AddMetadata(metadata, "sku_name", server.SKU.Name) + } + } + dp.add(list, "postgresql", *server.Properties.FullyQualifiedDomainName, metadata) + } + } + return list, nil +} + +func (dp *databaseProvider) sql(ctx context.Context) (*schema.Resources, error) { + client, err := armsql.NewServersClient(dp.SubscriptionID, dp.Credential, dp.clientOptions) + if err != nil { + return nil, fmt.Errorf("failed to create sql servers client: %w", err) + } + + list := schema.NewResources() + pager := client.NewListPager(nil) + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("failed to list sql servers: %w", err) + } + for _, server := range page.Value { + if server == nil || server.Properties == nil || server.Properties.FullyQualifiedDomainName == nil { + continue + } + var metadata map[string]string + if dp.extendedMetadata { + props := server.Properties + metadata = dp.metadata(server.ID, server.Name, server.Location, server.Tags) + addEnumMetadata(metadata, "public_network_access", props.PublicNetworkAccess) + schema.AddMetadata(metadata, "version", props.Version) + schema.AddMetadata(metadata, "state", props.State) + schema.AddMetadata(metadata, "minimal_tls_version", props.MinimalTLSVersion) + schema.AddMetadata(metadata, "administrator_login", props.AdministratorLogin) + } + dp.add(list, "sql", *server.Properties.FullyQualifiedDomainName, metadata) + } + } + return list, nil +} + +func (dp *databaseProvider) cosmosdb(ctx context.Context) (*schema.Resources, error) { + client, err := armcosmos.NewDatabaseAccountsClient(dp.SubscriptionID, dp.Credential, dp.clientOptions) + if err != nil { + return nil, fmt.Errorf("failed to create cosmos db accounts client: %w", err) + } + + list := schema.NewResources() + pager := client.NewListPager(nil) + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("failed to list cosmos db accounts: %w", err) + } + for _, account := range page.Value { + if account == nil || account.Properties == nil { + continue + } + props := account.Properties + var metadata map[string]string + if dp.extendedMetadata { + metadata = dp.metadata(account.ID, account.Name, account.Location, account.Tags) + addEnumMetadata(metadata, "public_network_access", props.PublicNetworkAccess) + addEnumMetadata(metadata, "kind", account.Kind) + schema.AddMetadata(metadata, "provisioning_state", props.ProvisioningState) + addEnumMetadata(metadata, "minimal_tls_version", props.MinimalTLSVersion) + if props.DisableLocalAuth != nil { + metadata["local_auth_disabled"] = fmt.Sprintf("%v", *props.DisableLocalAuth) + } + } + if props.DocumentEndpoint != nil { + dp.add(list, "cosmosdb", extractDNSFromURL(*props.DocumentEndpoint), metadata) + } + // Multi-region accounts also serve each region on its own hostname. + for _, location := range slices.Concat(props.WriteLocations, props.ReadLocations) { + if location != nil && location.DocumentEndpoint != nil { + dp.add(list, "cosmosdb", extractDNSFromURL(*location.DocumentEndpoint), metadata) + } + } + } + } + return list, nil +} + +func (dp *databaseProvider) metadata(resourceID, name, location *string, tags map[string]*string) map[string]string { + metadata := make(map[string]string) + + schema.AddMetadata(metadata, "name", name) + schema.AddMetadata(metadata, "resource_id", resourceID) + metadata["subscription_id"] = dp.SubscriptionID + metadata["owner_id"] = dp.SubscriptionID + schema.AddMetadata(metadata, "location", location) + if resourceID != nil { + if _, resourceGroup := parseAzureResourceID(*resourceID); resourceGroup != "" { + metadata["resource_group"] = resourceGroup + } + } + if tagString := buildAzureTagString(tags); tagString != "" { + metadata["tags"] = tagString + } + return metadata +} + +func addEnumMetadata[T ~string](metadata map[string]string, key string, value *T) { + if value != nil && *value != "" { + metadata[key] = string(*value) + } +} + +func addInt32Metadata(metadata map[string]string, key string, value *int32) { + if value != nil { + metadata[key] = fmt.Sprintf("%d", *value) + } +} diff --git a/pkg/providers/azure/databases_test.go b/pkg/providers/azure/databases_test.go new file mode 100644 index 00000000..6d172ca9 --- /dev/null +++ b/pkg/providers/azure/databases_test.go @@ -0,0 +1,147 @@ +package azure + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const testSubscriptionID = "00000000-0000-0000-0000-000000000000" + +var fakeARMResponses = map[string]string{ + "/subscriptions/" + testSubscriptionID + "/providers/Microsoft.Cache/redis": `{"value":[ + {"id":"/subscriptions/` + testSubscriptionID + `/resourceGroups/rg-cache/providers/Microsoft.Cache/Redis/contoso-cache","name":"contoso-cache","location":"eastus", + "tags":{"env":"prod"}, + "properties":{"hostName":"contoso-cache.redis.cache.windows.net","port":6379,"sslPort":6380,"enableNonSslPort":false,"publicNetworkAccess":"Disabled","redisVersion":"6.0","provisioningState":"Succeeded"}}, + {"id":"/subscriptions/` + testSubscriptionID + `/resourceGroups/rg-cache/providers/Microsoft.Cache/Redis/provisioning","name":"provisioning","properties":{}} + ]}`, + "/subscriptions/" + testSubscriptionID + "/providers/Microsoft.DBforMySQL/flexibleServers": `{"value":[ + {"id":"/subscriptions/` + testSubscriptionID + `/resourceGroups/rg-db/providers/Microsoft.DBforMySQL/flexibleServers/contoso-mysql","name":"contoso-mysql","location":"westeurope", + "sku":{"name":"Standard_B1ms","tier":"Burstable"}, + "properties":{"fullyQualifiedDomainName":"contoso-mysql.mysql.database.azure.com","version":"8.0.21","state":"Ready","network":{"publicNetworkAccess":"Enabled"}}} + ]}`, + "/subscriptions/" + testSubscriptionID + "/providers/Microsoft.DBforPostgreSQL/flexibleServers": `{"value":[ + {"id":"/subscriptions/` + testSubscriptionID + `/resourceGroups/rg-db/providers/Microsoft.DBforPostgreSQL/flexibleServers/contoso-pg","name":"contoso-pg","location":"westeurope", + "properties":{"fullyQualifiedDomainName":"contoso-pg.postgres.database.azure.com","version":"16","state":"Ready","network":{"publicNetworkAccess":"Disabled"}}} + ]}`, + "/subscriptions/" + testSubscriptionID + "/providers/Microsoft.Sql/servers": `{"value":[ + {"id":"/subscriptions/` + testSubscriptionID + `/resourceGroups/rg-db/providers/Microsoft.Sql/servers/contoso-sql","name":"contoso-sql","location":"eastus", + "properties":{"fullyQualifiedDomainName":"contoso-sql.database.windows.net","version":"12.0","state":"Ready","publicNetworkAccess":"Enabled","minimalTlsVersion":"1.2"}} + ]}`, + "/subscriptions/" + testSubscriptionID + "/providers/Microsoft.DocumentDB/databaseAccounts": `{"value":[ + {"id":"/subscriptions/` + testSubscriptionID + `/resourceGroups/rg-db/providers/Microsoft.DocumentDB/databaseAccounts/contoso-cosmos","name":"contoso-cosmos","location":"eastus","kind":"GlobalDocumentDB", + "properties":{"documentEndpoint":"https://contoso-cosmos.documents.azure.com:443/","publicNetworkAccess":"Enabled", + "writeLocations":[{"locationName":"East US","documentEndpoint":"https://contoso-cosmos-eastus.documents.azure.com:443/"}], + "readLocations":[{"locationName":"East US","documentEndpoint":"https://contoso-cosmos-eastus.documents.azure.com:443/"},{"locationName":"West US","documentEndpoint":"https://contoso-cosmos-westus.documents.azure.com:443/"}]}} + ]}`, +} + +type fakeCredential struct{} + +func (fakeCredential) GetToken(context.Context, policy.TokenRequestOptions) (azcore.AccessToken, error) { + return azcore.AccessToken{Token: "fake", ExpiresOn: time.Now().Add(time.Hour)}, nil +} + +// newTestDatabaseProvider points the real ARM clients at a fake endpoint. +// TLS is required because the SDK refuses to send bearer tokens over plain HTTP. +func newTestDatabaseProvider(t *testing.T) *databaseProvider { + t.Helper() + server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, ok := fakeARMResponses[r.URL.Path] + if !ok { + t.Errorf("unexpected request: %s %s", r.Method, r.URL.Path) + w.WriteHeader(http.StatusNotFound) + return + } + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(body)) + })) + t.Cleanup(server.Close) + + return &databaseProvider{ + id: "test", + SubscriptionID: testSubscriptionID, + Credential: fakeCredential{}, + extendedMetadata: true, + clientOptions: &arm.ClientOptions{ClientOptions: policy.ClientOptions{ + Cloud: cloud.Configuration{Services: map[cloud.ServiceName]cloud.ServiceConfiguration{ + cloud.ResourceManager: {Endpoint: server.URL, Audience: "https://management.azure.com"}, + }}, + Transport: server.Client(), + }}, + } +} + +func TestDatabaseProviderFetchers(t *testing.T) { + dp := newTestDatabaseProvider(t) + + tests := []struct { + service string + hosts []string + metadata map[string]string + }{ + { + service: "redis", + hosts: []string{"contoso-cache.redis.cache.windows.net"}, + metadata: map[string]string{ + "public_network_access": "Disabled", "resource_group": "rg-cache", "ssl_port": "6380", + "non_ssl_port_enabled": "false", "tags": "env=prod", + }, + }, + { + service: "mysql", + hosts: []string{"contoso-mysql.mysql.database.azure.com"}, + metadata: map[string]string{"public_network_access": "Enabled", "sku_name": "Standard_B1ms", "version": "8.0.21"}, + }, + { + service: "postgresql", + hosts: []string{"contoso-pg.postgres.database.azure.com"}, + metadata: map[string]string{"public_network_access": "Disabled", "version": "16"}, + }, + { + service: "sql", + hosts: []string{"contoso-sql.database.windows.net"}, + metadata: map[string]string{"public_network_access": "Enabled", "minimal_tls_version": "1.2"}, + }, + { + service: "cosmosdb", + hosts: []string{ + "contoso-cosmos.documents.azure.com", + "contoso-cosmos-eastus.documents.azure.com", + "contoso-cosmos-westus.documents.azure.com", + }, + metadata: map[string]string{"public_network_access": "Enabled", "kind": "GlobalDocumentDB"}, + }, + } + + fetchers := dp.fetchers() + require.Len(t, fetchers, len(tests)) + for i, tt := range tests { + t.Run(tt.service, func(t *testing.T) { + require.Equal(t, tt.service, fetchers[i].service) + + resources, err := fetchers[i].fetch(context.Background()) + require.NoError(t, err) + + var hosts []string + for _, resource := range resources.Items { + assert.Equal(t, tt.service, resource.Service) + assert.Equal(t, providerName, resource.Provider) + for key, value := range tt.metadata { + assert.Equal(t, value, resource.Metadata[key], key) + } + hosts = append(hosts, resource.DNSName) + } + assert.Equal(t, tt.hosts, hosts) + }) + } +} From f04417cc5952a4df746775bfba2265c52762e43f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fan=20Can=20Bak=C4=B1r?= Date: Thu, 8 Oct 2026 18:05:47 +0300 Subject: [PATCH 2/4] azure: add cosmos db mongodb endpoints --- pkg/providers/azure/databases.go | 20 ++++++++++++++++---- pkg/providers/azure/databases_test.go | 11 +++++++++-- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/pkg/providers/azure/databases.go b/pkg/providers/azure/databases.go index 6355d3f6..3b07f5d1 100644 --- a/pkg/providers/azure/databases.go +++ b/pkg/providers/azure/databases.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "slices" + "strings" "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" @@ -230,13 +231,24 @@ func (dp *databaseProvider) cosmosdb(ctx context.Context) (*schema.Resources, er metadata["local_auth_disabled"] = fmt.Sprintf("%v", *props.DisableLocalAuth) } } - if props.DocumentEndpoint != nil { - dp.add(list, "cosmosdb", extractDNSFromURL(*props.DocumentEndpoint), metadata) + isMongo := account.Kind != nil && *account.Kind == armcosmos.DatabaseAccountKindMongoDB + addEndpoint := func(endpoint *string) { + if endpoint == nil { + return + } + host := extractDNSFromURL(*endpoint) + dp.add(list, "cosmosdb", host, metadata) + // MongoDB accounts serve the wire protocol on a sibling host that the + // API does not return; deriving it keeps the sovereign cloud suffix. + if isMongo { + dp.add(list, "cosmosdb", strings.Replace(host, ".documents.", ".mongo.cosmos.", 1), metadata) + } } + addEndpoint(props.DocumentEndpoint) // Multi-region accounts also serve each region on its own hostname. for _, location := range slices.Concat(props.WriteLocations, props.ReadLocations) { - if location != nil && location.DocumentEndpoint != nil { - dp.add(list, "cosmosdb", extractDNSFromURL(*location.DocumentEndpoint), metadata) + if location != nil { + addEndpoint(location.DocumentEndpoint) } } } diff --git a/pkg/providers/azure/databases_test.go b/pkg/providers/azure/databases_test.go index 6d172ca9..466ed977 100644 --- a/pkg/providers/azure/databases_test.go +++ b/pkg/providers/azure/databases_test.go @@ -41,7 +41,10 @@ var fakeARMResponses = map[string]string{ {"id":"/subscriptions/` + testSubscriptionID + `/resourceGroups/rg-db/providers/Microsoft.DocumentDB/databaseAccounts/contoso-cosmos","name":"contoso-cosmos","location":"eastus","kind":"GlobalDocumentDB", "properties":{"documentEndpoint":"https://contoso-cosmos.documents.azure.com:443/","publicNetworkAccess":"Enabled", "writeLocations":[{"locationName":"East US","documentEndpoint":"https://contoso-cosmos-eastus.documents.azure.com:443/"}], - "readLocations":[{"locationName":"East US","documentEndpoint":"https://contoso-cosmos-eastus.documents.azure.com:443/"},{"locationName":"West US","documentEndpoint":"https://contoso-cosmos-westus.documents.azure.com:443/"}]}} + "readLocations":[{"locationName":"East US","documentEndpoint":"https://contoso-cosmos-eastus.documents.azure.com:443/"},{"locationName":"West US","documentEndpoint":"https://contoso-cosmos-westus.documents.azure.com:443/"}]}}, + {"id":"/subscriptions/` + testSubscriptionID + `/resourceGroups/rg-db/providers/Microsoft.DocumentDB/databaseAccounts/contoso-mongo","name":"contoso-mongo","location":"eastus","kind":"MongoDB", + "properties":{"documentEndpoint":"https://contoso-mongo.documents.azure.com:443/","publicNetworkAccess":"Enabled", + "writeLocations":[{"locationName":"East US","documentEndpoint":"https://contoso-mongo-eastus.documents.azure.com:443/"}]}} ]}`, } @@ -118,8 +121,12 @@ func TestDatabaseProviderFetchers(t *testing.T) { "contoso-cosmos.documents.azure.com", "contoso-cosmos-eastus.documents.azure.com", "contoso-cosmos-westus.documents.azure.com", + "contoso-mongo.documents.azure.com", + "contoso-mongo.mongo.cosmos.azure.com", + "contoso-mongo-eastus.documents.azure.com", + "contoso-mongo-eastus.mongo.cosmos.azure.com", }, - metadata: map[string]string{"public_network_access": "Enabled", "kind": "GlobalDocumentDB"}, + metadata: map[string]string{"public_network_access": "Enabled"}, }, } From 6f6dd8def265b9f678ee3085aa724d9d6969dd3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fan=20Can=20Bak=C4=B1r?= Date: Thu, 8 Oct 2026 18:28:45 +0300 Subject: [PATCH 3/4] azure: keep listing other services when one fails --- pkg/providers/azure/azure.go | 68 ++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/pkg/providers/azure/azure.go b/pkg/providers/azure/azure.go index b813fad8..5616c410 100644 --- a/pkg/providers/azure/azure.go +++ b/pkg/providers/azure/azure.go @@ -119,9 +119,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { vmIPs, err := vmp.GetResource(ctx) if err != nil { gologger.Warning().Msgf("Error listing VM public IPs for subscription %s: %s", subscriptionID, err) - continue + } else { + resources.Merge(vmIPs) } - resources.Merge(vmIPs) } if p.services.Has("publicip") { @@ -129,9 +129,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { publicIPs, err := publicIPp.GetResource(ctx) if err != nil { gologger.Warning().Msgf("Error listing public IPs for subscription %s: %s", subscriptionID, err) - continue + } else { + resources.Merge(publicIPs) } - resources.Merge(publicIPs) } if p.services.Has("trafficmanager") { @@ -139,9 +139,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { trafficManager, err := trafficManagerp.GetResource(ctx) if err != nil { gologger.Warning().Msgf("Error listing traffic manager for subscription %s: %s", subscriptionID, err) - continue + } else { + resources.Merge(trafficManager) } - resources.Merge(trafficManager) } if p.services.Has("cdn") { @@ -149,9 +149,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { cdn, err := cdnp.GetResource(ctx) if err != nil { gologger.Warning().Msgf("Error listing CDN for subscription %s: %s", subscriptionID, err) - continue + } else { + resources.Merge(cdn) } - resources.Merge(cdn) } if p.services.Has("dns") { @@ -159,9 +159,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { dnsRecords, err := dnsp.GetResource(ctx) if err != nil { gologger.Warning().Msgf("Error listing DNS records for subscription %s: %s", subscriptionID, err) - continue + } else { + resources.Merge(dnsRecords) } - resources.Merge(dnsRecords) } if p.services.Has("loadbalancer") { @@ -169,9 +169,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { loadBalancers, err := lbp.GetResource(ctx) if err != nil { gologger.Warning().Msgf("Error listing load balancers for subscription %s: %s", subscriptionID, err) - continue + } else { + resources.Merge(loadBalancers) } - resources.Merge(loadBalancers) } if p.services.Has("applicationgateway") { @@ -179,9 +179,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { appGateways, err := agp.GetResource(ctx) if err != nil { gologger.Warning().Msgf("Error listing application gateways for subscription %s: %s", subscriptionID, err) - continue + } else { + resources.Merge(appGateways) } - resources.Merge(appGateways) } if p.services.Has("aks") { @@ -189,9 +189,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { aksClusters, err := aksp.GetResource(ctx) if err != nil { gologger.Warning().Msgf("Error listing AKS clusters for subscription %s: %s", subscriptionID, err) - continue + } else { + resources.Merge(aksClusters) } - resources.Merge(aksClusters) } if p.services.Has("storage") { @@ -199,9 +199,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { storageAccounts, err := storagep.GetResource(ctx) if err != nil { gologger.Warning().Msgf("Error listing storage accounts for subscription %s: %s", subscriptionID, err) - continue + } else { + resources.Merge(storageAccounts) } - resources.Merge(storageAccounts) } if p.services.Has("containerinstances") { @@ -209,9 +209,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { containerInstances, err := containerInstancesp.GetResource(ctx) if err != nil { gologger.Warning().Msgf("Error listing container instances for subscription %s: %s", subscriptionID, err) - continue + } else { + resources.Merge(containerInstances) } - resources.Merge(containerInstances) } if p.services.Has("appservice") { @@ -219,9 +219,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { appService, err := appServicep.GetResource(ctx) if err != nil { gologger.Warning().Msgf("Error listing App Service for subscription %s: %s", subscriptionID, err) - continue + } else { + resources.Merge(appService) } - resources.Merge(appService) } if p.services.Has("functions") { @@ -229,9 +229,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { functions, err := functionsp.GetResource(ctx) if err != nil { gologger.Warning().Msgf("Error listing Functions for subscription %s: %s", subscriptionID, err) - continue + } else { + resources.Merge(functions) } - resources.Merge(functions) } if p.services.Has("apimanagement") { @@ -239,9 +239,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { apiManagement, err := apimgmtp.GetResource(ctx) if err != nil { gologger.Warning().Msgf("Error listing API Management for subscription %s: %s", subscriptionID, err) - continue + } else { + resources.Merge(apiManagement) } - resources.Merge(apiManagement) } if p.services.Has("containerapps") { @@ -249,9 +249,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { containerApps, err := containerAppsp.GetResource(ctx) if err != nil { gologger.Warning().Msgf("Error listing container apps for subscription %s: %s", subscriptionID, err) - continue + } else { + resources.Merge(containerApps) } - resources.Merge(containerApps) } if p.services.Has("staticwebapps") { @@ -259,9 +259,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { staticWebApps, err := staticWebAppsp.GetResource(ctx) if err != nil { gologger.Warning().Msgf("Error listing static web apps for subscription %s: %s", subscriptionID, err) - continue + } else { + resources.Merge(staticWebApps) } - resources.Merge(staticWebApps) } if p.services.Has("frontdoor") { @@ -269,9 +269,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { frontDoors, err := fdp.GetResource(ctx) if err != nil { gologger.Warning().Msgf("Error listing Front Door for subscription %s: %s", subscriptionID, err) - continue + } else { + resources.Merge(frontDoors) } - resources.Merge(frontDoors) } dbp := &databaseProvider{Credential: p.Credential, SubscriptionID: subscriptionID, id: p.id, extendedMetadata: p.extendedMetadata} @@ -282,9 +282,9 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { databases, err := fetcher.fetch(ctx) if err != nil { gologger.Warning().Msgf("Error listing %s for subscription %s: %s", fetcher.service, subscriptionID, err) - continue + } else { + resources.Merge(databases) } - resources.Merge(databases) } } return resources, nil From 19a62c6e2a7c5bdf7243ed73fbdaf15ea30ebbef Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Thu, 8 Oct 2026 19:20:31 +0200 Subject: [PATCH 4/4] fix cosmos --- pkg/providers/azure/databases.go | 11 +++++++---- pkg/providers/azure/databases_test.go | 9 ++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/pkg/providers/azure/databases.go b/pkg/providers/azure/databases.go index 3b07f5d1..6a2e5864 100644 --- a/pkg/providers/azure/databases.go +++ b/pkg/providers/azure/databases.go @@ -231,16 +231,19 @@ func (dp *databaseProvider) cosmosdb(ctx context.Context) (*schema.Resources, er metadata["local_auth_disabled"] = fmt.Sprintf("%v", *props.DisableLocalAuth) } } - isMongo := account.Kind != nil && *account.Kind == armcosmos.DatabaseAccountKindMongoDB + // MongoDB 3.6 and later serve the wire protocol on a sibling host the API + // does not return. Version 3.2, and accounts that omit the version, use + // the documents host. Deriving the sibling keeps the sovereign cloud suffix. + mongoWire := account.Kind != nil && *account.Kind == armcosmos.DatabaseAccountKindMongoDB && + props.APIProperties != nil && props.APIProperties.ServerVersion != nil && + *props.APIProperties.ServerVersion != armcosmos.ServerVersionThree2 addEndpoint := func(endpoint *string) { if endpoint == nil { return } host := extractDNSFromURL(*endpoint) dp.add(list, "cosmosdb", host, metadata) - // MongoDB accounts serve the wire protocol on a sibling host that the - // API does not return; deriving it keeps the sovereign cloud suffix. - if isMongo { + if mongoWire { dp.add(list, "cosmosdb", strings.Replace(host, ".documents.", ".mongo.cosmos.", 1), metadata) } } diff --git a/pkg/providers/azure/databases_test.go b/pkg/providers/azure/databases_test.go index 466ed977..461e62d3 100644 --- a/pkg/providers/azure/databases_test.go +++ b/pkg/providers/azure/databases_test.go @@ -44,7 +44,12 @@ var fakeARMResponses = map[string]string{ "readLocations":[{"locationName":"East US","documentEndpoint":"https://contoso-cosmos-eastus.documents.azure.com:443/"},{"locationName":"West US","documentEndpoint":"https://contoso-cosmos-westus.documents.azure.com:443/"}]}}, {"id":"/subscriptions/` + testSubscriptionID + `/resourceGroups/rg-db/providers/Microsoft.DocumentDB/databaseAccounts/contoso-mongo","name":"contoso-mongo","location":"eastus","kind":"MongoDB", "properties":{"documentEndpoint":"https://contoso-mongo.documents.azure.com:443/","publicNetworkAccess":"Enabled", - "writeLocations":[{"locationName":"East US","documentEndpoint":"https://contoso-mongo-eastus.documents.azure.com:443/"}]}} + "apiProperties":{"serverVersion":"4.2"}, + "writeLocations":[{"locationName":"East US","documentEndpoint":"https://contoso-mongo-eastus.documents.azure.com:443/"}]}}, + {"id":"/subscriptions/` + testSubscriptionID + `/resourceGroups/rg-db/providers/Microsoft.DocumentDB/databaseAccounts/contoso-mongo32","name":"contoso-mongo32","location":"eastus","kind":"MongoDB", + "properties":{"documentEndpoint":"https://contoso-mongo32.documents.azure.com:443/","publicNetworkAccess":"Enabled", + "apiProperties":{"serverVersion":"3.2"}, + "writeLocations":[{"locationName":"East US","documentEndpoint":"https://contoso-mongo32-eastus.documents.azure.com:443/"}]}} ]}`, } @@ -125,6 +130,8 @@ func TestDatabaseProviderFetchers(t *testing.T) { "contoso-mongo.mongo.cosmos.azure.com", "contoso-mongo-eastus.documents.azure.com", "contoso-mongo-eastus.mongo.cosmos.azure.com", + "contoso-mongo32.documents.azure.com", + "contoso-mongo32-eastus.documents.azure.com", }, metadata: map[string]string{"public_network_access": "Enabled"}, },