diff --git a/azurerm/internal/services/apimanagement/api_management_api_resource.go b/azurerm/internal/services/apimanagement/api_management_api_resource.go index 096448636596..9373481b5139 100644 --- a/azurerm/internal/services/apimanagement/api_management_api_resource.go +++ b/azurerm/internal/services/apimanagement/api_management_api_resource.go @@ -175,6 +175,52 @@ func resourceArmApiManagementApi() *schema.Resource { Default: false, }, + "oauth2_authorization": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "authorization_server_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.ApiManagementChildName, + }, + "scope": { + Type: schema.TypeString, + Optional: true, + // There is currently no validation, as any length and characters can be used in the field + }, + }, + }, + }, + + "openid_authentication": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "openid_provider_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.ApiManagementChildName, + }, + "bearer_token_sending_methods": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + string(apimanagement.BearerTokenSendingMethodsAuthorizationHeader), + string(apimanagement.BearerTokenSendingMethodsQuery), + }, false), + }, + }, + }, + }, + }, + // Computed "is_current": { Type: schema.TypeBool, @@ -306,6 +352,16 @@ func resourceArmApiManagementApiCreateUpdate(d *schema.ResourceData, meta interf subscriptionKeyParameterNamesRaw := d.Get("subscription_key_parameter_names").([]interface{}) subscriptionKeyParameterNames := expandApiManagementApiSubscriptionKeyParamNames(subscriptionKeyParameterNamesRaw) + authenticationSettings := &apimanagement.AuthenticationSettingsContract{} + + oAuth2AuthorizationSettingsRaw := d.Get("oauth2_authorization").([]interface{}) + oAuth2AuthorizationSettings := expandApiManagementOAuth2AuthenticationSettingsContract(oAuth2AuthorizationSettingsRaw) + authenticationSettings.OAuth2 = oAuth2AuthorizationSettings + + openIDAuthorizationSettingsRaw := d.Get("openid_authentication").([]interface{}) + openIDAuthorizationSettings := expandApiManagementOpenIDAuthenticationSettingsContract(openIDAuthorizationSettingsRaw) + authenticationSettings.Openid = openIDAuthorizationSettings + params := apimanagement.APICreateOrUpdateParameter{ APICreateOrUpdateProperties: &apimanagement.APICreateOrUpdateProperties{ APIType: apiType, @@ -318,6 +374,7 @@ func resourceArmApiManagementApiCreateUpdate(d *schema.ResourceData, meta interf SubscriptionKeyParameterNames: subscriptionKeyParameterNames, APIVersion: utils.String(version), SubscriptionRequired: &subscriptionRequired, + AuthenticationSettings: authenticationSettings, }, } @@ -403,6 +460,14 @@ func resourceArmApiManagementApiRead(d *schema.ResourceData, meta interface{}) e if err := d.Set("subscription_key_parameter_names", flattenApiManagementApiSubscriptionKeyParamNames(props.SubscriptionKeyParameterNames)); err != nil { return fmt.Errorf("setting `subscription_key_parameter_names`: %+v", err) } + + if err := d.Set("oauth2_authorization", flattenApiManagementOAuth2Authorization(props.AuthenticationSettings.OAuth2)); err != nil { + return fmt.Errorf("setting `oauth2_authorization`: %+v", err) + } + + if err := d.Set("openid_authentication", flattenApiManagementOpenIDAuthentication(props.AuthenticationSettings.Openid)); err != nil { + return fmt.Errorf("setting `openid_authentication`: %+v", err) + } } return nil @@ -494,3 +559,83 @@ func flattenApiManagementApiSubscriptionKeyParamNames(paramNames *apimanagement. return []interface{}{result} } + +func expandApiManagementOAuth2AuthenticationSettingsContract(input []interface{}) *apimanagement.OAuth2AuthenticationSettingsContract { + if len(input) == 0 { + return nil + } + + oAuth2AuthorizationV := input[0].(map[string]interface{}) + return &apimanagement.OAuth2AuthenticationSettingsContract{ + AuthorizationServerID: utils.String(oAuth2AuthorizationV["authorization_server_name"].(string)), + Scope: utils.String(oAuth2AuthorizationV["scope"].(string)), + } +} + +func flattenApiManagementOAuth2Authorization(input *apimanagement.OAuth2AuthenticationSettingsContract) []interface{} { + if input == nil { + return make([]interface{}, 0) + } + + result := make(map[string]interface{}) + + authServerId := "" + if input.AuthorizationServerID != nil { + authServerId = *input.AuthorizationServerID + } + result["authorization_server_name"] = authServerId + if input.Scope != nil { + result["scope"] = *input.Scope + } + + return []interface{}{result} +} + +func expandApiManagementOpenIDAuthenticationSettingsContract(input []interface{}) *apimanagement.OpenIDAuthenticationSettingsContract { + if len(input) == 0 { + return nil + } + + openIDAuthorizationV := input[0].(map[string]interface{}) + return &apimanagement.OpenIDAuthenticationSettingsContract{ + OpenidProviderID: utils.String(openIDAuthorizationV["openid_provider_name"].(string)), + BearerTokenSendingMethods: expandApiManagementOpenIDAuthenticationSettingsBearerTokenSendingMethods(openIDAuthorizationV["bearer_token_sending_methods"].(*schema.Set).List()), + } +} + +func expandApiManagementOpenIDAuthenticationSettingsBearerTokenSendingMethods(input []interface{}) *[]apimanagement.BearerTokenSendingMethods { + if input == nil { + return nil + } + results := make([]apimanagement.BearerTokenSendingMethods, 0) + + for _, v := range input { + results = append(results, apimanagement.BearerTokenSendingMethods(v.(string))) + } + + return &results +} + +func flattenApiManagementOpenIDAuthentication(input *apimanagement.OpenIDAuthenticationSettingsContract) []interface{} { + if input == nil { + return make([]interface{}, 0) + } + + result := make(map[string]interface{}) + + openIdProviderId := "" + if input.OpenidProviderID != nil { + openIdProviderId = *input.OpenidProviderID + } + result["openid_provider_name"] = openIdProviderId + + bearerTokenSendingMethods := make([]interface{}, 0) + if s := input.BearerTokenSendingMethods; s != nil { + for _, v := range *s { + bearerTokenSendingMethods = append(bearerTokenSendingMethods, string(v)) + } + } + result["bearer_token_sending_methods"] = schema.NewSet(schema.HashString, bearerTokenSendingMethods) + + return []interface{}{result} +} diff --git a/azurerm/internal/services/apimanagement/tests/api_management_api_resource_test.go b/azurerm/internal/services/apimanagement/tests/api_management_api_resource_test.go index 20c7899ebce1..61dcea96c58f 100644 --- a/azurerm/internal/services/apimanagement/tests/api_management_api_resource_test.go +++ b/azurerm/internal/services/apimanagement/tests/api_management_api_resource_test.go @@ -34,8 +34,7 @@ func TestAccAzureRMApiManagementApi_basic(t *testing.T) { }) } -// Remove in 2.0 -func TestAccAzureRMApiManagementApi_basicClassic(t *testing.T) { +func TestAccAzureRMApiManagementApi_wordRevision(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_api_management_api", "test") resource.Test(t, resource.TestCase{ @@ -44,12 +43,33 @@ func TestAccAzureRMApiManagementApi_basicClassic(t *testing.T) { CheckDestroy: testCheckAzureRMApiManagementApiDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMApiManagementApi_basicClassic(data), + Config: testAccAzureRMApiManagementApi_wordRevision(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMApiManagementApiExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "revision", "one-point-oh"), + ), + }, + data.ImportStep(), + }, + }) +} + +func TestAccAzureRMApiManagementApi_blankPath(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_api_management_api", "test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMApiManagementApiDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMApiManagementApi_blankPath(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMApiManagementApiExists(data.ResourceName), resource.TestCheckResourceAttr(data.ResourceName, "soap_pass_through", "false"), resource.TestCheckResourceAttr(data.ResourceName, "is_current", "true"), resource.TestCheckResourceAttr(data.ResourceName, "is_online", "false"), + resource.TestCheckResourceAttr(data.ResourceName, "path", ""), ), }, data.ImportStep(), @@ -57,7 +77,7 @@ func TestAccAzureRMApiManagementApi_basicClassic(t *testing.T) { }) } -func TestAccAzureRMApiManagementApi_wordRevision(t *testing.T) { +func TestAccAzureRMApiManagementApi_version(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_api_management_api", "test") resource.Test(t, resource.TestCase{ @@ -66,10 +86,10 @@ func TestAccAzureRMApiManagementApi_wordRevision(t *testing.T) { CheckDestroy: testCheckAzureRMApiManagementApiDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMApiManagementApi_wordRevision(data), + Config: testAccAzureRMApiManagementApi_versionSet(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMApiManagementApiExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "revision", "one-point-oh"), + resource.TestCheckResourceAttr(data.ResourceName, "version", "v1"), ), }, data.ImportStep(), @@ -77,7 +97,7 @@ func TestAccAzureRMApiManagementApi_wordRevision(t *testing.T) { }) } -func TestAccAzureRMApiManagementApi_blankPath(t *testing.T) { +func TestAccAzureRMApiManagementApi_oauth2Authorization(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_api_management_api", "test") resource.Test(t, resource.TestCase{ @@ -86,13 +106,9 @@ func TestAccAzureRMApiManagementApi_blankPath(t *testing.T) { CheckDestroy: testCheckAzureRMApiManagementApiDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMApiManagementApi_blankPath(data), + Config: testAccAzureRMApiManagementApi_oauth2Authorization(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMApiManagementApiExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "soap_pass_through", "false"), - resource.TestCheckResourceAttr(data.ResourceName, "is_current", "true"), - resource.TestCheckResourceAttr(data.ResourceName, "is_online", "false"), - resource.TestCheckResourceAttr(data.ResourceName, "path", ""), ), }, data.ImportStep(), @@ -100,7 +116,7 @@ func TestAccAzureRMApiManagementApi_blankPath(t *testing.T) { }) } -func TestAccAzureRMApiManagementApi_version(t *testing.T) { +func TestAccAzureRMApiManagementApi_openidAuthentication(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_api_management_api", "test") resource.Test(t, resource.TestCase{ @@ -109,10 +125,9 @@ func TestAccAzureRMApiManagementApi_version(t *testing.T) { CheckDestroy: testCheckAzureRMApiManagementApiDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMApiManagementApi_versionSet(data), + Config: testAccAzureRMApiManagementApi_openidAuthentication(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMApiManagementApiExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "version", "v1"), ), }, data.ImportStep(), @@ -370,24 +385,6 @@ resource "azurerm_api_management_api" "test" { `, template, data.RandomInteger) } -// Remove in 2.0 -func testAccAzureRMApiManagementApi_basicClassic(data acceptance.TestData) string { - template := testAccAzureRMApiManagementApi_templateClassic(data) - return fmt.Sprintf(` -%s - -resource "azurerm_api_management_api" "test" { - name = "acctestapi-%d" - resource_group_name = azurerm_resource_group.test.name - api_management_name = azurerm_api_management.test.name - display_name = "api1" - path = "api1" - protocols = ["https"] - revision = "1" -} -`, template, data.RandomInteger) -} - func testAccAzureRMApiManagementApi_blankPath(data acceptance.TestData) string { template := testAccAzureRMApiManagementApi_template(data) return fmt.Sprintf(` @@ -575,31 +572,80 @@ resource "azurerm_api_management_api" "test" { `, template, data.RandomInteger, data.RandomInteger) } -func testAccAzureRMApiManagementApi_template(data acceptance.TestData) string { +func testAccAzureRMApiManagementApi_oauth2Authorization(data acceptance.TestData) string { + template := testAccAzureRMApiManagementApi_template(data) return fmt.Sprintf(` -provider "azurerm" { - features {} +%s + +resource "azurerm_api_management_authorization_server" "test" { + name = "acctestauthsrv-%d" + resource_group_name = azurerm_resource_group.test.name + api_management_name = azurerm_api_management.test.name + display_name = "Test Group" + authorization_endpoint = "https://azacctest.hashicorptest.com/client/authorize" + client_id = "42424242-4242-4242-4242-424242424242" + client_registration_endpoint = "https://azacctest.hashicorptest.com/client/register" + + grant_types = [ + "implicit", + ] + + authorization_methods = [ + "GET", + ] } -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" +resource "azurerm_api_management_api" "test" { + name = "acctestapi-%d" + resource_group_name = azurerm_resource_group.test.name + api_management_name = azurerm_api_management.test.name + display_name = "api1" + path = "api1" + protocols = ["https"] + revision = "1" + oauth2_authorization { + authorization_server_name = azurerm_api_management_authorization_server.test.name + scope = "acctest" + } +} +`, template, data.RandomInteger, data.RandomInteger) } -resource "azurerm_api_management" "test" { - name = "acctestAM-%d" - location = azurerm_resource_group.test.location +func testAccAzureRMApiManagementApi_openidAuthentication(data acceptance.TestData) string { + template := testAccAzureRMApiManagementApi_template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_api_management_openid_connect_provider" "test" { + name = "acctest-%d" + api_management_name = azurerm_api_management.test.name resource_group_name = azurerm_resource_group.test.name - publisher_name = "pub1" - publisher_email = "pub1@email.com" + client_id = "00001111-2222-3333-%d" + client_secret = "%d-cwdavsxbacsaxZX-%d" + display_name = "Initial Name" + metadata_endpoint = "https://azacctest.hashicorptest.com/example/foo" +} - sku_name = "Developer_1" +resource "azurerm_api_management_api" "test" { + name = "acctestapi-%d" + resource_group_name = azurerm_resource_group.test.name + api_management_name = azurerm_api_management.test.name + display_name = "api1" + path = "api1" + protocols = ["https"] + revision = "1" + openid_authentication { + openid_provider_name = azurerm_api_management_openid_connect_provider.test.name + bearer_token_sending_methods = [ + "authorizationHeader", + "query", + ] + } } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, template, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger) } -// Remove in 2.0 -func testAccAzureRMApiManagementApi_templateClassic(data acceptance.TestData) string { +func testAccAzureRMApiManagementApi_template(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -616,7 +662,8 @@ resource "azurerm_api_management" "test" { resource_group_name = azurerm_resource_group.test.name publisher_name = "pub1" publisher_email = "pub1@email.com" - sku_name = "Developer_1" + + sku_name = "Developer_1" } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } diff --git a/website/docs/r/api_management_api.html.markdown b/website/docs/r/api_management_api.html.markdown index d20bcc428351..028688100832 100644 --- a/website/docs/r/api_management_api.html.markdown +++ b/website/docs/r/api_management_api.html.markdown @@ -68,6 +68,10 @@ The following arguments are supported: * `import` - (Optional) A `import` block as documented below. +* `oauth2_authorization` - (Optional) An `oauth2_authorization` block as documented below. + +* `openid_authentication` - (Optional) An `openid_authentication` block as documented below. + * `service_url` - (Optional) Absolute URL of the backend service implementing this API. * `soap_pass_through` - (Optional) Should this API expose a SOAP frontend, rather than a HTTP frontend? Defaults to `false`. @@ -94,6 +98,22 @@ A `import` block supports the following: --- +A `oauth2_authorization` block supports the following: + +* `authorization_server_name` - (Required) OAuth authorization server identifier. The name of an [OAuth2 Authorization Server](https://www.terraform.io/docs/providers/azurerm/r/api_management_authorization_server.html). + +* `scope` - (Optional) Operations scope. + +--- + +A `openid_authentication` block supports the following: + +* `openid_provider_name` - (Required) OpenID Connect provider identifier. The name of an [OpenID Connect Provider](https://www.terraform.io/docs/providers/azurerm/r/api_management_openid_connect_provider.html). + +* `bearer_token_sending_methods` - (Optional) How to send token to the server. A list of zero or more methods. Valid values are `authorizationHeader` and `query`. + +--- + A `subscription_key_parameter_names` block supports the following: * `header` - (Required) The name of the HTTP Header which should be used for the Subscription Key.