Skip to content

Instantly share code, notes, and snippets.

@alexeldeib
Created March 24, 2020 07:59
Show Gist options
  • Save alexeldeib/3e113ceefed914d512064988dd04f581 to your computer and use it in GitHub Desktop.
Save alexeldeib/3e113ceefed914d512064988dd04f581 to your computer and use it in GitHub Desktop.
Azure DevOps Agent Joining
#!/usr/bin/env bash
#
#
# Script to bootstrap an Azure DevOps Agent noninteractively
#
#
set -o pipefail
set -o errexit
set -o nounset
AGENT_USER="${AGENT_USER:="azureuser"}"
AGENT_URL="${AGENT_URL:=https://vstsagentpackage.azureedge.net/agent/2.165.2/vsts-agent-linux-x64-2.165.2.tar.gz}"
AGENT_DIR="/home/${AGENT_USER}/agent"
TOKEN="${TOKEN:="__TOKEN__"}"
SERVER="${SERVER:="https://dev.azure.com/AzureContainerUpstream"}"
POOL="${POOL:="ace-test"}"
GOLANG_VERSION="1.13.8"
cleanup() {
echo "Cleaning up..."
rm -f "${AGENT_URL}" || true
exit
}
trap cleanup EXIT
set -x
echo "Downloading agent files"
curl -fsSl -o agent.tar.gz "${AGENT_URL}"
echo "Creating agent directory"
mkdir -p "${AGENT_DIR}"
echo "Unpacking agent files"
tar -xvf agent.tar.gz -C "${AGENT_DIR}"
echo "Moving to agent dir"
cd "${AGENT_DIR}"
echo "Setting up passwordless sudo"
tee "/etc/sudoers.d/${AGENT_USER}" > /dev/null <<EOF
${AGENT_USER} ALL=(ALL) NOPASSWD:ALL
EOF
echo "Configuring agent"
sudo -H -u "${AGENT_USER}" SERVER="${SERVER}" POOL="${POOL}" TOKEN="${TOKEN}" AGENT_USER="${AGENT_USER}" bash -c 'sudo chown -R "${AGENT_USER}:${AGENT_USER}" . && ./config.sh \
--unattended \
--acceptTeeEula \
--url "${SERVER}" \
--auth pat \
--token "${TOKEN}" \
--pool "${POOL}" \
--replace'
echo "Installing age"
sudo ./svc.sh install
echo "Starting age"
sudo ./svc.sh start
echo "updating apt"
sudo apt update
echo "Installing apt dependencies"
sudo apt-get install -y apt-transport-https libssl-dev libffi-dev python-dev build-essential jq default-jdk default-jre git-core util-linux python python-pip python-setuptools unzip upx zip ca-certificates curl software-properties-common qemu-kvm libvirt-bin virtinst bridge-utils cpu-checker cloud-image-utils tree protobuf-compiler
echo "Installing golang"
curl -O "https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz"
sudo tar -xvf "go${GOLANG_VERSION}.linux-amd64.tar.gz" -C /usr/local
# Add Go to path
sudo tee -a /home/${AGENT_USER}/.bashrc > /dev/null <<'EOF'
export GOPATH="/home/${AGENT_USER}/go"
export PATH=$PATH:"/usr/local/go/bin"
export PATH=$PATH:"${GOPATH}/bin"
EOF
export GOPATH="/home/${AGENT_USER}/go"
export PATH=$PATH:"/usr/local/go/bin"
export PATH=$PATH:"${GOPATH}/bin"
echo "Installing Azure CLI"
curl -sL https://packages.microsoft.com/keys/microsoft.asc | \
gpg --dearmor | \
sudo tee /etc/apt/trusted.gpg.d/microsoft.asc.gpg > /dev/null
AZ_REPO=$(lsb_release -cs)
echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $AZ_REPO main" | \
sudo tee /etc/apt/sources.list.d/azure-cli.list
sudo apt-get update
sudo apt-get install -y azure-cli < /dev/null
echo "Installing docker"
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io < /dev/null
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"adminUsername": {
"defaultValue": "azureuser",
"type": "string",
"metadata": {
"description": "Specifies a username for the Virtual Machine."
}
},
"adminPublicKey": {
"type": "string",
"metadata": {
"description": "Specifies the SSH rsa public key file as a string. Use \"ssh-keygen -t rsa -b 2048\" to generate your SSH key pairs."
}
},
"cleanupNsg": {
"defaultValue": "rg-cleanupservice-nsg",
"type": "string"
},
"count": {
"defaultValue": 1,
"type": "int",
"metadata": {
"description": "The desired number of agent VMs."
}
},
"customData": {
"type": "securestring",
"metadata": {
"description": "Base64 encoded shell script to join VM to Azure devops. **CONTAINS PAT SECRET TOKEN**."
}
},
"diagnosticsAccount": {
"type": "string",
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "The location in which the resources should be deployed."
}
},
"nsgName": {
"type": "string",
"metadata": {
"description": "Name of the Network Security Group"
}
},
"sku": {
"defaultValue": "Standard_D16s_v3",
"type": "string",
"metadata": {
"description": "The SKU of agent VM to deploy."
}
},
"virtualNetworkName": {
"defaultValue": "agentVnet",
"type": "string"
},
"vmssName": {
"defaultValue": "adoagent",
"type": "string"
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Network/virtualNetworks",
"apiVersion": "2019-11-01",
"name": "[parameters('virtualNetworkName')]",
"dependsOn": [
"[resourceId('Microsoft.Network/networkSecurityGroups', parameters('cleanupNsg'))]",
"[resourceId('Microsoft.Network/networkSecurityGroups', parameters('nsgName'))]"
],
"location": "[parameters('location')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"172.16.1.0/24",
"172.18.1.0/24"
]
},
"subnets": [
{
"name": "AzureBastionSubnet",
"properties": {
"addressPrefix": "172.18.1.0/24",
"serviceEndpoints": [],
"delegations": [],
"privateEndpointNetworkPolicies": "Enabled",
"privateLinkServiceNetworkPolicies": "Enabled"
}
},
{
"name": "default",
"properties": {
"addressPrefix": "172.16.1.0/24",
"networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('cleanupNsg'))]"
},
"serviceEndpoints": [],
"delegations": [],
"privateEndpointNetworkPolicies": "Enabled",
"privateLinkServiceNetworkPolicies": "Enabled"
}
}
],
"virtualNetworkPeerings": [],
"enableDdosProtection": false,
"enableVmProtection": false
}
},
{
"type": "Microsoft.Network/virtualNetworks/subnets",
"apiVersion": "2019-11-01",
"name": "[concat(parameters('virtualNetworkName'), '/AzureBastionSubnet')]",
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]"
],
"properties": {
"addressPrefix": "172.18.1.0/24",
"serviceEndpoints": [],
"delegations": [],
"privateEndpointNetworkPolicies": "Enabled",
"privateLinkServiceNetworkPolicies": "Enabled"
}
},
{
"type": "Microsoft.Network/virtualNetworks/subnets",
"apiVersion": "2019-11-01",
"name": "[concat(parameters('virtualNetworkName'), '/default')]",
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]"
],
"properties": {
"addressPrefix": "172.16.1.0/24",
"networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('nsgName'))]"
},
"serviceEndpoints": [],
"delegations": [],
"privateEndpointNetworkPolicies": "Enabled",
"privateLinkServiceNetworkPolicies": "Enabled"
}
},
{
"type": "Microsoft.Network/networkSecurityGroups",
"apiVersion": "2019-11-01",
"name": "[parameters('nsgName')]",
"location": "[resourceGroup().location]",
"properties": {
"securityRules": []
}
},
{
"type": "Microsoft.Compute/virtualMachineScaleSets",
"name": "[parameters('vmssName')]",
"location": "[resourceGroup().location]",
"apiVersion": "2019-07-01",
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]",
"[resourceId('Microsoft.Network/networkSecurityGroups', parameters('nsgName'))]"
],
"sku": {
"name": "[parameters('sku')]",
"capacity": "[parameters('count')]"
},
"properties": {
"overprovision": false,
"upgradePolicy": {
"mode": "Manual"
},
"virtualMachineProfile": {
"storageProfile": {
"osDisk": {
"createOption": "FromImage",
"caching": "ReadWrite"
},
"imageReference": {
"publisher": "Canonical",
"offer": "UbuntuServer",
"sku": "18.04-LTS",
"version": "latest"
}
},
"osProfile": {
"customData": "[parameters('customData')]",
"computerNamePrefix": "[parameters('vmssName')]",
"adminUsername": "[parameters('adminUsername')]",
"linuxConfiguration": {
"disablePasswordAuthentication": true,
"ssh": {
"publicKeys": [
{
"path": "[concat('/home/', parameters('adminUsername'), '/.ssh/authorized_keys')]",
"keyData": "[parameters('adminPublicKey')]"
}
]
}
}
},
"networkProfile": {
"networkInterfaceConfigurations": [
{
"name": "nic",
"properties": {
"primary": true,
"ipConfigurations": [
{
"name": "ipconfig",
"properties": {
"subnet": {
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), 'default')]"
}
}
}
],
"networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('nsgName'))]"
}
}
}
]
},
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": true,
"storageUri": "[parameters('diagnosticsAccount')]"
}
}
}
}
},
{
"type": "Microsoft.Network/networkSecurityGroups",
"apiVersion": "2019-11-01",
"name": "[parameters('cleanupNsg')]",
"location": "[resourceGroup().location]",
"properties": {
"securityRules": [
{
"name": "Cleanuptool-Allow-4001",
"properties": {
"protocol": "*",
"sourcePortRange": "*",
"destinationPortRange": "*",
"sourceAddressPrefix": "*",
"destinationAddressPrefix": "*",
"access": "Allow",
"priority": 4001,
"direction": "Inbound",
"sourcePortRanges": [],
"destinationPortRanges": [],
"sourceAddressPrefixes": [],
"destinationAddressPrefixes": []
}
},
{
"name": "Cleanuptool-Allow-101",
"properties": {
"protocol": "Tcp",
"sourcePortRange": "*",
"sourceAddressPrefix": "VirtualNetwork",
"destinationAddressPrefix": "*",
"access": "Allow",
"priority": 101,
"direction": "Inbound",
"sourcePortRanges": [],
"destinationPortRanges": [
"3389",
"5985",
"5986",
"22",
"2222",
"445",
"23",
"135"
],
"sourceAddressPrefixes": [],
"destinationAddressPrefixes": []
}
},
{
"name": "Cleanuptool-Allow-102",
"properties": {
"protocol": "Tcp",
"sourcePortRange": "*",
"sourceAddressPrefix": "AzureLoadBalancer",
"destinationAddressPrefix": "*",
"access": "Allow",
"priority": 102,
"direction": "Inbound",
"sourcePortRanges": [],
"destinationPortRanges": [
"3389",
"5985",
"5986",
"22",
"2222",
"445",
"23",
"135"
],
"sourceAddressPrefixes": [],
"destinationAddressPrefixes": []
}
},
{
"name": "Cleanuptool-Deny-103",
"properties": {
"protocol": "Tcp",
"sourcePortRange": "*",
"sourceAddressPrefix": "*",
"destinationAddressPrefix": "*",
"access": "Deny",
"priority": 103,
"direction": "Inbound",
"sourcePortRanges": [],
"destinationPortRanges": [
"3389",
"5985",
"5986",
"22",
"2222",
"445",
"23",
"135"
],
"sourceAddressPrefixes": [],
"destinationAddressPrefixes": []
}
},
{
"name": "Cleanuptool-Allow-105",
"properties": {
"protocol": "Udp",
"sourcePortRange": "*",
"sourceAddressPrefix": "VirtualNetwork",
"destinationAddressPrefix": "*",
"access": "Allow",
"priority": 105,
"direction": "Inbound",
"sourcePortRanges": [],
"destinationPortRanges": [
"53",
"111",
"123",
"445",
"593",
"1434",
"2049",
"5353",
"13",
"135",
"136",
"5632",
"11211",
"17",
"19",
"69",
"137",
"138",
"161",
"162",
"389",
"1900"
],
"sourceAddressPrefixes": [],
"destinationAddressPrefixes": []
}
},
{
"name": "Cleanuptool-Allow-106",
"properties": {
"protocol": "Udp",
"sourcePortRange": "*",
"sourceAddressPrefix": "AzureLoadBalancer",
"destinationAddressPrefix": "*",
"access": "Allow",
"priority": 106,
"direction": "Inbound",
"sourcePortRanges": [],
"destinationPortRanges": [
"53",
"111",
"123",
"445",
"593",
"1434",
"2049",
"5353",
"13",
"135",
"136",
"5632",
"11211",
"17",
"19",
"69",
"137",
"138",
"161",
"162",
"389",
"1900"
],
"sourceAddressPrefixes": [],
"destinationAddressPrefixes": []
}
},
{
"name": "Cleanuptool-Deny-107",
"properties": {
"protocol": "Udp",
"sourcePortRange": "*",
"sourceAddressPrefix": "*",
"destinationAddressPrefix": "*",
"access": "Deny",
"priority": 107,
"direction": "Inbound",
"sourcePortRanges": [],
"destinationPortRanges": [
"53",
"111",
"123",
"445",
"593",
"1434",
"2049",
"5353",
"13",
"135",
"136",
"5632",
"11211",
"17",
"19",
"69",
"137",
"138",
"161",
"162",
"389",
"1900"
],
"sourceAddressPrefixes": [],
"destinationAddressPrefixes": []
}
},
{
"name": "Cleanuptool-Allow-100",
"properties": {
"protocol": "Tcp",
"sourcePortRange": "*",
"destinationAddressPrefix": "*",
"access": "Allow",
"priority": 100,
"direction": "Inbound",
"sourcePortRanges": [],
"destinationPortRanges": [
"3389",
"5985",
"5986",
"22",
"2222",
"445",
"23",
"135"
],
"sourceAddressPrefixes": [
"167.220.148.0/23",
"131.107.147.0/24",
"131.107.159.0/24",
"131.107.160.0/24",
"131.107.174.0/24",
"167.220.24.0/24",
"167.220.26.0/24",
"167.220.238.0/27",
"167.220.238.128/27",
"167.220.238.192/27",
"167.220.238.64/27",
"167.220.232.0/23",
"167.220.255.0/25",
"167.220.242.0/27",
"167.220.242.128/27",
"167.220.242.192/27",
"167.220.242.64/27",
"94.245.87.0/24",
"167.220.196.0/23",
"194.69.104.0/25",
"191.234.97.0/26",
"167.220.0.0/23",
"167.220.2.0/24",
"167.220.226.0/23",
"157.58.216.64/26",
"194.69.119.64/26",
"167.220.249.128/26",
"207.68.190.32/27",
"13.106.174.32/27",
"13.106.4.96/27"
],
"destinationAddressPrefixes": []
}
},
{
"name": "Cleanuptool-Allow-104",
"properties": {
"protocol": "Udp",
"sourcePortRange": "*",
"destinationAddressPrefix": "*",
"access": "Allow",
"priority": 104,
"direction": "Inbound",
"sourcePortRanges": [],
"destinationPortRanges": [
"53",
"111",
"123",
"445",
"593",
"1434",
"2049",
"5353",
"13",
"135",
"136",
"5632",
"11211",
"17",
"19",
"69",
"137",
"138",
"161",
"162",
"389",
"1900"
],
"sourceAddressPrefixes": [
"167.220.148.0/23",
"131.107.147.0/24",
"131.107.159.0/24",
"131.107.160.0/24",
"131.107.174.0/24",
"167.220.24.0/24",
"167.220.26.0/24",
"167.220.238.0/27",
"167.220.238.128/27",
"167.220.238.192/27",
"167.220.238.64/27",
"167.220.232.0/23",
"167.220.255.0/25",
"167.220.242.0/27",
"167.220.242.128/27",
"167.220.242.192/27",
"167.220.242.64/27",
"94.245.87.0/24",
"167.220.196.0/23",
"194.69.104.0/25",
"191.234.97.0/26",
"167.220.0.0/23",
"167.220.2.0/24",
"167.220.226.0/23",
"157.58.216.64/26",
"194.69.119.64/26",
"167.220.249.128/26",
"207.68.190.32/27",
"13.106.174.32/27",
"13.106.4.96/27"
],
"destinationAddressPrefixes": []
}
}
]
}
}
]
}
@jchauncey
Copy link

Use the backports repo for installing golang -

# golang install
sudo add-apt-repository ppa:longsleep/golang-backports
sudo apt-get update
sudo apt-get install golang-go

It makes upgrading a lot easier

I also think you need to add the current user (azureuser) to the docker group. That doesnt happen by default in the package install.

Does this also give the node a system managed identity?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment