diff --git a/internal/services/containerapps/container_app_environment_managed_certificate_resource.go b/internal/services/containerapps/container_app_environment_managed_certificate_resource.go new file mode 100644 index 000000000000..579b80412f6c --- /dev/null +++ b/internal/services/containerapps/container_app_environment_managed_certificate_resource.go @@ -0,0 +1,250 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package containerapps + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/containerapps/2025-07-01/managedenvironments" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/containerapps/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" +) + +type ContainerAppEnvironmentManagedCertificateResource struct{} + +type ContainerAppEnvironmentManagedCertificateModel struct { + Name string `tfschema:"name"` + ContainerAppEnvironmentId string `tfschema:"container_app_environment_id"` + SubjectName string `tfschema:"subject_name"` + DomainControlValidation string `tfschema:"domain_control_validation"` + Tags map[string]interface{} `tfschema:"tags"` + ValidationToken string `tfschema:"validation_token"` +} + +var _ sdk.ResourceWithUpdate = ContainerAppEnvironmentManagedCertificateResource{} + +func (r ContainerAppEnvironmentManagedCertificateResource) ModelObject() interface{} { + return &ContainerAppEnvironmentManagedCertificateModel{} +} + +func (r ContainerAppEnvironmentManagedCertificateResource) ResourceType() string { + return "azurerm_container_app_environment_managed_certificate" +} + +func (r ContainerAppEnvironmentManagedCertificateResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return managedenvironments.ValidateManagedCertificateID +} + +func (r ContainerAppEnvironmentManagedCertificateResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.CertificateName, + Description: "The name of the Container Apps Managed Certificate.", + }, + + "container_app_environment_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: managedenvironments.ValidateManagedEnvironmentID, + Description: "The Container App Managed Environment ID to configure this Managed Certificate on.", + }, + + "subject_name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + Description: "The Subject Name of the Certificate. Must be a valid domain name.", + }, + + "domain_control_validation": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + Default: string(managedenvironments.ManagedCertificateDomainControlValidationHTTP), + ValidateFunc: validation.StringInSlice( + []string{ + string(managedenvironments.ManagedCertificateDomainControlValidationCNAME), + string(managedenvironments.ManagedCertificateDomainControlValidationHTTP), + }, + false, + ), + Description: "The domain control validation type for the managed certificate. Possible values are `CNAME` and `HTTP`. Defaults to `HTTP`.", + }, + + "tags": commonschema.Tags(), + } +} + +func (r ContainerAppEnvironmentManagedCertificateResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "validation_token": { + Type: pluginsdk.TypeString, + Computed: true, + Description: "The validation token for the managed certificate.", + }, + } +} + +func (r ContainerAppEnvironmentManagedCertificateResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.ContainerApps.ManagedEnvironmentClient + + var model ContainerAppEnvironmentManagedCertificateModel + + if err := metadata.Decode(&model); err != nil { + return err + } + + envId, err := managedenvironments.ParseManagedEnvironmentID(model.ContainerAppEnvironmentId) + if err != nil { + return err + } + + id := managedenvironments.NewManagedCertificateID(envId.SubscriptionId, envId.ResourceGroupName, envId.ManagedEnvironmentName, model.Name) + + existing, err := client.ManagedCertificatesGet(ctx, id) + if err != nil { + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + } + } + + if !response.WasNotFound(existing.HttpResponse) { + return metadata.ResourceRequiresImport(r.ResourceType(), id) + } + + env, err := client.Get(ctx, *envId) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", *envId, err) + } + + if env.Model == nil { + return fmt.Errorf("retrieving %s: `model` was nil", envId) + } + + certificate := managedenvironments.ManagedCertificate{ + Location: env.Model.Location, + Properties: &managedenvironments.ManagedCertificateProperties{ + SubjectName: pointer.To(model.SubjectName), + DomainControlValidation: pointer.To(managedenvironments.ManagedCertificateDomainControlValidation(model.DomainControlValidation)), + }, + Tags: tags.Expand(model.Tags), + } + + if err := client.ManagedCertificatesCreateOrUpdateThenPoll(ctx, id, certificate); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + + return nil + }, + } +} + +func (r ContainerAppEnvironmentManagedCertificateResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.ContainerApps.ManagedEnvironmentClient + + id, err := managedenvironments.ParseManagedCertificateID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + existing, err := client.ManagedCertificatesGet(ctx, *id) + if err != nil { + if response.WasNotFound(existing.HttpResponse) { + return metadata.MarkAsGone(id) + } + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + var state ContainerAppEnvironmentManagedCertificateModel + + state.Name = id.ManagedCertificateName + state.ContainerAppEnvironmentId = managedenvironments.NewManagedEnvironmentID(id.SubscriptionId, id.ResourceGroupName, id.ManagedEnvironmentName).ID() + + if model := existing.Model; model != nil { + state.Tags = tags.Flatten(model.Tags) + + if props := model.Properties; props != nil { + state.SubjectName = pointer.From(props.SubjectName) + state.DomainControlValidation = pointer.FromEnum(props.DomainControlValidation) + state.ValidationToken = pointer.From(props.ValidationToken) + } + } + + return metadata.Encode(&state) + }, + } +} + +func (r ContainerAppEnvironmentManagedCertificateResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.ContainerApps.ManagedEnvironmentClient + + id, err := managedenvironments.ParseManagedCertificateID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + if _, err := client.ManagedCertificatesDelete(ctx, *id); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + + return nil + }, + } +} + +func (r ContainerAppEnvironmentManagedCertificateResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.ContainerApps.ManagedEnvironmentClient + + var model ContainerAppEnvironmentManagedCertificateModel + + if err := metadata.Decode(&model); err != nil { + return err + } + + id, err := managedenvironments.ParseManagedCertificateID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + if metadata.ResourceData.HasChange("tags") { + patch := managedenvironments.ManagedCertificatePatch{ + Tags: tags.Expand(model.Tags), + } + + if _, err = client.ManagedCertificatesUpdate(ctx, *id, patch); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) + } + } + + return nil + }, + } +} diff --git a/internal/services/containerapps/container_app_environment_managed_certificate_resource_test.go b/internal/services/containerapps/container_app_environment_managed_certificate_resource_test.go new file mode 100644 index 000000000000..ab14fe8eab7d --- /dev/null +++ b/internal/services/containerapps/container_app_environment_managed_certificate_resource_test.go @@ -0,0 +1,353 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package containerapps_test + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/containerapps/2025-07-01/managedenvironments" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +type ContainerAppEnvironmentManagedCertificateResource struct{} + +func TestAccContainerAppEnvironmentManagedCertificate_basic(t *testing.T) { + if os.Getenv("ARM_TEST_DNS_ZONE") == "" || os.Getenv("ARM_TEST_DATA_RESOURCE_GROUP") == "" { + t.Skipf("Skipping as either ARM_TEST_DNS_ZONE or ARM_TEST_DATA_RESOURCE_GROUP is not set") + } + + data := acceptance.BuildTestData(t, "azurerm_container_app_environment_managed_certificate", "test") + r := ContainerAppEnvironmentManagedCertificateResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccContainerAppEnvironmentManagedCertificate_requiresImport(t *testing.T) { + if os.Getenv("ARM_TEST_DNS_ZONE") == "" || os.Getenv("ARM_TEST_DATA_RESOURCE_GROUP") == "" { + t.Skipf("Skipping as either ARM_TEST_DNS_ZONE or ARM_TEST_DATA_RESOURCE_GROUP is not set") + } + + data := acceptance.BuildTestData(t, "azurerm_container_app_environment_managed_certificate", "test") + r := ContainerAppEnvironmentManagedCertificateResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccContainerAppEnvironmentManagedCertificate_update(t *testing.T) { + if os.Getenv("ARM_TEST_DNS_ZONE") == "" || os.Getenv("ARM_TEST_DATA_RESOURCE_GROUP") == "" { + t.Skipf("Skipping as either ARM_TEST_DNS_ZONE or ARM_TEST_DATA_RESOURCE_GROUP is not set") + } + + data := acceptance.BuildTestData(t, "azurerm_container_app_environment_managed_certificate", "test") + r := ContainerAppEnvironmentManagedCertificateResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccContainerAppEnvironmentManagedCertificate_domainControlValidationHTTP(t *testing.T) { + if os.Getenv("ARM_TEST_DNS_ZONE") == "" || os.Getenv("ARM_TEST_DATA_RESOURCE_GROUP") == "" { + t.Skipf("Skipping as either ARM_TEST_DNS_ZONE or ARM_TEST_DATA_RESOURCE_GROUP is not set") + } + + data := acceptance.BuildTestData(t, "azurerm_container_app_environment_managed_certificate", "test") + r := ContainerAppEnvironmentManagedCertificateResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.domainControlValidationHTTP(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccContainerAppEnvironmentManagedCertificate_domainControlValidationCNAME(t *testing.T) { + if os.Getenv("ARM_TEST_DNS_ZONE") == "" || os.Getenv("ARM_TEST_DATA_RESOURCE_GROUP") == "" { + t.Skipf("Skipping as either ARM_TEST_DNS_ZONE or ARM_TEST_DATA_RESOURCE_GROUP is not set") + } + + data := acceptance.BuildTestData(t, "azurerm_container_app_environment_managed_certificate", "test") + r := ContainerAppEnvironmentManagedCertificateResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.domainControlValidationCNAME(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r ContainerAppEnvironmentManagedCertificateResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := managedenvironments.ParseManagedCertificateID(state.ID) + if err != nil { + return nil, err + } + + resp, err := client.ContainerApps.ManagedEnvironmentClient.ManagedCertificatesGet(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return pointer.To(false), nil + } + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) + } + + return pointer.To(resp.Model != nil), nil +} + +func (r ContainerAppEnvironmentManagedCertificateResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%[1]s + +resource "azurerm_container_app_custom_domain" "test" { + name = trimsuffix(trimprefix(azurerm_dns_txt_record.test.fqdn, "asuid."), ".") + container_app_id = azurerm_container_app.test.id + + lifecycle { + ignore_changes = [certificate_binding_type, container_app_environment_certificate_id] + } +} + +resource "azurerm_container_app_environment_managed_certificate" "test" { + name = "acctest-cacertmgd%[2]d" + container_app_environment_id = azurerm_container_app_environment.test.id + subject_name = trimsuffix(trimprefix(azurerm_dns_txt_record.test.fqdn, "asuid."), ".") + domain_control_validation = "HTTP" + + depends_on = [azurerm_container_app_custom_domain.test] +} +`, r.template(data), data.RandomInteger) +} + +func (r ContainerAppEnvironmentManagedCertificateResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_container_app_environment_managed_certificate" "import" { + name = azurerm_container_app_environment_managed_certificate.test.name + container_app_environment_id = azurerm_container_app_environment_managed_certificate.test.container_app_environment_id + subject_name = azurerm_container_app_environment_managed_certificate.test.subject_name + domain_control_validation = "HTTP" +} +`, r.basic(data)) +} + +func (r ContainerAppEnvironmentManagedCertificateResource) update(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%[1]s + +resource "azurerm_container_app_custom_domain" "test" { + name = trimsuffix(trimprefix(azurerm_dns_txt_record.test.fqdn, "asuid."), ".") + container_app_id = azurerm_container_app.test.id + + + lifecycle { + ignore_changes = [certificate_binding_type, container_app_environment_certificate_id] + } +} + +resource "azurerm_container_app_environment_managed_certificate" "test" { + name = "acctest-cacertmgd%[2]d" + container_app_environment_id = azurerm_container_app_environment.test.id + subject_name = trimsuffix(trimprefix(azurerm_dns_txt_record.test.fqdn, "asuid."), ".") + domain_control_validation = "HTTP" + + tags = { + environment = "testing" + } + + depends_on = [azurerm_container_app_custom_domain.test] +} +`, r.template(data), data.RandomInteger) +} + +func (r ContainerAppEnvironmentManagedCertificateResource) domainControlValidationHTTP(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%[1]s + +resource "azurerm_container_app_custom_domain" "test" { + name = trimsuffix(trimprefix(azurerm_dns_txt_record.test.fqdn, "asuid."), ".") + container_app_id = azurerm_container_app.test.id + + lifecycle { + ignore_changes = [certificate_binding_type, container_app_environment_certificate_id] + } +} + +resource "azurerm_container_app_environment_managed_certificate" "test" { + name = "acctest-cacertmgd%[2]d" + container_app_environment_id = azurerm_container_app_environment.test.id + subject_name = trimsuffix(trimprefix(azurerm_dns_txt_record.test.fqdn, "asuid."), ".") + domain_control_validation = "HTTP" + + depends_on = [azurerm_container_app_custom_domain.test] +} +`, r.template(data), data.RandomInteger) +} + +func (r ContainerAppEnvironmentManagedCertificateResource) domainControlValidationCNAME(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%[1]s + +resource "azurerm_container_app_custom_domain" "test" { + name = trimsuffix(trimprefix(azurerm_dns_txt_record.test.fqdn, "asuid."), ".") + container_app_id = azurerm_container_app.test.id + + lifecycle { + ignore_changes = [certificate_binding_type, container_app_environment_certificate_id] + } +} + +resource "azurerm_container_app_environment_managed_certificate" "test" { + name = "acctest-cacertmgd%[2]d" + container_app_environment_id = azurerm_container_app_environment.test.id + subject_name = trimsuffix(trimprefix(azurerm_dns_txt_record.test.fqdn, "asuid."), ".") + domain_control_validation = "CNAME" + + depends_on = [azurerm_container_app_custom_domain.test] +} +`, r.template(data), data.RandomInteger) +} + +func (r ContainerAppEnvironmentManagedCertificateResource) template(data acceptance.TestData) string { + dnsZone := os.Getenv("ARM_TEST_DNS_ZONE") + dataResourceGroup := os.Getenv("ARM_TEST_DATA_RESOURCE_GROUP") + + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-CAEnv-%[1]d" + location = "%[2]s" +} + +data "azurerm_dns_zone" "test" { + name = "%[3]s" + resource_group_name = "%[4]s" +} + +resource "azurerm_log_analytics_workspace" "test" { + name = "acctestCAEnv-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku = "PerGB2018" + retention_in_days = 30 +} + +resource "azurerm_container_app_environment" "test" { + name = "acctest-CAEnv%[1]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id +} + +resource "azurerm_container_app" "test" { + name = "acctest-capp-%[1]d" + resource_group_name = azurerm_resource_group.test.name + container_app_environment_id = azurerm_container_app_environment.test.id + revision_mode = "Single" + + template { + container { + name = "acctest-cont-%[1]d" + image = "jackofallops/azure-containerapps-python-acctest:v0.0.1" + cpu = 0.25 + memory = "0.5Gi" + } + } + + ingress { + allow_insecure_connections = true + external_enabled = true + target_port = 5000 + transport = "http" + + traffic_weight { + latest_revision = true + percentage = 100 + } + } +} + +resource "azurerm_dns_cname_record" "test" { + name = "contapp%[1]d" + resource_group_name = data.azurerm_dns_zone.test.resource_group_name + zone_name = data.azurerm_dns_zone.test.name + ttl = 300 + record = azurerm_container_app.test.latest_revision_fqdn +} + +resource "azurerm_dns_txt_record" "test" { + name = "asuid.contapp%[1]d" + resource_group_name = data.azurerm_dns_zone.test.resource_group_name + zone_name = data.azurerm_dns_zone.test.name + ttl = 60 + + record { + value = azurerm_container_app.test.custom_domain_verification_id + } + + depends_on = [ + azurerm_dns_cname_record.test + ] +} +`, data.RandomInteger, data.Locations.Primary, dnsZone, dataResourceGroup) +} diff --git a/internal/services/containerapps/registration.go b/internal/services/containerapps/registration.go index e9553b12bd5c..a8710e70c118 100644 --- a/internal/services/containerapps/registration.go +++ b/internal/services/containerapps/registration.go @@ -38,6 +38,7 @@ func (r Registration) Resources() []sdk.Resource { ContainerAppEnvironmentCertificateResource{}, ContainerAppEnvironmentCustomDomainResource{}, ContainerAppEnvironmentDaprComponentResource{}, + ContainerAppEnvironmentManagedCertificateResource{}, ContainerAppEnvironmentResource{}, ContainerAppEnvironmentStorageResource{}, ContainerAppResource{}, diff --git a/website/docs/r/container_app_environment_managed_certificate.html.markdown b/website/docs/r/container_app_environment_managed_certificate.html.markdown new file mode 100644 index 000000000000..e2bba9c46fa7 --- /dev/null +++ b/website/docs/r/container_app_environment_managed_certificate.html.markdown @@ -0,0 +1,129 @@ +--- +subcategory: "Container Apps" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_container_app_environment_managed_certificate" +description: |- + Manages a Container App Environment Managed Certificate. +--- + +# azurerm_container_app_environment_managed_certificate + +Manages a Container App Environment Managed Certificate. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_log_analytics_workspace" "example" { + name = "example-workspace" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + sku = "PerGB2018" + retention_in_days = 30 +} + +resource "azurerm_container_app_environment" "example" { + name = "example-environment" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + log_analytics_workspace_id = azurerm_log_analytics_workspace.example.id +} + +resource "azurerm_container_app" "example" { + name = "example-app" + resource_group_name = azurerm_resource_group.example.name + container_app_environment_id = azurerm_container_app_environment.example.id + revision_mode = "Single" + + template { + container { + name = "example-container" + image = "mcr.microsoft.com/k8se/quickstart:latest" + cpu = 0.25 + memory = "0.5Gi" + } + } + + ingress { + external_enabled = true + target_port = 80 + transport = "http" + + traffic_weight { + latest_revision = true + percentage = 100 + } + } +} + +resource "azurerm_container_app_custom_domain" "example" { + name = "example.com" + container_app_id = azurerm_container_app.example.id + + lifecycle { + ignore_changes = [certificate_binding_type, container_app_environment_certificate_id] + } +} + +resource "azurerm_container_app_environment_managed_certificate" "example" { + name = "example-managed-cert" + container_app_environment_id = azurerm_container_app_environment.example.id + subject_name = "example.com" + domain_control_validation = "HTTP" + + depends_on = [azurerm_container_app_custom_domain.example] +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `name` - (Required) The name of the Container Apps Environment Managed Certificate. Changing this forces a new resource to be created. + +* `container_app_environment_id` - (Required) The Container App Managed Environment ID to configure this Managed Certificate on. Changing this forces a new resource to be created. + +* `subject_name` - (Required) The Subject Name of the Certificate. Must be a valid domain name. Changing this forces a new resource to be created. + +--- + +* `domain_control_validation` - (Optional) The domain control validation type for the managed certificate. Possible values are `CNAME` and `HTTP`. Defaults to `HTTP`. Changing this forces a new resource to be created. + +~> **Note:** The supported validation methods depend on the domain. Azure will validate domain ownership based on the specified method. `HTTP` validation requires an HTTP endpoint at the domain, `CNAME` validation requires DNS CNAME record configuration. + +* `tags` - (Optional) A mapping of tags to assign to the resource. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Container App Environment Managed Certificate. + +* `validation_token` - The validation token for the managed certificate. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://developer.hashicorp.com/terraform/language/resources/configure#define-operation-timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Container App Environment Managed Certificate. +* `read` - (Defaults to 5 minutes) Used when retrieving the Container App Environment Managed Certificate. +* `update` - (Defaults to 30 minutes) Used when updating the Container App Environment Managed Certificate. +* `delete` - (Defaults to 30 minutes) Used when deleting the Container App Environment Managed Certificate. + +## Import + +A Container App Environment Managed Certificate can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_container_app_environment_managed_certificate.example "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.App/managedEnvironments/myenv/managedCertificates/mycertificate" +``` + +## API Providers + +This resource uses the following Azure API Providers: + +* `Microsoft.App` - 2025-07-01