Skip to content

Cleanup Azure self hosted runners #519

Cleanup Azure self hosted runners

Cleanup Azure self hosted runners #519

name: Cleanup Azure self hosted runners
run-name: Cleanup Azure self hosted runners
on:
schedule:
# Run every 6 hours
- cron: "0 */6 * * *"
workflow_dispatch:
permissions:
id-token: write # required for Azure login via OIDC
# The following secrets are required for this workflow to run:
# AZURE_CLIENT_ID - The Client ID of an Azure Managed Identity. It is recommended to set up a resource
# group specifically for self-hosted Actions Runners, and to add a federated identity
# to authenticate as the currently-running GitHub workflow.
# az identity create --name <managed-identity-name> -g <resource-group>
# az identity federated-credential create \
# --identity-name <managed-identity-name> \
# --resource-group <resource-group> \
# --name github-workflow \
# --issuer https://token.actions.githubusercontent.com \
# --subject repo:git-for-windows/git-for-windows-automation:ref:refs/heads/main \
# --audiences api://AzureADTokenExchange
# MSYS_NO_PATHCONV=1 \
# az role assignment create \
# --assignee <client-id-of-managed-identity> \
# --scope '/subscriptions/<subscription-id>/resourceGroups/<resource-group>' \
# --role 'Contributor'
# AZURE_TENANT_ID - The Tenant ID of the Azure Managed Identity (i.e. the Azure Active Directory in which
# the Identity lives)
# AZURE_SUBSCRIPTION_ID - The Subscription ID with which the Azure Managed Identity is associated
# (technically, this is not necessary for `az login --service-principal` with a
# managed identity, but `Azure/login` requires it anyway)
# AZURE_RESOURCE_GROUP - Resource group to find the runner(s) in. It's recommended to set up a resource
# group specifically for self-hosted Actions Runners.
jobs:
delete-runner:
if: github.repository_owner == 'git-for-windows'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Discover VMs to delete
env:
GH_APP_ID: ${{ secrets.GH_APP_ID }}
GH_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }}
run: |
active_vms=$(az vm list -g ${{ secrets.AZURE_RESOURCE_GROUP }} | jq -c '.[] | {name,timeCreated}')
current_time=$(date +%s)
one_hour_ago=$(($current_time - 3600))
if [ -z "$active_vms" ]; then
echo "No active VMs found, nothing to do."
exit 0
else
echo "Found these active VMs:"
echo $active_vms
fi
for active_vm in ${active_vms[@]}; do
vm_name=$(echo $active_vm | jq -r '.name')
# Use jq to extract and format the date-time string
vm_creation_time_string="$(echo $active_vm |
jq -r '.timeCreated | sub("\\.[0-9]+[+-][0-9]+:[0-9]+$"; "") | sub("T"; " ")')"
vm_creation_time=$(TZ=UTC date -d "$vm_creation_time_string" +%s)
if [ "$one_hour_ago" -lt "$vm_creation_time" ]; then
echo "::notice::The VM ${vm_name} was created less then 1 hour ago and shouldn't be deleted yet. Skipping."
elif test true = "$(if test ! -f .cli-authenticated; then
./gh-cli-auth-as-app.sh &&
>.cli-authenticated # only authenticate once
fi &&
gh api repos/$GITHUB_REPOSITORY/actions/runners \
--jq '.runners[] | select(.name == "'$vm_name'") | .busy')"; then
echo "::notice::The VM ${vm_name} is still busy."
else
echo "::warning::The VM ${vm_name} was created more than 3 hours ago and wasn't deleted. Let's do that now."
az vm delete -n "$vm_name" -g ${{ secrets.AZURE_RESOURCE_GROUP }} --yes
az network nsg delete -n "$vm_name"-nsg -g ${{ secrets.AZURE_RESOURCE_GROUP }}
az network vnet delete -n "$vm_name"-vnet -g ${{ secrets.AZURE_RESOURCE_GROUP }}
az network public-ip delete -n "$vm_name"-ip -g ${{ secrets.AZURE_RESOURCE_GROUP }}
fi
done