Terraform Provider config inside child module - terraform

I’m trying to create modules that will handle helm deployments. the structure goes like this
root module - call helm-traefik module
there’s an input (cluster name) that will be used to fetch data sources for the provider config inside the helm child module.
child module - helm-traefik
main tf. - call helm module
variables.tf
values.yaml
child module - helm
providers.tf - both provider config for kubernetes and helm are using kubelogin for authentication
datasources.tf
main.tf - helm_release
variables.tf
The issue is that I’m getting an error with tf plan and it says that Kubernetes cluster is unreachable. I’ve been reading docs regarding providers and I think the reason why I’m getting errors is that I don’t have the provider config for Kubernetes and helm in the root module level. Any feasible solution for this use case? I want to have a separation between the helm module in a way it can be consumed regardless of the helm chart to be deployed.
Also, If I put the provider config from the child module to the root module, that would mean I need to create a provider config for each cluster I want to manage.
on helm - child module, this is how I generate the provider config
datasources.tf
locals {
# The purpose of the cluster.
purpose = split("-", "${var.cluster_name}")[0]
# The network environment of the cluster.
customer_env = split("-", "${var.cluster_name}")[2]
}
data "azurerm_kubernetes_cluster" "cluster" {
resource_group_name = "rg-${local.purpose}-${local.customer_env}-001"
name = "aks-${local.purpose}-${local.customer_env}"
}
provider.tf
provider "kubernetes" {
host = data.azurerm_kubernetes_cluster.cluster.kube_config.0.host
cluster_ca_certificate = base64decode(data.azurerm_kubernetes_cluster.cluster.kube_config.0.cluster_ca_certificate)
exec {
api_version = "client.authentication.k8s.io/v1beta1"
command = "kubelogin"
args = [
"get-token",
"--login", "spn",
"--environment", "AzurePublicCloud",
"--server-id", "6dae42f8-4368-4678-94ff-3960e28e3630",
"--tenant-id", data.azurerm_client_config.current.tenant_id,
"--client-id", data.azurerm_client_config.current.client_id,
"--client-secret", data.azurerm_key_vault_secret.service_principal_key.value,
]
}
}

Related

Terraform child module does not inherit provider from root module

