Terraform: Inappropriate value for attribute "ingress" while creating SG
Asked Answered
S

3

14

I'm creating a Security group using terraform, and when I'm running terraform plan. It is giving me an error like some fields are required, and all those fields are optional.

Terraform Version: v1.0.5

AWS Provider version: v3.57.0

main.tf

resource "aws_security_group" "sg_oregon" {
  name        = "tf-sg"
  description = "Allow web traffics"
  vpc_id      = aws_vpc.vpc_terraform.id

  ingress = [
    {
      description      = "HTTP"
      from_port        = 80
      to_port          = 80
      protocol         = "tcp"
      cidr_blocks      = ["0.0.0.0/0"]  
    },
  {
      description      = "HTTPS"
      from_port        = 443
      to_port          = 443
      protocol         = "tcp"
      cidr_blocks      = ["0.0.0.0/0"]  
  },

    {
      description      = "SSH"
      from_port        = 22
      to_port          = 22
      protocol         = "tcp"
      cidr_blocks      = ["0.0.0.0/0"]  
    }
  ]


  egress = [
    {
      description      = "for all outgoing traffics"
      from_port        = 0
      to_port          = 0
      protocol         = "-1"
      cidr_blocks      = ["0.0.0.0/0"]
      ipv6_cidr_blocks = ["::/0"]
      
    }
  ]

  tags = {
    Name = "sg-for-subnet"
  }
}

error in console

│ Inappropriate value for attribute "ingress": element 0: attributes "ipv6_cidr_blocks", "prefix_list_ids", "security_groups", and "self" are required.

│ Inappropriate value for attribute "egress": element 0: attributes "prefix_list_ids", "security_groups", and "self" are required.

I'm following this doc: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group

Any help would be appreciated.

Senseless answered 6/9, 2021 at 20:23 Comment(0)
L
31

Since you are using Attributes as Blocks you have to provide values for all options:

resource "aws_security_group" "sg_oregon" {
  name        = "tf-sg"
  description = "Allow web traffics"
  vpc_id      = aws_vpc.vpc_terraform.id

  ingress = [
    {
      description      = "HTTP"
      from_port        = 80
      to_port          = 80
      protocol         = "tcp"
      cidr_blocks      = ["0.0.0.0/0"]  
      ipv6_cidr_blocks = []
      prefix_list_ids = []
      security_groups = []
      self = false
    },
  {
      description      = "HTTPS"
      from_port        = 443
      to_port          = 443
      protocol         = "tcp"
      cidr_blocks      = ["0.0.0.0/0"]  
      ipv6_cidr_blocks = []
      prefix_list_ids = []
      security_groups = []
      self = false      
  },

    {
      description      = "SSH"
      from_port        = 22
      to_port          = 22
      protocol         = "tcp"
      cidr_blocks      = ["0.0.0.0/0"]  
      ipv6_cidr_blocks = []
      prefix_list_ids = []
      security_groups = []
      self = false      
    }
  ]


  egress = [
    {
      description      = "for all outgoing traffics"
      from_port        = 0
      to_port          = 0
      protocol         = "-1"
      cidr_blocks      = ["0.0.0.0/0"]
      ipv6_cidr_blocks = ["::/0"]
      prefix_list_ids = []
      security_groups = []
      self = false
    }
  ]

  tags = {
    Name = "sg-for-subnet"
  }
}
Leeuwarden answered 6/9, 2021 at 21:28 Comment(11)
I only noticed this behaviour recently when Radek Simko added these to the AWS provider docs. What's the benefit of attributes as blocks style versus dynamic blocks? They both feel a bit clunky so I'm surprised that there's two different ways to do things like this.Connection
@Connection You can create the rules using for loops, and it helps when providing default values for parameters with maps. In OP's case it would be probably easier to use block syntax though. The issue I find with attributes as blocks is that some blocks support this alternative way, some not. So its not consistent in TF.Leeuwarden
I just saw github.com/hashicorp/terraform-plugin-framework/issues/85 so I guess blocks might be being killed in favour of attributes as blocks?Connection
@Connection It would break a lot of code:-) But I can see the benefits of using attributes as blocks in place of regular blocks.Leeuwarden
it looks like it needs ConfigMode: schema.SchemaConfigModeAttr, on the attribute for attributes as block to work. github.com/hashicorp/terraform-provider-aws/commit/… has a nice write up too.Connection
@Leeuwarden can you tell me some benefits of using attributes as blocks? and why in the example of terraform officials, It's written without all parameters? registry.terraform.io/modules/terraform-aws-modules/…Senseless
@AbhishekKumar The docs are outdated. And the benefits can be expressed in one line ingress = var.my_ingress_rules. This would allow you to set all rules at once using your input variable, rather then iterate over var.my_ingress_rules and use dynamic blocks. Much easier to read, write and debug.Leeuwarden
Allright... i did as described here, and Terraform is now complaining: "Null values are not allowed for this attribute value." What next?Nomology
@Nomology I would suggest making new question, specific to your issue and TF code.Leeuwarden
@Leeuwarden - good idea, i will do that.Nomology
i figured it out. One of my values was referring a null value. It was not any of the values set to [ ]. I had to set that one to [ ] also, for terraform to be able to move forward. I'm learning that TF error messages are not very specific.Nomology
M
8

