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