Skip to content

Commit bc20404

Browse files
committed
Add azurerm_api_management_custom_domain resource
Signed-off-by: Sune Keller <[email protected]>
1 parent f68bfcc commit bc20404

7 files changed

+621
-1
lines changed

azurerm/helpers/azure/string.go

+10
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
package azure
22

33
import (
4+
"regexp"
45
"strings"
56
)
67

8+
var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
9+
var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
10+
711
func StringContains(sourceStr, subStr string) bool {
812
sourceStr, subStr = strings.ToUpper(sourceStr), strings.ToUpper(subStr)
913
return strings.Contains(sourceStr, subStr)
1014
}
15+
16+
func ToSnakeCase(str string) string {
17+
snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}")
18+
snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}")
19+
return strings.ToLower(snake)
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
package apimanagement
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"strings"
7+
"time"
8+
9+
"github.com/Azure/azure-sdk-for-go/services/apimanagement/mgmt/2019-12-01/apimanagement"
10+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
11+
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
12+
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
13+
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
14+
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
15+
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
16+
)
17+
18+
var apiManagementCustomDomainResourceName = "azurerm_api_management_custom_domain"
19+
20+
func resourceArmApiManagementCustomDomain() *schema.Resource {
21+
return &schema.Resource{
22+
Create: apiManagementCustomDomainCreateUpdate,
23+
Read: apiManagementCustomDomainRead,
24+
Update: apiManagementCustomDomainCreateUpdate,
25+
Delete: apiManagementCustomDomainDelete,
26+
Importer: &schema.ResourceImporter{
27+
State: schema.ImportStatePassthrough,
28+
},
29+
30+
Timeouts: &schema.ResourceTimeout{
31+
Create: schema.DefaultTimeout(30 * time.Minute),
32+
Read: schema.DefaultTimeout(5 * time.Minute),
33+
Update: schema.DefaultTimeout(30 * time.Minute),
34+
Delete: schema.DefaultTimeout(30 * time.Minute),
35+
},
36+
37+
Schema: map[string]*schema.Schema{
38+
"resource_group_name": azure.SchemaResourceGroupName(),
39+
40+
"api_management_name": azure.SchemaApiManagementName(),
41+
42+
"management": {
43+
Type: schema.TypeList,
44+
Optional: true,
45+
AtLeastOneOf: []string{"management", "portal", "developer_portal", "proxy", "scm"},
46+
Elem: &schema.Resource{
47+
Schema: apiManagementResourceHostnameSchema(),
48+
},
49+
},
50+
"portal": {
51+
Type: schema.TypeList,
52+
Optional: true,
53+
AtLeastOneOf: []string{"management", "portal", "developer_portal", "proxy", "scm"},
54+
Elem: &schema.Resource{
55+
Schema: apiManagementResourceHostnameSchema(),
56+
},
57+
},
58+
"developer_portal": {
59+
Type: schema.TypeList,
60+
Optional: true,
61+
AtLeastOneOf: []string{"management", "portal", "developer_portal", "proxy", "scm"},
62+
Elem: &schema.Resource{
63+
Schema: apiManagementResourceHostnameSchema(),
64+
},
65+
},
66+
"proxy": {
67+
Type: schema.TypeList,
68+
Optional: true,
69+
AtLeastOneOf: []string{"management", "portal", "developer_portal", "proxy", "scm"},
70+
Elem: &schema.Resource{
71+
Schema: apiManagementResourceHostnameProxySchema(),
72+
},
73+
},
74+
"scm": {
75+
Type: schema.TypeList,
76+
Optional: true,
77+
AtLeastOneOf: []string{"management", "portal", "developer_portal", "proxy", "scm"},
78+
Elem: &schema.Resource{
79+
Schema: apiManagementResourceHostnameSchema(),
80+
},
81+
},
82+
},
83+
}
84+
}
85+
86+
func apiManagementCustomDomainCreateUpdate(d *schema.ResourceData, meta interface{}) error {
87+
client := meta.(*clients.Client).ApiManagement.ServiceClient
88+
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d)
89+
defer cancel()
90+
91+
log.Printf("[INFO] preparing arguments for API Management Custom domain creation.")
92+
93+
name := d.Get("api_management_name").(string)
94+
resourceGroup := d.Get("resource_group_name").(string)
95+
96+
existing, err := client.Get(ctx, resourceGroup, name)
97+
if err != nil {
98+
return fmt.Errorf("Error finding API Management (API Management %q / Resource Group %q): %s", name, resourceGroup, err)
99+
}
100+
101+
if d.IsNewResource() {
102+
if existing.ServiceProperties.HostnameConfigurations != nil {
103+
return tf.ImportAsExistsError(apiManagementCustomDomainResourceName, *existing.ID)
104+
}
105+
}
106+
107+
existing.ServiceProperties.HostnameConfigurations = expandAzureRmApiManagementHostnameConfigurations(d)
108+
109+
if _, err := client.CreateOrUpdate(ctx, resourceGroup, name, existing); err != nil {
110+
return fmt.Errorf("Error creating/updating Custom Custom domain (API Management %q / Resource Group %q): %+v", name, resourceGroup, err)
111+
}
112+
113+
read, err := client.Get(ctx, resourceGroup, name)
114+
if err != nil {
115+
return fmt.Errorf("Error retrieving Custom Custom domain (API Management %q / Resource Group %q): %+v", name, resourceGroup, err)
116+
}
117+
if read.ID == nil {
118+
return fmt.Errorf("Cannot read Custom domain (API Management %q / Resource Group %q) ID", name, resourceGroup)
119+
}
120+
121+
d.SetId(*read.ID)
122+
123+
return apiManagementCustomDomainRead(d, meta)
124+
}
125+
126+
func apiManagementCustomDomainRead(d *schema.ResourceData, meta interface{}) error {
127+
client := meta.(*clients.Client).ApiManagement.ServiceClient
128+
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
129+
defer cancel()
130+
131+
id, err := azure.ParseAzureResourceID(d.Id())
132+
if err != nil {
133+
return err
134+
}
135+
136+
resourceGroup := id.ResourceGroup
137+
name := id.Path["service"]
138+
139+
resp, err := client.Get(ctx, resourceGroup, name)
140+
if err != nil {
141+
if utils.ResponseWasNotFound(resp.Response) {
142+
log.Printf("API Management Service %q was not found in Resource Group %q - removing from state!", name, resourceGroup)
143+
d.SetId("")
144+
return nil
145+
}
146+
147+
return fmt.Errorf("making Read request on API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err)
148+
}
149+
150+
d.Set("resource_group_name", resourceGroup)
151+
d.Set("api_management_name", resp.Name)
152+
153+
if props := resp.ServiceProperties.HostnameConfigurations; props != nil {
154+
configs := flattenApiManagementHostnameConfiguration(resp.ServiceProperties.HostnameConfigurations, d)
155+
for _, config := range configs {
156+
for key, v := range config.(map[string]interface{}) {
157+
if err := d.Set(key, v); err != nil {
158+
return fmt.Errorf("setting `hostname_configuration` %q: %+v", key, err)
159+
}
160+
}
161+
}
162+
}
163+
164+
return nil
165+
}
166+
167+
func apiManagementCustomDomainDelete(d *schema.ResourceData, meta interface{}) error {
168+
client := meta.(*clients.Client).ApiManagement.ServiceClient
169+
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d)
170+
defer cancel()
171+
172+
id, err := azure.ParseAzureResourceID(d.Id())
173+
if err != nil {
174+
return err
175+
}
176+
177+
resourceGroup := id.ResourceGroup
178+
name := id.Path["service"]
179+
180+
resp, err := client.Get(ctx, resourceGroup, name)
181+
if err != nil {
182+
if utils.ResponseWasNotFound(resp.Response) {
183+
log.Printf("API Management Service %q was not found in Resource Group %q - removing from state!", name, resourceGroup)
184+
d.SetId("")
185+
return nil
186+
}
187+
188+
return fmt.Errorf("making Read request on API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err)
189+
}
190+
191+
log.Printf("[DEBUG] Deleting API Management Custom domain (API Management %q / Resource Group %q)", name, resourceGroup)
192+
193+
resp.ServiceProperties.HostnameConfigurations = nil
194+
195+
if _, err := client.CreateOrUpdate(ctx, resourceGroup, name, resp); err != nil {
196+
return fmt.Errorf("Error deleting Custom Custom domain (API Management %q / Resource Group %q): %+v", name, resourceGroup, err)
197+
}
198+
199+
return nil
200+
}
201+
202+
func flattenApiManagementHostnameConfiguration(input *[]apimanagement.HostnameConfiguration, d *schema.ResourceData) []interface{} {
203+
results := make([]interface{}, 0)
204+
if input == nil {
205+
return results
206+
}
207+
208+
managementResults := make([]interface{}, 0)
209+
portalResults := make([]interface{}, 0)
210+
developerPortalResults := make([]interface{}, 0)
211+
proxyResults := make([]interface{}, 0)
212+
scmResults := make([]interface{}, 0)
213+
214+
for _, config := range *input {
215+
output := make(map[string]interface{})
216+
217+
if config.HostName != nil {
218+
output["host_name"] = *config.HostName
219+
}
220+
221+
if config.NegotiateClientCertificate != nil {
222+
output["negotiate_client_certificate"] = *config.NegotiateClientCertificate
223+
}
224+
225+
if config.KeyVaultID != nil {
226+
output["key_vault_id"] = *config.KeyVaultID
227+
}
228+
229+
// Iterate through old state to find sensitive props not returned by API.
230+
// This must be done in order to avoid state diffs.
231+
// NOTE: this information won't be available during times like Import, so this is a best-effort.
232+
snakeCaseConfigType := azure.ToSnakeCase(string(config.Type))
233+
if valsRaw, ok := d.GetOk(snakeCaseConfigType); ok {
234+
vals := valsRaw.([]interface{})
235+
for _, val := range vals {
236+
oldConfig := val.(map[string]interface{})
237+
238+
if oldConfig["host_name"] == *config.HostName {
239+
output["certificate_password"] = oldConfig["certificate_password"]
240+
output["certificate"] = oldConfig["certificate"]
241+
}
242+
}
243+
}
244+
245+
switch strings.ToLower(string(config.Type)) {
246+
case strings.ToLower(string(apimanagement.HostnameTypeProxy)):
247+
// only set SSL binding for proxy types
248+
if config.DefaultSslBinding != nil {
249+
output["default_ssl_binding"] = *config.DefaultSslBinding
250+
}
251+
proxyResults = append(proxyResults, output)
252+
253+
case strings.ToLower(string(apimanagement.HostnameTypeManagement)):
254+
managementResults = append(managementResults, output)
255+
256+
case strings.ToLower(string(apimanagement.HostnameTypePortal)):
257+
portalResults = append(portalResults, output)
258+
259+
case strings.ToLower(string(apimanagement.HostnameTypeDeveloperPortal)):
260+
developerPortalResults = append(developerPortalResults, output)
261+
262+
case strings.ToLower(string(apimanagement.HostnameTypeScm)):
263+
scmResults = append(scmResults, output)
264+
}
265+
}
266+
267+
return []interface{}{
268+
map[string]interface{}{
269+
"management": managementResults,
270+
"portal": portalResults,
271+
"developer_portal": developerPortalResults,
272+
"proxy": proxyResults,
273+
"scm": scmResults,
274+
},
275+
}
276+
}

azurerm/internal/services/apimanagement/api_management_resource.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,7 @@ func flattenApiManagementHostnameConfigurations(input *[]apimanagement.HostnameC
835835
if len(existingHostnames) > 0 {
836836
v := existingHostnames[0].(map[string]interface{})
837837

838-
if valsRaw, ok := v[strings.ToLower(string(config.Type))]; ok {
838+
if valsRaw, ok := v[azure.ToSnakeCase(string(config.Type))]; ok {
839839
vals := valsRaw.([]interface{})
840840
for _, val := range vals {
841841
oldConfig := val.(map[string]interface{})

azurerm/internal/services/apimanagement/registration.go

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func (r Registration) SupportedResources() map[string]*schema.Resource {
4343
"azurerm_api_management_authorization_server": resourceArmApiManagementAuthorizationServer(),
4444
"azurerm_api_management_backend": resourceArmApiManagementBackend(),
4545
"azurerm_api_management_certificate": resourceArmApiManagementCertificate(),
46+
"azurerm_api_management_custom_domain": resourceArmApiManagementCustomDomain(),
4647
"azurerm_api_management_diagnostic": resourceArmApiManagementDiagnostic(),
4748
"azurerm_api_management_group": resourceArmApiManagementGroup(),
4849
"azurerm_api_management_group_user": resourceArmApiManagementGroupUser(),

0 commit comments

Comments
 (0)