Drive an OpenStack security group's ingress rules from a single map variable
using for_each. Add or remove a port by editing data, not by copying resource
blocks — a clean, reusable pattern for web tiers and beyond.
Primary search phrase: Terraform OpenStack security group for_each rules
flowchart LR
VAR[(var.ingress_rules map)] -->|for_each| RULES[openstack_networking_secgroup_rule_v2.ingress]
RULES --> SG[openstack_networking_secgroup_v2.web]
SG --> EG[egress all IPv4]
Each entry in ingress_rules becomes one rule resource, keyed by its name, so
the collection stays stable as entries are added or removed.
export OS_CLOUD=openstack # or set `cloud` in terraform.tfvars
cp terraform.tfvars.example terraform.tfvars
terraform init
terraform plan
terraform apply| Name | Description | Type | Default |
|---|---|---|---|
cloud |
clouds.yaml entry to use | string |
"openstack" |
secgroup_name |
Group name | string |
"example-web-tier" |
ingress_rules |
Map of {protocol, port, cidr} keyed by rule name |
map(object) |
see variables.tf |
tags |
Tags on the group | list(string) |
see variables.tf |
| Name | Description |
|---|---|
secgroup_id |
UUID of the security group |
secgroup_name |
Name of the security group |
ingress_rule_ids |
Map of rule name to rule UUID |
- Why this approach:
for_eachover a map keys each rule by name, so deleting the middle rule does not re-create the others (unlikecount, which re-indexes). - Common mistakes: Using
countand watching unrelated rules churn; non-unique or unstable map keys; forgetting that changing a key forces replace of that rule. - Scaling considerations: Lift
ingress_rulesinto a module input and feed per-environment maps from*.tfvarsto reuse the pattern everywhere.
- A validation block rejects any rule that opens port 22 to
0.0.0.0/0, keeping least privilege enforceable even when rules come from external data. - Keep the map small and intentional — every entry is an attack-surface decision.
- Prefer
remote_group_id-based rules (seesecurity-group-remote-group) for tier-to-tier access; use CIDR rules mainly for genuinely external sources. - Groups are stateful; no reverse rules are required for replies.
| Symptom | Likely cause | Fix |
|---|---|---|
| Validation error on apply | A rule opens SSH to 0.0.0.0/0 |
Scope that entry's cidr |
| Many rules replaced on edit | Renamed a map key | Keep keys stable; rename is a destroy/create |
| Duplicate rule error | Same proto/port/cidr already exists | Remove the duplicate map entry |
| Rule not taking effect | Group not attached to the instance/port | Attach secgroup_id to the workload |
| Provider auth errors | Bad/missing clouds.yaml or OS_CLOUD |
See provider configuration |
terraform destroy