API๋ฅผ ํ†ตํ•ด PowerShell์—์„œ Google ์‚ฌ์šฉ์ž ๋งŒ๋“ค๊ธฐ

ะŸั€ะธะฒะตั‚!

์ด ๋ฌธ์„œ์—์„œ๋Š” G Suite ์‚ฌ์šฉ์ž๋ฅผ ์กฐ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด Google API์™€ PowerShell ์ƒํ˜ธ ์ž‘์šฉ์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ์กฐ์ง ์ „์ฒด์—์„œ ์—ฌ๋Ÿฌ ๋‚ด๋ถ€ ๋ฐ ํด๋ผ์šฐ๋“œ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ์Šน์ธ์€ Google ๋˜๋Š” Active Directory๋กœ ๊ท€์†๋˜๋ฉฐ ๊ทธ ์‚ฌ์ด์—๋Š” ๋ณต์ œ๋ณธ์„ ์œ ์ง€ํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ์‹ ๊ทœ ์ง์›์ด ํ‡ด์‚ฌํ•  ๊ฒฝ์šฐ ์ด ๋‘ ์‹œ์Šคํ…œ์—์„œ ๊ณ„์ •์„ ์ƒ์„ฑ/ํ™œ์„ฑํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ์„ธ์Šค๋ฅผ ์ž๋™ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์šฐ๋ฆฌ๋Š” ์ •๋ณด๋ฅผ ์ˆ˜์ง‘ํ•˜์—ฌ ๋‘ ์„œ๋น„์Šค ๋ชจ๋‘์— ๋ณด๋‚ด๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ถŒํ•œ ๋ถ€์—ฌ

์š”๊ตฌ ์‚ฌํ•ญ์„ ์ž‘์„ฑํ•  ๋•Œ ์‹ค์ œ ์ธ๊ฐ„ ๊ด€๋ฆฌ์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์Šน์ธ์„ ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์šฐ๋ฐœ์ ์ด๊ฑฐ๋‚˜ ์˜๋„์ ์ธ ๋Œ€๊ทœ๋ชจ ๋ณ€๊ฒฝ์ด ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ ์กฐ์น˜ ๋ถ„์„์„ ๋‹จ์ˆœํ™”ํ•ฉ๋‹ˆ๋‹ค.

Google API๋Š” ์ธ์ฆ ๋ฐ ์Šน์ธ์„ ์œ„ํ•ด OAuth 2.0 ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ ์‚ฌ๋ก€์™€ ์ž์„ธํ•œ ์„ค๋ช…์€ ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. OAuth 2.0์„ ์‚ฌ์šฉํ•˜์—ฌ Google API์— ์•ก์„ธ์Šค.

๋ฐ์Šคํฌํ†ฑ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ธ์ฆ์— ์‚ฌ์šฉ๋˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž์˜ ๋ถˆํ•„์š”ํ•œ ์ด๋™์ด ํ•„์š”ํ•˜์ง€ ์•Š์€ ์„œ๋น„์Šค ๊ณ„์ •์„ ์‚ฌ์šฉํ•˜๋Š” ์˜ต์…˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜ ๊ทธ๋ฆผ์€ Google ํŽ˜์ด์ง€์—์„œ ์„ ํƒํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค์— ๋Œ€ํ•œ ๊ฐœ๋žต์ ์ธ ์„ค๋ช…์ž…๋‹ˆ๋‹ค.

