# Admin SDK gebruiken in Python voor gebruikersbeheer [[TOC]] Gebruikersbeheer met de hand werkt prima voor een handvol mensen, maar zodra je organisatie groeit wil je provisioning automatiseren. De Admin SDK Directory API laat je gebruikers, groepen en organisatie-eenheden volledig programmatisch beheren. In Python is dat een kwestie van een service bouwen en de juiste methoden aanroepen. In dit artikel automatiseer je de volledige levenscyclus van een gebruiker: aanmaken bij instroom, bijwerken bij een functiewijziging en uitschakelen bij vertrek. ## Wat je vooraf nodig hebt Installeer eerst de officiele clientbibliotheken: ```bash pip install google-auth google-api-python-client ``` Je hebt verder nodig: - Een service account in een Google Cloud-project met de Admin SDK API ingeschakeld. - Domain-wide delegation voor dat service account, geautoriseerd door een super-beheerder in de Admin console. - Een beheerdersaccount in je domein dat het script mag impersoneren. ## Authenticatie met impersonation De Admin SDK werkt namens een beheerder. Een service account met domain-wide delegation kan een beheerder impersoneren via `with_subject`. ```python from google.oauth2 import service_account from googleapiclient.discovery import build SCOPES = ['https://www.googleapis.com/auth/admin.directory.user'] creds = service_account.Credentials.from_service_account_file( '/secrets/sa-key.json', scopes=SCOPES ).with_subject('beheerder@jouwdomein.nl') service = build('admin', 'directory_v1', credentials=creds) ``` :::warn title="Beperk de scope en bewaar de sleutel veilig" De scope `admin.directory.user` geeft volledige controle over alle gebruikers in je domein. Bewaar de service-accountsleutel in een secret manager (niet in je repo), geef alleen de scopes die je echt nodig hebt en log elke wijziging die je script doorvoert, zodat je altijd een audittrail hebt. ::: ## Een gebruiker aanmaken Voor een nieuwe gebruiker geef je minimaal een naam, een primair e-mailadres en een initieel wachtwoord mee. ```python nieuwe_gebruiker = { 'name': {'givenName': 'Jan', 'familyName': 'Jansen'}, 'primaryEmail': 'jan.jansen@jouwdomein.nl', 'password': genereer_sterk_wachtwoord(), 'changePasswordAtNextLogin': True, 'orgUnitPath': '/Verkoop', } service.users().insert(body=nieuwe_gebruiker).execute() ``` :::tip title="Forceer een eigen wachtwoord bij eerste login" Zet `changePasswordAtNextLogin` op `True` zodat de gebruiker bij de eerste login een eigen wachtwoord kiest. Genereer het tijdelijke wachtwoord willekeurig en lang, en deel het via een veilig kanaal, nooit via gewone e-mail. ::: ## De levenscyclus automatiseren Een typische HR-integratie dekt instroom, wijziging en uitstroom. Het patroon ziet er zo uit: 1. Een nieuwe medewerker in het HR-systeem leidt tot `users().insert()`. 2. Een functiewijziging leidt tot `users().update()` met een nieuwe organisatie-eenheid (`orgUnitPath`) of nieuwe groepslidmaatschappen. 3. Bij vertrek zet je eerst `suspended` op `True`, zodat e-mail en bestanden behouden blijven voor overdracht. 4. Na de bewaartermijn volgt `users().delete()` of een transfer van de data via de Data Transfer API. ## Een gebruiker uitschakelen Bij vertrek schakel je een account eerst uit in plaats van het direct te verwijderen. Zo blijven e-mail en bestanden beschikbaar voor overdracht. ```python service.users().update( userKey='jan.jansen@jouwdomein.nl', body={'suspended': True} ).execute() ``` ## Gebruikers ophalen en doorlopen Voor synchronisatie wil je vaak alle gebruikers ophalen. Houd rekening met paginering: gebruik `list_next()` om automatisch de volgende pagina op te halen. ```python gebruikers = [] request = service.users().list(customer='my_customer', maxResults=200) while request is not None: response = request.execute() gebruikers.extend(response.get('users', [])) request = service.users().list_next(request, response) ``` :::info title="my_customer is een handige alias" De waarde `my_customer` verwijst naar het account van de geauthenticeerde beheerder. Je hoeft je echte customer-ID dus niet hard te coderen. ::: ## Omgaan met rate limits De Directory API kent quota per project. Bij grootschalige synchronisaties loop je tegen `403 rateLimitExceeded` of `429`-fouten aan. Vang deze op met een exponentiele backoff en bundel waar mogelijk meerdere bewerkingen. :::howto title="Robuust schrijven naar de Directory API" 1. Vang `HttpError` af en lees `error.resp.status` uit. 2. Bij status `403` of `429`: wacht en probeer opnieuw met een oplopende wachttijd (bijvoorbeeld 1, 2, 4, 8 seconden) plus wat willekeurige jitter. 3. Gebruik batch-requests om meerdere `insert`- of `update`-calls te bundelen. 4. Log mislukte bewerkingen apart zodat je ze later gericht opnieuw kunt draaien. ::: ## Veelgestelde vragen :::faq ### Waarom krijg ik een 403 ondanks correcte scopes? Vaak ontbreekt de domain-wide delegation in de Admin console, of het geimpersoneerde account is geen beheerder. Controleer of de client-ID van het service account met exact deze scopes is geautoriseerd, en of het account uit `with_subject` superbeheerder- of voldoende rechten heeft. ### Kan ik een gebruiker direct verwijderen? Ja, met `users().delete()`, maar dat is onomkeerbaar. Schakel het account eerst uit met `suspended=True` en regel dataoverdracht voordat je verwijdert. ### Hoe ken ik een gebruiker aan een organisatie-eenheid toe? Zet het veld `orgUnitPath` in de body bij `insert` of `update`, bijvoorbeeld op `/Verkoop`. De OU moet al bestaan in je domein. ### Kan ik meerdere gebruikers in een keer aanmaken? Niet in een enkele call. Wel kun je batch-requests gebruiken om meerdere inserts efficient te bundelen, met backoff tegen rate limits. ### Welke scope heb ik minimaal nodig? Voor alleen-lezen synchronisatie volstaat `admin.directory.user.readonly`. Voor aanmaken, wijzigen en uitschakelen heb je de volledige scope `admin.directory.user` nodig. ::: Met de Admin SDK in Python automatiseer je gebruikersbeheer betrouwbaar en houd je je directory synchroon met je bronsystemen.