From 7c2fddbcc8315d896588e319a003cf7336aa4ae5 Mon Sep 17 00:00:00 2001 From: Owen Farrell Date: Thu, 7 Oct 2021 01:14:22 -0400 Subject: [PATCH] Support auto-approval of Synapse managed private endpoints Signed-off-by: Owen Farrell --- internal/services/cognitive/client/client.go | 13 +- internal/services/cosmos/client/client.go | 37 +- internal/services/mariadb/client/client.go | 25 +- internal/services/monitor/client/client.go | 5 + internal/services/mysql/client/client.go | 5 + internal/services/postgres/client/client.go | 5 + internal/services/purview/client/client.go | 9 +- internal/services/search/client/client.go | 17 +- internal/services/sql/client/client.go | 5 + internal/services/storage/client/client.go | 61 +- internal/services/synapse/client/client.go | 5 + ...napse_managed_private_endpoint_resource.go | 588 +++++++++++++++++- ..._managed_private_endpoint_resource_test.go | 465 +++++++++++++- ...pse_managed_private_endpoint.html.markdown | 4 + 14 files changed, 1168 insertions(+), 76 deletions(-) diff --git a/internal/services/cognitive/client/client.go b/internal/services/cognitive/client/client.go index 2a60db17c6f5e..ec1acfef62f80 100644 --- a/internal/services/cognitive/client/client.go +++ b/internal/services/cognitive/client/client.go @@ -6,8 +6,9 @@ import ( ) type Client struct { - AccountsClient *cognitiveservices.AccountsClient - DeletedAccountsClient *cognitiveservices.DeletedAccountsClient + AccountsClient *cognitiveservices.AccountsClient + DeletedAccountsClient *cognitiveservices.DeletedAccountsClient + PrivateEndpointConnectionsClient *cognitiveservices.PrivateEndpointConnectionsClient } func NewClient(o *common.ClientOptions) *Client { @@ -17,8 +18,12 @@ func NewClient(o *common.ClientOptions) *Client { deletedAccountsClient := cognitiveservices.NewDeletedAccountsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&deletedAccountsClient.Client, o.ResourceManagerAuthorizer) + privateEndpointConnectionsClient := cognitiveservices.NewPrivateEndpointConnectionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&privateEndpointConnectionsClient.Client, o.ResourceManagerAuthorizer) + return &Client{ - AccountsClient: &accountsClient, - DeletedAccountsClient: &deletedAccountsClient, + AccountsClient: &accountsClient, + DeletedAccountsClient: &deletedAccountsClient, + PrivateEndpointConnectionsClient: &privateEndpointConnectionsClient, } } diff --git a/internal/services/cosmos/client/client.go b/internal/services/cosmos/client/client.go index 6fb9fee58acb8..3631963a93db6 100644 --- a/internal/services/cosmos/client/client.go +++ b/internal/services/cosmos/client/client.go @@ -6,14 +6,15 @@ import ( ) type Client struct { - CassandraClient *documentdb.CassandraResourcesClient - DatabaseClient *documentdb.DatabaseAccountsClient - GremlinClient *documentdb.GremlinResourcesClient - MongoDbClient *documentdb.MongoDBResourcesClient - NotebookWorkspaceClient *documentdb.NotebookWorkspacesClient - SqlClient *documentdb.SQLResourcesClient - SqlResourceClient *documentdb.SQLResourcesClient - TableClient *documentdb.TableResourcesClient + CassandraClient *documentdb.CassandraResourcesClient + DatabaseClient *documentdb.DatabaseAccountsClient + GremlinClient *documentdb.GremlinResourcesClient + MongoDbClient *documentdb.MongoDBResourcesClient + NotebookWorkspaceClient *documentdb.NotebookWorkspacesClient + PrivateEndpointConnectionClient *documentdb.PrivateEndpointConnectionsClient + SqlClient *documentdb.SQLResourcesClient + SqlResourceClient *documentdb.SQLResourcesClient + TableClient *documentdb.TableResourcesClient } func NewClient(o *common.ClientOptions) *Client { @@ -32,6 +33,9 @@ func NewClient(o *common.ClientOptions) *Client { notebookWorkspaceClient := documentdb.NewNotebookWorkspacesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(¬ebookWorkspaceClient.Client, o.ResourceManagerAuthorizer) + privateEndpointConnectionClient := documentdb.NewPrivateEndpointConnectionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&privateEndpointConnectionClient.Client, o.ResourceManagerAuthorizer) + sqlClient := documentdb.NewSQLResourcesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&sqlClient.Client, o.ResourceManagerAuthorizer) @@ -42,13 +46,14 @@ func NewClient(o *common.ClientOptions) *Client { o.ConfigureClient(&tableClient.Client, o.ResourceManagerAuthorizer) return &Client{ - CassandraClient: &cassandraClient, - DatabaseClient: &databaseClient, - GremlinClient: &gremlinClient, - MongoDbClient: &mongoDbClient, - NotebookWorkspaceClient: ¬ebookWorkspaceClient, - SqlClient: &sqlClient, - SqlResourceClient: &sqlResourceClient, - TableClient: &tableClient, + CassandraClient: &cassandraClient, + DatabaseClient: &databaseClient, + GremlinClient: &gremlinClient, + MongoDbClient: &mongoDbClient, + NotebookWorkspaceClient: ¬ebookWorkspaceClient, + PrivateEndpointConnectionClient: &privateEndpointConnectionClient, + SqlClient: &sqlClient, + SqlResourceClient: &sqlResourceClient, + TableClient: &tableClient, } } diff --git a/internal/services/mariadb/client/client.go b/internal/services/mariadb/client/client.go index 0e1cb2344c0de..1c5853bf0c3c7 100644 --- a/internal/services/mariadb/client/client.go +++ b/internal/services/mariadb/client/client.go @@ -6,11 +6,12 @@ import ( ) type Client struct { - ConfigurationsClient *mariadb.ConfigurationsClient - DatabasesClient *mariadb.DatabasesClient - FirewallRulesClient *mariadb.FirewallRulesClient - ServersClient *mariadb.ServersClient - VirtualNetworkRulesClient *mariadb.VirtualNetworkRulesClient + ConfigurationsClient *mariadb.ConfigurationsClient + DatabasesClient *mariadb.DatabasesClient + FirewallRulesClient *mariadb.FirewallRulesClient + PrivateEndpointConnectionClient *mariadb.PrivateEndpointConnectionsClient + ServersClient *mariadb.ServersClient + VirtualNetworkRulesClient *mariadb.VirtualNetworkRulesClient } func NewClient(o *common.ClientOptions) *Client { @@ -23,6 +24,9 @@ func NewClient(o *common.ClientOptions) *Client { FirewallRulesClient := mariadb.NewFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&FirewallRulesClient.Client, o.ResourceManagerAuthorizer) + PrivateEndpointConnectionClient := mariadb.NewPrivateEndpointConnectionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&PrivateEndpointConnectionClient.Client, o.ResourceManagerAuthorizer) + ServersClient := mariadb.NewServersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&ServersClient.Client, o.ResourceManagerAuthorizer) @@ -30,10 +34,11 @@ func NewClient(o *common.ClientOptions) *Client { o.ConfigureClient(&VirtualNetworkRulesClient.Client, o.ResourceManagerAuthorizer) return &Client{ - ConfigurationsClient: &configurationsClient, - DatabasesClient: &DatabasesClient, - FirewallRulesClient: &FirewallRulesClient, - ServersClient: &ServersClient, - VirtualNetworkRulesClient: &VirtualNetworkRulesClient, + ConfigurationsClient: &configurationsClient, + DatabasesClient: &DatabasesClient, + FirewallRulesClient: &FirewallRulesClient, + PrivateEndpointConnectionClient: &PrivateEndpointConnectionClient, + ServersClient: &ServersClient, + VirtualNetworkRulesClient: &VirtualNetworkRulesClient, } } diff --git a/internal/services/monitor/client/client.go b/internal/services/monitor/client/client.go index fccbecf85e2a6..c56cc6a13d683 100644 --- a/internal/services/monitor/client/client.go +++ b/internal/services/monitor/client/client.go @@ -27,6 +27,7 @@ type Client struct { DiagnosticSettingsCategoryClient *classic.DiagnosticSettingsCategoryClient LogProfilesClient *classic.LogProfilesClient MetricAlertsClient *classic.MetricAlertsClient + PrivateEndpointConnectionsClient *classic.PrivateEndpointConnectionsClient ScheduledQueryRulesClient *classic.ScheduledQueryRulesClient } @@ -64,6 +65,9 @@ func NewClient(o *common.ClientOptions) *Client { MetricAlertsClient := classic.NewMetricAlertsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&MetricAlertsClient.Client, o.ResourceManagerAuthorizer) + PrivateEndpointConnectionsClient := classic.NewPrivateEndpointConnectionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&PrivateEndpointConnectionsClient.Client, o.ResourceManagerAuthorizer) + ScheduledQueryRulesClient := classic.NewScheduledQueryRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&ScheduledQueryRulesClient.Client, o.ResourceManagerAuthorizer) @@ -79,6 +83,7 @@ func NewClient(o *common.ClientOptions) *Client { DiagnosticSettingsCategoryClient: &DiagnosticSettingsCategoryClient, LogProfilesClient: &LogProfilesClient, MetricAlertsClient: &MetricAlertsClient, + PrivateEndpointConnectionsClient: &PrivateEndpointConnectionsClient, ScheduledQueryRulesClient: &ScheduledQueryRulesClient, } } diff --git a/internal/services/mysql/client/client.go b/internal/services/mysql/client/client.go index 640bc52d3765c..21855393d9e7a 100644 --- a/internal/services/mysql/client/client.go +++ b/internal/services/mysql/client/client.go @@ -9,6 +9,7 @@ type Client struct { ConfigurationsClient *mysql.ConfigurationsClient DatabasesClient *mysql.DatabasesClient FirewallRulesClient *mysql.FirewallRulesClient + PrivateEndpointConnectionClient *mysql.PrivateEndpointConnectionsClient ServersClient *mysql.ServersClient ServerKeysClient *mysql.ServerKeysClient ServerSecurityAlertPoliciesClient *mysql.ServerSecurityAlertPoliciesClient @@ -26,6 +27,9 @@ func NewClient(o *common.ClientOptions) *Client { FirewallRulesClient := mysql.NewFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&FirewallRulesClient.Client, o.ResourceManagerAuthorizer) + PrivateEndpointConnectionClient := mysql.NewPrivateEndpointConnectionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&PrivateEndpointConnectionClient.Client, o.ResourceManagerAuthorizer) + ServersClient := mysql.NewServersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&ServersClient.Client, o.ResourceManagerAuthorizer) @@ -45,6 +49,7 @@ func NewClient(o *common.ClientOptions) *Client { ConfigurationsClient: &ConfigurationsClient, DatabasesClient: &DatabasesClient, FirewallRulesClient: &FirewallRulesClient, + PrivateEndpointConnectionClient: &PrivateEndpointConnectionClient, ServersClient: &ServersClient, ServerKeysClient: &ServerKeysClient, ServerSecurityAlertPoliciesClient: &serverSecurityAlertPoliciesClient, diff --git a/internal/services/postgres/client/client.go b/internal/services/postgres/client/client.go index eb9af7723fd6b..ffac2f5324164 100644 --- a/internal/services/postgres/client/client.go +++ b/internal/services/postgres/client/client.go @@ -14,6 +14,7 @@ type Client struct { FlexibleServersConfigurationsClient *postgresqlflexibleservers.ConfigurationsClient FlexibleServerFirewallRuleClient *postgresqlflexibleservers.FirewallRulesClient FlexibleServerDatabaseClient *postgresqlflexibleservers.DatabasesClient + PrivateEndpointConnectionClient *postgresql.PrivateEndpointConnectionsClient ServersClient *postgresql.ServersClient ServerKeysClient *postgresql.ServerKeysClient ServerSecurityAlertPoliciesClient *postgresql.ServerSecurityAlertPoliciesClient @@ -32,6 +33,9 @@ func NewClient(o *common.ClientOptions) *Client { firewallRulesClient := postgresql.NewFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&firewallRulesClient.Client, o.ResourceManagerAuthorizer) + privateEndpointConnectionClient := postgresql.NewPrivateEndpointConnectionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&privateEndpointConnectionClient.Client, o.ResourceManagerAuthorizer) + serversClient := postgresql.NewServersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&serversClient.Client, o.ResourceManagerAuthorizer) @@ -70,6 +74,7 @@ func NewClient(o *common.ClientOptions) *Client { FlexibleServersClient: &flexibleServersClient, FlexibleServerFirewallRuleClient: &flexibleServerFirewallRuleClient, FlexibleServerDatabaseClient: &flexibleServerDatabaseClient, + PrivateEndpointConnectionClient: &privateEndpointConnectionClient, ServersClient: &serversClient, ServerKeysClient: &serverKeysClient, ServerSecurityAlertPoliciesClient: &serverSecurityAlertPoliciesClient, diff --git a/internal/services/purview/client/client.go b/internal/services/purview/client/client.go index 0bd60ee123d8e..993ba1b9809f2 100644 --- a/internal/services/purview/client/client.go +++ b/internal/services/purview/client/client.go @@ -6,14 +6,19 @@ import ( ) type Client struct { - AccountsClient *purview.AccountsClient + AccountsClient *purview.AccountsClient + PrivateEndpointConnectionsClient *purview.PrivateEndpointConnectionsClient } func NewClient(o *common.ClientOptions) *Client { accountsClient := purview.NewAccountsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&accountsClient.Client, o.ResourceManagerAuthorizer) + privateEndpointConnectionsClient := purview.NewPrivateEndpointConnectionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&privateEndpointConnectionsClient.Client, o.ResourceManagerAuthorizer) + return &Client{ - AccountsClient: &accountsClient, + AccountsClient: &accountsClient, + PrivateEndpointConnectionsClient: &privateEndpointConnectionsClient, } } diff --git a/internal/services/search/client/client.go b/internal/services/search/client/client.go index 50c4aae128b56..251d2bb807372 100644 --- a/internal/services/search/client/client.go +++ b/internal/services/search/client/client.go @@ -6,15 +6,19 @@ import ( ) type Client struct { - AdminKeysClient *search.AdminKeysClient - QueryKeysClient *search.QueryKeysClient - ServicesClient *search.ServicesClient + AdminKeysClient *search.AdminKeysClient + PrivateEndpointConnectionsClient *search.PrivateEndpointConnectionsClient + QueryKeysClient *search.QueryKeysClient + ServicesClient *search.ServicesClient } func NewClient(o *common.ClientOptions) *Client { adminKeysClient := search.NewAdminKeysClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&adminKeysClient.Client, o.ResourceManagerAuthorizer) + privateEndpointConnectionsClient := search.NewPrivateEndpointConnectionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&privateEndpointConnectionsClient.Client, o.ResourceManagerAuthorizer) + queryKeysClient := search.NewQueryKeysClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&queryKeysClient.Client, o.ResourceManagerAuthorizer) @@ -22,8 +26,9 @@ func NewClient(o *common.ClientOptions) *Client { o.ConfigureClient(&servicesClient.Client, o.ResourceManagerAuthorizer) return &Client{ - AdminKeysClient: &adminKeysClient, - QueryKeysClient: &queryKeysClient, - ServicesClient: &servicesClient, + AdminKeysClient: &adminKeysClient, + PrivateEndpointConnectionsClient: &privateEndpointConnectionsClient, + QueryKeysClient: &queryKeysClient, + ServicesClient: &servicesClient, } } diff --git a/internal/services/sql/client/client.go b/internal/services/sql/client/client.go index d74aaa3c58cbe..4f867dfae54ba 100644 --- a/internal/services/sql/client/client.go +++ b/internal/services/sql/client/client.go @@ -15,6 +15,7 @@ type Client struct { FailoverGroupsClient *sql.FailoverGroupsClient ManagedInstancesClient *msi.ManagedInstancesClient ManagedDatabasesClient *msi.ManagedDatabasesClient + PrivateEndpointConnectionsClient *msi.PrivateEndpointConnectionsClient ServersClient *sql.ServersClient ServerExtendedBlobAuditingPoliciesClient *sql.ExtendedServerBlobAuditingPoliciesClient ServerConnectionPoliciesClient *sql.ServerConnectionPoliciesClient @@ -49,6 +50,9 @@ func NewClient(o *common.ClientOptions) *Client { managedDatabasesClient := msi.NewManagedDatabasesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&managedDatabasesClient.Client, o.ResourceManagerAuthorizer) + privateEndpointConnectionsClient := msi.NewPrivateEndpointConnectionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&privateEndpointConnectionsClient.Client, o.ResourceManagerAuthorizer) + serversClient := sql.NewServersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&serversClient.Client, o.ResourceManagerAuthorizer) @@ -76,6 +80,7 @@ func NewClient(o *common.ClientOptions) *Client { FirewallRulesClient: &firewallRulesClient, ManagedInstancesClient: &managedInstancesClient, ManagedDatabasesClient: &managedDatabasesClient, + PrivateEndpointConnectionsClient: &privateEndpointConnectionsClient, ServersClient: &serversClient, ServerAzureADAdministratorsClient: &serverAzureADAdministratorsClient, ServerConnectionPoliciesClient: &serverConnectionPoliciesClient, diff --git a/internal/services/storage/client/client.go b/internal/services/storage/client/client.go index c6223498ae004..2ea5910151526 100644 --- a/internal/services/storage/client/client.go +++ b/internal/services/storage/client/client.go @@ -24,20 +24,21 @@ import ( ) type Client struct { - AccountsClient *storage.AccountsClient - FileSystemsClient *filesystems.Client - ADLSGen2PathsClient *paths.Client - ManagementPoliciesClient *storage.ManagementPoliciesClient - BlobServicesClient *storage.BlobServicesClient - BlobInventoryPoliciesClient *storage.BlobInventoryPoliciesClient - CloudEndpointsClient *storagesync.CloudEndpointsClient - EncryptionScopesClient *storage.EncryptionScopesClient - Environment az.Environment - FileServicesClient *storage.FileServicesClient - ObjectReplicationClient *storage.ObjectReplicationPoliciesClient - SyncServiceClient *storagesync.ServicesClient - SyncGroupsClient *storagesync.SyncGroupsClient - SubscriptionId string + AccountsClient *storage.AccountsClient + FileSystemsClient *filesystems.Client + ADLSGen2PathsClient *paths.Client + ManagementPoliciesClient *storage.ManagementPoliciesClient + BlobServicesClient *storage.BlobServicesClient + BlobInventoryPoliciesClient *storage.BlobInventoryPoliciesClient + CloudEndpointsClient *storagesync.CloudEndpointsClient + EncryptionScopesClient *storage.EncryptionScopesClient + Environment az.Environment + FileServicesClient *storage.FileServicesClient + ObjectReplicationClient *storage.ObjectReplicationPoliciesClient + PrivateEndpointConnectionClient *storage.PrivateEndpointConnectionsClient + SyncServiceClient *storagesync.ServicesClient + SyncGroupsClient *storagesync.SyncGroupsClient + SubscriptionId string resourceManagerAuthorizer autorest.Authorizer storageAdAuth *autorest.Authorizer @@ -74,6 +75,9 @@ func NewClient(options *common.ClientOptions) *Client { objectReplicationPolicyClient := storage.NewObjectReplicationPoliciesClientWithBaseURI(options.ResourceManagerEndpoint, options.SubscriptionId) options.ConfigureClient(&objectReplicationPolicyClient.Client, options.ResourceManagerAuthorizer) + privateEndpointConnectionClient := storage.NewPrivateEndpointConnectionsClientWithBaseURI(options.ResourceManagerEndpoint, options.SubscriptionId) + options.ConfigureClient(&privateEndpointConnectionClient.Client, options.ResourceManagerAuthorizer) + syncServiceClient := storagesync.NewServicesClientWithBaseURI(options.ResourceManagerEndpoint, options.SubscriptionId) options.ConfigureClient(&syncServiceClient.Client, options.ResourceManagerAuthorizer) @@ -83,20 +87,21 @@ func NewClient(options *common.ClientOptions) *Client { // TODO: switch Storage Containers to using the storage.BlobContainersClient // (which should fix #2977) when the storage clients have been moved in here client := Client{ - AccountsClient: &accountsClient, - FileSystemsClient: &fileSystemsClient, - ADLSGen2PathsClient: &adlsGen2PathsClient, - ManagementPoliciesClient: &managementPoliciesClient, - BlobServicesClient: &blobServicesClient, - BlobInventoryPoliciesClient: &blobInventoryPoliciesClient, - CloudEndpointsClient: &cloudEndpointsClient, - EncryptionScopesClient: &encryptionScopesClient, - Environment: options.Environment, - FileServicesClient: &fileServicesClient, - ObjectReplicationClient: &objectReplicationPolicyClient, - SubscriptionId: options.SubscriptionId, - SyncServiceClient: &syncServiceClient, - SyncGroupsClient: &syncGroupsClient, + AccountsClient: &accountsClient, + FileSystemsClient: &fileSystemsClient, + ADLSGen2PathsClient: &adlsGen2PathsClient, + ManagementPoliciesClient: &managementPoliciesClient, + BlobServicesClient: &blobServicesClient, + BlobInventoryPoliciesClient: &blobInventoryPoliciesClient, + CloudEndpointsClient: &cloudEndpointsClient, + EncryptionScopesClient: &encryptionScopesClient, + Environment: options.Environment, + FileServicesClient: &fileServicesClient, + ObjectReplicationClient: &objectReplicationPolicyClient, + PrivateEndpointConnectionClient: &privateEndpointConnectionClient, + SubscriptionId: options.SubscriptionId, + SyncServiceClient: &syncServiceClient, + SyncGroupsClient: &syncGroupsClient, resourceManagerAuthorizer: options.ResourceManagerAuthorizer, } diff --git a/internal/services/synapse/client/client.go b/internal/services/synapse/client/client.go index c839bf899be9a..d02d42fc7af27 100644 --- a/internal/services/synapse/client/client.go +++ b/internal/services/synapse/client/client.go @@ -16,6 +16,7 @@ type Client struct { IntegrationRuntimeAuthKeysClient *synapse.IntegrationRuntimeAuthKeysClient IntegrationRuntimesClient *synapse.IntegrationRuntimesClient KeysClient *synapse.KeysClient + PrivateEndpointConnectionClient *synapse.PrivateEndpointConnectionsClient PrivateLinkHubsClient *synapse.PrivateLinkHubsClient SparkPoolClient *synapse.BigDataPoolsClient SqlPoolClient *synapse.SQLPoolsClient @@ -46,6 +47,9 @@ func NewClient(o *common.ClientOptions) *Client { keysClient := synapse.NewKeysClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&keysClient.Client, o.ResourceManagerAuthorizer) + privateEndpointConnectionClient := synapse.NewPrivateEndpointConnectionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&privateEndpointConnectionClient.Client, o.ResourceManagerAuthorizer) + privateLinkHubsClient := synapse.NewPrivateLinkHubsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&privateLinkHubsClient.Client, o.ResourceManagerAuthorizer) @@ -91,6 +95,7 @@ func NewClient(o *common.ClientOptions) *Client { IntegrationRuntimeAuthKeysClient: &integrationRuntimeAuthKeysClient, IntegrationRuntimesClient: &integrationRuntimesClient, KeysClient: &keysClient, + PrivateEndpointConnectionClient: &privateEndpointConnectionClient, PrivateLinkHubsClient: &privateLinkHubsClient, SparkPoolClient: &sparkPoolClient, SqlPoolClient: &sqlPoolClient, diff --git a/internal/services/synapse/synapse_managed_private_endpoint_resource.go b/internal/services/synapse/synapse_managed_private_endpoint_resource.go index 9a57a1abdd83f..6fcf03fedd56a 100644 --- a/internal/services/synapse/synapse_managed_private_endpoint_resource.go +++ b/internal/services/synapse/synapse_managed_private_endpoint_resource.go @@ -6,12 +6,30 @@ import ( "log" "time" + "github.com/Azure/azure-sdk-for-go/services/cognitiveservices/mgmt/2021-04-30/cognitiveservices" + "github.com/Azure/azure-sdk-for-go/services/cosmos-db/mgmt/2021-06-15/documentdb" + "github.com/Azure/azure-sdk-for-go/services/mariadb/mgmt/2018-06-01/mariadb" + "github.com/Azure/azure-sdk-for-go/services/mysql/mgmt/2020-01-01/mysql" + "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2020-01-01/postgresql" + "github.com/Azure/azure-sdk-for-go/services/preview/purview/mgmt/2020-12-01-preview/purview" + "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2018-06-01-preview/sql" "github.com/Azure/azure-sdk-for-go/services/preview/synapse/2019-06-01-preview/managedvirtualnetwork" + "github.com/Azure/azure-sdk-for-go/services/search/mgmt/2020-03-13/search" + "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2021-01-01/storage" "github.com/Azure/azure-sdk-for-go/services/synapse/mgmt/2021-03-01/synapse" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + cosmosParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/cosmos/parse" + mariadbParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/mariadb/parse" + mysqlParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/mysql/parse" + networkParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/parse" networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" + postgresParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/postgres/parse" + purviewParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/purview/parse" + searchParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/search/parse" + sqlParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/sql/parse" + storageParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/synapse/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/synapse/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" @@ -20,6 +38,8 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/utils" ) +const approvalDescription = "Auto-approved by Terraform" + func resourceSynapseManagedPrivateEndpoint() *pluginsdk.Resource { return &pluginsdk.Resource{ Create: resourceSynapseManagedPrivateEndpointCreate, @@ -65,6 +85,13 @@ func resourceSynapseManagedPrivateEndpoint() *pluginsdk.Resource { ForceNew: true, ValidateFunc: networkValidate.PrivateLinkSubResourceName, }, + + "is_manual_connection": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + ForceNew: true, + }, }, } } @@ -81,6 +108,12 @@ func resourceSynapseManagedPrivateEndpointCreate(d *pluginsdk.ResourceData, meta return err } + targetResourceIdRaw := d.Get("target_resource_id").(string) + targetResourceId, err := azure.ParseAzureResourceID(targetResourceIdRaw) + if err != nil { + return err + } + workspace, err := workspaceClient.Get(ctx, workspaceId.ResourceGroup, workspaceId.Name) if err != nil { return fmt.Errorf("retrieving %s: %+v", workspaceId, err) @@ -110,7 +143,7 @@ func resourceSynapseManagedPrivateEndpointCreate(d *pluginsdk.ResourceData, meta // create managedPrivateEndpoint := managedvirtualnetwork.ManagedPrivateEndpoint{ Properties: &managedvirtualnetwork.ManagedPrivateEndpointProperties{ - PrivateLinkResourceID: utils.String(d.Get("target_resource_id").(string)), + PrivateLinkResourceID: utils.String(targetResourceIdRaw), GroupID: utils.String(d.Get("subresource_name").(string)), }, } @@ -136,6 +169,50 @@ func resourceSynapseManagedPrivateEndpointCreate(d *pluginsdk.ResourceData, meta return fmt.Errorf("waiting for provisioning state of %s: %+v", id, err) } + if !d.Get("is_manual_connection").(bool) { + privateEndpointName := fmt.Sprintf("%s.%s", id.WorkspaceName, id.Name) + + switch targetResourceId.Provider { + case "Microsoft.Cognitive": + err = approveManagedPrivateEndpointForCognitive(ctx, meta, id, targetResourceIdRaw, privateEndpointName) + case "Microsoft.DBforMariaDB": + err = approveManagedPrivateEndpointForMariaDB(ctx, meta, id, targetResourceIdRaw, privateEndpointName) + case "Microsoft.DBforMySQL": + err = approveManagedPrivateEndpointForMySQL(ctx, meta, id, targetResourceIdRaw, privateEndpointName) + case "Microsoft.DBforPostgreSQL": + err = approveManagedPrivateEndpointForPostgreSQL(ctx, meta, id, targetResourceIdRaw, privateEndpointName) + case "Microsoft.DocumentDB": + err = approveManagedPrivateEndpointForCosmos(ctx, meta, id, targetResourceIdRaw, privateEndpointName) + // TODO Implement support for "Microsoft.KeyVault" + case "Microsoft.Purview": + err = approveManagedPrivateEndpointForPurview(ctx, meta, id, targetResourceIdRaw, privateEndpointName) + case "Microsoft.Search": + err = approveManagedPrivateEndpointForSearch(ctx, meta, id, targetResourceIdRaw, privateEndpointName) + case "Microsoft.Sql": + err = approveManagedPrivateEndpointForSQL(ctx, meta, id, targetResourceIdRaw, privateEndpointName) + case "Microsoft.Storage": + err = approveManagedPrivateEndpointForStorage(ctx, meta, id, targetResourceIdRaw, privateEndpointName) + case "Microsoft.Synapse": + err = approveManagedPrivateEndpointForSynapse(ctx, meta, id, targetResourceIdRaw, privateEndpointName) + default: + err = fmt.Errorf("auto-approving %s: `%s` is not a supported provider", id, targetResourceId.Provider) + } + if err != nil { + return err + } + + stateConf := &pluginsdk.StateChangeConf{ + Pending: []string{"Pending"}, + Target: []string{"Approved"}, + Refresh: managedPrivateEndpointConnectionStateStatusRefreshFunc(ctx, client, id), + Timeout: time.Until(timeout), + PollInterval: 15 * time.Second, + } + if _, err = stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for connection state status of %s: %+v", id, err) + } + } + d.SetId(id.ID()) return resourceSynapseManagedPrivateEndpointRead(d, meta) } @@ -173,6 +250,9 @@ func resourceSynapseManagedPrivateEndpointRead(d *pluginsdk.ResourceData, meta i d.Set("target_resource_id", props.PrivateLinkResourceID) d.Set("subresource_name", props.GroupID) } + + d.Set("is_manual_connection", d.Get("is_manual_connection").(bool)) + return nil } @@ -212,3 +292,509 @@ func managedPrivateEndpointProvisioningStateRefreshFunc(ctx context.Context, cli return resp, *resp.Properties.ProvisioningState, nil } } + +func managedPrivateEndpointConnectionStateStatusRefreshFunc(ctx context.Context, client *managedvirtualnetwork.ManagedPrivateEndpointsClient, id parse.ManagedPrivateEndpointId) pluginsdk.StateRefreshFunc { + return func() (interface{}, string, error) { + resp, err := client.Get(ctx, id.ManagedVirtualNetworkName, id.Name) + if err != nil { + return nil, "", fmt.Errorf("polling for %s: %+v", id, err) + } + + if resp.Properties == nil || resp.Properties.ConnectionState == nil || resp.Properties.ConnectionState.Status == nil { + return resp, "", fmt.Errorf("nil Status returned for connection state of %s", id) + } + + return resp, *resp.Properties.ConnectionState.Status, nil + } +} + +func approveManagedPrivateEndpointForCognitive(ctx context.Context, meta interface{}, id parse.ManagedPrivateEndpointId, resourceId, privateEndpointName string) error { + client := meta.(*clients.Client).Cognitive.PrivateEndpointConnectionsClient + + targetResourceId, err := cosmosParse.DatabaseAccountID(resourceId) + if err != nil { + return err + } + + result, err := client.List(ctx, targetResourceId.ResourceGroup, targetResourceId.Name) + if err != nil { + return err + } + + var privateEndpointConnectionName *string + for _, val := range *result.Value { + if val.Properties == nil || val.Properties.PrivateEndpoint == nil || val.Properties.PrivateEndpoint.ID == nil { + continue + } + privateEndpointId, err := networkParse.PrivateEndpointID(*val.Properties.PrivateEndpoint.ID) + if err != nil { + continue + } + if privateEndpointName == privateEndpointId.Name { + privateEndpointConnectionName = val.Name + break + } + } + if privateEndpointConnectionName == nil { + return fmt.Errorf("finding private endpoint connection name for %s: %+v", id, err) + } + + properties := cognitiveservices.PrivateEndpointConnection{ + Properties: &cognitiveservices.PrivateEndpointConnectionProperties{ + PrivateLinkServiceConnectionState: &cognitiveservices.PrivateLinkServiceConnectionState{ + Status: cognitiveservices.PrivateEndpointServiceConnectionStatusApproved, + Description: utils.String(approvalDescription), + }, + }, + } + future, err := client.CreateOrUpdate(ctx, targetResourceId.ResourceGroup, targetResourceId.Name, *privateEndpointConnectionName, properties) + if err != nil { + return fmt.Errorf("approving %s: %+v", id, err) + } + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for approval of %s: %+v", id, err) + } + + return nil +} + +func approveManagedPrivateEndpointForCosmos(ctx context.Context, meta interface{}, id parse.ManagedPrivateEndpointId, resourceId, privateEndpointName string) error { + client := meta.(*clients.Client).Cosmos.PrivateEndpointConnectionClient + + targetResourceId, err := cosmosParse.DatabaseAccountID(resourceId) + if err != nil { + return err + } + + result, err := client.ListByDatabaseAccount(ctx, targetResourceId.ResourceGroup, targetResourceId.Name) + if err != nil { + return err + } + + var privateEndpointConnectionName *string + for _, val := range *result.Value { + if val.PrivateEndpoint == nil || val.PrivateEndpoint.ID == nil { + continue + } + privateEndpointId, err := networkParse.PrivateEndpointID(*val.PrivateEndpoint.ID) + if err != nil { + continue + } + if privateEndpointName == privateEndpointId.Name { + privateEndpointConnectionName = val.Name + break + } + } + if privateEndpointConnectionName == nil { + return fmt.Errorf("finding private endpoint connection name for %s: %+v", id, err) + } + + parameters := documentdb.PrivateEndpointConnection{ + PrivateEndpointConnectionProperties: &documentdb.PrivateEndpointConnectionProperties{ + PrivateLinkServiceConnectionState: &documentdb.PrivateLinkServiceConnectionStateProperty{ + Status: utils.String("Approved"), + Description: utils.String(approvalDescription), + }, + }, + } + future, err := client.CreateOrUpdate(ctx, targetResourceId.ResourceGroup, targetResourceId.Name, *privateEndpointConnectionName, parameters) + if err != nil { + return fmt.Errorf("approving %s: %+v", id, err) + } + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for approval of %s: %+v", id, err) + } + + return nil +} + +func approveManagedPrivateEndpointForPurview(ctx context.Context, meta interface{}, id parse.ManagedPrivateEndpointId, resourceId, privateEndpointName string) error { + client := meta.(*clients.Client).Purview.PrivateEndpointConnectionsClient + + targetResourceId, err := purviewParse.AccountID(resourceId) + if err != nil { + return err + } + + result, err := client.ListByAccount(ctx, targetResourceId.ResourceGroup, targetResourceId.Name, "") + if err != nil { + return err + } + + var privateEndpointConnectionName *string + for _, val := range result.Values() { + if val.PrivateEndpoint == nil || val.PrivateEndpoint.ID == nil { + continue + } + privateEndpointId, err := networkParse.PrivateEndpointID(*val.PrivateEndpoint.ID) + if err != nil { + continue + } + if privateEndpointName == privateEndpointId.Name { + privateEndpointConnectionName = val.Name + break + } + } + if privateEndpointConnectionName == nil { + return fmt.Errorf("finding private endpoint connection name for %s: %+v", id, err) + } + + request := purview.PrivateEndpointConnection{ + PrivateEndpointConnectionProperties: &purview.PrivateEndpointConnectionProperties{ + PrivateLinkServiceConnectionState: &purview.PrivateLinkServiceConnectionState{ + Status: purview.StatusApproved, + Description: utils.String(approvalDescription), + }, + }, + } + _, err = client.CreateOrUpdate(ctx, targetResourceId.ResourceGroup, targetResourceId.Name, *privateEndpointConnectionName, request) + if err != nil { + return fmt.Errorf("approving %s: %+v", id, err) + } + + return nil +} + +func approveManagedPrivateEndpointForMariaDB(ctx context.Context, meta interface{}, id parse.ManagedPrivateEndpointId, resourceId, privateEndpointName string) error { + client := meta.(*clients.Client).MariaDB.PrivateEndpointConnectionClient + + targetResourceId, err := mariadbParse.ServerID(resourceId) + if err != nil { + return err + } + + result, err := client.ListByServer(ctx, targetResourceId.ResourceGroup, targetResourceId.Name) + if err != nil { + return err + } + + var privateEndpointConnectionName *string + for _, val := range result.Values() { + if val.PrivateEndpoint == nil || val.PrivateEndpoint.ID == nil { + continue + } + privateEndpointId, err := networkParse.PrivateEndpointID(*val.PrivateEndpoint.ID) + if err != nil { + continue + } + if privateEndpointName == privateEndpointId.Name { + privateEndpointConnectionName = val.Name + break + } + } + if privateEndpointConnectionName == nil { + return fmt.Errorf("finding private endpoint connection name for %s: %+v", id, err) + } + + parameters := mariadb.PrivateEndpointConnection{ + PrivateEndpointConnectionProperties: &mariadb.PrivateEndpointConnectionProperties{ + PrivateLinkServiceConnectionState: &mariadb.PrivateLinkServiceConnectionStateProperty{ + Status: utils.String(string(mariadb.Approved)), + Description: utils.String(approvalDescription), + }, + }, + } + future, err := client.CreateOrUpdate(ctx, targetResourceId.ResourceGroup, targetResourceId.Name, *privateEndpointConnectionName, parameters) + if err != nil { + return fmt.Errorf("approving %s: %+v", id, err) + } + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for approval of %s: %+v", id, err) + } + + return nil +} + +func approveManagedPrivateEndpointForMySQL(ctx context.Context, meta interface{}, id parse.ManagedPrivateEndpointId, resourceId, privateEndpointName string) error { + client := meta.(*clients.Client).MySQL.PrivateEndpointConnectionClient + + targetResourceId, err := mysqlParse.ServerID(resourceId) + if err != nil { + return err + } + + result, err := client.ListByServer(ctx, targetResourceId.ResourceGroup, targetResourceId.Name) + if err != nil { + return err + } + + var privateEndpointConnectionName *string + for _, val := range result.Values() { + if val.PrivateEndpoint == nil || val.PrivateEndpoint.ID == nil { + continue + } + privateEndpointId, err := networkParse.PrivateEndpointID(*val.PrivateEndpoint.ID) + if err != nil { + continue + } + if privateEndpointName == privateEndpointId.Name { + privateEndpointConnectionName = val.Name + break + } + } + if privateEndpointConnectionName == nil { + return fmt.Errorf("finding private endpoint connection name for %s: %+v", id, err) + } + + parameters := mysql.PrivateEndpointConnection{ + PrivateEndpointConnectionProperties: &mysql.PrivateEndpointConnectionProperties{ + PrivateLinkServiceConnectionState: &mysql.PrivateLinkServiceConnectionStateProperty{ + Status: utils.String(string(mysql.Approved)), + Description: utils.String(approvalDescription), + }, + }, + } + future, err := client.CreateOrUpdate(ctx, targetResourceId.ResourceGroup, targetResourceId.Name, *privateEndpointConnectionName, parameters) + if err != nil { + return fmt.Errorf("approving %s: %+v", id, err) + } + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for approval of %s: %+v", id, err) + } + + return nil +} + +func approveManagedPrivateEndpointForPostgreSQL(ctx context.Context, meta interface{}, id parse.ManagedPrivateEndpointId, resourceId, privateEndpointName string) error { + client := meta.(*clients.Client).Postgres.PrivateEndpointConnectionClient + + targetResourceId, err := postgresParse.ServerID(resourceId) + if err != nil { + return err + } + + result, err := client.ListByServer(ctx, targetResourceId.ResourceGroup, targetResourceId.Name) + if err != nil { + return err + } + + var privateEndpointConnectionName *string + for _, val := range result.Values() { + if val.PrivateEndpoint == nil || val.PrivateEndpoint.ID == nil { + continue + } + privateEndpointId, err := networkParse.PrivateEndpointID(*val.PrivateEndpoint.ID) + if err != nil { + continue + } + if privateEndpointName == privateEndpointId.Name { + privateEndpointConnectionName = val.Name + break + } + } + if privateEndpointConnectionName == nil { + return fmt.Errorf("finding private endpoint connection name for %s: %+v", id, err) + } + + parameters := postgresql.PrivateEndpointConnection{ + PrivateEndpointConnectionProperties: &postgresql.PrivateEndpointConnectionProperties{ + PrivateLinkServiceConnectionState: &postgresql.PrivateLinkServiceConnectionStateProperty{ + Status: utils.String(string(postgresql.Approved)), + Description: utils.String(approvalDescription), + }, + }, + } + future, err := client.CreateOrUpdate(ctx, targetResourceId.ResourceGroup, targetResourceId.Name, *privateEndpointConnectionName, parameters) + if err != nil { + return fmt.Errorf("approving %s: %+v", id, err) + } + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for approval of %s: %+v", id, err) + } + + return nil +} + +func approveManagedPrivateEndpointForSearch(ctx context.Context, meta interface{}, id parse.ManagedPrivateEndpointId, resourceId, privateEndpointName string) error { + client := meta.(*clients.Client).Search.PrivateEndpointConnectionsClient + + targetResourceId, err := searchParse.SearchServiceID(resourceId) + if err != nil { + return err + } + + result, err := client.ListByService(ctx, targetResourceId.ResourceGroup, targetResourceId.Name, nil) + if err != nil { + return err + } + + var privateEndpointConnectionName *string + for _, val := range result.Values() { + if val.Properties == nil || val.Properties.PrivateEndpoint == nil || val.Properties.PrivateEndpoint.ID == nil { + continue + } + privateEndpointId, err := networkParse.PrivateEndpointID(*val.Properties.PrivateEndpoint.ID) + if err != nil { + continue + } + if privateEndpointName == privateEndpointId.Name { + privateEndpointConnectionName = val.Name + break + } + } + if privateEndpointConnectionName == nil { + return fmt.Errorf("finding private endpoint connection name for %s: %+v", id, err) + } + + privateEndpointConnection := search.PrivateEndpointConnection{ + Properties: &search.PrivateEndpointConnectionProperties{ + PrivateLinkServiceConnectionState: &search.PrivateEndpointConnectionPropertiesPrivateLinkServiceConnectionState{ + Status: search.Approved, + Description: utils.String(approvalDescription), + }, + }, + } + _, err = client.Update(ctx, targetResourceId.ResourceGroup, targetResourceId.Name, *privateEndpointConnectionName, privateEndpointConnection, nil) + if err != nil { + return fmt.Errorf("approving %s: %+v", id, err) + } + + return nil +} + +func approveManagedPrivateEndpointForSQL(ctx context.Context, meta interface{}, id parse.ManagedPrivateEndpointId, resourceId, privateEndpointName string) error { + client := meta.(*clients.Client).Sql.PrivateEndpointConnectionsClient + + targetResourceId, err := sqlParse.ServerID(resourceId) + if err != nil { + return err + } + + result, err := client.ListByServer(ctx, targetResourceId.ResourceGroup, targetResourceId.Name) + if err != nil { + return err + } + + var privateEndpointConnectionName *string + for _, val := range result.Values() { + if val.PrivateEndpoint == nil || val.PrivateEndpoint.ID == nil { + continue + } + privateEndpointId, err := networkParse.PrivateEndpointID(*val.PrivateEndpoint.ID) + if err != nil { + continue + } + if privateEndpointName == privateEndpointId.Name { + privateEndpointConnectionName = val.Name + break + } + } + if privateEndpointConnectionName == nil { + return fmt.Errorf("finding private endpoint connection name for %s: %+v", id, err) + } + + parameters := sql.PrivateEndpointConnection{ + PrivateEndpointConnectionProperties: &sql.PrivateEndpointConnectionProperties{ + PrivateLinkServiceConnectionState: &sql.PrivateLinkServiceConnectionStateProperty{ + Status: utils.String("Approved"), + Description: utils.String(approvalDescription), + }, + }, + } + future, err := client.CreateOrUpdate(ctx, targetResourceId.ResourceGroup, targetResourceId.Name, *privateEndpointConnectionName, parameters) + if err != nil { + return fmt.Errorf("approving %s: %+v", id, err) + } + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for approval of %s: %+v", id, err) + } + + return nil +} + +func approveManagedPrivateEndpointForStorage(ctx context.Context, meta interface{}, id parse.ManagedPrivateEndpointId, resourceId, privateEndpointName string) error { + client := meta.(*clients.Client).Storage.PrivateEndpointConnectionClient + + targetResourceId, err := storageParse.StorageAccountID(resourceId) + if err != nil { + return err + } + + result, err := client.List(ctx, targetResourceId.ResourceGroup, targetResourceId.Name) + if err != nil { + return err + } + + var privateEndpointConnectionName *string + for _, val := range *result.Value { + if val.PrivateEndpoint == nil || val.PrivateEndpoint.ID == nil { + continue + } + privateEndpointId, err := networkParse.PrivateEndpointID(*val.PrivateEndpoint.ID) + if err != nil { + continue + } + if privateEndpointName == privateEndpointId.Name { + privateEndpointConnectionName = val.Name + break + } + } + if privateEndpointConnectionName == nil { + return fmt.Errorf("finding private endpoint connection name for %s: %+v", id, err) + } + + properties := storage.PrivateEndpointConnection{ + PrivateEndpointConnectionProperties: &storage.PrivateEndpointConnectionProperties{ + PrivateLinkServiceConnectionState: &storage.PrivateLinkServiceConnectionState{ + Status: storage.Approved, + Description: utils.String(approvalDescription), + }, + }, + } + _, err = client.Put(ctx, targetResourceId.ResourceGroup, targetResourceId.Name, *privateEndpointConnectionName, properties) + if err != nil { + return fmt.Errorf("approving %s: %+v", id, err) + } + + return nil +} + +func approveManagedPrivateEndpointForSynapse(ctx context.Context, meta interface{}, id parse.ManagedPrivateEndpointId, resourceId, privateEndpointName string) error { + client := meta.(*clients.Client).Synapse.PrivateEndpointConnectionClient + + targetResourceId, err := parse.WorkspaceID(resourceId) + if err != nil { + return err + } + + result, err := client.List(ctx, targetResourceId.ResourceGroup, targetResourceId.Name) + if err != nil { + return err + } + + var privateEndpointConnectionName *string + for _, val := range result.Values() { + if val.PrivateEndpoint == nil || val.PrivateEndpoint.ID == nil { + continue + } + privateEndpointId, err := networkParse.PrivateEndpointID(*val.PrivateEndpoint.ID) + if err != nil { + continue + } + if privateEndpointName == privateEndpointId.Name { + privateEndpointConnectionName = val.Name + break + } + } + if privateEndpointConnectionName == nil { + return fmt.Errorf("finding private endpoint connection name for %s: %+v", id, err) + } + + request := synapse.PrivateEndpointConnection{ + PrivateEndpointConnectionProperties: &synapse.PrivateEndpointConnectionProperties{ + PrivateLinkServiceConnectionState: &synapse.PrivateLinkServiceConnectionState{ + Status: utils.String("Approved"), + Description: utils.String(approvalDescription), + }, + }, + } + future, err := client.Create(ctx, request, targetResourceId.ResourceGroup, targetResourceId.Name, *privateEndpointConnectionName) + if err != nil { + return fmt.Errorf("approving %s: %+v", id, err) + } + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for approval of %s: %+v", id, err) + } + + return nil +} diff --git a/internal/services/synapse/synapse_managed_private_endpoint_resource_test.go b/internal/services/synapse/synapse_managed_private_endpoint_resource_test.go index a32649389de54..0bb97df5aebce 100644 --- a/internal/services/synapse/synapse_managed_private_endpoint_resource_test.go +++ b/internal/services/synapse/synapse_managed_private_endpoint_resource_test.go @@ -45,6 +45,156 @@ func TestAccSynapseManagedPrivateEndpoint_requiresImport(t *testing.T) { }) } +func TestAccSynapseManagedPrivateEndpoint_autoApproveCognitive(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_synapse_managed_private_endpoint", "test") + r := SynapseManagedPrivateEndpointResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.autoApproveCognitive(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccSynapseManagedPrivateEndpoint_autoApproveCosmos(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_synapse_managed_private_endpoint", "test") + r := SynapseManagedPrivateEndpointResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.autoApproveCosmos(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccSynapseManagedPrivateEndpoint_autoApproveMariaDB(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_synapse_managed_private_endpoint", "test") + r := SynapseManagedPrivateEndpointResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.autoApproveMariaDB(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccSynapseManagedPrivateEndpoint_autoApproveMySQL(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_synapse_managed_private_endpoint", "test") + r := SynapseManagedPrivateEndpointResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.autoApproveMySQL(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccSynapseManagedPrivateEndpoint_autoApprovePostgreSQL(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_synapse_managed_private_endpoint", "test") + r := SynapseManagedPrivateEndpointResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.autoApprovePostgreSQL(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccSynapseManagedPrivateEndpoint_autoApprovePurview(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_synapse_managed_private_endpoint", "test") + r := SynapseManagedPrivateEndpointResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.autoApprovePurview(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccSynapseManagedPrivateEndpoint_autoApproveSQL(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_synapse_managed_private_endpoint", "test") + r := SynapseManagedPrivateEndpointResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.autoApproveSQL(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccSynapseManagedPrivateEndpoint_autoApproveSearch(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_synapse_managed_private_endpoint", "test") + r := SynapseManagedPrivateEndpointResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.autoApproveSearch(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccSynapseManagedPrivateEndpoint_autoApproveStorage(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_synapse_managed_private_endpoint", "test") + r := SynapseManagedPrivateEndpointResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.autoApproveStorage(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccSynapseManagedPrivateEndpoint_autoApproveSynapse(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_synapse_managed_private_endpoint", "test") + r := SynapseManagedPrivateEndpointResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.autoApproveSynapse(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func (r SynapseManagedPrivateEndpointResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := parse.ManagedPrivateEndpointID(state.ID) if err != nil { @@ -63,6 +213,9 @@ func (r SynapseManagedPrivateEndpointResource) Exists(ctx context.Context, clien } return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } + if state.Attributes["is_manual_connection"] == "false" && *resp.Properties.ConnectionState.Status != "Approved" { + return utils.Bool(false), nil + } return utils.Bool(true), nil } @@ -72,6 +225,15 @@ func (r SynapseManagedPrivateEndpointResource) basic(data acceptance.TestData) s return fmt.Sprintf(` %[1]s +resource "azurerm_storage_account" "test_endpoint" { + name = "acctestacce%[3]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_kind = "BlobStorage" + account_tier = "Standard" + account_replication_type = "LRS" +} + resource "azurerm_synapse_managed_private_endpoint" "test" { name = "acctestEndpoint%[2]d" synapse_workspace_id = azurerm_synapse_workspace.test.id @@ -80,9 +242,303 @@ resource "azurerm_synapse_managed_private_endpoint" "test" { depends_on = [azurerm_synapse_firewall_rule.test] } +`, template, data.RandomInteger, data.RandomString) +} + +func (r SynapseManagedPrivateEndpointResource) autoApproveCognitive(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_cognitive_account" "test" { + name = "acctestcogacc-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + kind = "Face" + sku_name = "S0" + custom_subdomain_name = "acctestcogacc-%[2]d" + + network_acls { + default_action = "Deny" + } +} + +resource "azurerm_synapse_managed_private_endpoint" "test" { + name = "acctestEndpoint%[2]d" + synapse_workspace_id = azurerm_synapse_workspace.test.id + target_resource_id = azurerm_cognitive_account.test.id + subresource_name = "account" + is_manual_connection = false + + depends_on = [azurerm_synapse_firewall_rule.test] +} `, template, data.RandomInteger) } +func (r SynapseManagedPrivateEndpointResource) autoApproveCosmos(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_cosmosdb_account" "test" { + name = "acctest-ca-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + offer_type = "Standard" + kind = "GlobalDocumentDB" + + consistency_policy { + consistency_level = "Eventual" + } + + geo_location { + location = azurerm_resource_group.test.location + failover_priority = 0 + } +} + +resource "azurerm_synapse_managed_private_endpoint" "test" { + name = "acctestEndpoint%[2]d" + synapse_workspace_id = azurerm_synapse_workspace.test.id + target_resource_id = azurerm_cosmosdb_account.test.id + subresource_name = "sql" + is_manual_connection = false + + depends_on = [azurerm_synapse_firewall_rule.test] +} +`, template, data.RandomInteger) +} + +func (r SynapseManagedPrivateEndpointResource) autoApproveMariaDB(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_mariadb_server" "test" { + name = "acctestmariadbsvr-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "GP_Gen5_2" + version = "10.3" + + public_network_access_enabled = false + + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + ssl_enforcement_enabled = true + storage_mb = 51200 +} + +resource "azurerm_synapse_managed_private_endpoint" "test" { + name = "acctestEndpoint%[2]d" + synapse_workspace_id = azurerm_synapse_workspace.test.id + target_resource_id = azurerm_mariadb_server.test.id + subresource_name = "mariadbServer" + is_manual_connection = false + + depends_on = [azurerm_synapse_firewall_rule.test] +} +`, template, data.RandomInteger) +} + +func (r SynapseManagedPrivateEndpointResource) autoApproveMySQL(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_mysql_server" "test" { + name = "acctestmysqlsvr-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "GP_Gen5_2" + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + ssl_enforcement_enabled = true + ssl_minimal_tls_version_enforced = "TLS1_1" + storage_mb = 51200 + version = "5.7" +} + +resource "azurerm_synapse_managed_private_endpoint" "test" { + name = "acctestEndpoint%[2]d" + synapse_workspace_id = azurerm_synapse_workspace.test.id + target_resource_id = azurerm_mysql_server.test.id + subresource_name = "mysqlServer" + is_manual_connection = false + + depends_on = [azurerm_synapse_firewall_rule.test] +} +`, template, data.RandomInteger) +} + +func (r SynapseManagedPrivateEndpointResource) autoApprovePostgreSQL(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_postgresql_server" "test" { + name = "acctest-psql-server-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "GP_Gen5_2" + version = "9.5" + + public_network_access_enabled = false + + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + ssl_enforcement_enabled = true + storage_mb = 51200 +} + +resource "azurerm_synapse_managed_private_endpoint" "test" { + name = "acctestEndpoint%[2]d" + synapse_workspace_id = azurerm_synapse_workspace.test.id + target_resource_id = azurerm_postgresql_server.test.id + subresource_name = "postgresqlServer" + is_manual_connection = false + + depends_on = [azurerm_synapse_firewall_rule.test] +} +`, template, data.RandomInteger) +} + +func (r SynapseManagedPrivateEndpointResource) autoApprovePurview(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_purview_account" "test" { + name = "acctestsw%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_synapse_managed_private_endpoint" "test" { + name = "acctestEndpoint%[2]d" + synapse_workspace_id = azurerm_synapse_workspace.test.id + target_resource_id = azurerm_purview_account.test.id + subresource_name = "portal" + is_manual_connection = false + + depends_on = [azurerm_synapse_firewall_rule.test] +} +`, template, data.RandomInteger) +} + +func (r SynapseManagedPrivateEndpointResource) autoApproveSearch(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_search_service" "test" { + name = "acctestsearchservice%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + sku = "standard" + + public_network_access_enabled = false + + tags = { + environment = "staging" + } +} + +resource "azurerm_synapse_managed_private_endpoint" "test" { + name = "acctestEndpoint%[2]d" + synapse_workspace_id = azurerm_synapse_workspace.test.id + target_resource_id = azurerm_search_service.test.id + subresource_name = "searchService" + is_manual_connection = false + + depends_on = [azurerm_synapse_firewall_rule.test] +} +`, template, data.RandomInteger) +} + +func (r SynapseManagedPrivateEndpointResource) autoApproveSQL(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_sql_server" "test" { + name = "acctestsqlserver%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + version = "12.0" + administrator_login = "mradministrator" + administrator_login_password = "thisIsDog11" +} + +resource "azurerm_synapse_managed_private_endpoint" "test" { + name = "acctestEndpoint%[2]d" + synapse_workspace_id = azurerm_synapse_workspace.test.id + target_resource_id = azurerm_sql_server.test.id + subresource_name = "SQLServer" + is_manual_connection = false + + depends_on = [azurerm_synapse_firewall_rule.test] +} +`, template, data.RandomInteger) +} + +func (r SynapseManagedPrivateEndpointResource) autoApproveStorage(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_storage_account" "test_endpoint" { + name = "acctestacce%[3]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_kind = "BlobStorage" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_synapse_managed_private_endpoint" "test" { + name = "acctestEndpoint%[2]d" + synapse_workspace_id = azurerm_synapse_workspace.test.id + target_resource_id = azurerm_storage_account.test_endpoint.id + subresource_name = "blob" + is_manual_connection = false + + depends_on = [azurerm_synapse_firewall_rule.test] +} +`, template, data.RandomInteger, data.RandomString) +} + +func (r SynapseManagedPrivateEndpointResource) autoApproveSynapse(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_storage_data_lake_gen2_filesystem" "test_endpoint" { + name = "accteste-%[2]d" + storage_account_id = azurerm_storage_account.test.id +} + +resource "azurerm_synapse_workspace" "test_endpoint" { + name = "acctestswe%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + storage_data_lake_gen2_filesystem_id = azurerm_storage_data_lake_gen2_filesystem.test_endpoint.id + sql_administrator_login = "sqladminuser" + sql_administrator_login_password = "H@Sh1CoR3!" +} + +resource "azurerm_synapse_managed_private_endpoint" "test" { + name = "acctestEndpoint%[2]d" + synapse_workspace_id = azurerm_synapse_workspace.test.id + target_resource_id = azurerm_synapse_workspace.test_endpoint.id + subresource_name = "sqlOnDemand" + is_manual_connection = false + + depends_on = [azurerm_synapse_firewall_rule.test] +} +`, template, data.RandomInteger, data.RandomString) +} + func (r SynapseManagedPrivateEndpointResource) requiresImport(data acceptance.TestData) string { config := r.basic(data) return fmt.Sprintf(` @@ -117,15 +573,6 @@ resource "azurerm_storage_account" "test" { account_replication_type = "LRS" } -resource "azurerm_storage_account" "test_endpoint" { - name = "acctestacce%[3]s" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - account_kind = "BlobStorage" - account_tier = "Standard" - account_replication_type = "LRS" -} - resource "azurerm_storage_data_lake_gen2_filesystem" "test" { name = "acctest-%[1]d" storage_account_id = azurerm_storage_account.test.id diff --git a/website/docs/r/synapse_managed_private_endpoint.html.markdown b/website/docs/r/synapse_managed_private_endpoint.html.markdown index 216cad4cf0057..eba2023211a96 100644 --- a/website/docs/r/synapse_managed_private_endpoint.html.markdown +++ b/website/docs/r/synapse_managed_private_endpoint.html.markdown @@ -85,6 +85,10 @@ The following arguments are supported: -> **NOTE:** Possible values are listed in [documentation](https://docs.microsoft.com/en-us/azure/private-link/private-endpoint-overview#dns-configuration). +* `is_manual_connection` - (Optional) When `false`, Terraform will automatically approve the private endpoint associated with the target resource. Changing this forces a new resource to be created. Defaults to `true`. + +-> **NOTE:** Not all providers support automated approval of managed private endpoints. Additionally, target resources must be configured to support the creation of private endpoints. For example: `Microsoft.Cognitive` resources must define a custom subdomain name, `Microsoft.DBforMariaDB`, `Microsoft.DBforMySQL`, and `Microsoft.DBforPostgreSQL` resources must use a `GeneralPurpose` or `MemoryOptimized` tier SKU. Resources that are configured + ## Attributes Reference The following attributes are exported: