Bootstrap your GitOps-enabled AKS cluster with Terraform: A code sample using the Flux v2 K8s Extension
In my previous posts, we learned how to get started with GitOps on AKS using the K8s extension for AKS.
Then, we took a look at the Flux CLI and explored how it can be used to bootstrap your cluster and generate FluxCD manifests so that we can use GitOps to implement GitOps 🤯, and implemented Flux’s image update automation capability.
From there, we built on the concept of image update automation, and showed you how you can use Flagger to automate canary deployments.
Continue readingStreamline Network Observability on AKS: A Step-by-Step Guide to enable the AKS add-on with Terraform
Have you ever had to troubleshoot network issues in your Kubernetes clusters? If so, you know how challenging it can be to identify and resolve problems.
To troubleshoot network issues you probably had to use a combination of tools like kubectl, tcpdump, wireshark, and netstat. The list goes on and on… While these tools are great for debugging and capturing network logs and traces, they don’t provide a holistic view of your cluster’s network traffic.
Monitoring Azure Container Apps With Azure Managed Grafana
The Azure Monitor team has announced the general availability of the Azure Managed Grafana (AMG) service. As part of the announcement, they also announced the availability of curated Grafana dashboards for various Azure services including Azure Container Apps 🎉
Grafana is very popular within the Cloud Native community and it seems natural to use it for Azure Container Apps (ACA) observability.
In this post, I will walk you through provisioning the ACA and AMG resources using the Terraform AzAPI provider and show you how easy it is to import the ACA dashboards into your AMG instance.
Continue readingTerraform: Azure Container Apps with Azure Managed Grafana using the AzAPI provider
Code snippet for the Monitoring Azure Container Apps With Azure Managed Grafana article.
main.tf
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">=3.0.0"
}
azapi = {
source = "azure/azapi"
version = ">=0.5.0"
}
}
}
provider "azurerm" {
features {
resource_group {
prevent_deletion_if_contains_resources = false
}
key_vault {
purge_soft_delete_on_destroy = false
}
}
}
resource "random_pet" "aca" {
length = 2
separator = ""
}
resource "random_integer" "aca" {
min = 000
max = 999
}
resource "random_string" "aca" {
length = 5
lower = true
upper = false
numeric = true
special = false
keepers = {
# Generate a new random_string on every run to avoid a conflict with the previous revision
none = timestamp()
}
}
locals {
resource_name = format("%s", random_pet.aca.id)
resource_name_unique = format("%s%s", random_pet.aca.id, random_integer.aca.result)
location = "eastus"
}
resource "azurerm_resource_group" "aca" {
name = "rg-${local.resource_name}"
location = local.location
}
resource "azurerm_log_analytics_workspace" "aca" {
name = "law-${local.resource_name_unique}"
resource_group_name = azurerm_resource_group.aca.name
location = azurerm_resource_group.aca.location
sku = "PerGB2018"
retention_in_days = 30
}
# https://docs.microsoft.com//azure/templates/microsoft.app/2022-03-01/managedenvironments?tabs=bicep&pivots=deployment-language-terraform
resource "azapi_resource" "env" {
type = "Microsoft.App/managedEnvironments@2022-03-01"
name = "env-${local.resource_name}"
parent_id = azurerm_resource_group.aca.id
location = azurerm_resource_group.aca.location
body = jsonencode({
properties = {
appLogsConfiguration = {
destination = "log-analytics"
logAnalyticsConfiguration = {
customerId = azurerm_log_analytics_workspace.aca.workspace_id
sharedKey = azurerm_log_analytics_workspace.aca.primary_shared_key
}
}
}
})
}
resource "azapi_resource" "helloworld" {
type = "Microsoft.App/containerApps@2022-03-01"
name = "helloworld"
parent_id = azurerm_resource_group.aca.id
location = azurerm_resource_group.aca.location
body = jsonencode({
properties = {
managedEnvironmentId = azapi_resource.env.id
configuration = {
ingress = {
allowInsecure = false
external = true
targetPort = 80
traffic = [
{
label = "dev"
latestRevision = true
weight = 100
}
]
}
}
template = {
containers = [
{
name = "helloworld"
image = "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest"
resources = {
cpu = 0.5
memory = "1.0Gi"
}
}
]
revisionSuffix = random_string.aca.result
scale = {
minReplicas = 0
maxReplicas = 30
rules = [
{
name = "http-rule"
http = {
metadata = {
concurrentRequests = "100"
}
}
}
]
}
}
}
})
# this tells azapi to pull out properties and stuff into the output attribute for the object
response_export_values = ["properties.configuration.ingress.fqdn"]
}
data "azurerm_subscription" "aca" {}
data "azurerm_client_config" "aca" {}
resource "azapi_resource" "amg" {
type = "Microsoft.Dashboard/grafana@2022-08-01"
name = "amg-${local.resource_name}"
parent_id = azurerm_resource_group.aca.id
location = azurerm_resource_group.aca.location
identity {
type = "SystemAssigned"
}
body = jsonencode({
properties = {
apiKey = "Enabled"
autoGeneratedDomainNameLabelScope = "TenantReuse"
deterministicOutboundIP = "Enabled"
publicNetworkAccess = "Enabled"
zoneRedundancy = "Disabled"
}
sku = {
name = "Standard"
}
})
# this tells azapi to pull out properties and stuff into the output attribute for the object
response_export_values = ["identity.principalId"]
}
resource "azurerm_role_assignment" "amg_reader" {
scope = data.azurerm_subscription.aca.id
role_definition_name = "Monitoring Reader"
principal_id = jsondecode(azapi_resource.amg.output).identity.principalId
}
resource "azurerm_role_assignment" "amg_admin" {
scope = azapi_resource.amg.id
role_definition_name = "Grafana Admin"
principal_id = data.azurerm_client_config.aca.object_id
}
output "helloworld_ingress_url" {
value = format("%s%s", "https://", jsondecode(azapi_resource.helloworld.output).properties.configuration.ingress.fqdn)
}
# optional to automate the dashboard import process
resource "null_resource" "aca" {
provisioner "local-exec" {
command = <<-EOT
az config set extension.use_dynamic_install=yes_without_prompt
az grafana dashboard import -g ${azurerm_resource_group.aca.name} -n ${azapi_resource.amg.name} --definition 16592
EOT
}
}
Resource References
Run Terraform With Azure Pipelines
This repo will walk you through an approach to provisioning Azure resources using Terraform code stored in a Git repo and leverage Azure Pipelines (YAML-based) to deploy to dev, test, and prod environments (all in different subscriptions) with approval gates in front of test and prod environments.
Prerequisites
Azure Subscription
If you don’t have Azure, go sign up for a free account and come back.
Azure DevOps
If you do not already have an Azure DevOps organization, follow these instructions to create one.
Continue readingGetting Started With Terraform on Azure
Check out the official Get Started - Azure tutorial here
Terraform basics
Download and Install
Download terraform from https://www.terraform.io/downloads.html
Extract the executable and add the folder to your PATH variable
Open a shell and type
terraform versionThe basic commands we’ll use are:
terraform initterraform planterraform applyterraform destroy
I advise you to use the
terraform fmtcommand often to ensure your scripts well formatted according to HashiCorp’s style conventions
Folder structure
Terrafrom executes on the files within a folder that are named with the .tf extension. Here is what a typical folder structure looks like: