<p>I'm after an example that would do the following:</p>

<ol>
<li>Create a Kubernetes cluster on GKE via Terraform's <a href="https://www.terraform.io/docs/providers/google/r/container_cluster.html" rel="nofollow noreferrer"><code>google_container_cluster</code></a></li>
<li>... and continue creating namespaces in it, I suppose via <a href="https://www.terraform.io/docs/providers/kubernetes/r/namespace.html" rel="nofollow noreferrer"><code>kubernetes_namespace</code></a></li>
</ol>

<p>The thing I'm not sure about is how to connect the newly created cluster and the namespace definition. For example, when adding <code>google_container_node_pool</code>, I can do something like <code>cluster = "${google_container_cluster.hosting.name}"</code> but I don't see anything similar for <code>kubernetes_namespace</code>.</p>

<p>In theory it is possible to reference resources from the GCP provider in K8S (or any other) provider in the same way you'd reference resources or data sources within the context of a single provider.</p>

<pre><code>provider "google" {
  region = "us-west1"
}

data "google_compute_zones" "available" {}

resource "google_container_cluster" "primary" {
  name = "the-only-marcellus-wallace"
  zone = "${data.google_compute_zones.available.names[0]}"
  initial_node_count = 3

  additional_zones = [
    "${data.google_compute_zones.available.names[1]}"
  ]

  master_auth {
    username = "mr.yoda"
    password = "adoy.rm"
  }

  node_config {
    oauth_scopes = [
      "https://www.googleapis.com/auth/compute",
      "https://www.googleapis.com/auth/devstorage.read_only",
      "https://www.googleapis.com/auth/logging.write",
      "https://www.googleapis.com/auth/monitoring"
    ]
  }
}

provider "kubernetes" {
  host = "https://${google_container_cluster.primary.endpoint}"
  username = "${google_container_cluster.primary.master_auth.0.username}"
  password = "${google_container_cluster.primary.master_auth.0.password}"
  client_certificate = "${base64decode(google_container_cluster.primary.master_auth.0.client_certificate)}"
  client_key = "${base64decode(google_container_cluster.primary.master_auth.0.client_key)}"
  cluster_ca_certificate = "${base64decode(google_container_cluster.primary.master_auth.0.cluster_ca_certificate)}"
}

resource "kubernetes_namespace" "n" {
  metadata {
    name = "blablah"
  }
}
</code></pre>

<p>However in practice it may not work as expected due to a known core bug breaking cross-provider dependencies, see <a href="https://github.com/hashicorp/terraform/issues/12393" rel="noreferrer">https://github.com/hashicorp/terraform/issues/12393</a> and <a href="https://github.com/hashicorp/terraform/issues/4149" rel="noreferrer">https://github.com/hashicorp/terraform/issues/4149</a> respectively.</p>

<p>The alternative solution would be:</p>

<ol>
<li>Use 2-staged apply and <a href="https://www.terraform.io/docs/commands/plan.html#resource-targeting" rel="noreferrer">target</a> the GKE cluster first, then anything else that depends on it, i.e. <code>terraform apply -target=google_container_cluster.primary</code> and then <code>terraform apply</code></li>
<li>Separate out GKE cluster config from K8S configs, give them completely isolated workflow and connect those via <a href="https://www.terraform.io/docs/state/remote.html" rel="noreferrer">remote state</a>.</li>
</ol>

<p><strong><code>/terraform-gke/main.tf</code></strong></p>

<pre><code>terraform {
  backend "gcs" {
    bucket  = "tf-state-prod"
    prefix  = "terraform/state"
  }
}

provider "google" {
  region = "us-west1"
}

data "google_compute_zones" "available" {}

resource "google_container_cluster" "primary" {
  name = "the-only-marcellus-wallace"
  zone = "${data.google_compute_zones.available.names[0]}"
  initial_node_count = 3

  additional_zones = [
    "${data.google_compute_zones.available.names[1]}"
  ]

  master_auth {
    username = "mr.yoda"
    password = "adoy.rm"
  }

  node_config {
    oauth_scopes = [
      "https://www.googleapis.com/auth/compute",
      "https://www.googleapis.com/auth/devstorage.read_only",
      "https://www.googleapis.com/auth/logging.write",
      "https://www.googleapis.com/auth/monitoring"
    ]
  }
}

output "gke_host" {
  value = "https://${google_container_cluster.primary.endpoint}"
}

output "gke_username" {
  value = "${google_container_cluster.primary.master_auth.0.username}"
}

output "gke_password" {
  value = "${google_container_cluster.primary.master_auth.0.password}"
}

output "gke_client_certificate" {
  value = "${base64decode(google_container_cluster.primary.master_auth.0.client_certificate)}"
}

output "gke_client_key" {
  value = "${base64decode(google_container_cluster.primary.master_auth.0.client_key)}"
}

output "gke_cluster_ca_certificate" {
  value = "${base64decode(google_container_cluster.primary.master_auth.0.cluster_ca_certificate)}"
}
</code></pre>

<p>Here we're exposing all the necessary configuration via <code>output</code>s and use backend to store the state, along with these outputs in a remote location, <a href="https://www.terraform.io/docs/backends/types/gcs.html" rel="noreferrer">GCS</a> in this case. This enables us to reference it in the config below.</p>

<p><strong><code>/terraform-k8s/main.tf</code></strong></p>

<pre><code>data "terraform_remote_state" "foo" {
  backend = "gcs"
  config {
    bucket  = "tf-state-prod"
    prefix  = "terraform/state"
  }
}

provider "kubernetes" {
  host = "https://${data.terraform_remote_state.foo.gke_host}"
  username = "${data.terraform_remote_state.foo.gke_username}"
  password = "${data.terraform_remote_state.foo.gke_password}"
  client_certificate = "${base64decode(data.terraform_remote_state.foo.gke_client_certificate)}"
  client_key = "${base64decode(data.terraform_remote_state.foo.gke_client_key)}"
  cluster_ca_certificate = "${base64decode(data.terraform_remote_state.foo.gke_cluster_ca_certificate)}"
}

resource "kubernetes_namespace" "n" {
  metadata {
    name = "blablah"
  }
}
</code></pre>

<p>What may or may not be obvious here is that cluster has to be created/updated before creating/updating any K8S resources (if such update relies on updates of the cluster).</p>

<hr>

<p>Taking the 2nd approach is generally advisable either way (even when/if the bug was not a factor and cross-provider references worked) as it reduces the blast radius and defines much clearer responsibility. It's (IMO) common for such deployment to have 1 person/team responsible for managing the cluster and a different one for managing K8S resources.</p>

<p>There may certainly be overlaps though - e.g. ops wanting to deploy logging &amp; monitoring infrastructure on top of a fresh GKE cluster, so cross provider dependencies aim to satisfy such use cases. For that reason I'd recommend subscribing to the GH issues mentioned above.</p>
