Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_container_app: Add support for ip_security_restriction block #23870

Merged
merged 19 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions internal/services/containerapps/container_app_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,42 @@ func TestAccContainerAppResource_scaleRulesUpdate(t *testing.T) {
})
}

func TestAccContainerAppResource_ipSecurityRulesUpdate(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_container_app", "test")
r := ContainerAppResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.basic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.ingressSecurityRestriction(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.ingressSecurityRestrictionUpdate(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.basic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func (r ContainerAppResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := containerapps.ParseContainerAppID(state.ID)
if err != nil {
Expand Down Expand Up @@ -1666,6 +1702,80 @@ resource "azurerm_container_app" "test" {
`, r.template(data), data.RandomInteger)
}

func (r ContainerAppResource) ingressSecurityRestriction(data acceptance.TestData) string {
return fmt.Sprintf(`
%s

resource "azurerm_container_app" "test" {
name = "acctest-capp-%[2]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-%[2]d"
image = "jackofallops/azure-containerapps-python-acctest:v0.0.1"
cpu = 0.25
memory = "0.5Gi"
}
}

ingress {
target_port = 5000
ip_security_restriction = [
{
name = "test"
description = "test"
action = "Allow"
ip_address_range = "0.0.0.0/0"
}
]
}
}
`, r.template(data), data.RandomInteger)
}

func (r ContainerAppResource) ingressSecurityRestrictionUpdate(data acceptance.TestData) string {
return fmt.Sprintf(`
%s

resource "azurerm_container_app" "test" {
name = "acctest-capp-%[2]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-%[2]d"
image = "jackofallops/azure-containerapps-python-acctest:v0.0.1"
cpu = 0.25
memory = "0.5Gi"
}
}

ingress {
target_port = 5000
ip_security_restriction = [
{
name = "test"
description = "test"
action = "Allow"
ip_address_range = "0.0.0.0/0"
},
{
name = "test2"
description = "test2"
action = "Deny"
ip_address_range = "10.0.0.0/8"
}
]
}
}
`, r.template(data), data.RandomInteger)
}

func (r ContainerAppResource) scaleRulesUpdate(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
Expand Down
132 changes: 110 additions & 22 deletions internal/services/containerapps/helpers/container_apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,15 @@ func FlattenContainerAppRegistries(input *[]containerapps.RegistryCredentials) [
}

type Ingress struct {
AllowInsecure bool `tfschema:"allow_insecure_connections"`
CustomDomains []CustomDomain `tfschema:"custom_domain"`
IsExternal bool `tfschema:"external_enabled"`
FQDN string `tfschema:"fqdn"`
TargetPort int `tfschema:"target_port"`
ExposedPort int `tfschema:"exposed_port"`
TrafficWeights []TrafficWeight `tfschema:"traffic_weight"`
Transport string `tfschema:"transport"`
AllowInsecure bool `tfschema:"allow_insecure_connections"`
CustomDomains []CustomDomain `tfschema:"custom_domain"`
IsExternal bool `tfschema:"external_enabled"`
FQDN string `tfschema:"fqdn"`
TargetPort int `tfschema:"target_port"`
ExposedPort int `tfschema:"exposed_port"`
TrafficWeights []TrafficWeight `tfschema:"traffic_weight"`
Transport string `tfschema:"transport"`
IpSecurityRestrictions []IpSecurityRestriction `tfschema:"ip_security_restriction"`
}

func ContainerAppIngressSchema() *pluginsdk.Schema {
Expand Down Expand Up @@ -186,6 +187,8 @@ func ContainerAppIngressSchema() *pluginsdk.Schema {
Description: "The FQDN of the ingress.",
},

"ip_security_restriction": ContainerAppIngressIpSecurityRestriction(),

"target_port": {
Type: pluginsdk.TypeInt,
Required: true,
Expand Down Expand Up @@ -271,13 +274,14 @@ func ExpandContainerAppIngress(input []Ingress, appName string) *containerapps.I

ingress := input[0]
result := &containerapps.Ingress{
AllowInsecure: pointer.To(ingress.AllowInsecure),
CustomDomains: expandContainerAppIngressCustomDomain(ingress.CustomDomains),
External: pointer.To(ingress.IsExternal),
Fqdn: pointer.To(ingress.FQDN),
TargetPort: pointer.To(int64(ingress.TargetPort)),
ExposedPort: pointer.To(int64(ingress.ExposedPort)),
Traffic: expandContainerAppIngressTraffic(ingress.TrafficWeights, appName),
AllowInsecure: pointer.To(ingress.AllowInsecure),
CustomDomains: expandContainerAppIngressCustomDomain(ingress.CustomDomains),
External: pointer.To(ingress.IsExternal),
Fqdn: pointer.To(ingress.FQDN),
TargetPort: pointer.To(int64(ingress.TargetPort)),
ExposedPort: pointer.To(int64(ingress.ExposedPort)),
Traffic: expandContainerAppIngressTraffic(ingress.TrafficWeights, appName),
IPSecurityRestrictions: expandIpSecurityRestrictions(ingress.IpSecurityRestrictions),
}
transport := containerapps.IngressTransportMethod(ingress.Transport)
result.Transport = &transport
Expand All @@ -292,13 +296,14 @@ func FlattenContainerAppIngress(input *containerapps.Ingress, appName string) []

ingress := *input
result := Ingress{
AllowInsecure: pointer.From(ingress.AllowInsecure),
CustomDomains: flattenContainerAppIngressCustomDomain(ingress.CustomDomains),
IsExternal: pointer.From(ingress.External),
FQDN: pointer.From(ingress.Fqdn),
TargetPort: int(pointer.From(ingress.TargetPort)),
ExposedPort: int(pointer.From(ingress.ExposedPort)),
TrafficWeights: flattenContainerAppIngressTraffic(ingress.Traffic, appName),
AllowInsecure: pointer.From(ingress.AllowInsecure),
CustomDomains: flattenContainerAppIngressCustomDomain(ingress.CustomDomains),
IsExternal: pointer.From(ingress.External),
FQDN: pointer.From(ingress.Fqdn),
TargetPort: int(pointer.From(ingress.TargetPort)),
ExposedPort: int(pointer.From(ingress.ExposedPort)),
TrafficWeights: flattenContainerAppIngressTraffic(ingress.Traffic, appName),
IpSecurityRestrictions: flattenContainerAppIngressIpSecurityRestrictions(ingress.IPSecurityRestrictions),
}

if ingress.Transport != nil {
Expand Down Expand Up @@ -417,13 +422,77 @@ func flattenContainerAppIngressCustomDomain(input *[]containerapps.CustomDomain)
return result
}

func flattenContainerAppIngressIpSecurityRestrictions(input *[]containerapps.IPSecurityRestrictionRule) []IpSecurityRestriction {
if input == nil {
return []IpSecurityRestriction{}
}

result := make([]IpSecurityRestriction, 0)
for _, v := range *input {
ipSecurityRestriction := IpSecurityRestriction{
Description: *v.Description,
davidkarlsen marked this conversation as resolved.
Show resolved Hide resolved
IpAddressRange: v.IPAddressRange,
Action: string(v.Action),
Name: v.Name,
}

result = append(result, ipSecurityRestriction)
}

return result
}

type TrafficWeight struct {
Label string `tfschema:"label"`
LatestRevision bool `tfschema:"latest_revision"`
RevisionSuffix string `tfschema:"revision_suffix"`
Weight int `tfschema:"percentage"`
}

type IpSecurityRestriction struct {
Action string `tfschema:"action"`
Description string `tfschema:"description"`
IpAddressRange string `tfschema:"ip_address_range"`
Name string `tfschema:"name"`
}

func ContainerAppIngressIpSecurityRestriction() *pluginsdk.Schema {
return &pluginsdk.Schema{
Type: pluginsdk.TypeList,
Optional: true,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"action": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(containerapps.PossibleValuesForAction(), false),
Description: "The action. Allow or Deny.",
},

"description": {
Type: pluginsdk.TypeString,
Optional: true,
Description: "Describe the IP restriction rule that is being sent to the container-app.",
},

"ip_address_range": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.IsCIDR,
Description: "CIDR notation to match incoming IP address.",
},

"name": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringIsNotEmpty,
Description: "Name for the IP restriction rule.",
},
},
},
}
}

func ContainerAppIngressTrafficWeight() *pluginsdk.Schema {
return &pluginsdk.Schema{
Type: pluginsdk.TypeList,
Expand Down Expand Up @@ -542,6 +611,25 @@ func flattenContainerAppIngressTraffic(input *[]containerapps.TrafficWeight, app
return result
}

func expandIpSecurityRestrictions(input []IpSecurityRestriction) *[]containerapps.IPSecurityRestrictionRule {
if input == nil {
return &[]containerapps.IPSecurityRestrictionRule{}
}

result := make([]containerapps.IPSecurityRestrictionRule, 0)
for _, v := range input {
ipSecurityRestrictionRule := containerapps.IPSecurityRestrictionRule{
Action: containerapps.Action(v.Action),
Name: v.Name,
IPAddressRange: v.IpAddressRange,
Description: pointer.To(v.Description),
}
result = append(result, ipSecurityRestrictionRule)
}

return &result
}

type Dapr struct {
AppId string `tfschema:"app_id"`
AppPort int `tfschema:"app_port"`
Expand Down
14 changes: 14 additions & 0 deletions website/docs/r/container_app.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,8 @@ An `ingress` block supports the following:

* `external_enabled` - (Optional) Are connections to this Ingress from outside the Container App Environment enabled? Defaults to `false`.

* `ip_security_restriction` - (Optional) IP-filtering rules.

* `target_port` - (Required) The target port on the container for the Ingress traffic.

* `exposed_port` - (Optional) The exposed port on the container for the Ingress traffic.
Expand All @@ -361,6 +363,18 @@ A `custom_domain` block supports the following:

---

A `ip_security_restriction` block supports the following:

* `action` - (Required) The IP-filter action. `Allow` or `Deny`
davidkarlsen marked this conversation as resolved.
Show resolved Hide resolved

* `description` - (Optional) Describe the IP restriction rule that is being sent to the container-app.

* `ip_address_range` - (Required) CIDR notation to match incoming IP address.

* `name` - (Required) Name for the IP restriction rule.

---

A `traffic_weight` block supports the following:

~> **Note:** This block only applies when `revision_mode` is set to `Multiple`.
Expand Down
Loading