Kubernetes secret with Flux and Terraform - kubernetes

I am new to terraform and devops in general. First I need to get ssh key from url to known host to later use for Flux.
data "helm_repository" "fluxcd" {
name = "fluxcd"
url = "https://charts.fluxcd.io"
}
resource "helm_release" "flux" {
name = "flux"
namespace = "flux"
repository = data.helm_repository.fluxcd.metadata[0].name
chart = "flux"
set {
name = "git.url"
value = "git.project"
}
set {
name = "git.secretName"
value = "flux-git-deploy"
}
set {
name = "syncGarbageCollection.enabled"
value = true
}
set_string {
name = "ssh.known_hosts"
value = Need this value from url
}
}
Then I need to generate key and use it to create kubernetes secret to communicate with gitlab repository.
resource "kubernetes_secret" "flux-git-deploy" {
metadata {
name = "flux-git-deploy"
namespace = "flux"
}
type = "Opaque"
data = {
identity = tls_private_key.flux.private_key_pem
}
}
resource "gitlab_deploy_key" "flux_deploy_key" {
title = "Title"
project = "ProjectID"
key = tls_private_key.flux.public_key_openssh
can_push = true
}
I am not sure if I am on the right track. Any advice will help.

There are few approaches you could use. These can be divided into "two categories":
generate manually the ssh_known_hosts and use the output through variables or files
create the file on the machine where you're running terraform with the command ssh-keyscan <git_domain> and set the path as value for ssh.known_hosts.
You can also use the file function directly in the variable or use the file output directly as env variable. Personally I would not recommend it because the value is saved directly in the terraform state but in this case it is not a critical issue. Critical would be if you're using ssh_keys or credentials.
Another approach would be to use the local-exec provisioner with a null_resource before you create the helm resource for flux and create the file directly in terraform. But additional to that you have to take care of accessing the file you created and also managing the triggers to run the command if a setting is changed.
In general, I would not use terraform for such things. It is fine to provide infrastructure like aws resources or services which are directly bound to the infrastructure but in order to create and run services you need a provisioning tool like ansible where you can run commands like "ssh-keyscan" directly as module. At the end you need a stable pipeline where you run ansible (or your favorite provisioning tool) after a terraform change.
But if you want to use only terraform you're going to right way.

Related

Is there a way to modularize parameter preparation with Bicep?

We have a bunch of naming- and ip-rules with a lot of resource groups to be created for dev, test and production. Because of the rules, we can calculate the names and ip ranges from two parameters: the stage to deploy to and the name of the service implemented by the resource group.
Example:
a service named "calculator" that includes a storage account and a vnet for stage "test" in a resource group
using
azure naming conventions
"xyz" as the abbreviation for our company
service name
additional specifier
10.1.0.0/16 for TEST
10.x.2.0/24 for "calculator"
we get:
resource group "rg-calculator-test"
storage account name = saxyzcalculatortest001
vnet = vnet-xyz-calculator-test 10.1.2.0/24
sub net 1 name = snet-xyz-calculator-test 10.1.2.0/26
sub net 2 name = snet-xyz-calculator-test-pep 10.1.2.128/28
To enforce consistency, I would like to share the calculation of all such names and addresses between all resource group deployment scripts.
Is there a way similar to "class files" or "includes" that I can use to include such a variable calculation into multiple .bicep files, so that I only need to pass a minimum set of variables (service name + stage) and the rest can be calculated in a consistent way without massive copy and paste of all those "var ... = ((stage == 'dev') ? '...' : (stage == 'test') ? '...' : '...')"?
I already did serch the documentation (maybe I simply did not recognize the Bicep-"thing" that can modularize the scripts in a way I need). What I was expecting was some synthak like
method CalculateDefaults 'modules/calculateDefaults.bicep' = {
name: 'CalculateDefaults'
params: {
stage: stage
serviceName: 'Calculator'
}
}
which produces outputs that I can use like
resource virtualNetwork 'Microsoft.Network/virtualNetworks#2021-02-01' = {
name: virtual_network_name
location: location
tags: tags
properties: {
addressSpace: {
addressPrefixes: [
CalculateDefaults.outputs.vnet_address_prefix
]
}

Is it possible to fetch the image tag from a deployment in EKS using terraform kubernetes provider?

Context:
I'm reusing terraform modules and I deploy microservices using helm provider within terraform.
Problem:
I'm trying to translate this line into terraform code, to get the current image tag live from prod (in the interest of reusing it). I'm already using kubernetes provider's auth and doesn't make sense to pull kubectl in my CI just for this.
k get deploy my-deployment -n staging -o jsonpath='{$.spec.template.spec.containers[:1].image}'
Kubernetes terraform provider doesn't seem to support data blocks nor helm provider outputs blocks.
Does anyone know how could we get (read) the image tag of a deployment using terraform?
EDIT:
My deployment looks like this:
resource "helm_release" "example" {
name = "my-redis-release"
repository = "https://charts.bitnami.com/bitnami"
chart = "redis"
version = "6.0.1"
values = [
"${file("values.yaml")}"
]
set {
name = "image.tag"
value = "latest"
}
}
The tag will be a hash that will change often and passed on from another repo.
latest in this case should be replaced by the current running tag in the cluster. I can get it using kubectl, using the line above, but not sure how using terraform.
It turns out there are multiple ways of doing it, where the easiest one for me is to reference the set argument of the helm_release resource:
output "helm_image_tag" {
value = [ for setting in helm_release.example.set : setting.value if setting.name == "image.tag" ]
}
The output will then be a list where you can reference it in a shell script (or another scripting language):
+ helm_image_tag = [
+ "latest",
]
If the list format does not suit you, you can create a map output:
output "helm_image_tag" {
value = { for setting in helm_release.example.set : setting.name => setting.value if setting.name == "image.tag" }
}
This produces the following output:
+ helm_image_tag = {
+ "image.tag" = "latest"
}
By using terraform output helm_image_tag you can access this output value and decide what to do with it in the CI.

Creating build Variables for azure devops build pipeline from external file through Terraform

I am creating Azure devops build pipeline through terraform and want my build variables to get passed through an external file while the build gets created. The same terraform code will be used to create pipelines of different templates, so accordingly the variables will also change, so inorder to make it reusable for all pipelines i want my terraform codes to take the build pipelines variables from external file and create it. Can't go with variable groups as well. So if any other process please advice.
Any help or advice would be really appreciated.
resource "azuredevops_build_definition" "build" {
project_id = azuredevops_project.project.id
name = "Sample Build Definition"
path = "\\ExampleFolder"
ci_trigger {
use_yaml = true
}
variable {
name = "PipelineVariable"
value = "Go Microsoft!"
}
variable {
name = "PipelineSecret"
secret_value = "ZGV2cw"
is_secret = true
}
the variables that above passed should instead get passed from external file.
You just need to define terraform variables for the values you require and use them in the resource:
variable "buildinfo"{ type = string default = "my value" }
and in the resource you just use:
.... value = var.buildinfo ....
you can pass the variable value for each run with a tfvars file or in your pipeline variables if you use a CI
you could do it like this:
Terraform:
variable "PipelineVariable"" {
}
Then define variables in the Pipeline like TF_VAR_PipelineVariable or in any stage you call a file (example file: .env) with source .env:
.env:
export TF_VAR_PipelineVariable=CONTENT

Trigger multiple azure devops Pipelines in Parallel to create VM's on Azure using same Terraform module

I have terraform Module for example to create a VM on Azure and it works when I trigger the Pipeline.
But When I trigger the Pipeline twice it fails to create two VM's. How do I manipulate terraform State file ? Only way I can think of is two run multiple pipeline in different agents, does that work ?
What we have done is create terraform "common" modules (basically a subdirectory with tf files), which we source into a terraform environment multiple times with different parameters.
These we usually put into a list with a loop.
In your environments terraform:
locals {
azure_vms = [
{ name = "vm1", size = "Standard_B2s" },
{ name = "vm2", size = "Standard_B4s" }
]
}
module "my_azure_vm" {
source = "./common/my_azure_vm"
for_each = { for vm in local.azure_vms : vm.name => vm }
size = each.value.size
name = each.value.name
}
In common my_azure_vm, you can define inputs for size and name, then use those to create the VM's with your standard parameters.

Terraform - Pass in Variable to "Source" Parameter

I'm using Terraform in a modular fashion in order to build out my infrastructure. I do this by having a configuration file that calls in the different modules. I want to pass an infrastructure variable which picks up what tagged version of the Github repository the application should be building out. Most importantly I'm trying to figure out how to make a concatenation of a string happen in the "source" variable of the configuration file.
module "athenaelb" {
source = "${concat("git::https://github.com/ORG/REPONAME.git?ref=",var.infra_version)}"
aws_access_key = "${var.aws_access_key}"
aws_secret_key = "${var.aws_secret_key}"
aws_region = "${var.aws_region}"
availability_zones = "${var.availability_zones}"
subnet_id = "${var.subnet_id}"
security_group = "${var.athenaelb_security_group}"
branch_name = "${var.branch_name}"
env = "${var.env}"
sns_topic = "${var.sns_topic}"
s3_bucket = "${var.elb_s3_bucket}"
athena_elb_sns_topic = "${var.athena_elb_sns_topic}"
infra_version = "${var.infra_version}"
}
I want it to compile and for the source to look like this (for example): git::https://github.com/ORG/REPONAME.git?ref=v1
Anyone have any thoughts on how to make this work?
Thanks,
Keren
This is not possible currently in Terraform itself.
The only way to achieve something like this is to use a separate script to interact with the git repository that Terraform clones into a subdirectory of the .terraform/modules directory and switch it to a different tag depending on which version you need. This is non-ideal since Terraform organizes these into directories based on a hash of the module path, but if you can identify the module in question it is safe to run git checkout within these repositories as long as you do not run terraform get again afterwards.
For more details and discussion on this issue, see issue #1439 in Terraform's issue tracker, where this feature was requested.
You could use envsubst or python jinja and use these wrapper scripts in your pipeline deploy script to actually build the scripts from .envsubst and .jinja files before your terraform plan/apply
https://github.com/uvoo/process-templates/tree/main/scripts
I wish terraform would support this but my guess is they never will so just add some simple functions/files into deploy scripts which is usually the best way to deploy.