3
votes

I am attempting to write automation to deploy instances in a shared VPC on GCP. I have a host network project and a service project. I can create a static internal IP address resource in the host project (resource "google_compute_address" "internal") in which I specify the VPC host project (NET_HUB_PROJ) but I am unable to use it when creating the instance. I receive the following error:

google_compute_instance.compute: Error creating instance: googleapi: 
Error 400: Invalid value for field 
'resource.networkInterfaces[0].networkIP': '10.128.0.10'. IP address 
'projects/prototype-network-hub/regions/us-central1/addresses/bh-int- 
ip' (10.128.0.10) is reserved by another project., invalid

My compute module:

data "google_compute_image" "image" {
  name    = "${var.IMAGE_NAME}"
  project = "${var.IMAGE_PROJECT}"
}

resource "google_compute_address" "internal" {
  name         = "${var.NAME}-int-ip"
  address_type = "INTERNAL"
  address      = "${var.PRIVATE_IP}"
  subnetwork   = "${var.NET_HUB_SUBNETWORK}"
  region       = "${var.NET_HUB_REGION}"
  project      = "${var.NET_HUB_PROJ}"
}

resource "google_compute_address" "external" {
  count        = "${var.EXT_IP_CREATE ? 1 : 0}"
  name         = "${var.NAME}-ext-ip"
  address_type = "EXTERNAL"
  region       = "${var.REGION}"
}

resource "google_compute_instance" "compute" {
   depends_on  = ["google_compute_address.external"] 
   name = "${var.NAME}"
   machine_type = "${var.MACHINE_TYPE}"
   zone = "${var.ZONE}"
   can_ip_forward = "${var.CAN_IP_FORWARD}"
   deletion_protection ="${var.DELETION_PROTECTION}"
   allow_stopping_for_update = "${var.ALLOW_STOPPING_FOR_UPDATE}"
   tags = ["allow-ssh"]
   metadata = {
    "network" = "${var.NETWORK}"
    "env" = "${var.ENV}"
    "role" = "${var.ROLE}"
    "region" = "${var.REGION}"
    "zone" = "${var.ZONE}"
   }
   labels = {
    "network" = "${var.NETWORK}"
    "env" = "${var.ENV}"
    "role" = "${var.ROLE}"
    "region" = "${var.REGION}"
    "zone" = "${var.ZONE}"
    }
   boot_disk {
      device_name = "${var.NAME}"
      auto_delete = "${var.BOOT_DISK_AUTO_DELETE}"
      initialize_params {
      size = "${var.BOOT_DISK_SIZE}"
      type = "${var.BOOT_DISK_TYPE}"
      image = "${data.google_compute_image.image.self_link}"
      }
   }

network_interface {
   network_ip = "${google_compute_address.internal.address}"
   subnetwork_project = "${var.NET_HUB_PROJ}"
   subnetwork   = "projects/prototype-network-hub/regions/us-central1/subnetworks/custom"
   access_config {
      nat_ip = "${element(concat(google_compute_address.external.*.address, list("")), 0)}"
   }
}
service_account {
   scopes = ["service-control", "service-management", "logging-write", "monitoring-write", "storage-ro", "https://www.googleapis.com/auth/trace.append" ]
   }
}

The end goal would be to accomplish the following: enter image description here

2
Have you checked that 10.128.0.10 was not used by another resource?norbjd
It is not being used by any other resource. I can confirm it has been created in the host networking project (prototype-network-hub) but not applied to any resource. The error msg alludes to that. It just specifies that it is reserved by the project. One interesting thing to note is using TF, I see an internal IP reservation: evernote.com/l/AfdR3FOkeFZPubjfrEWmcVA1II2uk8cU3qE, but when manually reserved as seen in the picture in my above post, it does not.glux

2 Answers

5
votes

EDIT (new answer): Per the GCP documentation, the static internal IP must belong to the service project (not the host network project as in your code) if you're looking to reserve internal IP on a shared VPC in a different project. See here: https://cloud.google.com/vpc/docs/provisioning-shared-vpc#reserve_internal_ip

Seeing as a shared-vpc is unlikely to be found in your TF codebase, you'll have to use data to get the self_link of the subnetwork to use for google_compute_address. Something like the following:

data "google_compute_subnetwork" "subnet" {
  name    = "${var.NET_HUB_SUBNETWORK}"
  project = "${var.NET_HUB_PROJ}"
  region  = "${var.NET_HUB_REGION}"
}

resource "google_compute_address" "internal" {
  name         = "${var.NAME}-int-ip"
  address_type = "INTERNAL"
  address      = "${var.PRIVATE_IP}"
  subnetwork   = "${data.google_compute_subnetwork.subnet.self_link}"
}

This should create the resource under your service project, yet with an address within the designated subnet.

When you deploy your instance you should see it referenced under the internal_ip column on your VM instances tab for the assigned instance.

(old answer for posterity): Unfortunately, google_compute_address doesn't contain a subnetwork_project like google_compute_instance. A fix around this is to provide a full URL to the subnetwork field in google_compute_address. Something like the following:

resource "google_compute_address" "internal" {
  name         = "${var.NAME}-int-ip"
  address_type = "INTERNAL"
  address      = "${var.PRIVATE_IP}"
  subnetwork   = "https://www.googleapis.com/compute/v1/projects/${var.NET_HUB_PROJ}/regions/${var.NET_HUB_REGION}/subnetworks/${var.NET_HUB_SUBNETWORK}"
}
1
votes

Adding my solution below:-


resource "google_compute_address" "internal_ip" {
  count        = 1
  name         = "${local.cluster_name}-int-ip-${count.index}"
  project      = <service project id>
  subnetwork   = <host project subnet self_link>
  address_type = "INTERNAL"
  region       = "asia-northeast1"
  purpose      = "GCE_ENDPOINT"
}

output "internal_ipaddr_info" {
  value = google_compute_address.internal_ip
}


resource "google_compute_instance" "test" {
  project        = module.gcp_service_project.project_id
  name           = "eps-gce-vm-d-swmgr-ane1-test"
  machine_type   = "n1-standard-1"
  zone           = "asia-northeast1-a"
  can_ip_forward = true
  boot_disk {
    initialize_params {
      image = "centos7"
    }
  }

  network_interface {
    subnetwork = <host project subnet self_link>
    network_ip = google_compute_address.internal_ip[0].self_link

  }

}