API-ээр дамжуулан PowerShell-ээс Google хэрэглэгчдийг үүсгэх

Сайн уу!

Энэ нийтлэлд G Suite хэрэглэгчдийг удирдахын тулд Google API-тай PowerShell-ийн харилцан үйлчлэлийн хэрэгжилтийг тайлбарлах болно.

Бид байгууллагын хэмжээнд хэд хэдэн дотоод болон үүлэн үйлчилгээг ашигладаг. Ихэнх тохиолдолд эдгээрийн зөвшөөрөл нь Google эсвэл Active Directory-д ирдэг бөгөөд бид тэдгээрийн хуулбарыг хадгалах боломжгүй тул шинэ ажилтан гарах үед та эдгээр хоёр системд бүртгэл үүсгэх/идэвхжүүлэх хэрэгтэй. Үйл явцыг автоматжуулахын тулд бид мэдээлэл цуглуулж, хоёр үйлчилгээнд илгээдэг скрипт бичихээр шийдсэн.

Зөвшөөрөл

Шаардлагуудыг боловсруулахдаа бид жинхэнэ хүний ​​администраторуудыг ашиглахаар шийдсэн бөгөөд энэ нь санамсаргүй эсвэл санаатайгаар их хэмжээний өөрчлөлт гарсан тохиолдолд хийсэн үйлдлийн дүн шинжилгээг хялбаршуулдаг.

Google API нь баталгаажуулалт болон зөвшөөрлийн хувьд OAuth 2.0 протоколыг ашигладаг. Хэрэглээний тохиолдол болон илүү дэлгэрэнгүй тайлбарыг эндээс олж болно: Google API-д хандахын тулд OAuth 2.0 ашиглаж байна.

Би ширээний програмуудад зөвшөөрөл олгоход ашигладаг скриптийг сонгосон. Үйлчилгээний данс ашиглах сонголт бас бий бөгөөд энэ нь хэрэглэгчээс шаардлагагүй хөдөлгөөн шаарддаггүй.

Доорх зураг нь Google хуудаснаас сонгосон хувилбарын бүдүүвч тайлбар юм.

API-ээр дамжуулан PowerShell-ээс Google хэрэглэгчдийг үүсгэх

  1. Эхлээд бид GET параметрүүдийг зааж өгсөн хэрэглэгчийг Google Бүртгэлийн баталгаажуулалтын хуудас руу илгээдэг.
    • програмын ID
    • Програмд ​​хандах шаардлагатай газрууд
    • процедурыг дуусгасны дараа хэрэглэгчийг дахин чиглүүлэх хаяг
    • бид токеныг шинэчлэх арга зам
    • Нууцлалын код
    • баталгаажуулах код дамжуулах формат

  2. Зөвшөөрөл дууссаны дараа хэрэглэгчийг GET параметрээр дамжуулсан алдаа эсвэл зөвшөөрлийн кодтой эхний хүсэлтэд заасан хуудас руу дахин чиглүүлэх болно.
  3. Аппликешн (скрипт) эдгээр параметрүүдийг хүлээн авах шаардлагатай бөгөөд хэрэв код хүлээн авсан бол жетон авахын тулд дараах хүсэлтийг хийнэ үү.
  4. Хэрэв хүсэлт зөв бол Google API нь дараахыг буцаана:
    • Бид хүсэлт гаргах боломжтой хандалтын токен
    • Энэ жетон хүчинтэй байх хугацаа
    • Хандалтын токеныг сэргээхийн тулд токеныг шинэчлэх шаардлагатай.

Эхлээд та Google API консол руу очих хэрэгтэй: Итгэмжлэл - Google API консол, хүссэн програмаа сонгоод Credentials хэсэгт клиент OAuth танигч үүсгэнэ үү. Тэнд (эсвэл дараа нь үүсгэсэн танигчийн шинж чанарт) та дахин чиглүүлэхийг зөвшөөрсөн хаягуудыг зааж өгөх хэрэгтэй. Манай тохиолдолд эдгээр нь өөр порттой хэд хэдэн localhost оруулгууд байх болно (доороос үзнэ үү).

Скриптийн алгоритмыг уншихад илүү тохиромжтой болгохын тулд та эхний алхмуудыг тусдаа функцээр харуулах боломжтой бөгөөд энэ нь Access болон програмын токенуудыг шинэчлэх болно.

$client_secret = 'Our Client Secret'
$client_id = 'Our Client ID'
function Get-GoogleAuthToken {
  if (-not [System.Net.HttpListener]::IsSupported) {
    "HttpListener is not supported."
    exit 1
  }
  $codeverifier = -join ((65..90) + (97..122) + (48..57) + 45 + 46 + 95 + 126 |Get-Random -Count 60| % {[char]$_})
  $hasher = new-object System.Security.Cryptography.SHA256Managed
  $hashByteArray = $hasher.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($codeverifier))
  $base64 = ((([System.Convert]::ToBase64String($hashByteArray)).replace('=','')).replace('+','-')).replace('/','_')
  $ports = @(10600,15084,39700,42847,65387,32079)
  $port = $ports[(get-random -Minimum 0 -maximum 5)]
  Write-Host "Start browser..."
  Start-Process "https://accounts.google.com/o/oauth2/v2/auth?code_challenge_method=S256&code_challenge=$base64&access_type=offline&client_id=$client_id&redirect_uri=http://localhost:$port&response_type=code&scope=https://www.googleapis.com/auth/admin.directory.user https://www.googleapis.com/auth/admin.directory.group"
  $listener = New-Object System.Net.HttpListener
  $listener.Prefixes.Add("http://localhost:"+$port+'/')
  try {$listener.Start()} catch {
    "Unable to start listener."
    exit 1
  }
  while (($code -eq $null)) {
    $context = $listener.GetContext()
    Write-Host "Connection accepted" -f 'mag'
    $url = $context.Request.RawUrl
    $code = $url.split('?')[1].split('=')[1].split('&')[0]
    if ($url.split('?')[1].split('=')[0] -eq 'error') {
      Write-Host "Error!"$code -f 'red'
      $buffer = [System.Text.Encoding]::UTF8.GetBytes("Error!"+$code)
      $context.Response.ContentLength64 = $buffer.Length
      $context.Response.OutputStream.Write($buffer, 0, $buffer.Length)
      $context.Response.OutputStream.Close()
      $listener.Stop()
      exit 1
    }
    $buffer = [System.Text.Encoding]::UTF8.GetBytes("Now you can close this browser tab.")
    $context.Response.ContentLength64 = $buffer.Length
    $context.Response.OutputStream.Write($buffer, 0, $buffer.Length)
    $context.Response.OutputStream.Close()
    $listener.Stop()
  }
  Return Invoke-RestMethod -Method Post -Uri "https://www.googleapis.com/oauth2/v4/token" -Body @{
    code = $code
    client_id = $client_id
    client_secret = $client_secret
    redirect_uri = 'http://localhost:'+$port
    grant_type = 'authorization_code'
    code_verifier   = $codeverifier
  }
  $code = $null

Бид OAuth клиент танигчийн шинж чанарт олж авсан Client ID болон Client Secret-ийг тохируулсан бөгөөд код шалгагч нь нөөцгүй тэмдэгтүүдээс санамсаргүй байдлаар үүсгэгдэх ёстой 43-128 тэмдэгтийн мөр юм: [AZ] / [az] / [0-9 ] / "-" / "." / "_" / "~".

Дараа нь энэ кодыг дахин дамжуулах болно. Энэ нь халдагчид хэрэглэгчийн зөвшөөрөл авсны дараа дахин чиглүүлэлт болгон буцаасан хариултыг таслан зогсоож болзошгүй эмзэг байдлыг арилгадаг.
Та одоогийн хүсэлтэд код баталгаажуулагчийг тодорхой текстээр (энэ нь утгагүй болгодог - энэ нь зөвхөн SHA256-г дэмждэггүй системд тохиромжтой) эсвэл BASE256Url-д кодлогдсон байх ёстой SHA64 алгоритмыг ашиглан хэш үүсгэх замаар илгээх боломжтой (ялгаатай Base64-ээс хүснэгтийн хоёр тэмдэгтээр) ба тэмдэгтийн мөрийн төгсгөлийг хасна: =.

Дараа нь бид зөвшөөрлийн дараа хариуг хүлээн авахын тулд локал машин дээр http-г сонсож эхлэх хэрэгтэй бөгөөд үүнийг дахин чиглүүлэлт болгон буцаана.