API๋ฅผ ํ†ตํ•ด PowerShell์—์„œ Google ์‚ฌ์šฉ์ž ๋งŒ๋“ค๊ธฐ

  1. ๋จผ์ € GET ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ง€์ •ํ•˜์—ฌ ์‚ฌ์šฉ์ž๋ฅผ Google ๊ณ„์ • ์ธ์ฆ ํŽ˜์ด์ง€๋กœ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ID
    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์•ก์„ธ์Šคํ•ด์•ผ ํ•˜๋Š” ์˜์—ญ
    • ์ ˆ์ฐจ ์™„๋ฃŒ ํ›„ ์‚ฌ์šฉ์ž๊ฐ€ ๋ฆฌ๋””๋ ‰์…˜๋  ์ฃผ์†Œ
    • ํ† ํฐ์„ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐฉ๋ฒ•
    • ๋ณด์•ˆ ์ฝ”๋“œ
    • ์ธ์ฆ๋ฒˆํ˜ธ ์ „์†ก ํ˜•์‹

  2. ์ธ์ฆ์ด ์™„๋ฃŒ๋œ ํ›„ ์‚ฌ์šฉ์ž๋Š” GET ๋งค๊ฐœ๋ณ€์ˆ˜์— ์˜ํ•ด ์ „๋‹ฌ๋œ ์˜ค๋ฅ˜ ๋˜๋Š” ์ธ์ฆ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ์ฒซ ๋ฒˆ์งธ ์š”์ฒญ์— ์ง€์ •๋œ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋””๋ ‰์…˜๋ฉ๋‹ˆ๋‹ค.
  3. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜(์Šคํฌ๋ฆฝํŠธ)์€ ์ด๋Ÿฌํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ˆ˜์‹ ํ•ด์•ผ ํ•˜๋ฉฐ, ์ฝ”๋“œ๋ฅผ ์ˆ˜์‹ ํ•œ ๊ฒฝ์šฐ ํ† ํฐ์„ ์–ป๊ธฐ ์œ„ํ•ด ๋‹ค์Œ ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  4. ์š”์ฒญ์ด ์ •ํ™•ํ•˜๋ฉด Google API๋Š” ๋‹ค์Œ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
    • ์š”์ฒญ์„ ํ•  ์ˆ˜ ์žˆ๋Š” ์•ก์„ธ์Šค ํ† ํฐ
    • ์ด ํ† ํฐ์˜ ์œ ํšจ ๊ธฐ๊ฐ„
    • ์•ก์„ธ์Šค ํ† ํฐ์„ ์ƒˆ๋กœ ๊ณ ์น˜๋ ค๋ฉด ์ƒˆ๋กœ ๊ณ ์นจ ํ† ํฐ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋จผ์ € Google API ์ฝ˜์†”๋กœ ์ด๋™ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž๊ฒฉ ์ฆ๋ช… - Google API ์ฝ˜์†”์—์„œ ์›ํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์„ ํƒํ•˜๊ณ  ์ž๊ฒฉ ์ฆ๋ช… ์„น์…˜์—์„œ ํด๋ผ์ด์–ธํŠธ OAuth ์‹๋ณ„์ž๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ(๋˜๋Š” ๋‚˜์ค‘์— ์ƒ์„ฑ๋œ ์‹๋ณ„์ž์˜ ์†์„ฑ)์—์„œ ๋ฆฌ๋””๋ ‰์…˜์ด ํ—ˆ์šฉ๋˜๋Š” ์ฃผ์†Œ๋ฅผ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ์˜ ๊ฒฝ์šฐ์—๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ํฌํŠธ๋ฅผ ๊ฐ€์ง„ ์—ฌ๋Ÿฌ localhost ํ•ญ๋ชฉ์ด ์žˆ์Šต๋‹ˆ๋‹ค(์•„๋ž˜ ์ฐธ์กฐ).

์Šคํฌ๋ฆฝํŠธ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋” ์‰ฝ๊ฒŒ ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•œ ์•ก์„ธ์Šค ๋ฐ ์ƒˆ๋กœ ๊ณ ์นจ ํ† ํฐ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ณ„๋„์˜ ํ•จ์ˆ˜์— ์ฒซ ๋ฒˆ์งธ ๋‹จ๊ณ„๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

$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 ํด๋ผ์ด์–ธํŠธ ์‹๋ณ„์ž ์†์„ฑ์—์„œ ์–ป์€ ํด๋ผ์ด์–ธํŠธ ID์™€ ํด๋ผ์ด์–ธํŠธ ๋น„๋ฐ€์„ ์„ค์ •ํ•˜๊ณ , ์ฝ”๋“œ ๊ฒ€์ฆ์ž๋Š” ์˜ˆ์•ฝ๋˜์ง€ ์•Š์€ ๋ฌธ์ž([AZ] / [az] / [43-128 ])์—์„œ ๋ฌด์ž‘์œ„๋กœ ์ƒ์„ฑ๋˜์–ด์•ผ ํ•˜๋Š” 0~9์ž์˜ ๋ฌธ์ž์—ด์ž…๋‹ˆ๋‹ค. / "-" / "." / "_" / "~".

