Zadanie z jakim się zmierzam, to ustalenie minimalnych uprawnień jakie powinien posiadać użytkownik, aby wykonać pewną czynność. W tym przypadku chodziło o budowanie specyficznej infrastruktury z wykorzystaniem skryptu Terraform, ale metoda sprawdzi się też w innych scenariuszach.
Zacząłem od stworzenia service principal, bo w moim przypadku skrypt miał być uruchamiany na service principal. Gdyby miało to być zwykłe konto użytkownika, to oczywiście należało by stworzyć konto:
az ad sp create-for-rbac --name myserviceprincipal
W wyniku tego polecenia jest tworzony service principal, a w zwróconym output można znaleźć coś w tym stylu:
{ "appId": "6...XXX...", "displayName": "myserviceprincipal", "password": "y...XXX...", "tenant": "6...XXX..." }
Mamy tu: appId, bo jest on wykorzystywany przez dalsze polecenia i można go utożsamiać z określonym service principal i password, bo to tajna część utworzonego właśnie service principal i wreszcie tenant określający, gdzie tego service principal można używać (w jakiej organizacji).
Kiedy chcesz zalogować się z wykorzystaniem tego service principal, to można to zrobić mniej więcej tak:
az login --service-principal --username "6...XXX..." --password "y...XXX..." --tenant "6...XXX..."
Jak widać, skorzystaliśmy ze wszystkiego oprócz… nazwy service principal.
Zakładając, że konto jest „gołe”, to w/w polecenie może kończyć się błędem
No subscriptions found for 6…XXX….
Oznacza on mniej więcej tyle, że użytkownik nie ma żadnych uprawnień na żadnej subskrypcji. Wystarczy nadać mu jakiekolwiek i problem powinien zniknąć.
Kolejny błąd jest w moim przypadku bardzo specyficzny dla mojego scenariusza. Błąd jest zwracany przez Terraforma, który narzeka, że w bieżącej sesji zalogowałem się na service principal, a tymczasem on (Terraform), na to nie pozwala. Jak chcę używać konta użytkownika to się loguję przez az login i działam, ale przy service principal, Terraform chce, żeby podać mu dane dotyczące uwierzytelnienia w postaci zmiennych środowiskowych. Oto fragment błędu:
building AzureRM Client: Authenticating using the Azure CLI is only supported as a User (not a Service Principal)
Ok. Definicja zmiennych środowiskowych może wyglądać tak:
export ARM_CLIENT_ID="6...XXX..." export ARM_CLIENT_SECRET="y...XXX..." export ARM_SUBSCRIPTION_ID="8...XXX..." export ARM_TENANT_ID="6...XXX..."
dodatkowo w skrypcie terraform (main.tf) należy dodać:
terraform { required_providers { azurerm = { source = "hashicorp/azurerm" version = "=2.91.0" } } } provider "azurerm" { features {} }
Od tej pory można już pracować „w pętli”. Kolejne uruchomienia skryptów kończą się błędami w takim stylu:
Error: retrieving Virtual Network: (Name „v…XXX…” / Resource Group „rg…XX…”): network.VirtualNetworksClient#Get: Failure responding to request: StatusCode=403 — Original Error: autorest/azure: Service returned an error. Status=403 Code=”AuthorizationFailed” Message=”The client '3…XXX…’ with object id '3…XXX…’ does not have authorization to perform action ’Microsoft.Network/virtualNetworks/read’ over scope '/subscriptions/8…XXX…/resourceGroups/rg…XXX…/providers/Microsoft.Network/virtualNetworks/v…XXX…’ or the scope is invalid. If access was recently granted, please refresh your credentials.”
Komunikat wyraźnie stwierdza jakiego uprawnienia brakuje. W tym przypadku jest to np. Microsoft.Network/virtualNetworks/read. Wystarczy więc je przyznać i potwórzyć uruchomienie skryptu, które tym razem powinno się prawdopodobnie znowu skończyć błędem, ale już innym, wskazującym na brak innego wymaganego uprawnienia.
W końcu… przy którejśtam próbie skrypt powinien się skończyć powodzeniem, a przyznane aktualnie upranienia dla service principal, będą tymi najmniejszymi jakie są potrzebne.
Warto jeszcze mieć na uwadze, że to jedno konto może być wykorzystywane do czegoś jeszcze i w takim przypadku, te inne scenariusze użycia również należy przećwiczyć.
Tak przy okazji – ja miałem jeszcze jeden ciekawy błąd, chociaż jak się okazało nie był on powiązany z nadawaniem uprawnień:
Error: making Read request for Flexible Server Configuration: (Configuration Name „pgbouncer.enabled” / Flexible Server Name „ps..XXX…” / Resource Group „r…XXX…”): postgresqlflexibleservers.ConfigurationsClient#Get: Failure sending request: StatusCode=504 — Original Error: context deadline exceeded
O co chodziło? Ten błąd był efektem polecenia zmieniającego opcję PostgreSQL server, ale… ten serwer był wyłączony. Skrypt nie mógł się do niego połączyć i stąd dochodziło do będu context deadline exceeded. Naprawienie sytuacji to po prostu uruchomienie serwera.
Na zakończenie ostatnia uwaga. Od momentu nadania uprawnienia, do momentu kiedy można zacząć z niego korzystać mija chwila lub dwie. To czas potrzebny na rozpropagowanie informacji o uprawnieniach z AAD do obiektów, które następnie z nich korzystają. Życzę więc cierpliwości.