You can avoid having to specify the so called optional parameters by declaring the rules in an alternative format:

resource "aws_security_group" "sg_oregon" {
  name        = "tf-sg"
  description = "Allow web traffics"
  vpc_id      = aws_vpc.vpc_terraform.id

  ingress {
    description = "HTTP"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "HTTPS"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "SSH"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    description      = "for all outgoing traffics"
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]

  }

  tags = {
    Name = "sg-for-subnet"
  }
}
Morelock answered 2/11, 2021 at 4:36 Comment(0)
D
1

Another useful approach is to put the rules in a variable, and use a loop in the resource, something like this:

locals {
  ingress_rules = [
    {
      description      = "HTTP"
      from_port        = 80
      to_port          = 80
      protocol         = "tcp"
      cidr_blocks      = ["0.0.0.0/0"]  
    },
    {
      description      = "HTTPS"
      from_port        = 443
      to_port          = 443
      protocol         = "tcp"
      cidr_blocks      = ["0.0.0.0/0"]  
    },
    {
      description      = "SSH"
      from_port        = 22
      to_port          = 22
      protocol         = "tcp"
      cidr_blocks      = ["0.0.0.0/0"]  
    }
  ]
}

resource "aws_security_group" "sg_oregon" {
  name        = "tf-sg"
  description = "Allow web traffics"
  vpc_id      = aws_vpc.vpc_terraform.id

  ingress = [
    for rule in local.ingress_rules : {
      cidr_blocks              = lookup(rule, "cidr_blocks", null)
      description              = lookup(rule, "description", null)
      from_port                = lookup(rule, "from_port", null)
      ipv6_cidr_blocks         = lookup(rule, "ipv6_cidr_blocks", null)
      prefix_list_ids          = lookup(rule, "prefix_list_ids", null)
      protocol                 = lookup(rule, "protocol", null)
      security_groups          = lookup(rule, "security_groups", null)
      self                     = lookup(rule, "self", null)
      source_security_group_id = lookup(rule, "source_security_group_id", null)
      to_port                  = lookup(rule, "to_port", null)
    }
  ]
}

You could even make a simple module that you use something like:

module "sg_oregon" {
  source = ... # wherever
  name = "tf-sg"
  description = "Allow web traffics"
  vpc_id      = aws_vpc.vpc_terraform.id
  ingress_rules = local.ingress_rules
  egress_rules = local.egress_rules
  tags = local.tags
}
Declared answered 7/3 at 14:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.