terraform azurerm_kubernetes_cluster nodes - azure-aks

I tied different approaches to get list of vm's of azurerm_kubernetes_cluster in terraform but with no success. There is number of possible elements in here: https://www.terraform.io/docs/providers/azurerm/d/kubernetes_cluster.html but not of them seams to allow getting list of VM's. Is there a way?

Ok I found way via subnet. To use this approach you need to have Kubernetes cluster created with advanced networking with your subnet which you know.
First section gets ip_configurations from subnet and extracts network interfaces names with ugly split.
data "null_data_source" "all_kubernetes_nic_name" {
count = "${length(azurerm_subnet.kubernetes.ip_configurations)}"
inputs {
nic = "${element(split("/", azurerm_subnet.kubernetes.ip_configurations[count.index]), 8)}"
}
}
Because of each kubernetes node acquiring number of ip addresses I need to distinct on previous list.
data "null_data_source" "kubernetes_nic_name" {
count = "${length(distinct(data.null_data_source.all_kubernetes_nic_name.*.outputs.nic))}"
inputs {
nic = "${element(distinct(data.null_data_source.all_kubernetes_nic_name.*.outputs.nic), count.index)}"
}
}
Then it's easy to get exact reference to network interface of each node in kubernetes cluster. Note resource_group_name being extracted directly from cluster object.
data "azurerm_network_interface" "kubernetes_nic" {
count = "${length(data.null_data_source.kubernetes_nic_name.*.outputs.nic)}"
name = "${data.null_data_source.kubernetes_nic_name.*.outputs.nic[count.index]}"
resource_group_name = "${azurerm_kubernetes_cluster.cluster.node_resource_group}"
}

Related

Terraform relation between docker-image and docker-container resource

I'm confused about the image property in the docker_container resource of a terraform tf file.
The following contents you see in all the tutorials:
resource "docker_image" "nginx" {
name = "nginx:latest"
keep_locally = false
}
resource "docker_container" "nginx" {
image = docker_image.nginx.latest
name = "tutorial"
ports {
internal = 80
external = 8000
}
}
This pulls the latest image.
But if you want a previous image, you need to specify the Digest, like this:
resource "docker_image" "nginx" {
name = "nginx:1.22.0#sha256:f2dfca5620b64b8e5986c1f3e145735ce6e291a7dc3cf133e0a460dca31aaf1f"
keep_locally = false
}
Then you need to create a container on that image with the docker_container Resource.
But there is no way you can specify that previous image tag or digest.
Things like image = docker_image.nginx:1.22.0#sha256:f2dfca5620b64b8e5986c1f3e145735ce6e291a7dc3cf133e0a460dca31aaf1f all fail. Various syntax versions are tried (quotes, no-quotes, etc, etc). They all result in an error.
The only way I could get this working was with the following:
resource "docker_image" "nginx" {
name = "nginx:1.22.0#sha256:f2dfca5620b64b8e5986c1f3e145735ce6e291a7dc3cf133e0a460dca31aaf1f"
keep_locally = false
}
resource "docker_container" "nginx" {
image = docker_image.nginx.latest
name = "Terraform-Nginx"
ports {
internal = 80
external = 8000
}
}
But now I'm confused.
What does ".latest" even mean in the image property of the docker_container resource?
There are a couple of things to understand here. The first being the resource arguments and the second being the resource attributes [1]. In most of the providers, when you want to create a certain resource, you have to provide values for at least the required arguments. There are optional arguments as well. When a resource is successfully created, it provides a set of attributes which can be referenced in another resource. In your example, you cannot create a Docker container without specifying the image name. So instead of hardcoding the image name in the container resource, you first define the image resource and then reference its arguments/attributes in the container resource. The example you mention:
image = docker_image.nginx:1.22.0#sha256:f2dfca5620b64b8e5986c1f3e145735ce6e291a7dc3cf133e0a460dca31aaf1f
is not valid because the Docker image resource provides no arguments and attributes named nginx:1.22.0#sha256:f2dfca5620b64b8e5986c1f3e145735ce6e291a7dc3cf133e0a460dca31aaf1f, i.e., the provider schema knows nothing about that. The provider documentation has sections on arguments and attributes. The Docker provider documentation is lacking some of the usual elements, but in this case, the attributes seem to be denoted with Read-only section [2]. There you will find the latest attribute that you use for referencing an image that was pulled. Also note that it says that the argument is deprecated:
latest (String, Deprecated) The ID of the image in the form of sha256:<hash> image digest. Do not confuse it with the default latest tag.
Based on the current documentation, you might want to use the following:
resource "docker_container" "nginx" {
image = docker_image.nginx.name
name = "tutorial"
ports {
internal = 80
external = 8000
}
}
The syntax used when referencing attributes/arguments is always:
<RESOURCE TYPE>.<NAME>.<ATTRIBUTE>
i.e., docker_image.nginx.latest or docker_iamge.nginx.name.
As a side note, to clear up any confusion, the way you are referencing the value for the image ID is called an implicit reference [3]. Since terraform creates resources in parallel, it helps when deciding the order in which the resources will be created. In this case the image will be pulled first and then the container will be created based on the image.
EDIT: updated the answer based on the input of #BertC.
[1] https://www.terraform.io/language/expressions/references#references-to-resource-attributes
[2] https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/image#read-only
[3] https://www.terraform.io/language/resources/behavior#resource-dependencies
In reaction to #MarkoE's answer :
Your answer showed to use the id string:
resource "docker_image" "nginx" {
name = "nginx:1.22.0#sha256:f2dfca5620b64b8e5986c1f3e145735ce6e291a7dc3cf133e0a460dca31aaf1f"
keep_locally = false
}
resource "docker_container" "nginx" {
image = docker_image.nginx.id
name = "Terraform-Nginx"
ports {
internal = 80
external = 8000
}
}
This gave me the following error:
docker_container.nginx: Creating...
╷
│ Error: Unable to create container with image sha256:1b84ed9be2d449f4242c521a3961fb417ecf773d2456477691f109aab3c5bb74nginx:1.22.0#sha256:f2dfca5620b64b8e5986c1f3e145735ce6e291a7dc3cf133e0a460dca31aaf1f: findImage1: error looking up local image
"sha256:1b84ed9be2d449f4242c521a3961fb417ecf773d2456477691f109aab3c5bb74nginx:1.22.0#sha256:f2dfca5620b64b8e5986c1f3e145735ce6e291a7dc3cf133e0a460dca31aaf1f": unable to inspect image
sha256:1b84ed9be2d449f4242c521a3961fb417ecf773d2456477691f109aab3c5bb74nginx:1.22.0#sha256:f2dfca5620b64b8e5986c1f3e145735ce6e291a7dc3cf133e0a460dca31aaf1f: Error response from daemon: no such image:
sha256:1b84ed9be2d449f4242c521a3961fb417ecf773d2456477691f109aab3c5bb74nginx:1.22.0#sha256:f2dfca5620b64b8e5986c1f3e145735ce6e291a7dc3cf133e0a460dca31aaf1f: invalid reference format
But from this link you provided I read:
The most common reference type is a reference to an attribute of a
resource which has been declared either with a resource or data block.
Which made me change the code to:
resource "docker_image" "nginx" {
name = "nginx:1.22.0#sha256:f2dfca5620b64b8e5986c1f3e145735ce6e291a7dc3cf133e0a460dca31aaf1f"
keep_locally = false
}
resource "docker_container" "nginx" {
image = docker_image.nginx.name
name = "Terraform-Nginx"
ports {
internal = 80
external = 8000
}
}
(Note the docker_image.nginx.name)
And now it works. I even get the right tag in the docker images command.
Tnanks Marko

How to fetch IP address of Jenkins Node When I know Computer name

I have the following code setup that fetches nodes instance for multiple nodes that are co-existing for our Jenkins setup:
Object nodes = Jenkins.getInstance().getLabel(label).getNodes()
for (int i = 0; i < nodes.size(); i++) {
Object computer = nodes[i].getComputer()
String nodeName = nodes[i].getNodeName()
}
Now inside the for loop I have computer and nodename available. But I need to know the IP address of each of these nodes. I am pretty new to groovy and Jenkins, so wanted to know if there is a method that I can call to directly get the IP address inside the for loop above.

Extending a resource created inside a module to avoid redundancy to set up a Docker Swarm with Terraform

