Création d'utilisateurs Google à partir de PowerShell via l'API
Salut!
Cet article décrira la mise en œuvre de l'interaction PowerShell avec l'API Google pour manipuler les utilisateurs de G Suite.
Nous utilisons plusieurs services internes et cloud dans toute l'organisation. Pour la plupart, l'autorisation revient à Google ou à Active Directory, entre lesquels nous ne pouvons pas maintenir de réplique ; par conséquent, lorsqu'un nouvel employé part, vous devez créer/activer un compte dans ces deux systèmes. Pour automatiser le processus, nous avons décidé d'écrire un script qui collecte des informations et les envoie aux deux services.
AUTORISATION
Lors de l'élaboration des exigences, nous avons décidé de faire appel à de véritables administrateurs humains pour l'autorisation, ce qui simplifie l'analyse des actions en cas de changements massifs accidentels ou intentionnels.
Les API Google utilisent le protocole OAuth 2.0 pour l'authentification et l'autorisation. Des cas d'utilisation et des descriptions plus détaillées peuvent être trouvés ici : Utiliser OAuth 2.0 pour accéder aux API Google.
J'ai choisi le script utilisé pour l'autorisation dans les applications de bureau. Il existe également la possibilité d'utiliser un compte de service, qui ne nécessite pas de mouvements inutiles de la part de l'utilisateur.
L'image ci-dessous est une description schématique du scénario sélectionné sur la page Google.
Tout d'abord, nous renvoyons l'utilisateur vers la page d'authentification du compte Google, en spécifiant les paramètres GET :
ID d'application
zones auxquelles l’application doit accéder
l'adresse vers laquelle l'utilisateur sera redirigé après avoir terminé la procédure
la façon dont nous mettrons à jour le jeton
Code de sécurité
format de transmission du code de vérification
Une fois l'autorisation terminée, l'utilisateur sera redirigé vers la page spécifiée dans la première requête, avec une erreur ou un code d'autorisation transmis par les paramètres GET.
L'application (script) devra recevoir ces paramètres et, si elle reçoit le code, faire la requête suivante pour obtenir des tokens
Si la requête est correcte, l'API Google renvoie :
Jeton d'accès avec lequel nous pouvons faire des demandes
La durée de validité de ce token
Jeton d’actualisation requis pour actualiser le jeton d’accès.
Vous devez d'abord accéder à la console API Google : Identifiants – Console API Google, sélectionnez l'application souhaitée et dans la section Identifiants créez un identifiant client OAuth. Là (ou plus tard, dans les propriétés de l'identifiant créé), vous devez spécifier les adresses vers lesquelles la redirection est autorisée. Dans notre cas, il s'agira de plusieurs entrées localhost avec des ports différents (voir ci-dessous).
Pour faciliter la lecture de l'algorithme du script, vous pouvez afficher les premières étapes dans une fonction distincte qui renverra les jetons d'accès et d'actualisation pour l'application :
Nous définissons l'ID client et le secret client obtenus dans les propriétés de l'identifiant client OAuth, et le vérificateur de code est une chaîne de 43 à 128 caractères qui doivent être générés aléatoirement à partir de caractères non réservés : [AZ] / [az] / [0-9 ] / "-" / "." / "_" / "~".
Ce code sera ensuite à nouveau transmis. Il élimine la vulnérabilité dans laquelle un attaquant pourrait intercepter une réponse renvoyée sous forme de redirection après l'autorisation de l'utilisateur.
Vous pouvez envoyer un vérificateur de code dans la requête actuelle en texte clair (ce qui le rend dénué de sens - cela ne convient que pour les systèmes qui ne prennent pas en charge SHA256), ou en créant un hachage à l'aide de l'algorithme SHA256, qui doit être codé en BASE64Url (différent de Base64 par deux caractères de table) et en supprimant les fins de ligne de caractères : =.
Ensuite, nous devons commencer à écouter http sur la machine locale afin de recevoir une réponse après autorisation, qui sera renvoyée sous forme de redirection.
Les tâches administratives sont effectuées sur un serveur spécial, nous ne pouvons pas exclure la possibilité que plusieurs administrateurs exécutent le script en même temps, il sélectionnera donc aléatoirement un port pour l'utilisateur actuel, mais j'ai spécifié des ports prédéfinis car ils doivent également être ajoutés comme étant approuvés dans la console API.
access_type=hors ligne signifie que l'application peut mettre à jour elle-même un token expiré sans interaction de l'utilisateur avec le navigateur, type_réponse=code définit le format de retour du code (une référence à l'ancienne méthode d'autorisation, lorsque l'utilisateur copiait le code du navigateur dans le script), portée indique l’étendue et le type d’accès. Ils doivent être séparés par des espaces ou %20 (selon l'encodage de l'URL). Une liste des zones d'accès avec leurs types peut être consultée ici : Portées OAuth 2.0 pour les API Google.
Après avoir reçu le code d'autorisation, l'application renverra un message de fermeture au navigateur, cessera d'écouter sur le port et enverra une requête POST pour obtenir le token. Nous y indiquons l'identifiant et le secret précédemment spécifiés de l'API de la console, l'adresse vers laquelle l'utilisateur sera redirigé et le type d'octroi conformément à la spécification du protocole.
En réponse, nous recevrons un jeton d'accès, sa période de validité en secondes et un jeton d'actualisation, avec lequel nous pourrons mettre à jour le jeton d'accès.
L'application doit stocker les jetons dans un endroit sécurisé avec une longue durée de conservation, donc jusqu'à ce que nous révoquions l'accès reçu, l'application ne renverra pas le jeton d'actualisation. À la fin, j'ai ajouté une demande de révocation du jeton ; si l'application n'a pas été complétée avec succès et que le jeton d'actualisation n'a pas été renvoyé, la procédure recommencera (nous avons considéré qu'il était dangereux de stocker les jetons localement sur le terminal, et nous ne le faisons pas). Je ne veux pas compliquer les choses avec la cryptographie ou ouvrir fréquemment le navigateur).
Comme vous l'avez déjà remarqué, lors de la révocation d'un jeton, Invoke-WebRequest est utilisé. Contrairement à Invoke-RestMethod, il ne renvoie pas les données reçues dans un format utilisable et affiche l'état de la demande.
Ensuite, le script vous demande de saisir le prénom et le nom de l’utilisateur, générant un login + email.
demandes
Les prochaines requêtes seront - tout d'abord, vous devez vérifier si un utilisateur avec le même login existe déjà afin d'obtenir une décision sur la création d'un nouveau ou l'activation de l'actuel.
J'ai décidé d'implémenter toutes les requêtes sous la forme d'une fonction avec une sélection, en utilisant switch :
Dans chaque requête, vous devez envoyer un en-tête Authorization contenant le type de jeton et le jeton d'accès lui-même. Actuellement, le type de jeton est toujours Bearer. Parce que nous devons vérifier que le jeton n'a pas expiré et le mettre à jour une heure après son émission, j'ai spécifié une demande pour une autre fonction qui renvoie un jeton d'accès. Le même morceau de code se trouve au début du script lors de la réception du premier jeton d'accès :
La requête email:$query demandera à l'API de rechercher un utilisateur avec exactement cette adresse e-mail, y compris les alias. Vous pouvez également utiliser un caractère générique : =, :, :{PRÉFIXE}*.
Pour obtenir des données, utilisez la méthode de requête GET, pour insérer des données (création d'un compte ou ajout d'un membre à un groupe) - POST, pour mettre à jour des données existantes - PUT, pour supprimer un enregistrement (par exemple, un membre d'un groupe) - SUPPRIMER.
Le script demandera également un numéro de téléphone (une chaîne non validée) et l'inclusion dans un groupe de distribution régional. Il décide quelle unité organisationnelle l'utilisateur doit avoir en fonction de l'unité d'organisation Active Directory sélectionnée et propose un mot de passe :
do {
$phone = Read-Host "Телефон в формате +7хххххххх"
} while (-not $phone)
do {
$moscow = Read-Host "В Московский офис? (y/n) "
} while (-not (($moscow -eq 'y') -or ($moscow -eq 'n')))
$orgunit = '/'
if ($OU -like "*OU=Delivery,OU=Users,OU=ROOT,DC=rocket,DC=local") {
Write-host "Будет создана в /Team delivery"
$orgunit = "/Team delivery"
}
$Password = -join ( 48..57 + 65..90 + 97..122 | Get-Random -Count 12 | % {[char]$_})+"*Ba"
Et puis il commence à manipuler le compte :
$query = @{
email = $email
givenName = $firstname
familyName = $lastname
password = $password
phone = $phone
orgunit = $orgunit
}
if ($GMailExist) {
Write-Host "Запускаем изменение аккаунта" -f mag
(GoogleQuery 'UpdateAccount' $query) | fl
write-host "Не забудь проверить группы у включенного $Username в Google."
} else {
Write-Host "Запускаем создание аккаунта" -f mag
(GoogleQuery 'CreateAccount' $query) | fl
}
if ($moscow -eq "y"){
write-host "Добавляем в группу moscowoffice"
$query = @{
groupkey = '[email protected]'
email = $email
}
(GoogleQuery 'AddMember' $query) | fl
}
Les fonctions de mise à jour et de création d'un compte ont une syntaxe similaire ; tous les champs supplémentaires ne sont pas obligatoires ; dans la section avec les numéros de téléphone, vous devez spécifier un tableau pouvant contenir jusqu'à un enregistrement avec le numéro et son type.
Afin de ne pas recevoir d'erreur lors de l'ajout d'un utilisateur à un groupe, nous pouvons d'abord vérifier s'il est déjà membre de ce groupe en obtenant une liste des membres du groupe ou une composition auprès de l'utilisateur lui-même.
L'interrogation de l'appartenance au groupe d'un utilisateur spécifique ne sera pas récursive et affichera uniquement l'appartenance directe. L’inclusion d’un utilisateur dans un groupe parent qui possède déjà un groupe enfant dont l’utilisateur est membre réussira.
Conclusion
Il ne reste plus qu'à envoyer à l'utilisateur le mot de passe du nouveau compte. Nous le faisons par SMS et envoyons des informations générales avec des instructions et nous nous connectons à une adresse e-mail personnelle, qui, avec un numéro de téléphone, a été fournie par le service de recrutement. Comme alternative, vous pouvez économiser de l'argent et envoyer votre mot de passe à un chat par télégramme secret, ce qui peut également être considéré comme le deuxième facteur (les MacBook seront une exception).
Merci d'avoir lu jusqu'au bout. Je serai heureux de voir des suggestions pour améliorer le style d'écriture des articles et je vous souhaite de détecter moins d'erreurs lors de la rédaction de scripts =)
Liste de liens qui peuvent être thématiquement utiles ou simplement répondre à des questions :