Захиргааны ажлыг тусгай сервер дээр гүйцэтгэдэг тул хэд хэдэн администратор скриптийг нэгэн зэрэг ажиллуулахыг үгүйсгэх аргагүй, тиймээс энэ нь одоогийн хэрэглэгчийн портыг санамсаргүй байдлаар сонгох болно, гэхдээ би урьдчилан тодорхойлсон портуудыг зааж өгсөн. тэдгээрийг API консолд итгэмжлэгдсэн байдлаар нэмэх шаардлагатай.

хандалтын_төрөл=офлайн Энэ нь хэрэглэгчийн хөтөчтэй харилцахгүйгээр програм нь хугацаа нь дууссан жетоныг өөрөө шинэчлэх боломжтой гэсэн үг,
хариултын_төрөл=код кодыг хэрхэн буцаах форматыг тохируулдаг (хэрэглэгч кодыг хөтчөөс скрипт рүү хуулсан хуучин зөвшөөрлийн аргын лавлагаа),
хамрах хүрээ хандалтын хамрах хүрээ, төрлийг заана. Тэдгээр нь хоосон зай эсвэл 20% (URL кодчилолын дагуу) тусгаарлагдсан байх ёстой. Төрөл бүхий хандалтын хэсгүүдийн жагсаалтыг эндээс харж болно: Google API-д зориулсан OAuth 2.0 хамрах хүрээ.

Зөвшөөрлийн кодыг хүлээн авсны дараа програм нь хөтөч рүү ойрын мессежийг буцааж, порт дээр сонсохоо зогсоож, жетон авахын тулд POST хүсэлт илгээнэ. Бид үүн дээр өмнө нь заасан ID болон консол API-ийн нууц, хэрэглэгчийг дахин чиглүүлэх хаяг, протоколын тодорхойлолтын дагуу grant_type зааж өгдөг.

Үүний хариуд бид Хандалтын токен, түүний хүчинтэй байх хугацаа секундээр, Refresh жетоныг хүлээн авах бөгөөд үүгээр бид Access токеныг шинэчлэх боломжтой.

Аппликешн нь жетоныг удаан хугацаагаар хадгалах боломжтой, найдвартай газар хадгалах ёстой тул хүлээн авсан хандалтыг цуцлах хүртэл програм нь сэргээх жетоныг буцааж өгөхгүй. Төгсгөлд нь би жетоныг хүчингүй болгох хүсэлтийг нэмсэн; хэрэв хэрэглүүр амжилттай дуусаагүй бөгөөд сэргээх токен буцаж ирээгүй бол энэ нь процедурыг дахин эхлүүлэх болно (бид жетоныг терминал дээр хадгалах нь аюултай гэж үзсэн. криптографийн тусламжтайгаар аливаа зүйлийг төвөгтэй болгох эсвэл хөтөчийг байнга нээхийг хүсэхгүй байна).

do {
  $token_result = Get-GoogleAuthToken
  $token = $token_result.access_token
  if ($token_result.refresh_token -eq $null) {
    Write-Host ("Session is not destroyed. Revoking token...")
    Invoke-WebRequest -Uri ("https://accounts.google.com/o/oauth2/revoke?token="+$token)
  }
} while ($token_result.refresh_token -eq $null)
$refresh_token = $token_result.refresh_token
$minute = ([int]("{0:mm}" -f ([timespan]::fromseconds($token_result.expires_in))))+((Get-date).Minute)-2
if ($minute -lt 0) {$minute += 60}
elseif ($minute -gt 59) {$minute -=60}
$token_expire = @{
  hour = ([int]("{0:hh}" -f ([timespan]::fromseconds($token_result.expires_in))))+((Get-date).Hour)
  minute = $minute
}

Та аль хэдийн анзаарсанчлан жетоныг хүчингүй болгохдоо Invoke-WebRequest ашигладаг. Invoke-RestMethod-ээс ялгаатай нь энэ нь хүлээн авсан өгөгдлийг ашиглах боломжтой форматаар буцааж өгөхгүй бөгөөд хүсэлтийн статусыг харуулдаг.

Дараа нь скрипт нь хэрэглэгчийн нэр, овог нэрийг оруулахыг хүсэх бөгөөд нэвтрэх + имэйл үүсгэх болно.

хүсэлтүүд