Let's say I've a module which creates some resources that represent a default server. Now I want to inherit this default server to customize it into different directions.
At the Manager node which inherits the DockerNode I want to run docker swarm init and get the join token. On all Worker nodes I want to join with the token.
So in my main.tf where I use the DockerNode I have defined the nodes like this:
module "manager" {
source = "./modules/swarm-node"
node_network = {
network_id = hcloud_network.swarm-network.id
ip = "10.0.1.10"
}
node_name_prefix = "swarm-manager"
server = var.server
docker_compose_version = var.docker_compose_version
volume_size = var.volume_size
volume_filesystem = var.volume_filesystem
ssh_keys = [hcloud_ssh_key.ssh-key-a.name, hcloud_ssh_key.ssh-key-b.name]
depends_on = [
hcloud_network_subnet.swarm-network-nodes
]
}
module "worker" {
count = 2
source = "./modules/swarm-node"
node_index = count.index
node_network = {
network_id = hcloud_network.swarm-network.id
ip = "10.0.1.10${count.index}"
}
node_name_prefix = "swarm-worker"
server = var.server
docker_compose_version = var.docker_compose_version
volume_size = var.volume_size
volume_filesystem = var.volume_filesystem
ssh_keys = [hcloud_ssh_key.ssh-key-a.name, hcloud_ssh_key.ssh-key-b.name]
depends_on = [
hcloud_network_subnet.swarm-network-nodes,
module.manager
]
}
How to run docker swarm init and return the join token on the server resource inside of module.manager?
How to join the swarm with each worker?
I've researched this for quite a while:
Some solutions expose the Docker Daemon over TCP and access it from the worker to get the token. I don't like to expose the Docker Daemon unnecessarily.
Some solutions copy the base module (in my case DockerNode) just to modify one or two lines. I like to to follow DRY.
Some solutions have an additional shell script, which read the .tfstate and SSH into each machine to do further customization. I would like to use Terraform for this with all it's benefits.

How to control whether a resource exists in Terraform?

I am building with Terraform a Cloud Scheduler job that will hit a service deployed in Cloud Run. Because the service and the scheduler are deployed in different pipelines, it is possible that the service does not exist yet when running the scheduler pipeline. This is why I am using data "google_cloud_run_service" to retrieve the service and then control whether it exists in the scheduler block. It is here that I don't find the right syntax to use in count.
data "google_cloud_run_service" "run-service" {
name = "serv-${var.project_env}"
location = var.region
}
resource "google_cloud_scheduler_job" "job" {
count = length(data.google_cloud_run_service.run-service.status)
name = "snap-job-${var.project_env}"
description = "Call write API on my service"
schedule = "* * * * *"
time_zone = "Etc/UTC"
http_target {
http_method = "GET"
uri = "${data.google_cloud_run_service.run-service.status[0].url}/write"
oidc_token {
service_account_email = google_service_account.sa_scheduler.email
}
}
depends_on = [google_app_engine_application.app]
}
The above length(data.google_cloud_run_service.run-service.status) control will not make any effect, and Terraform tries to create the scheduler even though there is no service defined.
I have also tried other variations with similar result such as length(data.google_cloud_run_service.run-service.status[0]) > 0 ? 1 : 0.
Other options that I tried will not work either with different errors:
data.google_cloud_run_service.run-service ? 1 : 0: data.google_cloud_run_service.run-service is object with 9 attributes;The condition expression must be of type bool
data.google_cloud_run_service.run-service.status[0].url ? 1 : 0: data.google_cloud_run_service.run-service is object with 9 attributes;The condition expression must be of type bool

How do you cache database queries with Google Cloud Run?

I have a server on GCR and it pings a db when called. I was thinking of just adding a simple mechanism for cacheing like
var lastDBUpdate int
var lastCache int
if lastDBUpdate > lastCache {
lastCache = now
return newResults
} else {
return cachedResults
}
// endpoints that modify the db update the lastDBUpdate global var
This would work if there was only one container (i.e. while my backend has little load), but as my app grows and multiple containers are created, the lastDBUpdate and lastCache variables will be out of sync amongst the different containers. So how can I cache db reads with GCR?
You can use Memorystore.
Here is a guide how to connect to a Redis instance from Cloud Run.

Resources