๊ทธ๋Ÿฌ๋ฉด ์ด ์ฝ”๋“œ๊ฐ€ ๋‹ค์‹œ ์ „์†ก๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๊ณต๊ฒฉ์ž๊ฐ€ ์‚ฌ์šฉ์ž ์ธ์ฆ ํ›„ ๋ฆฌ๋””๋ ‰์…˜์œผ๋กœ ๋ฐ˜ํ™˜๋œ ์‘๋‹ต์„ ๊ฐ€๋กœ์ฑŒ ์ˆ˜ ์žˆ๋Š” ์ทจ์•ฝ์„ฑ์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
ํ˜„์žฌ ์š”์ฒญ์˜ ์ฝ”๋“œ ๊ฒ€์ฆ๊ธฐ๋ฅผ ์ผ๋ฐ˜ ํ…์ŠคํŠธ(์˜๋ฏธ ์—†์Œ - SHA256์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ์‹œ์Šคํ…œ์—๋งŒ ์ ํ•ฉํ•จ)๋กœ ๋ณด๋‚ด๊ฑฐ๋‚˜ BASE256Url๋กœ ์ธ์ฝ”๋”ฉํ•ด์•ผ ํ•˜๋Š” SHA64 ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด์‹œ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(๋‹ค๋ฆ„) Base64์—์„œ ๋‘ ๊ฐœ์˜ ํ…Œ์ด๋ธ” ๋ฌธ์ž๋กœ) ๋ฐ ๋ฌธ์ž ์ค„ ๋์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค: =.

๋‹ค์Œ์œผ๋กœ, ์ธ์ฆ ํ›„ ์‘๋‹ต์„ ์ˆ˜์‹ ํ•˜๊ธฐ ์œ„ํ•ด ๋กœ์ปฌ ์‹œ์Šคํ…œ์—์„œ http ์ˆ˜์‹ ์„ ์‹œ์ž‘ํ•ด์•ผ ํ•˜๋ฉฐ, ์ด๋Š” ๋ฆฌ๋””๋ ‰์…˜์œผ๋กœ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

๊ด€๋ฆฌ ์ž‘์—…์€ ํŠน์ˆ˜ ์„œ๋ฒ„์—์„œ ์ˆ˜ํ–‰๋˜๋ฏ€๋กœ ์—ฌ๋Ÿฌ ๊ด€๋ฆฌ์ž๊ฐ€ ๋™์‹œ์— ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•  ๊ฐ€๋Šฅ์„ฑ์„ ๋ฐฐ์ œํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ํ˜„์žฌ ์‚ฌ์šฉ์ž์— ๋Œ€ํ•œ ํฌํŠธ๋ฅผ ๋ฌด์ž‘์œ„๋กœ ์„ ํƒํ•˜์ง€๋งŒ ๋ฏธ๋ฆฌ ์ •์˜๋œ ํฌํŠธ๋ฅผ ์ง€์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ API ์ฝ˜์†”์—์„œ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

access_type=์˜คํ”„๋ผ์ธ ์ด๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜์ง€ ์•Š๊ณ ๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋งŒ๋ฃŒ๋œ ํ† ํฐ์„ ์ž์ฒด์ ์œผ๋กœ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
์‘๋‹ต ์œ ํ˜•=์ฝ”๋“œ ์ฝ”๋“œ๊ฐ€ ๋ฐ˜ํ™˜๋˜๋Š” ๋ฐฉ์‹์˜ ํ˜•์‹์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค(์‚ฌ์šฉ์ž๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์Šคํฌ๋ฆฝํŠธ๋กœ ์ฝ”๋“œ๋ฅผ ๋ณต์‚ฌํ•  ๋•Œ ์ด์ „ ์ธ์ฆ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์ฐธ์กฐ).
๋ฒ”์œ„ ์•ก์„ธ์Šค ๋ฒ”์œ„์™€ ์œ ํ˜•์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. URL ์ธ์ฝ”๋”ฉ์— ๋”ฐ๋ผ ๊ณต๋ฐฑ์ด๋‚˜ %20์œผ๋กœ ๊ตฌ๋ถ„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์œ ํ˜•์ด ํฌํ•จ๋œ ์•ก์„ธ์Šค ์˜์—ญ ๋ชฉ๋ก์€ ์—ฌ๊ธฐ์—์„œ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Google API์šฉ OAuth 2.0 ๋ฒ”์œ„.

์ธ์ฆ ์ฝ”๋“œ๋ฅผ ๋ฐ›์€ ํ›„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋ธŒ๋ผ์šฐ์ €์— ๋‹ซ๊ธฐ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ํฌํŠธ ์ˆ˜์‹  ๋Œ€๊ธฐ๋ฅผ ์ค‘์ง€ํ•œ ํ›„ ํ† ํฐ์„ ์–ป๊ธฐ ์œ„ํ•ด POST ์š”์ฒญ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—๋Š” ์ฝ˜์†” API์—์„œ ์ด์ „์— ์ง€์ •ํ•œ ID์™€ ๋น„๋ฐ€, ํ”„๋กœํ† ์ฝœ ์‚ฌ์–‘์— ๋”ฐ๋ผ ์‚ฌ์šฉ์ž๊ฐ€ ๋ฆฌ๋””๋ ‰์…˜๋  ์ฃผ์†Œ ๋ฐ grant_type์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

์ด์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ ์šฐ๋ฆฌ๋Š” ์•ก์„ธ์Šค ํ† ํฐ, ์œ ํšจ ๊ธฐ๊ฐ„(์ดˆ), ์•ก์„ธ์Šค ํ† ํฐ์„ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋Š” ์ƒˆ๋กœ ๊ณ ์นจ ํ† ํฐ์„ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์œ ํšจ ๊ธฐ๊ฐ„์ด ๊ธด ์•ˆ์ „ํ•œ ์žฅ์†Œ์— ํ† ํฐ์„ ์ €์žฅํ•ด์•ผ ํ•˜๋ฏ€๋กœ, ์ˆ˜์‹ ๋œ ์•ก์„ธ์Šค ๊ถŒํ•œ์„ ์ทจ์†Œํ•  ๋•Œ๊นŒ์ง€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ƒˆ๋กœ ๊ณ ์นจ ํ† ํฐ์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์— ํ† ํฐ ์ทจ์†Œ ์š”์ฒญ์„ ์ถ”๊ฐ€ํ–ˆ๋Š”๋ฐ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜์ง€ ์•Š๊ณ  ์ƒˆ๋กœ ๊ณ ์นจ ํ† ํฐ์ด ๋ฐ˜ํ™˜๋˜์ง€ ์•Š์œผ๋ฉด ์ ˆ์ฐจ๊ฐ€ ๋‹ค์‹œ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค(์šฐ๋ฆฌ๋Š” ํ† ํฐ์„ ํ„ฐ๋ฏธ๋„์— ๋กœ์ปฌ๋กœ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•˜์ง€ ์•Š๋‹ค๊ณ  ํŒ๋‹จํ•˜์—ฌ ์•”ํ˜ธํ™”๋กœ ์ผ์„ ๋ณต์žกํ•˜๊ฒŒ ํ•˜๊ฑฐ๋‚˜ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ž์ฃผ ์—ด๊ณ  ์‹ถ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.)

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'])
      }
    }
  }
}

๊ฐ ์š”์ฒญ์—์„œ ํ† ํฐ ์œ ํ˜•๊ณผ ์•ก์„ธ์Šค ํ† ํฐ ์ž์ฒด๊ฐ€ ํฌํ•จ๋œ Authorization ํ—ค๋”๋ฅผ ๋ณด๋‚ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ํ† ํฐ ์œ ํ˜•์€ ํ•ญ์ƒ Bearer์ž…๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ํ† ํฐ์ด ๋งŒ๋ฃŒ๋˜์ง€ ์•Š์•˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ๋ฐœ๊ธ‰๋œ ์ง€ ํ•œ ์‹œ๊ฐ„ ํ›„์— ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ €๋Š” ์•ก์„ธ์Šค ํ† ํฐ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋‹ค๋ฅธ ํ•จ์ˆ˜์— ๋Œ€ํ•œ ์š”์ฒญ์„ ์ง€์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ์•ก์„ธ์Šค ํ† ํฐ์„ ๋ฐ›์„ ๋•Œ ์Šคํฌ๋ฆฝํŠธ ์‹œ์ž‘ ๋ถ€๋ถ„์— ๋™์ผํ•œ ์ฝ”๋“œ ์กฐ๊ฐ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

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
}

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

