diff --git a/azurerm/helpers/azure/sku.go b/azurerm/helpers/azure/sku.go index cdab76cd20be..09c3fb1625f4 100644 --- a/azurerm/helpers/azure/sku.go +++ b/azurerm/helpers/azure/sku.go @@ -4,8 +4,6 @@ import ( "fmt" "strconv" "strings" - - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) func SplitSku(sku string) (string, int32, error) { @@ -22,35 +20,3 @@ func SplitSku(sku string) (string, int32, error) { return skuParts[0], int32(capacity), nil } - -// MinCapacitySkuNameInSlice returns a SchemaValidateFunc which tests if the provided value -// is of type string and matches the value of an element in the valid slice -// will test with in lower case if ignoreCase is true will also validate if the -// capacity if above passed minCapacity value -func MinCapacitySkuNameInSlice(valid []string, minCapacity int32, ignoreCase bool) schema.SchemaValidateFunc { - return func(i interface{}, k string) (s []string, es []error) { - v, ok := i.(string) - if !ok { - es = append(es, fmt.Errorf("expected type of %s to be string", k)) - return - } - - name, capacity, err := SplitSku(v) - if err != nil { - es = append(es, err) - return - } - - for _, str := range valid { - if name == str || (ignoreCase && strings.EqualFold(name, str)) { - if capacity < minCapacity { - es = append(es, fmt.Errorf("expected %s capacity value to be greater that %d, got %d", k, minCapacity, capacity)) - } - return - } - } - - es = append(es, fmt.Errorf("expected %s to be one of %v, got %s", k, valid, name)) - return - } -} diff --git a/azurerm/internal/services/apimanagement/api_management_resource.go b/azurerm/internal/services/apimanagement/api_management_resource.go index 3de8b59e02f4..b9b120e7615a 100644 --- a/azurerm/internal/services/apimanagement/api_management_resource.go +++ b/azurerm/internal/services/apimanagement/api_management_resource.go @@ -19,6 +19,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/apimanagement/parse" + apimValidate "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/apimanagement/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" @@ -73,15 +74,10 @@ func resourceArmApiManagementService() *schema.Resource { }, "sku_name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: azure.MinCapacitySkuNameInSlice([]string{ - string(apimanagement.SkuTypeConsumption), - string(apimanagement.SkuTypeDeveloper), - string(apimanagement.SkuTypeBasic), - string(apimanagement.SkuTypeStandard), - string(apimanagement.SkuTypePremium), - }, 1, false), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: apimValidate.ApimSkuName(), }, "identity": { @@ -518,7 +514,10 @@ func resourceArmApiManagementServiceCreateUpdate(d *schema.ResourceData, meta in notificationSenderEmail := d.Get("notification_sender_email").(string) virtualNetworkType := d.Get("virtual_network_type").(string) - customProperties := expandApiManagementCustomProperties(d) + customProperties, err := expandApiManagementCustomProperties(d, sku.Name == apimanagement.SkuTypeConsumption) + if err != nil { + return err + } certificates := expandAzureRmApiManagementCertificates(d) properties := apimanagement.ServiceResource{ @@ -590,17 +589,27 @@ func resourceArmApiManagementServiceCreateUpdate(d *schema.ResourceData, meta in d.SetId(*read.ID) signInSettingsRaw := d.Get("sign_in").([]interface{}) - signInSettings := expandApiManagementSignInSettings(signInSettingsRaw) - signInClient := meta.(*clients.Client).ApiManagement.SignInClient - if _, err := signInClient.CreateOrUpdate(ctx, resourceGroup, name, signInSettings, ""); err != nil { - return fmt.Errorf(" setting Sign In settings for API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err) + if sku.Name == apimanagement.SkuTypeConsumption && len(signInSettingsRaw) > 0 { + return fmt.Errorf("`sign_in` is not support for sku tier `Consumption`") + } + if sku.Name != apimanagement.SkuTypeConsumption { + signInSettings := expandApiManagementSignInSettings(signInSettingsRaw) + signInClient := meta.(*clients.Client).ApiManagement.SignInClient + if _, err := signInClient.CreateOrUpdate(ctx, resourceGroup, name, signInSettings, ""); err != nil { + return fmt.Errorf(" setting Sign In settings for API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err) + } } signUpSettingsRaw := d.Get("sign_up").([]interface{}) - signUpSettings := expandApiManagementSignUpSettings(signUpSettingsRaw) - signUpClient := meta.(*clients.Client).ApiManagement.SignUpClient - if _, err := signUpClient.CreateOrUpdate(ctx, resourceGroup, name, signUpSettings, ""); err != nil { - return fmt.Errorf(" setting Sign Up settings for API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err) + if sku.Name == apimanagement.SkuTypeConsumption && len(signInSettingsRaw) > 0 { + return fmt.Errorf("`sign_up` is not support for sku tier `Consumption`") + } + if sku.Name != apimanagement.SkuTypeConsumption { + signUpSettings := expandApiManagementSignUpSettings(signUpSettingsRaw) + signUpClient := meta.(*clients.Client).ApiManagement.SignUpClient + if _, err := signUpClient.CreateOrUpdate(ctx, resourceGroup, name, signUpSettings, ""); err != nil { + return fmt.Errorf(" setting Sign Up settings for API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err) + } } policyClient := meta.(*clients.Client).ApiManagement.PolicyClient @@ -631,6 +640,8 @@ func resourceArmApiManagementServiceCreateUpdate(d *schema.ResourceData, meta in func resourceArmApiManagementServiceRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*clients.Client).ApiManagement.ServiceClient + signInClient := meta.(*clients.Client).ApiManagement.SignInClient + signUpClient := meta.(*clients.Client).ApiManagement.SignUpClient environment := meta.(*clients.Client).Account.Environment ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() @@ -654,18 +665,6 @@ func resourceArmApiManagementServiceRead(d *schema.ResourceData, meta interface{ return fmt.Errorf("making Read request on API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err) } - signInClient := meta.(*clients.Client).ApiManagement.SignInClient - signInSettings, err := signInClient.Get(ctx, resourceGroup, name) - if err != nil { - return fmt.Errorf("retrieving Sign In Settings for API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err) - } - - signUpClient := meta.(*clients.Client).ApiManagement.SignUpClient - signUpSettings, err := signUpClient.Get(ctx, resourceGroup, name) - if err != nil { - return fmt.Errorf("retrieving Sign Up Settings for API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err) - } - policyClient := meta.(*clients.Client).ApiManagement.PolicyClient policy, err := policyClient.Get(ctx, resourceGroup, name, apimanagement.PolicyExportFormatXML) if err != nil { @@ -700,8 +699,10 @@ func resourceArmApiManagementServiceRead(d *schema.ResourceData, meta interface{ d.Set("private_ip_addresses", props.PrivateIPAddresses) d.Set("virtual_network_type", props.VirtualNetworkType) - if err := d.Set("security", flattenApiManagementSecurityCustomProperties(props.CustomProperties)); err != nil { - return fmt.Errorf("setting `security`: %+v", err) + if resp.Sku != nil && resp.Sku.Name != "" { + if err := d.Set("security", flattenApiManagementSecurityCustomProperties(props.CustomProperties, resp.Sku.Name == apimanagement.SkuTypeConsumption)); err != nil { + return fmt.Errorf("setting `security`: %+v", err) + } } if err := d.Set("protocols", flattenApiManagementProtocolsCustomProperties(props.CustomProperties)); err != nil { @@ -727,16 +728,30 @@ func resourceArmApiManagementServiceRead(d *schema.ResourceData, meta interface{ return fmt.Errorf("setting `sku_name`: %+v", err) } - if err := d.Set("sign_in", flattenApiManagementSignInSettings(signInSettings)); err != nil { - return fmt.Errorf("setting `sign_in`: %+v", err) + if err := d.Set("policy", flattenApiManagementPolicies(d, policy)); err != nil { + return fmt.Errorf("setting `policy`: %+v", err) } - if err := d.Set("sign_up", flattenApiManagementSignUpSettings(signUpSettings)); err != nil { - return fmt.Errorf("setting `sign_up`: %+v", err) - } + if resp.Sku.Name != apimanagement.SkuTypeConsumption { + signInSettings, err := signInClient.Get(ctx, resourceGroup, name) + if err != nil { + return fmt.Errorf("retrieving Sign In Settings for API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err) + } + if err := d.Set("sign_in", flattenApiManagementSignInSettings(signInSettings)); err != nil { + return fmt.Errorf("setting `sign_in`: %+v", err) + } - if err := d.Set("policy", flattenApiManagementPolicies(d, policy)); err != nil { - return fmt.Errorf("setting `policy`: %+v", err) + signUpSettings, err := signUpClient.Get(ctx, resourceGroup, name) + if err != nil { + return fmt.Errorf("retrieving Sign Up Settings for API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if err := d.Set("sign_up", flattenApiManagementSignUpSettings(signUpSettings)); err != nil { + return fmt.Errorf("setting `sign_up`: %+v", err) + } + } else { + d.Set("sign_in", []interface{}{}) + d.Set("sign_up", []interface{}{}) } return tags.FlattenAndSet(d, resp.Tags) @@ -1146,7 +1161,7 @@ func flattenApiManagementServiceSkuName(input *apimanagement.ServiceSkuPropertie return fmt.Sprintf("%s_%d", string(input.Name), *input.Capacity) } -func expandApiManagementCustomProperties(d *schema.ResourceData) map[string]*string { +func expandApiManagementCustomProperties(d *schema.ResourceData, skuIsConsumption bool) (map[string]*string, error) { backendProtocolSsl3 := false backendProtocolTls10 := false backendProtocolTls11 := false @@ -1164,16 +1179,26 @@ func expandApiManagementCustomProperties(d *schema.ResourceData) map[string]*str frontendProtocolTls10 = v["enable_frontend_tls10"].(bool) frontendProtocolTls11 = v["enable_frontend_tls11"].(bool) tripleDesCiphers = v["enable_triple_des_ciphers"].(bool) + if skuIsConsumption && frontendProtocolSsl3 { + return nil, fmt.Errorf("`enable_frontend_ssl30` is not support for Sku Tier `Consumption`") + } + + if skuIsConsumption && tripleDesCiphers { + return nil, fmt.Errorf("`enable_triple_des_ciphers` is not support for Sku Tier `Consumption`") + } } customProperties := map[string]*string{ apimBackendProtocolSsl3: utils.String(strconv.FormatBool(backendProtocolSsl3)), apimBackendProtocolTls10: utils.String(strconv.FormatBool(backendProtocolTls10)), apimBackendProtocolTls11: utils.String(strconv.FormatBool(backendProtocolTls11)), - apimFrontendProtocolSsl3: utils.String(strconv.FormatBool(frontendProtocolSsl3)), apimFrontendProtocolTls10: utils.String(strconv.FormatBool(frontendProtocolTls10)), apimFrontendProtocolTls11: utils.String(strconv.FormatBool(frontendProtocolTls11)), - apimTripleDesCiphers: utils.String(strconv.FormatBool(tripleDesCiphers)), + } + + if !skuIsConsumption { + customProperties[apimFrontendProtocolSsl3] = utils.String(strconv.FormatBool(frontendProtocolSsl3)) + customProperties[apimTripleDesCiphers] = utils.String(strconv.FormatBool(tripleDesCiphers)) } if vp := d.Get("protocols").([]interface{}); len(vp) > 0 { @@ -1182,7 +1207,7 @@ func expandApiManagementCustomProperties(d *schema.ResourceData) map[string]*str customProperties[apimHttp2Protocol] = utils.String(strconv.FormatBool(enableHttp2)) } - return customProperties + return customProperties, nil } func expandAzureRmApiManagementVirtualNetworkConfigurations(d *schema.ResourceData) *apimanagement.VirtualNetworkConfiguration { @@ -1199,16 +1224,19 @@ func expandAzureRmApiManagementVirtualNetworkConfigurations(d *schema.ResourceDa } } -func flattenApiManagementSecurityCustomProperties(input map[string]*string) []interface{} { +func flattenApiManagementSecurityCustomProperties(input map[string]*string, skuIsConsumption bool) []interface{} { output := make(map[string]interface{}) output["enable_backend_ssl30"] = parseApiManagementNilableDictionary(input, apimBackendProtocolSsl3) output["enable_backend_tls10"] = parseApiManagementNilableDictionary(input, apimBackendProtocolTls10) output["enable_backend_tls11"] = parseApiManagementNilableDictionary(input, apimBackendProtocolTls11) - output["enable_frontend_ssl30"] = parseApiManagementNilableDictionary(input, apimFrontendProtocolSsl3) output["enable_frontend_tls10"] = parseApiManagementNilableDictionary(input, apimFrontendProtocolTls10) output["enable_frontend_tls11"] = parseApiManagementNilableDictionary(input, apimFrontendProtocolTls11) - output["enable_triple_des_ciphers"] = parseApiManagementNilableDictionary(input, apimTripleDesCiphers) + + if !skuIsConsumption { + output["enable_frontend_ssl30"] = parseApiManagementNilableDictionary(input, apimFrontendProtocolSsl3) + output["enable_triple_des_ciphers"] = parseApiManagementNilableDictionary(input, apimTripleDesCiphers) + } return []interface{}{output} } diff --git a/azurerm/internal/services/apimanagement/api_management_resource_test.go b/azurerm/internal/services/apimanagement/api_management_resource_test.go index 26d789517b01..a2e320b27101 100644 --- a/azurerm/internal/services/apimanagement/api_management_resource_test.go +++ b/azurerm/internal/services/apimanagement/api_management_resource_test.go @@ -287,6 +287,25 @@ func TestAccAzureRMApiManagement_identitySystemAssignedUpdateHostnameConfigurati }) } +func TestAccAzureRMApiManagement_consumption(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_api_management", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMApiManagementDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApiManagement_consumption(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApiManagementExists(data.ResourceName), + ), + }, + data.ImportStep(), + }, + }) +} + func testCheckAzureRMApiManagementDestroy(s *terraform.State) error { conn := acceptance.AzureProvider.Meta().(*clients.Client).ApiManagement.ServiceClient ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext @@ -1330,3 +1349,25 @@ resource "azurerm_api_management" "test" { } `, template, data.RandomInteger) } + +func testAccAzureRMApiManagement_consumption(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 = "Consumption_0" +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} diff --git a/azurerm/internal/services/apimanagement/validate/api_management.go b/azurerm/internal/services/apimanagement/validate/api_management.go new file mode 100644 index 000000000000..3b64b0268bd9 --- /dev/null +++ b/azurerm/internal/services/apimanagement/validate/api_management.go @@ -0,0 +1,15 @@ +package validate + +import ( + "regexp" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" +) + +func ApimSkuName() schema.SchemaValidateFunc { + return validation.StringMatch( + regexp.MustCompile(`^Consumption_0$|^Basic_(1|2)$|^Developer_1$|^Premium_([1-9]|10)$|^Standard_[1-4]$`), + `This is not a valid Api Management sku name.`, + ) +} diff --git a/azurerm/internal/services/apimanagement/validate/api_management_test.go b/azurerm/internal/services/apimanagement/validate/api_management_test.go new file mode 100644 index 000000000000..6a49b591d1f8 --- /dev/null +++ b/azurerm/internal/services/apimanagement/validate/api_management_test.go @@ -0,0 +1,77 @@ +package validate + +import "testing" + +func TestApimSkuName(t *testing.T) { + tests := []struct { + name string + input string + valid bool + }{ + { + name: "Consumption_0", + input: "Consumption_0", + valid: true, + }, + { + name: "Consumption_1", + input: "Consumption_1", + valid: false, + }, + { + name: "Basic_3", + input: "Basic_3", + valid: false, + }, + { + name: "Basic_1", + input: "Basic_1", + valid: true, + }, + { + name: "Developer_1", + input: "Developer_1", + valid: true, + }, + { + name: "Premium_0", + input: "Premium_0", + valid: false, + }, + { + name: "Premium_11", + input: "Premium_11", + valid: false, + }, + { + name: "Premium_7", + input: "Premium_7", + valid: true, + }, + { + name: "Standard_7", + input: "Standard_7", + valid: false, + }, + { + name: "standard_2", + input: "standard_2", + valid: false, + }, + { + name: "PREMIUM_7", + input: "PREMIUM_7", + valid: false, + }, + } + var validationFunction = ApimSkuName() + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := validationFunction(tt.input, "") + valid := err == nil + if valid != tt.valid { + t.Errorf("Expected valid status %t but got %t for input %s", tt.valid, valid, tt.input) + } + }) + } +}