diff --git a/internal/services/apimanagement/api_management_resource.go b/internal/services/apimanagement/api_management_resource.go index ab7e6130e726..cb5548318267 100644 --- a/internal/services/apimanagement/api_management_resource.go +++ b/internal/services/apimanagement/api_management_resource.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/services/apimanagement/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/apimanagement/schemaz" @@ -493,6 +494,38 @@ func resourceApiManagementSchema() map[string]*pluginsdk.Schema { }, }, + "delegation": { + Type: pluginsdk.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "subscriptions_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + "user_registration_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + "url": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.IsURLWithHTTPorHTTPS, + }, + "validation_key": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validate.Base64EncodedString, + Sensitive: true, + }, + }, + }, + }, + "sign_up": { Type: pluginsdk.TypeList, Optional: true, @@ -908,6 +941,18 @@ func resourceApiManagementServiceCreateUpdate(d *pluginsdk.ResourceData, meta in } } + delegationSettingsRaw := d.Get("delegation").([]interface{}) + if sku.Name == apimanagement.SkuTypeConsumption && len(delegationSettingsRaw) > 0 { + return fmt.Errorf("`delegation` is not support for sku tier `Consumption`") + } + if sku.Name != apimanagement.SkuTypeConsumption && len(delegationSettingsRaw) > 0 { + delegationSettings := expandApiManagementDelegationSettings(delegationSettingsRaw) + delegationClient := meta.(*clients.Client).ApiManagement.DelegationSettingsClient + if _, err := delegationClient.CreateOrUpdate(ctx, id.ResourceGroup, id.ServiceName, delegationSettings, ""); err != nil { + return fmt.Errorf(" setting Delegation settings for %s: %+v", id, err) + } + } + policyClient := meta.(*clients.Client).ApiManagement.PolicyClient policiesRaw := d.Get("policy").([]interface{}) policy, err := expandApiManagementPolicies(policiesRaw) @@ -951,6 +996,7 @@ func resourceApiManagementServiceRead(d *pluginsdk.ResourceData, meta interface{ client := meta.(*clients.Client).ApiManagement.ServiceClient signInClient := meta.(*clients.Client).ApiManagement.SignInClient signUpClient := meta.(*clients.Client).ApiManagement.SignUpClient + delegationClient := meta.(*clients.Client).ApiManagement.DelegationSettingsClient tenantAccessClient := meta.(*clients.Client).ApiManagement.TenantAccessClient ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() @@ -1078,9 +1124,24 @@ func resourceApiManagementServiceRead(d *pluginsdk.ResourceData, meta interface{ if err := d.Set("sign_up", flattenApiManagementSignUpSettings(signUpSettings)); err != nil { return fmt.Errorf("setting `sign_up`: %+v", err) } + + delegationSettings, err := delegationClient.Get(ctx, id.ResourceGroup, id.ServiceName) + if err != nil { + return fmt.Errorf("retrieving Delegation Settings for %s: %+v", *id, err) + } + + delegationValidationKeyContract, err := delegationClient.ListSecrets(ctx, id.ResourceGroup, id.ServiceName) + if err != nil { + return fmt.Errorf("retrieving Delegation Validation Key for %s: %+v", *id, err) + } + + if err := d.Set("delegation", flattenApiManagementDelegationSettings(delegationSettings, delegationValidationKeyContract)); err != nil { + return fmt.Errorf("setting `delegation`: %+v", err) + } } else { d.Set("sign_in", []interface{}{}) d.Set("sign_up", []interface{}{}) + d.Set("delegation", []interface{}{}) } if resp.Sku.Name != apimanagement.SkuTypeConsumption { @@ -1808,6 +1869,78 @@ func flattenApiManagementSignInSettings(input apimanagement.PortalSigninSettings } } +func expandApiManagementDelegationSettings(input []interface{}) apimanagement.PortalDelegationSettings { + if len(input) == 0 { + return apimanagement.PortalDelegationSettings{} + } + + vs := input[0].(map[string]interface{}) + + props := apimanagement.PortalDelegationSettingsProperties{ + UserRegistration: &apimanagement.RegistrationDelegationSettingsProperties{ + Enabled: utils.Bool(vs["user_registration_enabled"].(bool)), + }, + Subscriptions: &apimanagement.SubscriptionsDelegationSettingsProperties{ + Enabled: utils.Bool(vs["subscriptions_enabled"].(bool)), + }, + } + + validationKey := vs["validation_key"].(string) + if !vs["user_registration_enabled"].(bool) && !vs["subscriptions_enabled"].(bool) && validationKey == "" { + // for some reason we cannot leave this empty + props.ValidationKey = utils.String("cGxhY2Vob2xkZXIxCg==") + } + if validationKey != "" { + props.ValidationKey = utils.String(validationKey) + } + + url := vs["url"].(string) + if !vs["user_registration_enabled"].(bool) && !vs["subscriptions_enabled"].(bool) && url == "" { + // for some reason we cannot leave this empty + props.URL = utils.String("https://www.placeholder.com") + } + if url != "" { + props.URL = utils.String(url) + } + + return apimanagement.PortalDelegationSettings{ + PortalDelegationSettingsProperties: &props, + } +} + +func flattenApiManagementDelegationSettings(input apimanagement.PortalDelegationSettings, keyContract apimanagement.PortalSettingValidationKeyContract) []interface{} { + url := "" + subscriptionsEnabled := false + userRegistrationEnabled := false + + if props := input.PortalDelegationSettingsProperties; props != nil { + if props.URL != nil { + url = *props.URL + } + + if props.Subscriptions != nil && props.Subscriptions.Enabled != nil { + subscriptionsEnabled = *props.Subscriptions.Enabled + } + + if props.UserRegistration != nil && props.UserRegistration.Enabled != nil { + userRegistrationEnabled = *props.UserRegistration.Enabled + } + } + validationKey := "" + if keyContract.ValidationKey != nil { + validationKey = *keyContract.ValidationKey + } + + return []interface{}{ + map[string]interface{}{ + "url": url, + "subscriptions_enabled": subscriptionsEnabled, + "user_registration_enabled": userRegistrationEnabled, + "validation_key": validationKey, + }, + } +} + func expandApiManagementSignUpSettings(input []interface{}) apimanagement.PortalSignupSettings { if len(input) == 0 { return apimanagement.PortalSignupSettings{ diff --git a/internal/services/apimanagement/api_management_resource_test.go b/internal/services/apimanagement/api_management_resource_test.go index 6afaefddd7fb..57455e56c3f8 100644 --- a/internal/services/apimanagement/api_management_resource_test.go +++ b/internal/services/apimanagement/api_management_resource_test.go @@ -200,6 +200,49 @@ func TestAccApiManagement_signInSignUpSettings(t *testing.T) { }) } +func TestAccApiManagement_delegationSettings(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_api_management", "test") + r := ApiManagementResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.delegationSettings(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.delegationSettingsDisabled(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.delegationSettings(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccApiManagement_policy(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_api_management", "test") r := ApiManagementResource{} @@ -1117,6 +1160,64 @@ resource "azurerm_api_management" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } +func (ApiManagementResource) delegationSettings(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_api_management" "test" { + name = "acctestAM-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + publisher_name = "pub1" + publisher_email = "pub1@email.com" + + sku_name = "Developer_1" + + delegation { + url = "https://google.com" + subscriptions_enabled = true + validation_key = "aW50ZWdyYXRpb24mMjAyMzAzMTAxODMwJkxRaUxzcUVsaUpEaHJRK01YZkJYV3paUi9qdzZDSWMrazhjUXB0bVdyTGxKcVYrd0R4OXRqMGRzTWZXU3hmeGQ0a2V0WjcrcE44U0dJdDNsYUQ3Rk5BPT0=" + user_registration_enabled = true + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + +func (ApiManagementResource) delegationSettingsDisabled(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_api_management" "test" { + name = "acctestAM-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + publisher_name = "pub1" + publisher_email = "pub1@email.com" + + sku_name = "Developer_1" + + delegation { + subscriptions_enabled = false + user_registration_enabled = false + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + func (ApiManagementResource) complete(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/internal/services/apimanagement/client/client.go b/internal/services/apimanagement/client/client.go index dfa91a58207a..9389252d9597 100644 --- a/internal/services/apimanagement/client/client.go +++ b/internal/services/apimanagement/client/client.go @@ -20,6 +20,7 @@ type Client struct { BackendClient *apimanagement.BackendClient CacheClient *apimanagement.CacheClient CertificatesClient *apimanagement.CertificateClient + DelegationSettingsClient *apimanagement.DelegationSettingsClient DeletedServicesClient *apimanagement.DeletedServicesClient DiagnosticClient *apimanagement.DiagnosticClient EmailTemplateClient *apimanagement.EmailTemplateClient @@ -93,6 +94,9 @@ func NewClient(o *common.ClientOptions) *Client { diagnosticClient := apimanagement.NewDiagnosticClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&diagnosticClient.Client, o.ResourceManagerAuthorizer) + delegationSettingsClient := apimanagement.NewDelegationSettingsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&delegationSettingsClient.Client, o.ResourceManagerAuthorizer) + deletedServicesClient := apimanagement.NewDeletedServicesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&deletedServicesClient.Client, o.ResourceManagerAuthorizer) @@ -188,6 +192,7 @@ func NewClient(o *common.ClientOptions) *Client { BackendClient: &backendClient, CacheClient: &cacheClient, CertificatesClient: &certificatesClient, + DelegationSettingsClient: &delegationSettingsClient, DeletedServicesClient: &deletedServicesClient, DiagnosticClient: &diagnosticClient, EmailTemplateClient: &emailTemplateClient, diff --git a/website/docs/r/api_management.html.markdown b/website/docs/r/api_management.html.markdown index b7fde3816632..0f75467ddf7c 100644 --- a/website/docs/r/api_management.html.markdown +++ b/website/docs/r/api_management.html.markdown @@ -65,6 +65,8 @@ The following arguments are supported: * `client_certificate_enabled` - (Optional) Enforce a client certificate to be presented on each request to the gateway? This is only supported when SKU type is `Consumption`. +* `delegation` - (Optional) A `delegation` block as defined below. + * `gateway_disabled` - (Optional) Disable the gateway in main region? This is only supported when `additional_location` is set. * `min_api_version` - (Optional) The version which the control plane API calls to API Management service are limited with version equal to or newer than. @@ -135,6 +137,18 @@ A `certificate` block supports the following: --- +A `delegation` block supports the following: + +* `subscriptions_enabled` - (Optional) Should subscription requests be delegated to an external url? Defaults to `false`. + +* `user_registration_enabled` - (Optional) Should user registration requests be delegated to an external url? Defaults to `false`. + +* `url` - (Optional) The delegation URL. + +* `validation_key` - (Optional) A base64-encoded validation key to validate, that a request is coming from Azure API Management. + +--- + A `hostname_configuration` block supports the following: * `management` - (Optional) One or more `management` blocks as documented below.