๊ณ„์ •์„ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜๋Š” ๊ตฌ๋ฌธ์ด ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์ถ”๊ฐ€ ํ•„๋“œ๊ฐ€ ํ•„์š”ํ•œ ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ์ „ํ™”๋ฒˆํ˜ธ๊ฐ€ ์žˆ๋Š” ์„น์…˜์—์„œ๋Š” ๋ฒˆํ˜ธ์™€ ํ•ด๋‹น ์œ ํ˜•์ด ํฌํ•จ๋œ ๋ ˆ์ฝ”๋“œ๋ฅผ ์ตœ๋Œ€ XNUMX๊ฐœ๊นŒ์ง€ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฐ์—ด์„ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž๋ฅผ ๊ทธ๋ฃน์— ์ถ”๊ฐ€ํ•  ๋•Œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ํ•˜๋ ค๋ฉด ๋จผ์ € ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ๊ทธ๋ฃน ๊ตฌ์„ฑ์› ๋ชฉ๋ก์ด๋‚˜ ๊ตฌ์„ฑ ๋ชฉ๋ก์„ ์–ป์–ด์„œ ํ•ด๋‹น ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋ฏธ ์ด ๊ทธ๋ฃน์˜ ๊ตฌ์„ฑ์›์ธ์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŠน์ • ์‚ฌ์šฉ์ž์˜ ๊ทธ๋ฃน ๋ฉค๋ฒ„์‹ญ์„ ์ฟผ๋ฆฌํ•˜๋ฉด ์žฌ๊ท€์ ์ด์ง€ ์•Š์œผ๋ฉฐ ์ง์ ‘ ๋ฉค๋ฒ„์‹ญ๋งŒ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ๊ตฌ์„ฑ์›์œผ๋กœ ์†ํ•œ ํ•˜์œ„ ๊ทธ๋ฃน์ด ์ด๋ฏธ ์žˆ๋Š” ์ƒ์œ„ ๊ทธ๋ฃน์— ์‚ฌ์šฉ์ž๋ฅผ ํฌํ•จํ•˜๋ฉด ์„ฑ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

๋‚จ์€ ๊ฒƒ์€ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ƒˆ ๊ณ„์ •์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ณด๋‚ด๋Š” ๊ฒƒ๋ฟ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” SMS๋ฅผ ํ†ตํ•ด ์ด๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  ์ฑ„์šฉ ๋ถ€์„œ์—์„œ ์ œ๊ณตํ•œ ์ „ํ™”๋ฒˆํ˜ธ์™€ ํ•จ๊ป˜ ๊ฐœ์ธ ์ด๋ฉ”์ผ์— ๋Œ€ํ•œ ์ง€์นจ ๋ฐ ๋กœ๊ทธ์ธ๊ณผ ํ•จ๊ป˜ ์ผ๋ฐ˜ ์ •๋ณด๋ฅผ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ๋Œ€์•ˆ์œผ๋กœ, ๋น„์šฉ์„ ์ ˆ์•ฝํ•˜๊ณ  ๋น„๋ฐ€ ์ „๋ณด ์ฑ„ํŒ…์— ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋‘ ๋ฒˆ์งธ ์š”์†Œ๋กœ ๊ฐ„์ฃผ๋  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค(MacBook์€ ์˜ˆ์™ธ์ž„).

๋๊นŒ์ง€ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์‚ฌ ์ž‘์„ฑ ์Šคํƒ€์ผ์„ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•œ ์ œ์•ˆ์„ ๋ณด๊ฒŒ ๋˜์–ด ๊ธฐ์˜๊ณ  ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ์˜ค๋ฅ˜๊ฐ€ ์ค„์–ด๋“ค๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค =)

์ฃผ์ œ๋ณ„๋กœ ์œ ์šฉํ•˜๊ฑฐ๋‚˜ ๋‹จ์ˆœํžˆ ์งˆ๋ฌธ์— ๋‹ต๋ณ€ํ•  ์ˆ˜ ์žˆ๋Š” ๋งํฌ ๋ชฉ๋ก:

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€