Дараагийн хүсэлтүүд нь юуны түрүүнд шинээр нэвтрэх эсвэл одоогийн нэвтрэх эрхийг идэвхжүүлэх шийдвэр гаргахын тулд ижил нэвтрэх эрхтэй хэрэглэгч байгаа эсэхийг шалгах хэрэгтэй.

Би шилжүүлэгчийг ашиглан бүх хүсэлтийг нэг функцийн форматаар сонгохоор шийдсэн:

function GoogleQuery {
  param (
    $type,
    $query
  )
  switch ($type) {
    "SearchAccount" {
      Return Invoke-RestMethod -Method Get -Uri "https://www.googleapis.com/admin/directory/v1/users" -Headers @{Authorization = "Bearer "+(Get-GoogleToken)} -Body @{
        domain = 'rocketguys.com'
        query  = "email:$query"
      }
    }
    "UpdateAccount" {
      $body = @{
        name  = @{
          givenName = $query['givenName']
          familyName = $query['familyName']
        }
        suspended = 'false'
        password = $query['password']
        changePasswordAtNextLogin = 'true'
        phones = @(@{
          primary = 'true'
          value = $query['phone']
          type = "mobile"
        })
        orgUnitPath = $query['orgunit']
      }
      Return Invoke-RestMethod -Method Put -Uri ("https://www.googleapis.com/admin/directory/v1/users/"+$query['email']) -Headers @{Authorization = "Bearer "+(Get-GoogleToken)} -Body (ConvertTo-Json $body) -ContentType 'application/json; charset=utf-8'
    }
    
    "CreateAccount" {
      $body = @{
        primaryEmail = $query['email']
        name  = @{
          givenName = $query['givenName']
          familyName = $query['familyName']
        }
        suspended = 'false'
        password = $query['password']
        changePasswordAtNextLogin = 'true'
        phones = @(@{
          primary = 'true'
          value = $query['phone']
          type = "mobile"
        })
        orgUnitPath = $query['orgunit']
      }
      Return Invoke-RestMethod -Method Post -Uri "https://www.googleapis.com/admin/directory/v1/users" -Headers @{Authorization = "Bearer "+(Get-GoogleToken)} -Body (ConvertTo-Json $body) -ContentType 'application/json; charset=utf-8'
    }
    "AddMember" {
      $body = @{
        userKey = $query['email']
      }
      $ifrequest = Invoke-RestMethod -Method Get -Uri "https://www.googleapis.com/admin/directory/v1/groups" -Headers @{Authorization = "Bearer "+(Get-GoogleToken)} -Body $body
      $array = @()
      foreach ($group in $ifrequest.groups) {$array += $group.email}
      if ($array -notcontains $query['groupkey']) {
        $body = @{
          email = $query['email']
          role = "MEMBER"
        }
        Return Invoke-RestMethod -Method Post -Uri ("https://www.googleapis.com/admin/directory/v1/groups/"+$query['groupkey']+"/members") -Headers @{Authorization = "Bearer "+(Get-GoogleToken)} -Body (ConvertTo-Json $body) -ContentType 'application/json; charset=utf-8'
      } else {
        Return ($query['email']+" now is a member of "+$query['groupkey'])
      }
    }
  }
}

Хүсэлт бүрт та жетоны төрөл болон Хандалтын токеныг агуулсан Зөвшөөрлийн толгой хэсгийг илгээх шаардлагатай. Одоогоор токен төрөл нь үргэлж Bearer байна. Учир нь бид жетоны хугацаа дуусаагүй эсэхийг шалгаж, түүнийг олгосон цагаас хойш нэг цагийн дараа шинэчлэх шаардлагатай байгаа тул би Хандалтын токен буцаах өөр функцийн хүсэлтийг зааж өгсөн. Эхний Access жетоныг хүлээн авах үед ижил код нь скриптийн эхэнд байна:

