Toggle Azure VM creation in Terraform - azure-virtual-machine

I have to provision a Windows VM in Azure using terraform, only the condition is, VM(along with dependent resources, like VNET, NSG, PublicIP, etc) should be created for all other environments except DEV.
If I run terraform plan with below terraform code. I am getting this error.
##[error]Terraform command 'plan' failed with exit code '1'.: Missing resource instance key | Missing resource instance key | Missing resource instance key
[0m on main_infra_app.tf line 284, in resource "azurerm_network_interface" "network-interface":
284: subnet_id = "${[4mazurerm_subnet.snet[0m.id}"
[0m
Because azurerm_subnet.snet has "count" set, its attributes must be accessed
on specific instances.
For example, to correlate with indices of a referring resource, use:
azurerm_subnet.snet[count.index]
Terraform code:
resource "azurerm_virtual_network" "vnet-main" {
count = "${var.env == "dev" ? 0 : 1}"
name = "$var.name"
address_space = ["10.0.0.0/16"]
location = "$var.location"
resource_group_name = "${azurerm_resource_group.rg.name}"
}
#Create Public IPs
resource "azurerm_public_ip" "PublicIP" {
count = "${var.env == "dev" ? 0 : 1}"
name = "${var.ip}"
location = "${azurerm_resource_group.rg.location}"
resource_group_name = "${azurerm_resource_group.rg.name}"
allocation_method = "Static"
}
#Create Subnet
resource "azurerm_subnet" "snet" {
count = "${var.env == "dev" ? 0 : 1}"
name = "${var.subnet}"
resource_group_name = "${azurerm_resource_group.rg.name}"
virtual_network_name = "azurerm_virtual_network.vnet-main"
address_prefix = "10.0.2.0/24"
}
#Create Network Security Group
resource "azurerm_network_security_group" "NSG" {
count = "${var.env == "dev" ? 0 : 1}"
name = "${var.nsg}"
location = "${azurerm_resource_group.rg.location}"
resource_group_name = "${azurerm_resource_group.rg.name}"
security_rule {
...
}
security_rule {
...
}
}
#Create Network Interface
resource "azurerm_network_interface" "network-interface" {
count = "${var.env == "dev" ? 0 : 1}"
name = "${var.nic}"
location = "${azurerm_resource_group.rg.location}"
resource_group_name = "${azurerm_resource_group.rg.name}"
network_security_group_id = "${var.devops_stage == "dev" ? azurerm_network_security_group.NSG[count.index] : azurerm_network_security_group.NSG.id}"
ip_configuration {
name = "IP-Conf-1"
subnet_id = "${var.devops_stage == "dev" ? azurerm_subnet.snet[count.index] : azurerm_subnet.snet.id}"
private_ip_address_allocation = "Dynamic"
public_ip_address_id = "${var.devops_stage == "dev" ? azurerm_public_ip.PublicIP[count.index] : azurerm_public_ip.PublicIP.id}"
}
}
resource "azurerm_virtual_machine" "vm" {
count = "${var.env == "dev" ? 0 : 1}"
name = var.vm_name
location = "${azurerm_resource_group.rg.location}"
resource_group_name = "${azurerm_resource_group.rg.name}"
network_interface_ids = "${var.env == "dev" ? azurerm_network_interface.network-interface[count.index] : azurerm_network_interface.network-interface[count.index]}"
vm_size = "Standard_D13_v2"
..
..
..
}
Please guide me on this.

As I see from your Terraform code, you want to add the condition that if the variable env is matched with "dev" to judge create the VM and other resources or not. I think you only need to add the condition at the count, not any other places. So finally, the Terraform code should be like this:
resource "azurerm_virtual_network" "vnet-main" {
count = "${var.env == "dev" ? 0 : 1}"
name = "${var.name}"
address_space = ["10.0.0.0/16"]
location = "${azurerm_resource_group.rg.location}"
resource_group_name = "${azurerm_resource_group.rg.name}"
}
#Create Public IPs
resource "azurerm_public_ip" "PublicIP" {
count = "${var.env == "dev" ? 0 : 1}"
name = "${var.ip}"
location = "${azurerm_resource_group.rg.location}"
resource_group_name = "${azurerm_resource_group.rg.name}"
allocation_method = "Static"
}
#Create Subnet
resource "azurerm_subnet" "snet" {
count = "${var.env == "dev" ? 0 : 1}"
name = "${var.subnet}"
resource_group_name = "${azurerm_resource_group.rg.name}"
virtual_network_name = "${azurerm_virtual_network.vnet-main[count.index].name}"
address_prefix = "10.0.2.0/24"
}
#Create Network Security Group
resource "azurerm_network_security_group" "NSG" {
count = "${var.env == "dev" ? 0 : 1}"
name = "${var.nsg}"
location = "${azurerm_resource_group.rg.location}"
resource_group_name = "${azurerm_resource_group.rg.name}"
security_rule {
...
}
security_rule {
...
}
}
#Create Network Interface
resource "azurerm_network_interface" "network-interface" {
count = "${var.env == "dev" ? 0 : 1}"
name = "${var.nic}"
location = "${azurerm_resource_group.rg.location}"
resource_group_name = "${azurerm_resource_group.rg.name}"
network_security_group_id = "${azurerm_network_security_group.NSG[count.index].id}"
ip_configuration {
name = "IP-Conf-1"
subnet_id = "${azurerm_subnet.snet[count.index].id}"
private_ip_address_allocation = "Dynamic"
public_ip_address_id = "${azurerm_public_ip.PublicIP[count.index].id}"
}
}
resource "azurerm_virtual_machine" "vm" {
count = "${var.env == "dev" ? 0 : 1}"
name = var.vm_name
location = "${azurerm_resource_group.rg.location}"
resource_group_name = "${azurerm_resource_group.rg.name}"
network_interface_ids = "${azurerm_network_interface.network-interface[count.index].id}"
vm_size = "Standard_D13_v2"
..
..
..
}

Related

How to enable IF condition on terrafrom modules

I need to create TF CloudWatch Metrices only of the env is QAT and PROD. Currently using TF modules to create those in all env.
module "aws_cloudwatch_log_metric_filter" {
source = "https://github.com/modules.git//aws-cloudwatch-log-metric-filter"
log_group_name = "/aws/lambda/${var.lambda_name}"
pattern = "{$.message = \"---------------- Message ----------------\"}"
}
locals {
base_tags = {
environment = var.environment
}
}
Main resource where I am calling module.
resource "aws_cloudwatch_log_metric_filter" "log_metric" {
count = var.count
name = "Metric"
pattern = var.pattern
log_group_name = var.log_group_name
metric_transformation {
name = "name"
namespace = "namespace"
value = "1"
default_value = "0"
}
}
You could do this in the module:
resource "aws_cloudwatch_log_metric_filter" "log_metric" {
count = var.environment == "QAT" || var.environment == "PROD" ? 1 : 0
name = "Metric"
pattern = var.pattern
log_group_name = "${var.environment}-${var.log_group_name}"
metric_transformation {
name = "name"
namespace = "namespace"
value = "1"
default_value = "0"
}
}
The log_group_name = "${var.environment}-${var.log_group_name}" will ensure you don't get issues with the same log group name.
A simple but good solution :
locals {
cw_env = ["QUA", "PROD"]
}
resource "aws_cloudwatch_log_metric_filter" "log_metric" {
count = contains(local.cw_env, var.env) ? 1 : 0
name = "Metric"
pattern = var.pattern
log_group_name = var.log_group_name
metric_transformation {
name = "name"
namespace = "namespace"
value = "1"
default_value = "0"
}
}
Note you can also use it at the above level :
module "aws_cloudwatch_log_metric_filter" {
source = "https://github.com/modules.git//aws-cloudwatch-log-metric-filter"
count = contains(local.cw_env, local.base_tags.environment) ? 1 : 0
log_group_name = "/aws/lambda/${var.lambda_name}"
pattern = "{$.message = \"---------------- Message ----------------\"}"
}
locals {
base_tags = {
environment = var.environment
}
cw_env = ["QUA", "PROD"]
}

GCP Fetch Public IP address for compute instance created from google_compute_instance_template

How can I fetch public IP address for compute instances created by google_compute_instance_template using terraform.
google_compute_instance_template
There is no attribute to get name of the compute instances created . I can see only name_prefix which will be suffixed with some random string.
I was thinking of using google_compute_instance data source which as attribute network_interface.0.access_config.0.nat_ip , but it requires compute instance name.
eg.
data "google_compute_instance" "appserver" {
name = "primary-application-server"
zone = "us-central1-a"
}
Below is the code I am using , I am attaching compute instance group manager to the backend service of a load balancer.
I need the public ip of the instance for connecting to SQL database
resource "google_compute_region_instance_group_manager" "mig" {
project = var.project
name = "${var.name}-instance-group"
region = var.region
version {
instance_template = google_compute_instance_template.instance_template.id
name = "primary"
}
named_port {
name = "https"
port = 443
}
base_instance_name = "${var.name}-mig"
target_size = var.instance_count
}
resource "google_compute_instance_template" "instance_template" {
name_prefix = "${var.name}-instance"
project = var.project
machine_type = var.machine_type
labels = var.labels
tags = local.firewall_rules.target_tags
can_ip_forward = var.can_ip_forward
metadata_startup_script = file("${path.module}/${var.startup_script}")
region = var.region
#min_cpu_platform = var.min_cpu_platform
dynamic "disk" {
for_each = local.all_disks
content {
auto_delete = lookup(disk.value, "auto_delete", null)
boot = lookup(disk.value, "boot", null)
device_name = lookup(disk.value, "device_name", null)
disk_name = lookup(disk.value, "disk_name", null)
disk_size_gb = lookup(disk.value, "disk_size_gb", lookup(disk.value, "disk_type", null) == "local-ssd" ? "375" : null)
disk_type = lookup(disk.value, "disk_type", null)
interface = lookup(disk.value, "interface", lookup(disk.value, "disk_type", null) == "local-ssd" ? "NVME" : null)
mode = lookup(disk.value, "mode", null)
source = lookup(disk.value, "source", null)
source_image = lookup(disk.value, "source_image", null)
type = lookup(disk.value, "disk_type", null) == "local-ssd" ? "SCRATCH" : "PERSISTENT"
#labels = lookup(disk.value, "disk_labels", {})
dynamic "disk_encryption_key" {
for_each = compact([var.disk_encryption_key == null ? null : 1])
content {
kms_key_self_link = var.disk_encryption_key
}
}
}
}
dynamic "service_account" {
for_each = [var.service_account]
content {
email = lookup(service_account.value, "email", null)
scopes = lookup(service_account.value, "scopes", null)
}
}
dynamic "network_interface" {
for_each = var.network_interfaces
iterator = config
content {
network = config.value.network
subnetwork = "projects/${var.project}/regions/${var.region}/subnetworks/${config.value.subnetwork}"
network_ip = try(config.value.addresses.internal, null)
dynamic "access_config" {
for_each = config.value.nat ? [""] : []
content {
nat_ip = try(config.value.addresses.external, null)
}
}
dynamic "alias_ip_range" {
for_each = local.network_interface_options[config.key].alias_ips != null ? local.network_interface_options[config.key].alias_ips : {}
iterator = config_alias
content {
subnetwork_range_name = config_alias.key
ip_cidr_range = config_alias.value
}
}
nic_type = local.network_interface_options[config.key].nic_type
}
}
lifecycle {
create_before_destroy = "true"
}
# scheduling must have automatic_restart be false when preemptible is true.
scheduling {
automatic_restart = !var.options.preemptible
on_host_maintenance = local.on_host_maintenance
preemptible = var.options.preemptible
}
dynamic "shielded_instance_config" {
for_each = var.shielded_config != null ? [var.shielded_config] : []
iterator = config
content {
enable_secure_boot = config.value.enable_secure_boot
enable_vtpm = config.value.enable_vtpm
enable_integrity_monitoring = config.value.enable_integrity_monitoring
}
}
dynamic "confidential_instance_config" {
for_each = var.confidential_compute ? [""] : []
content {
enable_confidential_compute = true
}
}
I think this can be done by the following:
#Get the list of instances
data "google_compute_region_instance_group" "mig_data" {
name = google_compute_region_instance_group_manager.mig.name
region = var.region
}
#Get each instance data
data "google_compute_instance" "intance_data" {
count = length(data.google_compute_region_instance_group.mig_data.instances)
self_link = data.google_compute_region_instance_group.mig_data.instances[count.index].instance
}
#Print the data needed
output "public_ips" {
value = [
for instance in data.google_compute_instance.intance_data:
[
instance.name, instance.network_interface.0.access_config.0.nat_ip
]
]
}
Basically we need to get the list of instances created in the MIG using the data.google_compute_region_instance_group and then iterate over this list to get the data for each instance.
I'm using output just to print the IPs but of course you can use those values in any other resource if you want.

How to create iam-user module in terraform to cover 3 type of iam-user scenarios

Can you please help here on how to create iam-user module in terraform to cover 3 type of iam-user scenarios ?
PS: I don't want to create nested directory under modules/iam/iam-user/ to make each iam-user cases separately.
Following are the scenarios:
// Type 1
resource "aws_iam_user" "aws_iam_user_000" {
name = "user-000"
permissions_boundary = data.aws_iam_policy.permission_boundary.arn
}
resource "aws_iam_user_policy_attachment" "aws_iam_user_000" {
policy_arn = aws_iam_policy.s3_iam_policy.arn
user = aws_iam_user.aws_iam_user_000.name
}
// Type 2
resource "aws_iam_user" "aws_iam_user_001" {
path = "/"
for_each = toset(var.user_lists)
name = each.value
force_destroy = true
permissions_boundary = data.aws_iam_policy.permission_boundary.arn
}
resource "aws_iam_group" "aws_iam_group_001" {
name = "group-0001"
}
resource "aws_iam_user_group_membership" "group-membership" {
for_each = toset(var.user_lists)
user = aws_iam_user.aws_iam_user_001[each.value].name
groups = [aws_iam_group.aws_iam_group_001.name]
}
// Type 3
resource "aws_iam_user" "aws_iam_user_0002" {
name = "user-002"
tags = { "user_type" = "admin_account" }
permissions_boundary = data.aws_iam_policy.permission_boundary.arn
}
If I understand you correctly, you should be able to accomplish this using count and for_each with variables as below.
variables.tf
variable "is_admin" {
type = bool
default = false
}
variable "user_lists" {
type = list(any)
default = null
}
main.tf
// Type 1 and Type 3
resource "aws_iam_user" "this" {
count = var.user_lists == null ? 1 : 0
name = var.is_admin ? "user-000" : "user-002"
permissions_boundary = data.aws_iam_policy.permission_boundary.arn
tags = var.is_admin ? { "user_type" = "admin_account" } : null
}
resource "aws_iam_user_policy_attachment" "this" {
count = var.user_lists == null ? 1 : 0
policy_arn = aws_iam_policy.s3_iam_policy.arn
user = aws_iam_user.this[0].name
}
// Type 2
resource "aws_iam_user" "from_list" {
for_each = var.user_lists != null ? toset(var.user_lists) : []
path = "/"
name = each.value
force_destroy = true
permissions_boundary = data.aws_iam_policy.permission_boundary.arn
}
resource "aws_iam_group" "from_list" {
count = var.user_lists == null ? 1 : 0
name = "group-0001"
}
resource "aws_iam_user_group_membership" "this" {
for_each = var.user_lists != null ? toset(var.user_lists) : []
user = aws_iam_user.from_list[each.value].name
groups = [aws_iam_group.from_list[0].name]
}

How to add or remove access_config on terraform GCP with variable

How to add or remove block code access_config { } on terraform with GCP.
I have variable:
external_ip = false
if external IP is value false code:
resource "google_compute_instance_from_template" "default_name_index" {
name = "${length(var.instances[change_with_index].instance_backup_ip) == 1 ? var.instances[change_with_index].instance_backup_name : format("%s-%s", var.instances[change_with_index].instance_backup_name, count.index + 1)}"
count = length(var.instances[change_with_index].instance_backup_ip)
source_instance_template = "projects/${var.provider_project}/global/instanceTemplates/${replace(var.instances[change_with_index].instance_name, "-app-image", "")}-${var.release_version}"
network_interface {
network = var.instances[change_with_index].instance_network
subnetwork = var.instances[change_with_index].instance_subnetwork
network_ip = var.instances[change_with_index].instance_backup_ip[count.index]
}
}
if external_ip is value true code:
resource "google_compute_instance_from_template" "default_name_index" {
name = "${length(var.instances[change_with_index].instance_backup_ip) == 1 ? var.instances[change_with_index].instance_backup_name : format("%s-%s", var.instances[change_with_index].instance_backup_name, count.index + 1)}"
count = length(var.instances[change_with_index].instance_backup_ip)
source_instance_template = "projects/${var.provider_project}/global/instanceTemplates/${replace(var.instances[change_with_index].instance_name, "-app-image", "")}-${var.release_version}"
network_interface {
network = var.instances[change_with_index].instance_network
subnetwork = var.instances[change_with_index].instance_subnetwork
network_ip = var.instances[change_with_index].instance_backup_ip[count.index]
#access_config will add in here
access_config
{
}
}
}
thank u for helping me.
You can do this with dynamic blocks and for_each:
resource "google_compute_instance_from_template" "default_name_index" {
name = "${length(var.instances[change_with_index].instance_backup_ip) == 1 ? var.instances[change_with_index].instance_backup_name : format("%s-%s", var.instances[change_with_index].instance_backup_name, count.index + 1)}"
count = length(var.instances[change_with_index].instance_backup_ip)
source_instance_template = "projects/${var.provider_project}/global/instanceTemplates/${replace(var.instances[change_with_index].instance_name, "-app-image", "")}-${var.release_version}"
network_interface {
network = var.instances[change_with_index].instance_network
subnetwork = var.instances[change_with_index].instance_subnetwork
network_ip = var.instances[change_with_index].instance_backup_ip[count.index]
#access_config will add in here
dynamic "access_config"
{
for_each = external_ip == false ? [] : [1]
content {
// the normal content of access_config
}
}
}
}

Terraform GCP: The resource was not found for an external ip address

I'm trying to create the base VPC network on google cloud platform using terraform on which I could then launch instances, GKE etc, however I end up with the following error message:
Error: Error creating RouterNat: googleapi: Error 404: The resource 'projects/lol/regions/mars-north1/addresses/39.144.42.123' was not found, notFound
This is the minimal terraform code without variables which leads to this state:
module "gcp_vpc" {
count = var.project_type == "aws" ? 0 : 1
source = "terraform-google-modules/network/google"
version = "3.0.1"
project_id = var.project_id
network_name = "${var.project_name}-lol-vpc"
routing_mode = "GLOBAL"
subnets = [
{
subnet_name = "${var.project_name}-lol-primary-subnet-${count.index}"
subnet_ip = "10.222.0.0/28"
subnet_region = var.gcp_region
subnet_private_access = "true"
subnet_flow_logs = "true"
}
]
secondary_ranges = {
"${var.project_name}-lol-secondary-subnets" = [
{
count = length(var.public_subnets)
range_name = "${var.project_name}-lol-subnet-alias-${count.index}"
ip_cidr_range = var.public_subnets[count.index]
},
{
count = length(var.private_subnets)
range_name = "${var.project_name}-lol-private-subnet-${count.index}"
ip_cidr_range = var.private_subnets[count.index]
},
]
}
}
module "cloud-nat" {
count = var.project_type == "aws" ? 0 : 1
source = "terraform-google-modules/cloud-nat/google"
version = "~> 1.3"
project_id = var.project_id
region = var.gcp_region
router = "${var.project_name}-lol-router"
create_router = true
network = module.gcp_vpc[0].network_name
# subnetworks = module.gcp_vpc[0].subnets_self_links
nat_ips = google_compute_address.nat.*.address
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES"
min_ports_per_vm = "128"
icmp_idle_timeout_sec = "15"
tcp_established_idle_timeout_sec = "600"
tcp_transitory_idle_timeout_sec = "15"
udp_idle_timeout_sec = "15"
}
resource "google_compute_address" "nat" {
count = var.project_type == "aws" ? 0 : length(var.public_subnets)
name = "${var.project_name}-lol-eip-nat-${count.index}"
project = var.project_id
region = var.gcp_region
}
output "gcp_nat_ips" {
value = google_compute_address.nat.*.address
}
However, I can see the external ip address 39.144.42.123 on the GCP web console. Can someone please help with where should I be looking at to solve this issue? Is it terraform, the terraform provider or GCP?