Przygotowanie subskrypcji Dev/Test - Azure Sandbox Subscription
Słowem wstępu
Polubiłem naukę na platformie Microsoft Learn, bo oferuje sandboxowe, tymczasowe subskrypcje. Dzięki temu nie muszę się martwić o koszty czy też późniejsze sprzątanie po ćwiczeniu - zostają one automatycznie usunięte po określonym czasie. Niestety przy niektórych ćwiczeniach konieczne jest użycie własnej subskrypcji i tutaj pojawiał się mały problem - trzeba własnoręcznie czyścić zasoby. Niby nic wielkiego, można pracować z jedną grupą zasobów i później ją usunąć ale czasami to nie wystarcza. Dodatkowo trzeba o tym pamiętać, a pozostawienie uruchomionych usług może sporo kosztować…
Można też ustawić alerty, które ostrzegą nas o przekroczeniu limitu. Jednak z alertami jest taki problem, że koszty z niektórych usług w billingu potrafią pojawiać się ze sporym opóźnieniem, czasami dochodzącym nawet do 24 godzin! Na chwilę obecną nie wiadomo mi o żadnym rozwiązaniu od Microsoftu, które gwarantowało by, że nie przekroczymy określonej kwoty w modelu Pay-As-You-Go.
Myśląc o tym wpadłem na pomysł wykorzystania usługi Azure Automation, w której możemy cyklicznie wykonywać skrypt usuwający zasoby. Rozwiązanie nie idealne ale na moje potrzeby wystarczające. 🙂
Mała uwaga - poniższy skrypt usuwa wszystkie zasoby w danej subskrypcji, więc chcąc go wykorzystać zabezpiecz obecne zasoby lub utwórz nową subskrypcję ☁
Jak się do tego zabrać?
1. Na początku warto utworzyć osobną subskrypcję i nazwać ją sandbox. Dzięki temu w łatwy sposób poznamy przeznaczenie subskrypcji. Oczywiście krok ten nie jest wymagany.
2. Tworzymy grupę zasobów rg-automation przeznaczoną tylko dla usługi Azure Automation.
3. W utworzonej grupie tworzymy Azure Automation Account.
4. Po utworzeniu usługi tworzymy runbook z naszym skryptem
Warto tutaj wspomnieć o tym, że runbook powinien być typu PowerShell Workflow, umożliwi to równoległe wykonywanie pętli usuwającej grupy zasobów. Przydatne ze względu na to, że płacimy za czas wykonywania naszego skryptu.
5. Przeklejamy poniższy skrypt, pamiętając aby nazwa workflow zgadzała się z nazwą runbooka.
workflow PeriodicallyDeleteResourceGroups
{
Write-Output "---------Logging in...---------"
$connectionName = "AzureRunAsConnection"
try
{
# Get the connection "AzureRunAsConnection "
$servicePrincipalConnection=Get-AutomationConnection -Name $connectionName
"Logging in to Azure..."
Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
}
catch {
if (!$servicePrincipalConnection)
{
$ErrorMessage = "Connection $connectionName not found."
throw $ErrorMessage
} else{
Write-Error -Message $_.Exception
throw $_.Exception
}
}
Write-Output "---------Logged in---------"
Write-Output "---------Starting deleting...---------"
$azrg = Get-AzureRmResourceGroup
foreach -parallel ($rg in $azrg)
{
if($rg.Tags.count -eq 0 -Or ($rg.Tags.count -ne 0 -And $rg.Tags["Locked"] -ne "yes"))
{
Write-Output ("Removing resource group: `"" + $rg.ResourceGroupName + "`"")
Remove-AzureRmResourceGroup -Name $rg.ResourceGroupName -Force
}
}
Write-Output "---------Deleting finished---------"
}
Działanie powyższego skryptu jest proste:
- Logowanie/uwierzytelnienie się za pomocą Azure Run As account, które zostało utworzone podczas tworzenia Azure Azutomation.
- Pobranie wszystkich resource group.
- Usunięcie resource group, które nie posiadają taga Lock:yes.
6. Wstrzymujemy się z uruchomieniem skryptu! Jeśli go uruchomiliśmy 🤦♂️ to mamy właśnie okazję zobaczyć samodestrukcję usługi 🤭 oraz jak znikają wszystkie zasoby w subskrypcji. 😈
7. Dodajemy tag Locked:yes do naszej grupy zasobów.
Uruchomienie skryptu w tym momencie będzie miało taki sam skutek jak poprzednio, nawet mając ustawiony tag na grupie zasobów. Dzieje się tak dlatego, że uruchamiając skrypt korzystamy z nieaktualnych modułów i warunek sprawdzający tagi nie zadziała prawidłowo.
8. Aktualizujemy moduły zgodnie z dokumentacją. W skrócie pobieramy skrypt z repozytorium na GitHubie, tworzymy nowy Powershell runbook z pobranym skryptem, zapisujemy go i publikujemy. Następnie uruchamiany z wymaganymi argumentami. Aktualizacja modułów może chwilę potrwać.
9. Przed uruchomieniem skryptu warto zabezpieczyć obecne zasoby za pomocą mechanizmu Lock resources, o którym pisałem we wcześniejszym poście o zabezpieczaniu zasobów w Azure przed usunięciem.
10. Mając zabezpieczone zasoby oraz zaktualizowane moduły jesteśmy gotowi do uruchomienia skryptu. Polecam się nim teraz pobawić dodając kilka grup zasobów, dodając do nich tagi, Lock resources, a nawet dodając do nich zasoby i przetestować zachowanie skryptu.
11. Pozostało już tylko ustawić harmonogram uruchamiania skryptu.
12. Cieszymy się subskrypcją, która będzie czyszczona według naszego harmonogramu!
Dodatkowo runbook możemy przypiąć do dashboardu, co ułatwi jego uruchamianie po skończonej zabawie z usługami. 🙂
Aktualizacja 02.12.2023
W dniu dzisiejszym zauważyłem, że skrypt przestał działać (utworzona wczoraj resource grupa nadal istniała). Powodem było wygaśnięcie certyfikatu dla app registration z którego korzysta Azure Automation, który był ważny do 1.12.2023.
W związku z czym aby dalej cieszyć się czyszczonym środowiskiem musiałbym utworzyć nowy certifikat. Całe szczęście Azure Automation wspiera Managed Identity, dzięki czemu odpada konieczność generowania nowych certifikatów.
To co należało zrobić to:
1. Przejść do Automation Account > sekcja Account Settings
> zakładka Identity
i włączyć system assigned managed identity
2. Nadać uprawnienia do subskrypcji, klikając w Azure role assignments
3. Zaktualizować skrypt, aby korzystał z Managed Identity. Cały zaktualizowany kod przedstawiam poniżej
workflow PeriodicallyDeleteResourceGroups
{
Write-Output "---------Logging in...---------"
try
{
"Logging in to Azure..."
Connect-AzureRMAccount –Identity
Write-Output "---------Logged in---------"
}
catch {
Write-Error -Message $_.Exception
throw $_.Exception
}
Write-Output "---------Starting deleting...---------"
$azrg = Get-AzureRmResourceGroup
foreach -parallel ($rg in $azrg)
{
if($rg.Tags.count -eq 0 -Or ($rg.Tags.count -ne 0 -And $rg.Tags["Locked"] -ne "yes"))
{
Write-Output ("Removing resource group: `"" + $rg.ResourceGroupName + "`"")
Remove-AzureRmResourceGroup -Name $rg.ResourceGroupName -Force
}
}
Write-Output "---------Deleting finished---------"
}
4. Usunąć stare app registration.
Podsumowanie
W dość prosty sposób przygotowałem subskrypcję do zabaw z usługami, gdzie nie muszę się martwić o pozostawione usługi.
Skrypt ten służy mi już od ponad roku i mogę wyciągnąć kilka wniosków z jego używania:
- pod koniec dnia nie muszę martwić się o to, że zapomniałem usunąć zasoby
- mając przypięty runbook do dashboardu wystarczą tylko trzy kliknięcia aby posprzątać subskrypcję
- mając w głowie myśl, że moje zasoby zostaną usunięte zacząłem częściej myśleć w podejściu IaC (Infrastructure as Code)
- kilka razy zapomniałem o skrypcie, za co skrypt mi się odwdzięczył, powiadamiając mnie (w dość brutalny sposób) o późnej porze 🤷♂️
Rozwiązanie nie jest idealne i zawsze można je ulepszyć (np. wykorzystując Even Grid, aby reagować na dodanie nowych zasobów), jednak na chwilę obecną skrypt ten jest dla mnie w zupełności wystarczający. ☁