function Get-GoogleToken {
  if (((Get-date).Hour -gt $token_expire.hour) -or (((Get-date).Hour -ge $token_expire.hour) -and ((Get-date).Minute -gt $token_expire.minute))) {
  Write-Host "Token Expired. Refreshing..."
    $request = (Invoke-RestMethod -Method Post -Uri "https://www.googleapis.com/oauth2/v4/token" -ContentType 'application/x-www-form-urlencoded' -Body @{
      client_id = $client_id
      client_secret = $client_secret
      refresh_token = $refresh_token
      grant_type = 'refresh_token'
    })
    $token = $request.access_token
    $minute = ([int]("{0:mm}" -f ([timespan]::fromseconds($request.expires_in))))+((Get-date).Minute)-2
    if ($minute -lt 0) {$minute += 60}
    elseif ($minute -gt 59) {$minute -=60}
    $script:token_expire = @{
      hour = ([int]("{0:hh}" -f ([timespan]::fromseconds($request.expires_in))))+((Get-date).Hour)
      minute = $minute
    }
  }
  return $token
}

Нэвтрэх эсэхийг шалгаж байна:

function Check_Google {
  $query = (GoogleQuery 'SearchAccount' $username)
  if ($query.users -ne $null) {
    $user = $query.users[0]
    Write-Host $user.name.fullName' - '$user.PrimaryEmail' - suspended: '$user.Suspended
    $GAresult = $user
  }
  if ($GAresult) {
      $return = $GAresult
  } else {$return = 'gg'}
  return $return
}

Имэйл:$query хүсэлт нь API-аас яг ийм имэйлтэй хэрэглэгчийг хайхыг хүсэх болно. Та мөн орлуулагч тэмдэг ашиглаж болно: =, :, :{ӨГДӨЛТ}*.

Өгөгдөл авахын тулд GET хүсэлтийн аргыг ашиглан өгөгдөл оруулах (акаунт үүсгэх эсвэл бүлэгт гишүүн нэмэх) - POST, одоо байгаа өгөгдлийг шинэчлэх - PUT, бичлэгийг устгах (жишээлбэл, бүлгийн гишүүн) - УСТГАХ.

Скрипт нь утасны дугаар (баталгаагүй мөр) болон бүс нутгийн түгээлтийн бүлэгт оруулахыг хүсэх болно. Энэ нь сонгогдсон Active Directory OU дээр тулгуурлан хэрэглэгч ямар зохион байгуулалтын нэгжтэй байх ёстойг шийдэж, нууц үгээ гаргаж ирдэг:

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"

Тэгээд тэр дансаа удирдаж эхлэв:

$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
}

Бүртгэлийг шинэчлэх, үүсгэх функцууд нь ижил төстэй синтакстай байдаг; бүх нэмэлт талбарууд шаардлагагүй, утасны дугаартай хэсэгт та дугаар, түүний төрлийг агуулсан нэг хүртэлх бичлэг агуулсан массивыг зааж өгөх хэрэгтэй.

Бүлэгт хэрэглэгч нэмэхэд алдаа гарахгүйн тулд эхлээд тухайн бүлгийн гишүүдийн жагсаалт эсвэл бүрдүүлэлтийг хэрэглэгчээс авах замаар тухайн бүлгийн гишүүн болсон эсэхийг шалгаж болно.

Тодорхой хэрэглэгчийн бүлгийн гишүүнчлэлийг асуух нь рекурсив биш бөгөөд зөвхөн шууд гишүүнчлэлийг харуулах болно. Хэрэглэгчийн гишүүн болсон хүүхдийн бүлэгтэй хэрэглэгчийг эцэг эхийн бүлэгт оруулах нь амжилтанд хүрэх болно.

дүгнэлт

Үлдсэн зүйл бол хэрэглэгчдэд шинэ дансны нууц үгийг илгээх явдал юм. Бид үүнийг SMS-ээр хийж, ерөнхий мэдээллийг зааварчилгаа, элсэлтийн хэлтсээс утасны дугаарын хамт өгсөн хувийн имэйл рүү илгээдэг. Өөр нэг хувилбар бол та мөнгөө хэмнэж, нууц үгээ телеграмм чат руу илгээх боломжтой бөгөөд үүнийг хоёр дахь хүчин зүйл гэж үзэж болно (MacBooks нь үл хамаарах зүйл болно).

Дуустал нь уншсан танд баярлалаа. Нийтлэл бичих хэв маягийг сайжруулах зөвлөмжийг харахад би баяртай байх болно, мөн скрипт бичихдээ бага алдаа гаргахыг хүсч байна =)

Сэдвийн хувьд хэрэгцээтэй эсвэл асуултанд хариулж болох холбоосуудын жагсаалт:

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх