Ziel: Diese Übersicht soll als GitHub-README dienen. Sie erklärt fünf praktikable Wege, Dateien per PowerShell nach Google Drive zu bringen – inkl. Code und klaren Vor-/Nachteilen.
Es gibt zwei große Klassen:
- API-basierte Uploads (OAuth / Service Accounts)
- Dateisystem-basierte Uploads über Google Drive for desktop (Mirror/Stream)
Die fünf Methoden im Detail:
- (a) API mit manuellem Benutzer-Login pro Durchlauf
- (b) Google One / Premium 2 TB + eigener normaler Bot-User
- (c) Service Accounts (wo es wirklich funktioniert)
- (d) Drive for Windows Mirror
- (e) Drive for Windows Stream
- My Drive: Persönlicher Drive-Bereich eines Google-Users.
- Shared Drive: Team-/Organisationslaufwerk (typisch in Google Workspace).
- OAuth Desktop Client: OAuth-Client für lokale Apps/Skripte.
- Refresh Token: Ermöglicht langfristige, wiederholbare Token-Erneuerung ohne erneuten Login.
- Service Account: Technische Identität in Google Cloud.
- Proof-of-Concept
- Sehr seltene, kontrollierte Uploads
- Kein Token-Persistenz-Wunsch
- OAuth Desktop Client in Google Cloud anlegen.
- Auth-URL generieren.
- Benutzer loggt sich im Browser ein.
- Code wird in PowerShell eingefügt.
- Access Token wird geholt.
- Upload.
# --- Konfiguration ---
$ClientId = "<CLIENT_ID>"
$ClientSecret = "<CLIENT_SECRET>"
$RedirectUri = "http://localhost"
$Scope = "https://www.googleapis.com/auth/drive.file"
# --- 1) Consent-URL generieren (jedes Mal neu!) ---
$authUrl = "https://accounts.google.com/o/oauth2/v2/auth?" + `
"client_id=$([uri]::EscapeDataString($ClientId))" + `
"&redirect_uri=$([uri]::EscapeDataString($RedirectUri))" + `
"&response_type=code" + `
"&scope=$([uri]::EscapeDataString($Scope))" + `
"&access_type=online"
Write-Host "Öffne diese URL im Browser:"
Write-Host $authUrl
# --- 2) Code aus Redirect-URL kopieren ---
$AuthCode = Read-Host "Code aus der Browser-URL"
# --- 3) Code → Access Token ---
$token = Invoke-RestMethod -Method Post `
-Uri "https://oauth2.googleapis.com/token" `
-ContentType "application/x-www-form-urlencoded" `
-Body @{
code = $AuthCode
client_id = $ClientId
client_secret = $ClientSecret
redirect_uri = $RedirectUri
grant_type = "authorization_code"
}
$AccessToken = $token.access_token
# --- 4) Simple Upload ---
$filePath = "C:\Temp\report.pdf"
$fileBytes = [System.IO.File]::ReadAllBytes($filePath)
Invoke-RestMethod -Method Post `
-Uri "https://www.googleapis.com/upload/drive/v3/files?uploadType=media" `
-Headers @{ Authorization = "Bearer $AccessToken" } `
-ContentType "application/pdf" `
-Body $fileBytes- Einfach verständlich
- Kein langfristiges Token-Handling
- Gut für Tests & einmalige Aktionen
- Nicht automatisierbar ohne Nutzerinteraktion
- Kein guter Fit für geplante Jobs/Server
Statt eines GCP-Service-Accounts wird ein zusätzlicher normaler Google-Account erstellt. Dieser Bot-Account wird in die Google One Familienfreigabe aufgenommen, sodass er vom Speicher profitiert.
Danach:
- Einmalig OAuth-Consent für den Bot-User
- Refresh Token speichern
- Wiederholte Uploads ohne erneuten Login
- Privat-/Consumer-Kontext
- Kein Google Workspace vorhanden
- Wunsch nach „quasi headless“ Automatisierung
$ClientId = "<CLIENT_ID>"
$ClientSecret = "<CLIENT_SECRET>"
$RefreshToken = "<REFRESH_TOKEN_DES_BOT_USERS>"
function Get-GoogleAccessToken {
param(
[Parameter(Mandatory)] [string] $ClientId,
[Parameter(Mandatory)] [string] $ClientSecret,
[Parameter(Mandatory)] [string] $RefreshToken
)
$resp = Invoke-RestMethod -Method Post `
-Uri "https://oauth2.googleapis.com/token" `
-ContentType "application/x-www-form-urlencoded" `
-Body @{
client_id = $ClientId
client_secret = $ClientSecret
refresh_token = $RefreshToken
grant_type = "refresh_token"
}
$resp.access_token
}
function Upload-GDriveMultipart {
param(
[Parameter(Mandatory)] [string] $FilePath,
[Parameter(Mandatory)] [string] $AccessToken,
[string] $FileName = $(Split-Path $FilePath -Leaf),
[string] $ParentFolderId
)
if (-not (Test-Path $FilePath)) {
throw "Datei nicht gefunden: $FilePath"
}
$metadata = @{ name = $FileName }
if ($ParentFolderId) { $metadata.parents = @($ParentFolderId) }
$boundary = [guid]::NewGuid().ToString()
$metaJson = $metadata | ConvertTo-Json -Depth 5
$fileBytes = [System.IO.File]::ReadAllBytes($FilePath)
$ms = New-Object System.IO.MemoryStream
$sw = New-Object System.IO.StreamWriter($ms)
$sw.WriteLine("--$boundary")
$sw.WriteLine("Content-Type: application/json; charset=UTF-8")
$sw.WriteLine()
$sw.WriteLine($metaJson)
$sw.WriteLine("--$boundary")
$sw.WriteLine("Content-Type: application/octet-stream")
$sw.WriteLine("Content-Transfer-Encoding: binary")
$sw.WriteLine()
$sw.Flush()
$ms.Write($fileBytes, 0, $fileBytes.Length)
$sw = New-Object System.IO.StreamWriter($ms)
$sw.WriteLine()
$sw.WriteLine("--$boundary--")
$sw.Flush()
$ms.Position = 0
Invoke-RestMethod -Method Post `
-Uri "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart" `
-Headers @{ Authorization = "Bearer $AccessToken" } `
-ContentType "multipart/related; boundary=$boundary" `
-Body $ms.ToArray()
}
$AccessToken = Get-GoogleAccessToken -ClientId $ClientId -ClientSecret $ClientSecret -RefreshToken $RefreshToken
Upload-GDriveMultipart `
-FilePath "C:\Temp\export.csv" `
-AccessToken $AccessToken `
-ParentFolderId "<OPTIONAL_FOLDER_ID>"- Sehr praktikabel ohne Workspace
- Speicher-Upgrade leicht nutzbar
- Nach Setup recht „automationsfreundlich“
- Kein echter Service Account
- Initiales OAuth-Setup nötig
- Identity-/Governance-Thema bei gemeinsam genutzten Setups
Service Accounts eignen sich hervorragend für headless Automatisierung – aber realistisch vor allem in Google Workspace-Szenarien.
-
Google Workspace + Shared Drives
- Service Account als Mitglied zum Shared Drive hinzufügen.
- Uploads laufen ohne Benutzer-Login.
-
Google Workspace + Domain-Wide Delegation (Admin-Setup)
- Service Account darf im Namen eines Users handeln.
- Privaten Consumer-Gmail-Konten für direkten, headless Zugriff auf deren My Drive.
Hinweis: Dieses Beispiel ist am zuverlässigsten mit PowerShell 7+.
function Get-ServiceAccountAccessToken {
param(
[Parameter(Mandatory)] [string] $JsonKeyPath,
[Parameter(Mandatory)] [string] $Scope,
# Optional für Domain-Wide Delegation:
[string] $SubjectUser
)
$key = Get-Content $JsonKeyPath -Raw | ConvertFrom-Json
$now = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
$exp = $now + 3600
$header = @{ alg = "RS256"; typ = "JWT" } | ConvertTo-Json -Compress
$payload = @{
iss = $key.client_email
scope = $Scope
aud = "https://oauth2.googleapis.com/token"
iat = $now
exp = $exp
}
if ($SubjectUser) { $payload.sub = $SubjectUser }
$payload = $payload | ConvertTo-Json -Compress
function To-Base64Url([string]$s) {
[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($s)).
TrimEnd("=").Replace("+","-").Replace("/","_")
}
$unsigned = "$(To-Base64Url $header).$(To-Base64Url $payload)"
$rsa = [System.Security.Cryptography.RSA]::Create()
$rsa.ImportFromPem($key.private_key)
$sigBytes = $rsa.SignData(
[Text.Encoding]::UTF8.GetBytes($unsigned),
[System.Security.Cryptography.HashAlgorithmName]::SHA256,
[System.Security.Cryptography.RSASignaturePadding]::Pkcs1
)
$signature = [Convert]::ToBase64String($sigBytes).
TrimEnd("=").Replace("+","-").Replace("/","_")
$jwt = "$unsigned.$signature"
$resp = Invoke-RestMethod -Method Post `
-Uri "https://oauth2.googleapis.com/token" `
-ContentType "application/x-www-form-urlencoded" `
-Body @{
grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer"
assertion = $jwt
}
$resp.access_token
}
$scope = "https://www.googleapis.com/auth/drive"
# SA als sich selbst (Shared Drive Mitgliedschaft vorausgesetzt)
$token = Get-ServiceAccountAccessToken -JsonKeyPath "C:\keys\sa.json" -Scope $scope
# Domain-Wide Delegation (nur Workspace Admin-Szenario)
# $token = Get-ServiceAccountAccessToken -JsonKeyPath "C:\keys\sa.json" -Scope $scope -SubjectUser "user@domain.tld"Danach Upload wie in (b) über Multipart.
- Echte headless Automation
- Ideal für Server, CI/CD, ETL-Jobs
- Sauber in Enterprise-Governance
- Für Consumer-My-Drive ungeeignet ohne zusätzliche Konstrukte
- Admin-/Shared-Drive-Setup nötig
- Höhere Initialkomplexität
Drive wird als lokal gespiegelt eingebunden. PowerShell schreibt einfach in einen Ordner. Der Desktop-Client synchronisiert automatisch.
- Workstation-Automatisierung
- Schnelle Lösung ohne API
- Skripte für persönliche Workflows
# Pfad kann je nach Setup variieren
$root = Join-Path $env:USERPROFILE "Google Drive"
$target = Join-Path $root "My Drive\PS_Automation"
New-Item -ItemType Directory -Path $target -Force | Out-Null
"Mirror upload test $(Get-Date)" |
Set-Content -Path (Join-Path $target "mirror_test.txt") -Encoding UTF8
Copy-Item "C:\Temp\report.pdf" -Destination $target -Force- Extrem einfach
- Keine API-Keys, keine OAuth-Flows im Code
- Ideal für viele kleine lokale Automations
- Offline-freundlicher als Stream
- PC + Drive-App müssen laufen
- Keine feine API-Kontrolle über Metadaten/Berechtigungen
- Nicht ideal für „echte Server-Automation“
Drive erscheint als virtuelles Laufwerk. Dateien werden gestreamt und gecacht.
- Große Drive-Bestände
- Lokaler Speicher soll geschont werden
# Laufwerksbuchstabe kann abweichen
$driveLetter = "G:"
$target = Join-Path $driveLetter "My Drive\PS_Automation"
New-Item -ItemType Directory -Path $target -Force | Out-Null
"Stream upload test $(Get-Date)" |
Set-Content -Path (Join-Path $target "stream_test.txt") -Encoding UTF8- Spart lokalen Speicher
- Sehr komfortabel bei großen Datenmengen
- PowerShell kann wie mit einem normalen Laufwerk arbeiten
- Stärker netzwerk-/cache-abhängig
- Bei vielen Dateioperationen potenziell weniger robust als Mirror
- Ebenfalls nicht headless
- Beste Praxis: (b) Bot-User + Refresh Token
- Schnellste Lösung: (d) Mirror
- Gold-Standard: (c) Service Account + Shared Drive
- Optional: Domain-Wide Delegation für User-Impersonation
- (a) Manuell pro Durchlauf
- Refresh Tokens sicher speichern (z. B. Windows Credential Manager).
- Scopes minimal halten (
drive.filestattdrive, wenn ausreichend). - Für große Dateien ggf. auf „resumable upload“ umsteigen.
- Bei Desktop-Varianten auf Konflikte achten, wenn mehrere Geräte parallel schreiben.
-
Muss es headless auf einem Server laufen?
- Ja → (c) (Workspace/Shared Drive)
- Nein → weiter
-
Willst du API-Kontrolle (Ordner-IDs, Metadaten, elegante Fehlerlogik)?
- Ja → (b) im Consumer-Umfeld, (c) im Workspace
- Nein → (d) oder (e)
-
Skriptstabilität wichtiger als lokaler Speicher?
- Ja → (d) Mirror
- Nein → (e) Stream