Last active
February 23, 2022 07:06
-
-
Save brentmcconnell/a8769f1e3a4010c71e1cd764887e6325 to your computer and use it in GitHub Desktop.
ExternalDNS on AKS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
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: / | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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