Skip to content

Instantly share code, notes, and snippets.

@brentmcconnell
Last active February 23, 2022 07:06
Show Gist options
  • Save brentmcconnell/a8769f1e3a4010c71e1cd764887e6325 to your computer and use it in GitHub Desktop.
Save brentmcconnell/a8769f1e3a4010c71e1cd764887e6325 to your computer and use it in GitHub Desktop.
ExternalDNS on AKS
#!/bin/bash
set -e
K8S_CONTEXT=$(kubectl config current-context)
echo -e "K8S_CONTEXT: $K8S_CONTEXT\n"
# Verify if we want to proceed
read -p "Are you sure you want to install cert-manager [y/N]?"
if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then
exit
fi
##################
# Setup Cert-Manager #
##################
kubectl apply \
--validate=false \
-f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.13/deploy/manifests/00-crds.yaml
# Create namespace
kubectl create namespace cert-manager
# Label the ingress namespace to disable resource validation
kubectl label namespace ingress cert-manager.io/disable-validation=true
# Label the cert-manager namespace to disable resource validation
kubectl label namespace cert-manager certmanager.k8s.io/disable-validation=true
# Add the Jetstack Helm repository
helm repo add jetstack https://charts.jetstack.io
# Update your local Helm chart repository cache
helm repo update
# Install the cert-manager Helm chart
helm install cert-manager \
--wait --namespace cert-manager \
--version v0.13.0 \
jetstack/cert-manager
# Setup ClusterIssuer
cat <<-EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: $LETS_ENCRYPT_EMAIL
privateKeySecretRef:
name: letsencrypt
solvers:
- http01:
ingress:
class: nginx
EOF
#!/bin/bash
set -e
AZURE_CLOUD=${AZURE_CLOUD-AzureUSGovernmentCloud}
LOCATION=${LOCATION-usgovvirginia}
SP_NAME=${SP_NAME-AKS_DNS_SP_$RANDOM}
K8S_CONTEXT=$(kubectl config current-context)
if [[ -z "$DOMAIN_NAME" || -z "$DNS_RG" ]]; then
echo "ERROR: Some Environment variables are missing!"
echo -e "ERROR: The following are required:\n"
echo " DNS_RG: Resource Group for DNS Zone"
echo " DOMAIN_NAME: Domain Name (ie. example.com)"
echo " LOCATION (optional): Azure region. Default is usgovvirginia"
echo " AZURE_CLOUD (optional): Selected Azure Cloud. "
echo " Default is AzureUSGovernmentCloud."
echo " Use 'AzurePublicCloud' for Commercial"
exit 1
fi
echo "DNS_RG: $DNS_RG"
echo "DOMAIN_NAME: $DOMAIN_NAME"
echo "LOCATION: $LOCATION"
echo "AZURE_CLOUD: $AZURE_CLOUD"
echo -e "K8S_CONTEXT: $K8S_CONTEXT\n"
# Verify if we want to proceed
read -p "Are you sure you want to install external-dns [y/N]?"
if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then
exit
fi
# Create RG for DNS Zone. If one already exists
# return the id for it.
DNS_RG_ID=$(
az group create \
--name $DNS_RG \
--location $LOCATION \
--query id -o tsv
)
# Create DNS Zone. If one already exists
# return id for it.
DNS_ZONE_ID=$(
az network dns zone create \
-g $DNS_RG -n $DOMAIN_NAME \
--query id -o tsv
)
# Create Service Principal
SP_TOKEN=$(az ad sp create-for-rbac -n $SP_NAME -o json)
# Grab info from the Service Principal
SP_APPID=$(echo $SP_TOKEN | jq -e -r 'select(.appId != null) | .appId')
SP_TENANTID=$(echo $SP_TOKEN | jq -e -r 'select(.tenant != null) | .tenant')
SP_PASSWORD=$(echo $SP_TOKEN | jq -e -r 'select(.password != null) | .password')
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
# Assign Reader to SP for RG
az role assignment create \
--role "Reader" \
--assignee $SP_APPID \
--scope $DNS_RG_ID 1>/dev/null
# Assign Contributor to SP for DNSZone
az role assignment create \
--role "Contributor" \
--assignee $SP_APPID \
--scope $DNS_ZONE_ID 1>/dev/null
# Add bitnami repo for external-dns chart
helm repo add bitnami https://charts.bitnami.com/bitnami
# Create namespace for external-dns
kubectl create namespace external-dns
# Install external-dns chart
helm install external-dns bitnami/external-dns \
--wait --namespace external-dns \
--set provider=azure \
--set azure.resourceGroup=AzureDNS \
--set azure.tenantId=$SP_TENANTID \
--set azure.subscriptionId=$SUBSCRIPTION_ID \
--set azure.aadClientId=$SP_APPID \
--set azure.aadClientSecret=$SP_PASSWORD \
--set azure.cloud=$AZURE_CLOUD \
--set policy=sync \
--set domainFilters={$DOMAIN_NAME}
echo "=================================================="
echo "Current DNS Nameservers for $DOMAIN_NAME"
host -t ns $DOMAIN_NAME
echo "NOTE: ++++++++++++++++++++++++++++++++++++++++++++"
echo " Ensure your domain registrar is using"
echo " the following DNS nameservers for resolution"
echo " before continuing."
echo " +++++++++++++++++++++++++++++++++++++++++++++"
az network dns zone show \
-g $DNS_RG -n $DOMAIN_NAME \
-o tsv --query nameServers
#!/bin/bash
set -e
color()(set -o pipefail;"$@" 2>&1>&3|sed $'s,.*,\e[31m&\e[m,'>&2)3>&1
validateTools() {
command -v az 2&> /dev/null
if [ $? -ne 0 ]; then
echo "ERROR: Requires Azure CLI (az). Aborting..."
exit 1
fi
command -v kubectl 2&> /dev/null
if [ $? -ne 0 ]; then
echo "ERROR: Requires Kubectl (kubectl). Aborting..."
exit 1
fi
command -v jq 2&> /dev/null
if [ $? -ne 0 ]; then
echo "ERROR: Requires JQuery (jq). Aborting..."
exit 1
fi
command -v helm 2&> /dev/null
if [ $? -ne 0 ]; then
echo "ERROR: Requires Helm v3.1.2_1+ (helm). Aborting..."
exit 1
fi
}
validateTools
# For Important Info
CRED='\033[1;40;41m'
CEND='\033[0m'
AKS_CREATE=${AKS_CREATE-true}
if [ "$AKS_CREATE" != "true" ]; then
if [[ -z "$RG" || -z "$AKS_NAME" ]]; then
echo "ERROR: Set RG and AKS_NAME as environment variables to continue."
echo "Otherwise unset AKS_CREATE environment varialbe or set to 'true'."
exit 1
fi
fi
RND=$(echo $RANDOM | grep -o ..$)
RG=${RG-AKS-$RND-RG}
NODESIZE=${NODESIZE-Standard_DS2_v2}
NODECOUNT=${NODECOUNT-2}
LOCATION=${LOCATION-usgovvirginia}
AKS_NAME=${AKS_NAME-AKSCluster-$RND}
DNS_RG=${DNS_RG-AzureDNS}
DOMAIN_NAME=${DOMAIN_NAME-designingdevops.com}
SP_NAME=${SP_NAME-AKS_DNS_SP_$RANDOM}
AZURE_CLOUD=${AZURE_CLOUD-AzureUSGovernmentCloud}
K8S_CONTEXT=$(kubectl config current-context)
echo "RG: $RG"
echo "AKS_NAME: $AKS_NAME"
echo "NODESIZE: $NODESIZE"
echo "LOCATION: $LOCATION"
echo "DNS_RG: $DNS_RG"
echo "DOMAIN_NAME: $DOMAIN_NAME"
echo "AKS_CREATE: $AKS_CREATE"
echo "NODECOUNT: $NODECOUNT"
echo "AZURE_CLOUD: $AZURE_CLOUD"
if [ "$AKS_CREATE" != "true" ]; then
echo "K8S_CONTEXT: $K8S_CONTEXT"
if [ "$K8S_CONTEXT" != "$AKS_NAME" ]; then
echo -e "\nERROR: Your existing K8S_CONTEXT should match your AKS_NAME\n"
exit 1
fi
echo -e "\nNOTE: Will NOT Create AKS Cluster"
echo -e "Will use $CRED $K8S_CONTEXT $CEND as target!\n"
else
if [ "$K8S_CONTEXT" == "$AKS_NAME" ]; then
echo "K8S_CONTEXT: $K8S_CONTEXT"
echo -e "\n$CRED ERROR: K8S_CONTEXT cannot match AKS_NAME for new clusters. $CEND"
echo "You may have environment variables set."
echo "During cluster create unset RG and AKS_NAME"
echo "or set to something different."
exit 1
fi
fi
# Verify if we want to proceed
echo "NOTE: Override the above defaults with environment variables!"
read -p "Are you sure you want to Proceed [y/N]?"
if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then
exit
fi
if [ "$AKS_CREATE" == "true" ]; then
###############################################
# CREATE AKS CLUSTER ==========================
###############################################
echo "Creating AKS Cluster named=$AKS_NAME in $RG"
az group create -l $LOCATION -n $RG 1>/dev/null
az aks create \
--resource-group $RG \
--name $AKS_NAME \
--vm-set-type VirtualMachineScaleSets \
--node-count $NODECOUNT \
--load-balancer-sku standard \
--node-vm-size $NODESIZE \
--generate-ssh-keys \
--location $LOCATION 1>/dev/null
az aks get-credentials \
--name $AKS_NAME \
--resource-group $RG 1>/dev/null
fi
###############################################
# CREATE NGINX INGRESS WITH STATIC IP =========
###############################################
# Get the Resource Group of our AKS Cluster
echo "Installing NGINX on $AKS_NAME!!"
AKS_CLUSTER_RG=$(
az aks show \
--resource-group $RG \
--name $AKS_NAME \
--query nodeResourceGroup -o tsv
)
# Create a Public IP and get the id of the address. If one exists already
# in the RG with the same name. The existing IP will be returned.
PUBLIC_IP=$(
az network public-ip create \
--resource-group $AKS_CLUSTER_RG \
--name IP-PublicIP1 \
--sku Standard \
--allocation-method static \
--query publicIp.ipAddress -o tsv
)
echo "Created/Retrieved Azure PublicIP=$PUBLIC_IP"
# Add stable chart repo
helm repo add stable https://kubernetes-charts.storage.googleapis.com
# Create namespace for Ingress
kubectl create namespace ingress || true
# Use Helm to deploy an NGINX ingress controller with static IP
helm install nginx-ingress stable/nginx-ingress \
--wait --namespace ingress \
--set controller.replicaCount=2 \
--set controller.service.loadBalancerIP="$PUBLIC_IP" \
--set controller.publishService.enabled=true \
--set controller.publishService.pathOverride=ingress/nginx-ingress-controller
###############################################
# CREATE EXTERNALDNS SERVICE ==================
###############################################
# Create RG for DNS Zone. If one already exists
# return the id for it.
DNS_RG_ID=$(
az group create \
--name $DNS_RG \
--location $LOCATION \
--query id -o tsv
)
# Create DNS Zone. If one already exists
# return id for it.
DNS_ZONE_ID=$(
az network dns zone create \
-g $DNS_RG -n $DOMAIN_NAME \
--query id -o tsv
)
# Create Service Principal
SP_TOKEN=$(az ad sp create-for-rbac -n $SP_NAME -o json)
# Grab info from the Service Principal
SP_APPID=$(echo $SP_TOKEN | jq -e -r 'select(.appId != null) | .appId')
SP_TENANTID=$(echo $SP_TOKEN | jq -e -r 'select(.tenant != null) | .tenant')
SP_PASSWORD=$(echo $SP_TOKEN | jq -e -r 'select(.password != null) | .password')
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
# Assign Reader to SP for RG
az role assignment create \
--role "Reader" \
--assignee $SP_APPID \
--scope $DNS_RG_ID 1>/dev/null
# Assign Contributor to SP for DNSZone
az role assignment create \
--role "Contributor" \
--assignee $SP_APPID \
--scope $DNS_ZONE_ID 1>/dev/null
# Add bitnami repo for external-dns chart
helm repo add bitnami https://charts.bitnami.com/bitnami
# Create namespace for external-dns
kubectl create namespace external-dns
# Install external-dns chart
helm install external-dns bitnami/external-dns \
--wait --namespace external-dns \
--set provider=azure \
--set azure.resourceGroup=AzureDNS \
--set azure.tenantId=$SP_TENANTID \
--set azure.subscriptionId=$SUBSCRIPTION_ID \
--set azure.aadClientId=$SP_APPID \
--set azure.aadClientSecret=$SP_PASSWORD \
--set azure.cloud=$AZURE_CLOUD \
--set policy=sync \
--set domainFilters={$DOMAIN_NAME}
echo "=================================================="
echo "Current DNS Nameservers for $DOMAIN_NAME"
host -t ns $DOMAIN_NAME
echo "NOTE: ++++++++++++++++++++++++++++++++++++++++++++"
echo " Ensure your domain registrar is using"
echo " the following DNS nameservers for resolution"
echo " before continuing."
echo " +++++++++++++++++++++++++++++++++++++++++++++"
az network dns zone show \
-g $DNS_RG -n $DOMAIN_NAME \
-o tsv --query nameServers
###############################################
# CREATE CERT-MANAGER SERVICE==================
###############################################
kubectl apply \
--validate=false \
-f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.13/deploy/manifests/00-crds.yaml
# Create namespace
kubectl create namespace cert-manager
# Label the ingress namespace to disable resource validation
kubectl label namespace ingress cert-manager.io/disable-validation=true
# Label the cert-manager namespace to disable resource validation
kubectl label namespace cert-manager certmanager.k8s.io/disable-validation=true
# Add the Jetstack Helm repository
helm repo add jetstack https://charts.jetstack.io
# Update your local Helm chart repository cache
helm repo update
# Install the cert-manager Helm chart
helm install cert-manager \
--wait --namespace cert-manager \
--version v0.13.0 \
jetstack/cert-manager
# Setup ClusterIssuer
cat <<-EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: $LETS_ENCRYPT_EMAIL
privateKeySecretRef:
name: letsencrypt
solvers:
- http01:
ingress:
class: nginx
EOF
#!/bin/bash
set -e
validateTools() {
command -v az 2&> /dev/null
if [ $? -ne 0 ]; then
echo "ERROR: Requires Azure CLI (az). Aborting..."
exit 1
fi
command -v az 2&> /dev/null
if [ $? -ne 0 ]; then
echo "ERROR: Requires Azure CLI (az). Aborting..."
exit 1
fi
command -v kubectl 2&> /dev/null
if [ $? -ne 0 ]; then
echo "ERROR: Requires Kubectl (kubectl). Aborting..."
exit 1
fi
command -v helm 2&> /dev/null
if [ $? -ne 0 ]; then
echo "ERROR: Requires Helm v3.1.2_1+ (helm). Aborting..."
exit 1
fi
}
validateTools
RND=$(echo $RANDOM | grep -o ..$)
RG=${RG-AKS-$RND-RG}
NODESIZE=${NODESIZE-Standard_DS2_v2}
NODECOUNT=${NODECOUNT-2}
LOCATION=${LOCATION-usgovvirginia}
AKS_NAME=${AKS_NAME-AKSCluster-$RND}
echo "RND: $RND"
echo "RG: $RG"
echo "NODESIZE: $NODESIZE"
echo "NODECOUNT: $NODECOUNT"
echo "LOCATION: $LOCATION"
echo "AKS_NAME: $AKS_NAME"
# Verify if we want to proceed
read -p "Are you sure you wish to create an AKS Cluster [y/N]?"
if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then
exit
fi
az group create -l $LOCATION -n $RG
az aks create \
--resource-group $RG \
--name $AKS_NAME \
--vm-set-type VirtualMachineScaleSets \
--node-count $NODECOUNT \
--load-balancer-sku standard \
--node-vm-size $NODESIZE \
--generate-ssh-keys \
--location $LOCATION
az aks get-credentials \
--name $AKS_NAME \
--resource-group $RG
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: nginx
annotations:
nginx.ingress.kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt
spec:
tls:
- hosts:
- nginx.designingdevops.com
secretName: tls-secret
rules:
- host: nginx.designingdevops.com
http:
paths:
- backend:
serviceName: nginx-svc
servicePort: 80
path: /
#!/bin/bash
# This script creates an Azure Public IP and binds
# an the NGINX Ingress controller to it
set -e
if [[ -z "$RG" || -z "$AKS_NAME" ]]; then
echo "ERROR: Some Environment variables are missing!"
echo -e "ERROR: These are required:\n"
echo " RG: Resource Group"
echo " AKS_NAME: AKS.Cluster Name"
echo " LOCATION (optional): Azure region. Default is usgovvirginia"
exit 1
fi
LOCATION=${LOCATION-usgovvirginia}
K8S_CONTEXT=$(kubectl config current-context)
echo "AKS_NAME: $AKS_NAME"
echo "RG: $RG"
echo "LOCATION: $LOCATION"
echo -e "K8S_CONTEXT: $K8S_CONTEXT\n"
# Verify if we want to proceed
read -p "Are you sure you want to install NGINX [y/N]?"
if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then
exit
fi
# Get the Resource Group of our AKS Cluster
AKS_CLUSTER_RG=$(
az aks show \
--resource-group $RG \
--name $AKS_NAME \
--query nodeResourceGroup -o tsv
)
# Create a Public IP and get the id of the address. If one exists already
# in the RG with the same name. The existing IP will be returned.
PUBLIC_IP=$(
az network public-ip create \
--resource-group $AKS_CLUSTER_RG \
--name IP-PublicIP1 \
--sku Standard \
--allocation-method static \
--query publicIp.ipAddress -o tsv
)
# Add stable chart repo
helm repo add stable https://kubernetes-charts.storage.googleapis.com
# Create namespace for Ingress
kubectl create namespace ingress
# Use Helm to deploy an NGINX ingress controller with static IP
helm install nginx-ingress stable/nginx-ingress \
--wait --namespace ingress \
--set controller.replicaCount=2 \
--set controller.service.loadBalancerIP="$PUBLIC_IP" \
--set controller.publishService.enabled=true \
--set controller.publishService.pathOverride=ingress/nginx-ingress-controller
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: ClusterIP
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: nginx
annotations:
nginx.ingress.kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt
spec:
tls:
- hosts:
- nginx.designingdevops.com
secretName: tls-secret
rules:
- host: nginx.designingdevops.com
http:
paths:
- backend:
serviceName: nginx-svc
servicePort: 80
path: /
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment