Skip to content

Commit 923fd84

Browse files
authored
Merge pull request #25356 from hashicorp/f/container-app-custom-domain-managed-cert-support
`azurerm_container_app_custom_domain` - support the ability to use Azure Managed Certificates
2 parents 869bf6a + a56e208 commit 923fd84

File tree

3 files changed

+89
-16
lines changed

3 files changed

+89
-16
lines changed

internal/services/containerapps/container_app_custom_domain_resource.go

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,15 @@ func (a ContainerAppCustomDomainResource) Arguments() map[string]*pluginsdk.Sche
5353

5454
"container_app_environment_certificate_id": {
5555
Type: pluginsdk.TypeString,
56-
Required: true,
56+
Optional: true,
5757
ForceNew: true,
58+
RequiredWith: []string{"certificate_binding_type"},
5859
ValidateFunc: managedenvironments.ValidateCertificateID,
5960
},
6061

6162
"certificate_binding_type": {
6263
Type: pluginsdk.TypeString,
63-
Required: true,
64+
Optional: true,
6465
ForceNew: true,
6566
ValidateFunc: validation.StringInSlice(containerapps.PossibleValuesForBindingType(), false),
6667
Description: "The Binding type. Possible values include `Disabled` and `SniEnabled`.",
@@ -106,9 +107,12 @@ func (a ContainerAppCustomDomainResource) Create() sdk.ResourceFunc {
106107

107108
id := parse.NewContainerAppCustomDomainId(containerAppId.SubscriptionId, containerAppId.ResourceGroupName, containerAppId.ContainerAppName, model.Name)
108109

109-
certificateId, err := managedenvironments.ParseCertificateID(model.CertificateId)
110-
if err != nil {
111-
return err
110+
var certificateId *managedenvironments.CertificateId
111+
if model.CertificateId != "" {
112+
certificateId, err = managedenvironments.ParseCertificateID(model.CertificateId)
113+
if err != nil {
114+
return err
115+
}
112116
}
113117

114118
containerApp, err := client.Get(ctx, *containerAppId)
@@ -149,11 +153,17 @@ func (a ContainerAppCustomDomainResource) Create() sdk.ResourceFunc {
149153
customDomains = *existingCustomDomains
150154
}
151155

152-
customDomains = append(customDomains, containerapps.CustomDomain{
153-
BindingType: pointer.To(containerapps.BindingType(model.BindingType)),
154-
CertificateId: pointer.To(certificateId.ID()),
155-
Name: model.Name,
156-
})
156+
customDomain := containerapps.CustomDomain{
157+
Name: model.Name,
158+
BindingType: pointer.To(containerapps.BindingTypeDisabled),
159+
}
160+
161+
if certificateId != nil {
162+
customDomain.CertificateId = pointer.To(certificateId.ID())
163+
customDomain.BindingType = pointer.To(containerapps.BindingType(model.BindingType))
164+
}
165+
166+
customDomains = append(customDomains, customDomain)
157167

158168
containerApp.Model.Properties.Configuration.Ingress.CustomDomains = pointer.To(customDomains)
159169

@@ -200,11 +210,14 @@ func (a ContainerAppCustomDomainResource) Read() sdk.ResourceFunc {
200210
found = true
201211
state.Name = id.CustomDomainName
202212
state.ContainerAppId = containerAppId.ID()
203-
certId, err := managedenvironments.ParseCertificateIDInsensitively(pointer.From(v.CertificateId))
204-
if err != nil {
205-
return err
213+
if pointer.From(v.CertificateId) != "" {
214+
certId, err := managedenvironments.ParseCertificateIDInsensitively(pointer.From(v.CertificateId))
215+
if err != nil {
216+
return err
217+
}
218+
state.CertificateId = certId.ID()
206219
}
207-
state.CertificateId = certId.ID()
220+
208221
state.BindingType = string(pointer.From(v.BindingType))
209222
}
210223
}

internal/services/containerapps/container_app_custom_domain_resource_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,25 @@ func TestAccContainerAppCustomDomainResource_basic(t *testing.T) {
7575
})
7676
}
7777

78+
func TestAccContainerAppCustomDomainResource_managedCertificate(t *testing.T) {
79+
if os.Getenv("ARM_TEST_DNS_ZONE") == "" || os.Getenv("ARM_TEST_DATA_RESOURCE_GROUP") == "" {
80+
t.Skipf("Skipping as either ARM_TEST_DNS_ZONE or ARM_TEST_DATA_RESOURCE_GROUP is not set")
81+
}
82+
83+
data := acceptance.BuildTestData(t, "azurerm_container_app_custom_domain", "test")
84+
r := ContainerAppCustomDomainResource{}
85+
86+
data.ResourceTest(t, r, []acceptance.TestStep{
87+
{
88+
Config: r.managedCertificate(data),
89+
Check: acceptance.ComposeTestCheckFunc(
90+
check.That(data.ResourceName).ExistsInAzure(r),
91+
),
92+
},
93+
data.ImportStep(),
94+
})
95+
}
96+
7897
func TestAccContainerAppCustomDomainResource_multiple(t *testing.T) {
7998
if os.Getenv("ARM_TEST_DNS_ZONE") == "" || os.Getenv("ARM_TEST_DATA_RESOURCE_GROUP") == "" {
8099
t.Skipf("Skipping as either ARM_TEST_DNS_ZONE or ARM_TEST_DATA_RESOURCE_GROUP is not set")
@@ -142,6 +161,27 @@ resource "azurerm_container_app_custom_domain" "test" {
142161
certificate_binding_type = "SniEnabled"
143162
}
144163
164+
`, r.template(data))
165+
}
166+
167+
func (r ContainerAppCustomDomainResource) managedCertificate(data acceptance.TestData) string {
168+
return fmt.Sprintf(`
169+
provider azurerm {
170+
features {}
171+
}
172+
173+
%s
174+
175+
resource "azurerm_container_app_custom_domain" "test" {
176+
name = trimprefix(azurerm_dns_txt_record.test.fqdn, "asuid.")
177+
container_app_id = azurerm_container_app.test.id
178+
179+
lifecycle {
180+
ignore_changes = [certificate_binding_type, container_app_environment_certificate_id]
181+
}
182+
}
183+
184+
145185
`, r.template(data))
146186
}
147187

website/docs/r/container_app_custom_domain.html.markdown

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,22 @@ resource "azurerm_container_app_custom_domain" "example" {
9191
9292
```
9393

94+
## Example Usage - Managed Certificate
95+
96+
```hcl
97+
resource "azurerm_container_app_custom_domain" "example" {
98+
name = trimprefix(azurerm_dns_txt_record.example.fqdn, "asuid.")
99+
container_app_id = azurerm_container_app.example.id
100+
101+
lifecycle {
102+
// When using an Azure created Managed Certificate these values must be added to ignore_changes to prevent resource recreation.
103+
ignore_changes = [certificate_binding_type, container_app_environment_certificate_id]
104+
}
105+
}
106+
107+
```
108+
109+
94110
## Arguments Reference
95111

96112
The following arguments are supported:
@@ -101,9 +117,13 @@ The following arguments are supported:
101117

102118
* `container_app_id` - (Required) The ID of the Container App to which this Custom Domain should be bound. Changing this forces a new resource to be created.
103119

104-
* `container_app_environment_certificate_id` - (Required) The ID of the Container App Environment Certificate to use. Changing this forces a new resource to be created.
120+
* `container_app_environment_certificate_id` - (Optional) The ID of the Container App Environment Certificate to use. Changing this forces a new resource to be created.
121+
122+
-> **NOTE:** Omit this value if you wish to use an Azure Managed certificate. You must create the relevant DNS verification steps before this process will be successful.
123+
124+
* `certificate_binding_type` - (Optional) The Certificate Binding type. Possible values include `Disabled` and `SniEnabled`. Required with `container_app_environment_certificate_id`. Changing this forces a new resource to be created.
105125

106-
* `certificate_binding_type` - (Required) The Certificate Binding type. Possible values include `Disabled` and `SniEnabled`. Changing this forces a new resource to be created.
126+
!> **NOTE:** If using an Azure Managed Certificate `container_app_environment_certificate_id` and `certificate_binding_type` should be added to `ignore_changes` to prevent resource recreation due to these values being modified asynchronously outside of Terraform.
107127

108128
## Timeouts
109129

0 commit comments

Comments
 (0)