From 787132e5dbe7b58e4b9a62e1a69a682bcbb9bd58 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Fri, 4 Nov 2022 10:47:17 -0400 Subject: [PATCH] feat: Add support for creating IAM role/instance profile with policies (#302) --- .pre-commit-config.yaml | 2 +- README.md | 23 +++++++++- examples/complete/README.md | 6 +++ examples/complete/main.tf | 49 ++++++++++----------- examples/complete/outputs.tf | 30 +++++++++++++ main.tf | 84 ++++++++++++++++++++++++++++++++++-- outputs.tf | 34 +++++++++++++++ variables.tf | 60 +++++++++++++++++++++++++- wrappers/README.md | 4 +- wrappers/main.tf | 11 ++++- 10 files changed, 267 insertions(+), 36 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d2a92f95..314c02b1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.74.1 + rev: v1.76.0 hooks: - id: terraform_fmt - id: terraform_wrapper_module_for_each diff --git a/README.md b/README.md index c300adac..a7af2b00 100644 --- a/README.md +++ b/README.md @@ -184,14 +184,21 @@ No modules. | Name | Type | |------|------| +| [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource | +| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_instance.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource | | [aws_spot_instance_request.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/spot_instance_request) | resource | +| [aws_iam_policy_document.assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | +| [aws_ssm_parameter.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [ami](#input\_ami) | ID of AMI to use for the instance | `string` | `""` | no | +| [ami](#input\_ami) | ID of AMI to use for the instance | `string` | `null` | no | +| [ami\_ssm\_parameter](#input\_ami\_ssm\_parameter) | SSM parameter name for the AMI ID. For Amazon Linux AMI SSM parameters see [reference](https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-public-parameters-ami.html) | `string` | `"/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"` | no | | [associate\_public\_ip\_address](#input\_associate\_public\_ip\_address) | Whether to associate a public IP address with an instance in a VPC | `bool` | `null` | no | | [availability\_zone](#input\_availability\_zone) | AZ to start the instance in | `string` | `null` | no | | [capacity\_reservation\_specification](#input\_capacity\_reservation\_specification) | Describes an instance's Capacity Reservation targeting option | `any` | `{}` | no | @@ -199,6 +206,7 @@ No modules. | [cpu\_credits](#input\_cpu\_credits) | The credit option for CPU usage (unlimited or standard) | `string` | `null` | no | | [cpu\_threads\_per\_core](#input\_cpu\_threads\_per\_core) | Sets the number of CPU threads per core for an instance (has no effect unless cpu\_core\_count is also set). | `number` | `null` | no | | [create](#input\_create) | Whether to create an instance | `bool` | `true` | no | +| [create\_iam\_instance\_profile](#input\_create\_iam\_instance\_profile) | Determines whether an IAM instance profile is created or to use an existing IAM instance profile | `bool` | `false` | no | | [create\_spot\_instance](#input\_create\_spot\_instance) | Depicts if the instance is a spot instance | `bool` | `false` | no | | [disable\_api\_stop](#input\_disable\_api\_stop) | If true, enables EC2 Instance Stop Protection. | `bool` | `null` | no | | [disable\_api\_termination](#input\_disable\_api\_termination) | If true, enables EC2 Instance Termination Protection | `bool` | `null` | no | @@ -211,6 +219,13 @@ No modules. | [hibernation](#input\_hibernation) | If true, the launched EC2 instance will support hibernation | `bool` | `null` | no | | [host\_id](#input\_host\_id) | ID of a dedicated host that the instance will be assigned to. Use when an instance is to be launched on a specific dedicated host | `string` | `null` | no | | [iam\_instance\_profile](#input\_iam\_instance\_profile) | IAM Instance Profile to launch the instance with. Specified as the name of the Instance Profile | `string` | `null` | no | +| [iam\_role\_description](#input\_iam\_role\_description) | Description of the role | `string` | `null` | no | +| [iam\_role\_name](#input\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no | +| [iam\_role\_path](#input\_iam\_role\_path) | IAM role path | `string` | `null` | no | +| [iam\_role\_permissions\_boundary](#input\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | +| [iam\_role\_policies](#input\_iam\_role\_policies) | Policies attached to the IAM role | `map(string)` | `{}` | no | +| [iam\_role\_tags](#input\_iam\_role\_tags) | A map of additional tags to add to the IAM role/profile created | `map(string)` | `{}` | no | +| [iam\_role\_use\_name\_prefix](#input\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`iam_role_name` or `name`) is used as a prefix | `bool` | `true` | no | | [instance\_initiated\_shutdown\_behavior](#input\_instance\_initiated\_shutdown\_behavior) | Shutdown behavior for the instance. Amazon defaults this to stop for EBS-backed instances and terminate for instance-store instances. Cannot be set on instance-store instance | `string` | `null` | no | | [instance\_type](#input\_instance\_type) | The type of instance to start | `string` | `"t3.micro"` | no | | [ipv6\_address\_count](#input\_ipv6\_address\_count) | A number of IPv6 addresses to associate with the primary network interface. Amazon EC2 chooses the IPv6 addresses from the range of your subnet | `number` | `null` | no | @@ -251,6 +266,12 @@ No modules. |------|-------------| | [arn](#output\_arn) | The ARN of the instance | | [capacity\_reservation\_specification](#output\_capacity\_reservation\_specification) | Capacity reservation specification of the instance | +| [iam\_instance\_profile\_arn](#output\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | +| [iam\_instance\_profile\_id](#output\_iam\_instance\_profile\_id) | Instance profile's ID | +| [iam\_instance\_profile\_unique](#output\_iam\_instance\_profile\_unique) | Stable and unique string identifying the IAM instance profile | +| [iam\_role\_arn](#output\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | +| [iam\_role\_name](#output\_iam\_role\_name) | The name of the IAM role | +| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | | [id](#output\_id) | The ID of the instance | | [instance\_state](#output\_instance\_state) | The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped` | | [ipv6\_addresses](#output\_ipv6\_addresses) | The IPv6 address assigned to the instance, if applicable. | diff --git a/examples/complete/README.md b/examples/complete/README.md index 327f765f..99e48366 100644 --- a/examples/complete/README.md +++ b/examples/complete/README.md @@ -66,6 +66,12 @@ No inputs. |------|-------------| | [ec2\_complete\_arn](#output\_ec2\_complete\_arn) | The ARN of the instance | | [ec2\_complete\_capacity\_reservation\_specification](#output\_ec2\_complete\_capacity\_reservation\_specification) | Capacity reservation specification of the instance | +| [ec2\_complete\_iam\_instance\_profile\_arn](#output\_ec2\_complete\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | +| [ec2\_complete\_iam\_instance\_profile\_id](#output\_ec2\_complete\_iam\_instance\_profile\_id) | Instance profile's ID | +| [ec2\_complete\_iam\_instance\_profile\_unique](#output\_ec2\_complete\_iam\_instance\_profile\_unique) | Stable and unique string identifying the IAM instance profile | +| [ec2\_complete\_iam\_role\_arn](#output\_ec2\_complete\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | +| [ec2\_complete\_iam\_role\_name](#output\_ec2\_complete\_iam\_role\_name) | The name of the IAM role | +| [ec2\_complete\_iam\_role\_unique\_id](#output\_ec2\_complete\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | | [ec2\_complete\_id](#output\_ec2\_complete\_id) | The ID of the instance | | [ec2\_complete\_instance\_state](#output\_ec2\_complete\_instance\_state) | The state of the instance. One of: `pending`, `running`, `shutting-down`, `terminated`, `stopping`, `stopped` | | [ec2\_complete\_primary\_network\_interface\_id](#output\_ec2\_complete\_primary\_network\_interface\_id) | The ID of the instance's primary network interface | diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 30c94051..301931b6 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -33,7 +33,7 @@ module "ec2_complete" { name = local.name ami = data.aws_ami.amazon_linux.id - instance_type = "c5.4xlarge" + instance_type = "c5.xlarge" # used to set core count below availability_zone = element(module.vpc.azs, 0) subnet_id = element(module.vpc.private_subnets, 0) vpc_security_group_ids = [module.security_group.security_group_id] @@ -41,6 +41,12 @@ module "ec2_complete" { associate_public_ip_address = true disable_api_stop = false + create_iam_instance_profile = true + iam_role_description = "IAM role for EC2 instance" + iam_role_policies = { + AdministratorAccess = "arn:aws:iam::aws:policy/AdministratorAccess" + } + # only one of these can be enabled at a time hibernation = true # enclave_options_enabled = true @@ -83,9 +89,6 @@ module "ec2_network_interface" { name = "${local.name}-network-interface" - ami = data.aws_ami.amazon_linux.id - instance_type = "c5.large" - network_interface = [ { device_index = 0 @@ -102,8 +105,6 @@ module "ec2_metadata_options" { name = "${local.name}-metadata-options" - ami = data.aws_ami.amazon_linux.id - instance_type = "c5.4xlarge" subnet_id = element(module.vpc.private_subnets, 0) vpc_security_group_ids = [module.security_group.security_group_id] @@ -122,7 +123,6 @@ module "ec2_t2_unlimited" { name = "${local.name}-t2-unlimited" - ami = data.aws_ami.amazon_linux.id instance_type = "t2.micro" cpu_credits = "unlimited" subnet_id = element(module.vpc.private_subnets, 0) @@ -137,7 +137,6 @@ module "ec2_t3_unlimited" { name = "${local.name}-t3-unlimited" - ami = data.aws_ami.amazon_linux.id instance_type = "t3.micro" cpu_credits = "unlimited" subnet_id = element(module.vpc.private_subnets, 0) @@ -196,7 +195,6 @@ module "ec2_multiple" { name = "${local.name}-multi-${each.key}" - ami = data.aws_ami.amazon_linux.id instance_type = each.value.instance_type availability_zone = each.value.availability_zone subnet_id = each.value.subnet_id @@ -218,7 +216,6 @@ module "ec2_spot_instance" { name = "${local.name}-spot-instance" create_spot_instance = true - ami = data.aws_ami.amazon_linux.id availability_zone = element(module.vpc.azs, 0) subnet_id = element(module.vpc.private_subnets, 0) vpc_security_group_ids = [module.security_group.security_group_id] @@ -308,6 +305,22 @@ module "ec2_targeted_capacity_reservation" { tags = local.tags } +resource "aws_ec2_capacity_reservation" "open" { + instance_type = "t3.micro" + instance_platform = "Linux/UNIX" + availability_zone = "${local.region}a" + instance_count = 1 + instance_match_criteria = "open" +} + +resource "aws_ec2_capacity_reservation" "targeted" { + instance_type = "t3.micro" + instance_platform = "Linux/UNIX" + availability_zone = "${local.region}a" + instance_count = 1 + instance_match_criteria = "targeted" +} + ################################################################################ # Supporting Resources ################################################################################ @@ -363,19 +376,3 @@ resource "aws_kms_key" "this" { resource "aws_network_interface" "this" { subnet_id = element(module.vpc.private_subnets, 0) } - -resource "aws_ec2_capacity_reservation" "open" { - instance_type = "t3.micro" - instance_platform = "Linux/UNIX" - availability_zone = "${local.region}a" - instance_count = 1 - instance_match_criteria = "open" -} - -resource "aws_ec2_capacity_reservation" "targeted" { - instance_type = "t3.micro" - instance_platform = "Linux/UNIX" - availability_zone = "${local.region}a" - instance_count = 1 - instance_match_criteria = "targeted" -} diff --git a/examples/complete/outputs.tf b/examples/complete/outputs.tf index e87dfe82..f99f56c1 100644 --- a/examples/complete/outputs.tf +++ b/examples/complete/outputs.tf @@ -44,6 +44,36 @@ output "ec2_complete_tags_all" { value = module.ec2_complete.tags_all } +output "ec2_complete_iam_role_name" { + description = "The name of the IAM role" + value = module.ec2_complete.iam_role_name +} + +output "ec2_complete_iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = module.ec2_complete.iam_role_arn +} + +output "ec2_complete_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.ec2_complete.iam_role_unique_id +} + +output "ec2_complete_iam_instance_profile_arn" { + description = "ARN assigned by AWS to the instance profile" + value = module.ec2_complete.iam_instance_profile_arn +} + +output "ec2_complete_iam_instance_profile_id" { + description = "Instance profile's ID" + value = module.ec2_complete.iam_instance_profile_id +} + +output "ec2_complete_iam_instance_profile_unique" { + description = "Stable and unique string identifying the IAM instance profile" + value = module.ec2_complete.iam_instance_profile_unique +} + # EC2 T2 Unlimited output "ec2_t2_unlimited_id" { description = "The ID of the instance" diff --git a/main.tf b/main.tf index b1c79a63..9a0d4a23 100644 --- a/main.tf +++ b/main.tf @@ -1,13 +1,25 @@ +data "aws_partition" "current" {} + locals { create = var.create && var.putin_khuylo is_t_instance_type = replace(var.instance_type, "/^t(2|3|3a){1}\\..*$/", "1") == "1" ? true : false } +data "aws_ssm_parameter" "this" { + count = local.create ? 1 : 0 + + name = var.ami_ssm_parameter +} + +################################################################################ +# Instance +################################################################################ + resource "aws_instance" "this" { count = local.create && !var.create_spot_instance ? 1 : 0 - ami = var.ami + ami = try(coalesce(var.ami, data.aws_ssm_parameter.this[0].value), null) instance_type = var.instance_type cpu_core_count = var.cpu_core_count cpu_threads_per_core = var.cpu_threads_per_core @@ -24,7 +36,7 @@ resource "aws_instance" "this" { key_name = var.key_name monitoring = var.monitoring get_password_data = var.get_password_data - iam_instance_profile = var.iam_instance_profile + iam_instance_profile = var.create_iam_instance_profile ? aws_iam_instance_profile.this[0].name : var.iam_instance_profile associate_public_ip_address = var.associate_public_ip_address private_ip = var.private_ip @@ -141,10 +153,14 @@ resource "aws_instance" "this" { volume_tags = var.enable_volume_tags ? merge({ "Name" = var.name }, var.volume_tags) : null } +################################################################################ +# Spot Instance +################################################################################ + resource "aws_spot_instance_request" "this" { count = local.create && var.create_spot_instance ? 1 : 0 - ami = var.ami + ami = try(coalesce(var.ami, data.aws_ssm_parameter.this[0].value), null) instance_type = var.instance_type cpu_core_count = var.cpu_core_count cpu_threads_per_core = var.cpu_threads_per_core @@ -161,7 +177,7 @@ resource "aws_spot_instance_request" "this" { key_name = var.key_name monitoring = var.monitoring get_password_data = var.get_password_data - iam_instance_profile = var.iam_instance_profile + iam_instance_profile = var.create_iam_instance_profile ? aws_iam_instance_profile.this[0].name : var.iam_instance_profile associate_public_ip_address = var.associate_public_ip_address private_ip = var.private_ip @@ -285,3 +301,63 @@ resource "aws_spot_instance_request" "this" { tags = merge({ "Name" = var.name }, var.tags) volume_tags = var.enable_volume_tags ? merge({ "Name" = var.name }, var.volume_tags) : null } + +################################################################################ +# IAM Role / Instance Profile +################################################################################ + +locals { + iam_role_name = try(coalesce(var.iam_role_name, var.name), "") +} + +data "aws_iam_policy_document" "assume_role_policy" { + count = var.create && var.create_iam_instance_profile ? 1 : 0 + + statement { + sid = "EC2AssumeRole" + actions = ["sts:AssumeRole"] + + principals { + type = "Service" + identifiers = ["ec2.${data.aws_partition.current.dns_suffix}"] + } + } +} + +resource "aws_iam_role" "this" { + count = var.create && var.create_iam_instance_profile ? 1 : 0 + + name = var.iam_role_use_name_prefix ? null : local.iam_role_name + name_prefix = var.iam_role_use_name_prefix ? "${local.iam_role_name}-" : null + path = var.iam_role_path + description = var.iam_role_description + + assume_role_policy = data.aws_iam_policy_document.assume_role_policy[0].json + permissions_boundary = var.iam_role_permissions_boundary + force_detach_policies = true + + tags = merge(var.tags, var.iam_role_tags) +} + +resource "aws_iam_role_policy_attachment" "this" { + for_each = { for k, v in var.iam_role_policies : k => v if var.create && var.create_iam_instance_profile } + + policy_arn = each.value + role = aws_iam_role.this[0].name +} + +resource "aws_iam_instance_profile" "this" { + count = var.create && var.create_iam_instance_profile ? 1 : 0 + + role = aws_iam_role.this[0].name + + name = var.iam_role_use_name_prefix ? null : local.iam_role_name + name_prefix = var.iam_role_use_name_prefix ? "${local.iam_role_name}-" : null + path = var.iam_role_path + + tags = merge(var.tags, var.iam_role_tags) + + lifecycle { + create_before_destroy = true + } +} diff --git a/outputs.tf b/outputs.tf index 0df1de42..45f42545 100644 --- a/outputs.tf +++ b/outputs.tf @@ -77,3 +77,37 @@ output "spot_instance_id" { description = "The Instance ID (if any) that is currently fulfilling the Spot Instance request" value = try(aws_spot_instance_request.this[0].spot_instance_id, "") } + +################################################################################ +# IAM Role / Instance Profile +################################################################################ + +output "iam_role_name" { + description = "The name of the IAM role" + value = try(aws_iam_role.this[0].name, null) +} + +output "iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = try(aws_iam_role.this[0].arn, null) +} + +output "iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = try(aws_iam_role.this[0].unique_id, null) +} + +output "iam_instance_profile_arn" { + description = "ARN assigned by AWS to the instance profile" + value = try(aws_iam_instance_profile.this[0].arn, null) +} + +output "iam_instance_profile_id" { + description = "Instance profile's ID" + value = try(aws_iam_instance_profile.this[0].id, null) +} + +output "iam_instance_profile_unique" { + description = "Stable and unique string identifying the IAM instance profile" + value = try(aws_iam_instance_profile.this[0].unique_id, null) +} diff --git a/variables.tf b/variables.tf index fbdb5b44..584a62d6 100644 --- a/variables.tf +++ b/variables.tf @@ -10,10 +10,16 @@ variable "name" { default = "" } +variable "ami_ssm_parameter" { + description = "SSM parameter name for the AMI ID. For Amazon Linux AMI SSM parameters see [reference](https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-public-parameters-ami.html)" + type = string + default = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" +} + variable "ami" { description = "ID of AMI to use for the instance" type = string - default = "" + default = null } variable "associate_public_ip_address" { @@ -316,3 +322,55 @@ variable "putin_khuylo" { type = bool default = true } + +################################################################################ +# IAM Role / Instance Profile +################################################################################ + +variable "create_iam_instance_profile" { + description = "Determines whether an IAM instance profile is created or to use an existing IAM instance profile" + type = bool + default = false +} + +variable "iam_role_name" { + description = "Name to use on IAM role created" + type = string + default = null +} + +variable "iam_role_use_name_prefix" { + description = "Determines whether the IAM role name (`iam_role_name` or `name`) is used as a prefix" + type = bool + default = true +} + +variable "iam_role_path" { + description = "IAM role path" + type = string + default = null +} + +variable "iam_role_description" { + description = "Description of the role" + type = string + default = null +} + +variable "iam_role_permissions_boundary" { + description = "ARN of the policy that is used to set the permissions boundary for the IAM role" + type = string + default = null +} + +variable "iam_role_policies" { + description = "Policies attached to the IAM role" + type = map(string) + default = {} +} + +variable "iam_role_tags" { + description = "A map of additional tags to add to the IAM role/profile created" + type = map(string) + default = {} +} diff --git a/wrappers/README.md b/wrappers/README.md index f1a51a5e..dbb2afd1 100644 --- a/wrappers/README.md +++ b/wrappers/README.md @@ -14,7 +14,7 @@ This wrapper does not implement any extra functionality. terraform { source = "tfr:///terraform-aws-modules/ec2-instance/aws//wrappers" # Alternative source: - # source = "git::git@github.com:terraform-aws-modules/terraform-aws-ec2-instance.git?ref=master//wrappers" + # source = "git::git@github.com:terraform-aws-modules/terraform-aws-ec2-instance.git//wrappers?ref=master" } inputs = { @@ -72,7 +72,7 @@ module "wrapper" { terraform { source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" # Alternative source: - # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git?ref=master//wrappers" + # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" } inputs = { diff --git a/wrappers/main.tf b/wrappers/main.tf index 35c42ccc..4b9237bf 100644 --- a/wrappers/main.tf +++ b/wrappers/main.tf @@ -5,7 +5,8 @@ module "wrapper" { create = try(each.value.create, var.defaults.create, true) name = try(each.value.name, var.defaults.name, "") - ami = try(each.value.ami, var.defaults.ami, "") + ami_ssm_parameter = try(each.value.ami_ssm_parameter, var.defaults.ami_ssm_parameter, "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2") + ami = try(each.value.ami, var.defaults.ami, null) associate_public_ip_address = try(each.value.associate_public_ip_address, var.defaults.associate_public_ip_address, null) availability_zone = try(each.value.availability_zone, var.defaults.availability_zone, null) capacity_reservation_specification = try(each.value.capacity_reservation_specification, var.defaults.capacity_reservation_specification, {}) @@ -56,4 +57,12 @@ module "wrapper" { spot_valid_from = try(each.value.spot_valid_from, var.defaults.spot_valid_from, null) disable_api_stop = try(each.value.disable_api_stop, var.defaults.disable_api_stop, null) putin_khuylo = try(each.value.putin_khuylo, var.defaults.putin_khuylo, true) + create_iam_instance_profile = try(each.value.create_iam_instance_profile, var.defaults.create_iam_instance_profile, false) + iam_role_name = try(each.value.iam_role_name, var.defaults.iam_role_name, null) + iam_role_use_name_prefix = try(each.value.iam_role_use_name_prefix, var.defaults.iam_role_use_name_prefix, true) + iam_role_path = try(each.value.iam_role_path, var.defaults.iam_role_path, null) + iam_role_description = try(each.value.iam_role_description, var.defaults.iam_role_description, null) + iam_role_permissions_boundary = try(each.value.iam_role_permissions_boundary, var.defaults.iam_role_permissions_boundary, null) + iam_role_policies = try(each.value.iam_role_policies, var.defaults.iam_role_policies, {}) + iam_role_tags = try(each.value.iam_role_tags, var.defaults.iam_role_tags, {}) }