My problem
I am having trouble defining the provider for my module.
Terraform fails to find the provider's plugin when I run terraform init and it shows the wrong provider for my module when I run terraform providers.
Setup
I am using Terraform version 1.3.7 on Debian 11.
Here's an example of what I am trying to do.
I have a main.tf where is my main configuration and modules. In this example I use a single module for creating a docker container.
.
├── main.tf
└── modules/
└── container_module/
└── main.tf
In the root module project/main.tf file, I define the provider and call the module:
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "3.0.1"
}
}
}
provider "docker" {
host = "unix:///var/run/docker.sock"
}
module "container" {
source = "./modules/container_module"
}
In the modules/container_module/main.tf, I create the docker container resource:
resource "docker_image" "debian" {
name = "debian:latest"
}
resource "docker_container" "foo" {
image = docker_image.debian.image_id
name = "foo"
}
What I expect to happen
When I run terraform init, it should download the provider's plugin from kreuzwerker/docker.
What actually happens
Instead, terraform downloads the plugin from kreuzwerker/docker once, then attempt to download it again from hashicorp/docker.
Here's the command's output:
terraform init
Initializing modules...
- container in modules/container_module
Initializing the backend...
Initializing provider plugins...
- Finding latest version of hashicorp/docker...
- Finding kreuzwerker/docker versions matching "3.0.1"...
- Installing kreuzwerker/docker v3.0.1...
- Installed kreuzwerker/docker v3.0.1 (self-signed, key ID BD080C4571C6104C)
Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html
╷
│ Error: Failed to query available provider packages
│
│ Could not retrieve the list of available versions for provider hashicorp/docker: provider registry registry.terraform.io does not have a provider named
│ registry.terraform.io/hashicorp/docker
│
│ Did you intend to use kreuzwerker/docker? If so, you must specify that source address in each module which requires that provider. To see which modules are currently depending on
│ hashicorp/docker, run the following command:
│ terraform providers
╵
When I run terraform providers I get two different sources depending on the file:
terraform providers
Providers required by configuration:
.
├── provider[registry.terraform.io/kreuzwerker/docker] 3.0.1
└── module.container
└── provider[registry.terraform.io/hashicorp/docker]
According to the documentation, the child modules should inherit the provider from their parent:
Default Behavior: Inherit Default Providers:
If the child module does not declare any configuration aliases, the providers argument is optional. If you omit it, a child module inherits all of the default provider configurations from its parent module. (Default provider configurations are ones that don't use the alias argument.)
I have already checked this
Do terraform modules need required_providers?
This answer confirms the provider inheritence.
Terraform provider's resources not available in other tf files:
This question didn't help.
Terraform, providers miss inherits on module
This answer to this similar question says that I should add required_providers in the child module, but it is for an older version and it contradicts what I saw elsewhere.
I have the same issue when I create a providers.tf file in the root directory.
My question
How should I declare my provider so that the child module can inherit the provider from the root module?
kreuzwerker/docker is not a hashicorp provider. Thus as explained here, you have to explicitly define required_providers in each module, as such providers are not inherited.

Did you intend to use mongodb/mongodbatlas? If so, you must specify that │ source address in each module which requires that provider

I have below in my provider.tf in the root directory
terraform {
required_version = ">= 1.0.5"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "= 3.8.0"
}
mongodbatlas = {
source = "mongodb/mongodbatlas"
}
}
}
I am getting below error in terraform init stage
Error: Failed to query available provider packages
│
│ Could not retrieve the list of available versions for provider
│ hashicorp/mongodbatlas: provider registry registry.terraform.io does not
│ have a provider named registry.terraform.io/hashicorp/mongodbatlas
│
│ Did you intend to use mongodb/mongodbatlas? If so, you must specify that
│ source address in each module which requires that provider. To see which
│ modules are currently depending on hashicorp/mongodbatlas, run the
│ following command:
│ terraform providers
After this error, I included below in my mongodb atlas child module too which needed it and it worked.
terraform {
required_providers {
mongodbatlas = {
source = "mongodb/mongodbatlas"
}
}
}
My question is why do I need to do this? I don't have to do it for any other required providers? Thanks in advance.
similar question is answered here - Terraform using both required_providers and provider blocks
gist - if the provider you are using is not one of the official provider by Terraform then you need to specify the terraform required_providers block to let terraform know the source to download the provider plugin.
LIST OF OFFICIAL PROVIDERS BY TERRAFORM -
https://registry.terraform.io/browse/providers?category=database%2Chashicorp%2Cinfrastructure-management%2Cpublic-cloud%2Casset%2Ccloud-automation%2Ccommunication-messaging%2Cvcs%2Cutility%2Cweb%2Csecurity-authentication%2Cplatform%2Clogging-monitoring%2Cnetworking%2Cinfrastructure%2Cdata-management%2Cci-cd%2Ccontainer-orchestration&tier=official
Just for visitors who are having the same error, follow the instructions
Check the message:
Did you intend to use mongodb/mongodbatlas? If so, you must specify
that source address in each module which requires that provider.
You need to put your MongoDB provider inside each module you're using.
Run terraform providers and check which modules require MongoDB.
Create a versions.tf file inside each module.
Add the following code (with your desired version):
terraform {
required_providers {
mongodbatlas = {
source = "mongodb/mongodbatlas",
version = "1.8.0"
}
}
}
About the need to put required_providers inside each module
Testing Atlas Provider Versions that are NOT hosted on Terraform Registry (i.e. pre-release versions)
To test development / pre-release versions of the Terraform Atlas
Provider that are not hosted on the Terraform Registry, you will need
to create a Terraform Provider Network Mirror.
The provider network mirror protocol is an optional protocol which you
can implement to provide an alternative installation source for
Terraform providers, regardless of their origin registries. Terraform
uses network mirrors only if you activate them explicitly in the CLI
configuration's provider_installation block. When enabled, a network
mirror can serve providers belonging to any registry hostname, which
can allow an organization to serve all of the Terraform providers they
intend to use from an internal server, rather than from each
provider's origin registry.
source: https://github.com/mongodb/terraform-provider-mongodbatlas

ArgoCD bootstrapping with terraform in Azure Pipeline

I am trying to deploy ArgoCD and applications located in subfolders through Terraform in an AKS cluster.
This is my Folder structure tree:
I'm using app of apps approach, so first I will deploy ArgoCD (this will manage itself as well) and later ArgoCD will let me SYNC the cluster-addons and application manually once installed.
apps
cluster-addons
AKV2K8S
Cert-Manager
Ingress-nginx
application
application-A
argocd
override-values.yaml
Chart
When I run the command "helm install ..." manually in the AKS cluster everything is installed fine.
ArgoCD is installed and later when I access ArgoCD I see that rest of applications are missing and I can sync them manually.
However, If I want to install it through Terraform only ArgoCD is installed but looks like it does not "detect" the override_values.yaml file:
i mean, ArgoCD and ArgoCD application set controller are installed in the cluster but ArgoCD does not "detect" the values.yaml files that are customized for my AKS cluster. If I run "helm install" manually on the cluster everything works but not through Terraform
resource "helm_release" "argocd_applicationset" {
name = "argocd-applicationset"
repository = https://argoproj.github.io/argo-helm
chart = "argocd-applicationset"
namespace = "argocd"
version = "1.11.0"
}
resource "helm_release" "argocd" {
name = "argocd"
repository = https://argoproj.github.io/argo-helm
chart = "argo-cd"
namespace = "argocd"
version = "3.33.6"
values = [
"${file("values.yaml")}"
]
values.yaml file is located in the folder where I have the TF code to install argocd and argocd applicationset.
I tried to change the name of the file" values.yaml" to "override_values.yaml" but same issue.
I have many things changed into the override_values.yaml file so I cannot use "set" inside the TF code...
Also, I tried adding:
values = [
"${yamlencode(file("values.yaml"))}"
]
but I get this error in "apply" step in the pipeline:
error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {} "argo-cd:\r\n ## ArgoCD configuration\r\n ## Ref: https://github.com/argoproj/argo-cd\r\n
Probably because is not a JSON file? It does make sense to convert this file into a JSON one?
Any idea if I can pass this override values yaml file through terraform?
If not, please may you post a clear/full example with mock variables on how to do that using Azure pipeline?
Thanks in advance!
The issue was with the values identation in TF code.
The issue was resolved when I resolve that:
resource "helm_release" "argocd_applicationset" {
name = "argocd-applicationset"
repository = https://argoproj.github.io/argo-helm
chart = "argocd-applicationset"
namespace = "argocd"
version = "1.11.0"
}
resource "helm_release" "argocd" {
name = "argocd"
repository = https://argoproj.github.io/argo-helm
chart = "argo-cd"
namespace = "argocd"
version = "3.33.6"
values = [file("values.yaml")]
It is working fine also with quoting.

Configured provider not recognized inside module

I am working on a Terraform configuration for a new project. The project consists of multiple microservices for which I've written modules. The project is supposed to be hosted on Digitalocean, so I installed the Terraform DigitalOcean Provider as a required provider:
# ./versions.tf
terraform {
required_version = ">= 0.14"
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "2.3.0"
}
}
}
which is then 'initialized' in ./main.tf:
provider "digitalocean" {
token = var.do_token
}
However, when I try to run terraform init it fails because of error Error: Failed to query available provider packages.
terraform providers reveals that this happens because a module tries to require provider hashicorp/digitalocean instead of digitalocean/digitalocean:
├── provider[registry.terraform.io/digitalocean/digitalocean] 2.3.0
├── provider[registry.terraform.io/hashicorp/kubernetes] 1.13.3
├── provider[registry.terraform.io/hashicorp/kubernetes-alpha] 0.2.1
└── module.spaces
└── provider[registry.terraform.io/hashicorp/digitalocean]
I have tried to pass a the provider through the providers option in the module, but that didn't seem to make a difference:
module "spaces" {
source = "./Spaces"
providers = {
digitalocean = digitalocean
}
}
Is this a bug within Terraform 0.14 perhaps, or am I just misunderstanding?
Thanks.
It's failing when it's used in a submodule because you only have the required_providers configuration at the top level, you need to add it in each module.
This is explained in detail in this thread
The provider source needs to be declared in each module because an
interesting side effect of the provider source work is that we can now
use multiple providers with the same name in configuration. Each
module could use a provider named 'dns' with a different source.
Internally, terraform uses the source to create a FQN (fully-qualified
name) for each provider, so you could have for example three modules,
each using one of "mildwonkey/dns" "hashicorp/dns" and "yourname/dns".

Azure AKS with agic - how to create it with Terraform?

I'm currently setting up an AGIC (Kubernetes Application Gateway Ingress Controller) for an AKS environment (https://azure.github.io/application-gateway-kubernetes-ingress/setup/install-existing/#using-a-service-principal).
As the whole environment is setup with Terraform, I'd like to install the necessary Helm repository also with Terraform.
Thought, the following simple code should do the trick:
data "helm_repository" "agic_repo" {
name = "agic_repository"
url = "https://appgwingress.blob.core.windows.net/ingress-azure-helm-package/"
}
resource "helm_release" "agic" {
name = "agic"
namespace = "agic"
repository = data.helm_repository.agic_repo.metadata[0].url
chart = "application-gateway-kubernetes-ingress"
depends_on = [
data.helm_repository.agic_repo,
]
}
But I ran into this issue:
module.agic.helm_release.agic: Creating...
Error: chart "application-gateway-kubernetes-ingress" not found in https://appgwingress.blob.core.windows.net/ingress-azure-helm-package/ repository
on ../../modules/agic/main.tf line 91, in resource "helm_release" "agic":
91: resource "helm_release" "agic" {
So it looks, as if the package cannot be found. Did anyone else solve this before?
I'm not familiar with Helm, so I don't know how to 'browse' within the Helm repos to check whether I'm addressing the right URI...
So I added the repo manually with
helm repo add application-gateway-kubernetes-ingress https://appgwingress.blob.core.windows.net/ingress-azure-helm-package/
When I search for the repo I receive:
V5T:~$ helm search | grep ingress
application-gateway-kubernetes-ingress/ingress-azure 1.0.0 1.0.0 Use Azure Application Gateway as the ingress for an Azure...
Any help appreciated!
P.S.: Sure, I could do it with a bash one-liner, but would be great to have the whole environment created by Terraform...
According to the data you provided it has to be this:
resource "helm_release" "agic" {
name = "agic"
namespace = "agic"
repository = data.helm_repository.agic_repo.metadata[0].url
chart = "ingress-azure"
depends_on = [
data.helm_repository.agic_repo,
]
}
so the chart name is different

Resources