From 8060a683b2bebbb0a150ea97e10ef77e1919a43a Mon Sep 17 00:00:00 2001 From: "J. Patrick Fulton" Date: Wed, 16 Aug 2023 15:55:31 -0500 Subject: [PATCH 1/4] Update to use standard public IPs. --- bicep/modules/nic.bicep | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bicep/modules/nic.bicep b/bicep/modules/nic.bicep index 0b3fe5a..ef398c0 100644 --- a/bicep/modules/nic.bicep +++ b/bicep/modules/nic.bicep @@ -21,11 +21,11 @@ param vnetName string param subnetName string = 'default' var publicIpAddressName = '${serverName}-public-ip' -var publicIPAddressType = 'Dynamic' +var publicIPAddressType = 'Static' resource publicIp 'Microsoft.Network/publicIPAddresses@2021-03-01' = { sku: { - name: 'Basic' + name: 'Standard' } name: publicIpAddressName location: location From 09154adffe6a74df43209b6bef7be16f653ecf20 Mon Sep 17 00:00:00 2001 From: "J. Patrick Fulton" Date: Wed, 16 Aug 2023 19:26:37 -0500 Subject: [PATCH 2/4] First pass as bicep for linux server with public key auth only and AzureAD integration. --- bicep/linux-server.bicep | 61 +++++++++ bicep/linux-server.bicepparam | 7 + bicep/modules/linux-server/vm.bicep | 152 +++++++++++++++++++++ bicep/modules/nic.bicep | 1 - scripts/create-linux-server.sh | 205 ++++++++++++++++++++++++++++ 5 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 bicep/linux-server.bicep create mode 100644 bicep/linux-server.bicepparam create mode 100644 bicep/modules/linux-server/vm.bicep create mode 100755 scripts/create-linux-server.sh diff --git a/bicep/linux-server.bicep b/bicep/linux-server.bicep new file mode 100644 index 0000000..889e5b3 --- /dev/null +++ b/bicep/linux-server.bicep @@ -0,0 +1,61 @@ +@description('Bool to determine if the vm will be created as a spot instance.') +param isSpot bool = true + +@description('Name for the virtual machine.') +param serverName string + +@description('Size for the virtual machine. Allowed SKUs support nested virtualization for the WSL.') +@allowed([ + 'Standard_DS1_v2' + 'Standard_DS2_v2' + 'Standard_DS3_v2' + 'Standard_DS4_v2' + 'Standard_DS5_v2' + 'Standard_D2s_v3' + 'Standard_D4s_v3' + 'Standard_D8s_v3' + 'Standard_D16s_v3' + 'Standard_D32s_v3' + 'Standard_D48s_v3' + 'Standard_D64s_v3' +]) +param vmSize string = 'Standard_DS1_v2' + +@description( +'''Region for the virtual machine and associated resources. +Allowed values include US regions. +Defaults to the region of the resource group. +''' +) +param location string = resourceGroup().location + +@description('Name of the virtual network for this VM to attach to.') +param vnetName string = 'personal-network-vnet' + +@description('Admin username.') +param adminUsername string = 'jpfulton' + +@description('Public key data for the admin user.') +param adminPublicKeyData string + +module nicModule 'modules/nic.bicep' = { + name: 'nic-deploy' + params: { + location: location + serverName: serverName + vnetName: vnetName + } +} + +module vmModule 'modules/linux-server/vm.bicep' = { + name: 'vm-deploy' + params: { + adminUsername: adminUsername + adminPublicKeyData: adminPublicKeyData + location: location + nicId: nicModule.outputs.nicId + serverName: serverName + vmSize: vmSize + isSpot: isSpot + } +} diff --git a/bicep/linux-server.bicepparam b/bicep/linux-server.bicepparam new file mode 100644 index 0000000..5d6648e --- /dev/null +++ b/bicep/linux-server.bicepparam @@ -0,0 +1,7 @@ +using './linux-server.bicep' + +param isSpot = bool(readEnvironmentVariable('IS_SPOT', 'true')) +param serverName = readEnvironmentVariable('SERVER_NAME') +param vmSize = readEnvironmentVariable('VM_SIZE') +param adminUsername = readEnvironmentVariable('ADMIN_USERNAME') +param adminPublicKeyData = readEnvironmentVariable('ADMIN_PUBLIC_KEY') diff --git a/bicep/modules/linux-server/vm.bicep b/bicep/modules/linux-server/vm.bicep new file mode 100644 index 0000000..e2f8539 --- /dev/null +++ b/bicep/modules/linux-server/vm.bicep @@ -0,0 +1,152 @@ +@description('Server name for the virtual machine.') +param serverName string + +@description('Region for the resources. Allowed values include US regions.') +@allowed([ + 'centralus' + 'eastus' + 'eastus2' + 'eastus3' + 'northcentralus' + 'southcentralus' + 'westcentralus' +]) +param location string + +@description('Admin account user name.') +param adminUsername string + +@description('Resource Id of the NIC to associate with the virtual machine.') +param nicId string + +@description('Size for the virtual machine.') +@allowed([ + 'Standard_DS1_v2' + 'Standard_DS2_v2' + 'Standard_DS3_v2' + 'Standard_DS4_v2' + 'Standard_DS5_v2' + 'Standard_D2s_v3' + 'Standard_D4s_v3' + 'Standard_D8s_v3' + 'Standard_D16s_v3' + 'Standard_D32s_v3' + 'Standard_D48s_v3' + 'Standard_D64s_v3' +]) +param vmSize string = 'Standard_DS1_v2' + +@description('Indicates if this vm instance should be a spot instance.') +param isSpot bool = true + +@description('Public key of the admin user.') +param adminPublicKeyData string + +// configuration properties required for spot instance creation +var spotConfig = { + priority: 'Spot' + evictionPolicy: 'Deallocate' + billingProfile: { + maxPrice: -1 + } +} + +// configuration properties required for standard instance creation +var standardConfig = { + priority: 'Regular' +} + +// core virtual machine properties +var coreVmProperties = { + hardwareProfile: { + vmSize: vmSize + } + storageProfile: { + imageReference: { + publisher: 'canonical' + offer: '0001-com-ubuntu-server-jammy' + sku: '22_04-lts-gen2' + version: 'latest' + } + osDisk: { + osType: 'Linux' + name: '${serverName}_OsDisk' + createOption: 'FromImage' + caching: 'ReadWrite' + managedDisk: { + storageAccountType: 'Standard_LRS' + } + deleteOption: 'Delete' + diskSizeGB: 32 + } + dataDisks: [] + diskControllerType: 'SCSI' + } + osProfile: { + computerName: serverName + adminUsername: adminUsername + linuxConfiguration: { + disablePasswordAuthentication: true + ssh: { + publicKeys: [ + { + path: '/home/${adminUsername}/.ssh/authorized_keys' + keyData: adminPublicKeyData + } + ] + } + provisionVMAgent: true + patchSettings: { + patchMode: 'ImageDefault' + assessmentMode: 'ImageDefault' + } + enableVMAgentPlatformUpdates: false + } + secrets: [] + allowExtensionOperations: true + } + securityProfile: { + uefiSettings: { + secureBootEnabled: true + vTpmEnabled: true + } + encryptionAtHost: true + securityType: 'TrustedLaunch' + } + networkProfile: { + networkInterfaces: [ + { + id: nicId + properties: { + deleteOption: 'Delete' + } + } + ] + } + diagnosticsProfile: { + bootDiagnostics: { + enabled: true + } + } +} + +resource virtualMachine 'Microsoft.Compute/virtualMachines@2023-03-01' = { + name: serverName + location: location + identity: { + type: 'SystemAssigned' + } + properties: isSpot ? union(coreVmProperties, spotConfig) : union(coreVmProperties, standardConfig) +} + +resource AADSSHLoginForLinuxExtension 'Microsoft.Compute/virtualMachines/extensions@2023-03-01' = { + parent: virtualMachine + name: 'AADSSHLoginForLinux' + location: location + properties: { + publisher: 'Microsoft.Azure.ActiveDirectory' + type: 'AADSSHLoginForLinux' + typeHandlerVersion: '1.0' + autoUpgradeMinorVersion: true + } +} diff --git a/bicep/modules/nic.bicep b/bicep/modules/nic.bicep index ef398c0..312ea85 100644 --- a/bicep/modules/nic.bicep +++ b/bicep/modules/nic.bicep @@ -1,5 +1,4 @@ @description('Server name for the virtual machine.') -@maxLength(15) param serverName string @description('Region for the resources. Allowed values include US regions.') diff --git a/scripts/create-linux-server.sh b/scripts/create-linux-server.sh new file mode 100755 index 0000000..7446b9f --- /dev/null +++ b/scripts/create-linux-server.sh @@ -0,0 +1,205 @@ +#!/usr/bin/env bash + +CURRENT_SCRIPT_DIR="$(dirname "$0")/"; +echo "Running ${0} from ${CURRENT_SCRIPT_DIR}"; +echo "---"; +echo; + +# Import library functions +source ${CURRENT_SCRIPT_DIR}lib/azure-cli-functions.sh; +source ${CURRENT_SCRIPT_DIR}lib/sshpass-functions.sh; + +# Set remote execution PS script +REMOTE_EXECUTION_PS_FILE="${CURRENT_SCRIPT_DIR}../powershell/admin/run-file-on-remote-server.ps1"; + +print-usage () { + echo "Usage: ${0} [options] [resource-group] [server-name]"; + echo; + echo "Options:"; + echo "---"; + echo; +} + +parse-script-inputs () { + echo "Parsing script inputs..."; + echo "---"; + + if [ "$#" -lt 2 ] + then + print-usage $@; + exit 1; + fi + + SCRIPT_NAME=$(basename "$0"); + OPTIONS=$(getopt --options o --long openvpn --name "$SCRIPT_NAME" -- "$@"); + if [ $? -ne 0 ] + then + echo "Incorrect options."; + print-usage; + exit 1; + fi + + OPENVPN=0; + + eval set -- "$OPTIONS"; + shift 6; # jump past the getopt options in the options string + + while true; do + case "$1" in + -o|--openvpn) + OPENVPN=1; shift ;; + --) + shift; ;; + *) + break ;; + esac + done + + if [ "$OPENVPN" -eq 1 ] + then + echo "Enabling OpenVPN installation."; + fi + + RESOURCE_GROUP="$1"; + if [ "$RESOURCE_GROUP" == "" ] + then + print-usage; + exit 1; + fi + + SERVER_NAME="$2"; + if [ "$SERVER_NAME" == "" ] + then + print-usage; + exit 1; + fi + + echo "---"; + echo; +} + +get-user-inputs () { + read -p "Enter a private DNS zone name [private.jpatrickfulton.com]: " PRIVATE_DNS_ZONE; + PRIVATE_DNS_ZONE=${PRIVATE_DNS_ZONE:-private.jpatrickfulton.com}; + + read -p "Enter a vm size [Standard_DS1_v2]: " VM_SIZE; + VM_SIZE=${VM_SIZE:-Standard_DS1_v2}; + + read -p "Create server as spot instance (true/false)[true]: " IS_SPOT; + IS_SPOT=${IS_SPOT:-true}; + + if [ ! "$IS_SPOT" = "true" ] && [ ! "$IS_SPOT" = "false" ] + then + echo "Spot instance prompt must be either true or false. Exiting..."; + exit 1; + fi + + if [ "$IS_SPOT" = true ] + then + read -p "Tag for restart after eviction (true/false)[true]: " SPOT_RESTART; + SPOT_RESTART=${SPOT_RESTART:-true}; + + if [ ! "$SPOT_RESTART" = "true" ] && [ ! "$SPOT_RESTART" = "false" ] + then + echo "Spot instance restart prompt must be either true or false. Exiting..."; + exit 1; + fi + fi + + read -p "Enter an admin account username [jpfulton]: " ADMIN_USERNAME; + ADMIN_USERNAME=${ADMIN_USERNAME:-jpfulton}; + + read -p "Enter the path to the admin public SSH key [~/.ssh/id_rsa.pub]: " ADMIN_PUBLIC_KEY_FILE; + ADMIN_PUBLIC_KEY_FILE=${ADMIN_PUBLIC_KEY_FILE:-~/.ssh/id_rsa.pub}; + + if [ ! -f $ADMIN_PUBLIC_KEY_FILE ] + then + echo "Cannot find admin public key file. Exiting..."; + exit 1; + fi + + read -p "Enter the path to the admin private SSH key [~/.ssh/id_rsa]: " ADMIN_PRIVATE_KEY_FILE; + ADMIN_PRIVATE_KEY_FILE=${ADMIN_PRIVATE_KEY_FILE:-~/.ssh/id_rsa}; + + if [ ! -f $ADMIN_PRIVATE_KEY_FILE ] + then + echo "Cannot find admin private key file. Exiting..."; + exit 1; + fi + + SERVER_FQDN="${SERVER_NAME}.${PRIVATE_DNS_ZONE}"; + + echo; + echo; + echo "---"; + echo "Using resource group: $RESOURCE_GROUP"; + echo "Using server name: $SERVER_NAME"; + echo "Using full server private DNS name: $SERVER_FQDN"; + echo "Using admin account username: $ADMIN_USERNAME"; + echo "Using admin public key file: $ADMIN_PUBLIC_KEY_FILE"; + echo "Using admin private key file: $ADMIN_PRIVATE_KEY_FILE"; + echo; + echo "Public key: $(cat $ADMIN_PUBLIC_KEY_FILE)"; + echo; + + echo "---"; + echo; +} + +deploy () { + echo "Launching deployment..."; + + export SERVER_NAME="$SERVER_NAME"; + export VM_SIZE="$VM_SIZE"; + export IS_SPOT="$IS_SPOT"; + export ADMIN_USERNAME="$ADMIN_USERNAME"; + export ADMIN_PUBLIC_KEY="$(cat $ADMIN_PUBLIC_KEY_FILE)"; + + local TEMPLATE_FILE="${CURRENT_SCRIPT_DIR}../bicep/linux-server.bicep"; + local PARAM_FILE="${CURRENT_SCRIPT_DIR}../bicep/linux-server.bicepparam"; + az deployment group create \ + --resource-group $RESOURCE_GROUP \ + --template-file $TEMPLATE_FILE \ + --parameters $PARAM_FILE; + + if [ $? -ne 0 ] + then + echo "Deployment failed. Exiting."; + exit 1; + fi + + echo "---"; + echo; +} + +restart-vm () { + echo "Restarting VM to allow settings to take effect..."; + + az vm restart \ + -g $RESOURCE_GROUP \ + -n $SERVER_NAME; + + echo "---"; + echo; +} + +main () { + validate-az-cli-install; + validate-sshpass-install; + + # parse script inputs and gather user inputs + parse-script-inputs $@; + get-user-inputs; + + # check for signed in Azure CLI user + check-signed-in-user; + + # deploy bicep template + deploy; + + echo "---"; + echo "Done."; + echo; +} + +time main $@; From cce35b14ab29511fc18b60a94201faaebe350a08 Mon Sep 17 00:00:00 2001 From: "J. Patrick Fulton" Date: Wed, 16 Aug 2023 20:52:37 -0500 Subject: [PATCH 3/4] Add foundations for remote script execution and base package upgrades. --- linux-scripts/update-base-packages.sh | 5 ++ scripts/create-linux-server.sh | 71 +++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100755 linux-scripts/update-base-packages.sh diff --git a/linux-scripts/update-base-packages.sh b/linux-scripts/update-base-packages.sh new file mode 100755 index 0000000..cc040ea --- /dev/null +++ b/linux-scripts/update-base-packages.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +DEBIAN_FRONTEND="noninteractive"; +sudo -E apt-get update; +sudo -E apt-get full-upgrade -y; \ No newline at end of file diff --git a/scripts/create-linux-server.sh b/scripts/create-linux-server.sh index 7446b9f..b8d39c7 100755 --- a/scripts/create-linux-server.sh +++ b/scripts/create-linux-server.sh @@ -172,6 +172,68 @@ deploy () { echo; } +login-to-admin-acct () { + echo "Logging into to admin account and recording host key..."; + + ssh -i $ADMIN_PRIVATE_KEY_FILE \ + -o StrictHostKeyChecking=accept-new \ + ${ADMIN_USERNAME}@${SERVER_FQDN} \ + sleep 1; + + echo "---"; + echo; +} + +scp-file-to-admin-home () { + if [ "$#" -ne 1 ] + then + echo "ERROR: scp-file-to-admin-home function requires one argument. Exiting..."; + echo "INFO: Required argument one: Path to file to copy."; + echo; + + exit 1; + fi + + local FILE="$1"; + + if [ ! -f $FILE ] + then + echo "Cannot find file: ${FILE} Exiting..."; + exit 1; + fi + + echo "SCPing ${FILE} to remote admin home folder..."; + scp \ + -i $ADMIN_PRIVATE_KEY_FILE \ + $FILE \ + ${ADMIN_USERNAME}@${SERVER_FQDN}:~/; + + echo "---"; + echo; +} + +run-script-from-admin-home () { + if [ "$#" -ne 1 ] + then + echo "ERROR: exec-script-as-sudo-from-admin-home function requires one argument. Exiting..."; + echo "INFO: Required argument one: Path to file to copy."; + echo; + + exit 1; + fi + + local SCRIPT="$1"; + + echo "Executing remote script: ${SCRIPT}..."; + + ssh -i $ADMIN_PRIVATE_KEY_FILE \ + ${ADMIN_USERNAME}@${SERVER_FQDN} \ + "./${SCRIPT}"; + + echo "---"; + echo; +} + restart-vm () { echo "Restarting VM to allow settings to take effect..."; @@ -197,6 +259,15 @@ main () { # deploy bicep template deploy; + # log into admin account and record host key + login-to-admin-acct; + + # copy setup scripts to server + scp-file-to-admin-home ${CURRENT_SCRIPT_DIR}../linux-scripts/update-base-packages.sh; + + # execute remote setup scripts + run-script-from-admin-home update-base-packages.sh; + echo "---"; echo "Done."; echo; From 3e8f20616f0d8807cea6b6e9fb7fae18df806335 Mon Sep 17 00:00:00 2001 From: "J. Patrick Fulton" Date: Wed, 16 Aug 2023 23:29:28 -0500 Subject: [PATCH 4/4] Complete linux vm setup. --- linux-scripts/clean-up.sh | 3 ++ .../setup-eviction-shutdown-system.sh | 18 +++++++ linux-scripts/setup-firewall.sh | 24 +++++++++ linux-scripts/setup-motd.sh | 20 +++++++ linux-scripts/setup-node-and-yarn.sh | 53 +++++++++++++++++++ linux-scripts/setup-sms-notifier.sh | 36 +++++++++++++ linux-scripts/update-notifier-config.sh | 8 +++ scripts/create-linux-server.sh | 46 ++++++++++++++++ 8 files changed, 208 insertions(+) create mode 100755 linux-scripts/clean-up.sh create mode 100755 linux-scripts/setup-eviction-shutdown-system.sh create mode 100755 linux-scripts/setup-firewall.sh create mode 100755 linux-scripts/setup-motd.sh create mode 100755 linux-scripts/setup-node-and-yarn.sh create mode 100755 linux-scripts/setup-sms-notifier.sh create mode 100755 linux-scripts/update-notifier-config.sh diff --git a/linux-scripts/clean-up.sh b/linux-scripts/clean-up.sh new file mode 100755 index 0000000..fabb516 --- /dev/null +++ b/linux-scripts/clean-up.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +rm -f *; \ No newline at end of file diff --git a/linux-scripts/setup-eviction-shutdown-system.sh b/linux-scripts/setup-eviction-shutdown-system.sh new file mode 100755 index 0000000..922c810 --- /dev/null +++ b/linux-scripts/setup-eviction-shutdown-system.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +BASE_REPO_URL="https://raw.githubusercontent.com/jpfulton/example-linux-configs/main"; + +# Set up eviction query and shutdown script +EVICTION_QUERY_CRON_SNIPPET_FILE="preempt-query"; +EVICTION_QUERY_SCRIPT="query-for-preempt-event.sh"; +echo "Setting up eviction query script..."; + +sudo wget -q ${BASE_REPO_URL}/usr/local/sbin/${EVICTION_QUERY_SCRIPT}; +sudo chmod ug+x ./${EVICTION_QUERY_SCRIPT} +sudo mv ./${EVICTION_QUERY_SCRIPT} /usr/local/sbin/ + +sudo wget -q ${BASE_REPO_URL}/etc/cron.d/${EVICTION_QUERY_CRON_SNIPPET_FILE}; +sudo mv ./${EVICTION_QUERY_CRON_SNIPPET_FILE} /etc/cron.d/ + +echo "---"; +echo; \ No newline at end of file diff --git a/linux-scripts/setup-firewall.sh b/linux-scripts/setup-firewall.sh new file mode 100755 index 0000000..1a06c05 --- /dev/null +++ b/linux-scripts/setup-firewall.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +BASE_REPO_URL="https://raw.githubusercontent.com/jpfulton/example-linux-configs/main"; + +# Set up local firewall basics +DEFAULTS_PATH="/etc/default/"; +UFW_DEFAULTS_FILE="ufw"; +if [ $(sudo ufw status | grep -c inactive) -ge 1 ] + then + echo "Local firewall is inactive. Configuring and enabling with SSH rule..."; + + sudo wget -q ${BASE_REPO_URL}${DEFAULTS_PATH}${UFW_DEFAULTS_FILE} -O ${UFW_DEFAULTS_FILE}; + sudo mv ${UFW_DEFAULTS_FILE} ${DEFAULTS_PATH}; + + sudo ufw allow ssh; + sudo ufw show added; + sudo ufw --force enable; + sudo ufw status numbered; + + else + echo "Local fireall is active. No configuration or rules applied."; +fi +echo "---"; +echo; \ No newline at end of file diff --git a/linux-scripts/setup-motd.sh b/linux-scripts/setup-motd.sh new file mode 100755 index 0000000..b224ed5 --- /dev/null +++ b/linux-scripts/setup-motd.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +DEBIAN_FRONTEND="noninteractive"; +BASE_REPO_URL="https://raw.githubusercontent.com/jpfulton/example-linux-configs/main"; + +# Set up custom MOTD script +MOTD_PATH="/etc/update-motd.d/"; +MOTD_FILE="01-custom"; +if [ ! -f ${MOTD_PATH}${MOTD_FILE} ] + then + echo "Setting up custom MOTD script..."; + + sudo -E apt-get install -y neofetch inxi; + sudo wget -q ${BASE_REPO_URL}${MOTD_PATH}${MOTD_FILE} -O ${MOTD_FILE}; + sudo chmod a+x ./${MOTD_FILE}; + sudo mv ./${MOTD_FILE} ${MOTD_PATH}${MOTD_FILE}; + + echo "---"; + echo; +fi \ No newline at end of file diff --git a/linux-scripts/setup-node-and-yarn.sh b/linux-scripts/setup-node-and-yarn.sh new file mode 100755 index 0000000..a600a69 --- /dev/null +++ b/linux-scripts/setup-node-and-yarn.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +DEBIAN_FRONTEND="noninteractive"; +BASE_REPO_URL="https://raw.githubusercontent.com/jpfulton/example-linux-configs/main"; + +setup-nodejs () { + # Install Node as needed + which node >> /dev/null; + if [ $? -eq 0 ] + then + local NODE_VERSION=$(node --version); + if [ $NODE_VERSION == "v12.22.9" ] + then + echo "Default node package detected. Removing."; + sudo -E apt-get remove nodejs; + sudo -E apt-get autoremove; + else + echo "Detected alternate version of node: ${NODE_VERSION}"; + echo "Ensure that version is above v18.0.0 or manually use nvm."; + fi + else + echo "Node not detected. Preparing installation of node v18.x."; + + sudo curl -sL https://deb.nodesource.com/setup_18.x | sudo bash -; + sudo -E apt-get install -y nodejs; + fi + + echo "---"; + echo; +} + +setup-yarn () { + # Install Yarn as needed + which yarn >> /dev/null; + if [ $? -eq 1 ] + then + echo "Yarn not detected. Preparing to install."; + + sudo curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | sudo gpg --dearmor | sudo tee /usr/share/keyrings/yarnkey.gpg >/dev/null; + sudo echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian stable main" | sudo tee /etc/apt/sources.list.d/yarn.list; + sudo -E apt-get update; + sudo -E apt-get install -y yarn; + else + local YARN_VERSION=$(yarn --version); + echo "Found yarn version: ${YARN_VERSION}"; + fi + + echo "---"; + echo; +} + +setup-nodejs; +setup-yarn; \ No newline at end of file diff --git a/linux-scripts/setup-sms-notifier.sh b/linux-scripts/setup-sms-notifier.sh new file mode 100755 index 0000000..58215d8 --- /dev/null +++ b/linux-scripts/setup-sms-notifier.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# Install or upgrade sms-notify-cli utility +which sms-notify-cli >> /dev/null; +if [ $? -eq 1 ] + then + echo "sms-notify-cli utility not detected. Preparing to install."; + sudo yarn global add @jpfulton/net-sms-notifier-cli; + else + echo "Found sms-notify-cli utility. Attempting update."; + sudo yarn global upgrade @jpfulton/net-sms-notifier-cli@latest; +fi +echo "---"; +echo; + +# Initialize sms-notify-cli configuration +NOTIFIER_CONFIG="/etc/sms-notifier/notifier.json"; +if [ -f $NOTIFIER_CONFIG ] + then + echo "Found notifier configuration. Validating with current version."; + + sudo sms-notify-cli validate; + if [ $? -eq 0 ] + then + echo "Configuration file validation passes on current version."; + else + echo "Invalid configuration file. Manually correct."; + fi + else + echo "No notifier configuration found. Initializing..."; + echo "Manual configuration to the ${NOTIFIER_CONFIG} file will be required."; + sudo sms-notify-cli init; +fi + +echo "---"; +echo; diff --git a/linux-scripts/update-notifier-config.sh b/linux-scripts/update-notifier-config.sh new file mode 100755 index 0000000..bbaf39e --- /dev/null +++ b/linux-scripts/update-notifier-config.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +sudo chown root:root ~/notifier.json +sudo chmod 0640 ~/notifier.json +sudo mv ~/notifier.json /etc/sms-notifier/notifier.json + +echo "---"; +echo; diff --git a/scripts/create-linux-server.sh b/scripts/create-linux-server.sh index b8d39c7..a4d3292 100755 --- a/scripts/create-linux-server.sh +++ b/scripts/create-linux-server.sh @@ -234,6 +234,24 @@ run-script-from-admin-home () { echo; } +scp-notifier-config () { + echo "Copying SMS Nofifier config..."; + + local NOTIFIER_CONFIG="/etc/sms-notifier/notifier.json" + local REMOTE_LOCATION="~/" + + if [ -f $NOTIFIER_CONFIG ] + then + scp -i $ADMIN_PRIVATE_KEY_FILE \ + $NOTIFIER_CONFIG \ + ${ADMIN_USERNAME}@${SERVER_FQDN}:${REMOTE_LOCATION}; + + run-script-from-admin-home update-notifier-config.sh; + else + echo "WARN: Manual installation of notifier config will be required."; + fi +} + restart-vm () { echo "Restarting VM to allow settings to take effect..."; @@ -264,9 +282,37 @@ main () { # copy setup scripts to server scp-file-to-admin-home ${CURRENT_SCRIPT_DIR}../linux-scripts/update-base-packages.sh; + scp-file-to-admin-home ${CURRENT_SCRIPT_DIR}../linux-scripts/setup-firewall.sh; + scp-file-to-admin-home ${CURRENT_SCRIPT_DIR}../linux-scripts/setup-motd.sh; + scp-file-to-admin-home ${CURRENT_SCRIPT_DIR}../linux-scripts/setup-node-and-yarn.sh; + scp-file-to-admin-home ${CURRENT_SCRIPT_DIR}../linux-scripts/setup-sms-notifier.sh; + scp-file-to-admin-home ${CURRENT_SCRIPT_DIR}../linux-scripts/setup-eviction-shutdown-system.sh; + scp-file-to-admin-home ${CURRENT_SCRIPT_DIR}../linux-scripts/update-notifier-config.sh; + scp-file-to-admin-home ${CURRENT_SCRIPT_DIR}../linux-scripts/clean-up.sh; # execute remote setup scripts run-script-from-admin-home update-base-packages.sh; + run-script-from-admin-home setup-firewall.sh; + run-script-from-admin-home setup-motd.sh; + run-script-from-admin-home setup-node-and-yarn.sh; + + + if [ "$IS_SPOT" = "true" ] + then + run-script-from-admin-home setup-sms-notifier.sh; + scp-notifier-config; + run-script-from-admin-home setup-eviction-shutdown-system.sh; + + if [ "$SPOT_RESTART" = "true" ] + then + echo "Tagging VM for restart after eviction..."; + + local VM_ID=$(az-get-vm-resource-id $RESOURCE_GROUP $SERVER_NAME); + az-add-tag-to-resource $VM_ID "AttemptRestartAfterEviction=true"; + fi + fi + + run-script-from-admin-home clean-up.sh; echo "---"; echo "Done.";