Sistem yönetiminde web servislerle iletişim kurmak artık günlük rutinin ayrılmaz bir parçası haline geldi. REST API’lara sorgu atmak, webhook tetiklemek, dosya indirmek ya da bir servisin ayakta olup olmadığını kontrol etmek… Bunların hepsini PowerShell üzerinden halledebilirsiniz. Invoke-WebRequest ve Invoke-RestMethod cmdlet’leri, script’lerinize gerçek bir güç katıyor. Gelin bu iki araca derinlemesine bakalım.
Invoke-WebRequest ve Invoke-RestMethod Farkı
İkisi de HTTP istekleri yapar ama yaklaşımları farklıdır. Bu farkı anlamak, hangi senaryoda hangisini kullanacağınızı bilmek açısından kritik.
Invoke-WebRequest, ham HTTP yanıtını size döner. Status code, header’lar, ham içerik, HTML parse edilmiş linkler… Hepsine erişebilirsiniz. Bir web sayfasını scrape etmek ya da response header’larını incelemek istediğinizde bu cmdlet işe yarar.
Invoke-RestMethod ise JSON veya XML döndüren API’larla çalışmak için optimize edilmiştir. Yanıtı otomatik olarak parse eder ve size doğrudan PowerShell nesnesi olarak verir. REST API’larla çalışırken hayatınızı kolaylaştırır.
Basit bir karşılaştırma yapalım:
# Invoke-WebRequest - ham yanıt döner
$response = Invoke-WebRequest -Uri "https://api.github.com/users/octocat"
$response.StatusCode # 200
$response.Content # Ham JSON string
$response.Headers # Response header'ları
# Invoke-RestMethod - parse edilmiş nesne döner
$user = Invoke-RestMethod -Uri "https://api.github.com/users/octocat"
$user.login # Direkt property erişimi
$user.public_repos # Parse etmeye gerek yok
Gördüğünüz gibi Invoke-RestMethod JSON’ı otomatik olarak PowerShell nesnesine çevirdi. Ekstra ConvertFrom-Json adımına gerek kalmadı.
Temel Parametre Seti
Her iki cmdlet de benzer parametreleri paylaşır. En çok kullandıklarınızı ezberleyin:
- -Uri: İstek yapılacak URL. Zorunlu parametredir.
- -Method: HTTP metodu. GET, POST, PUT, DELETE, PATCH, HEAD gibi değerler alır. Belirtilmezse varsayılan GET’tir.
- -Headers: Hashtable formatında HTTP header’ları eklemek için kullanılır.
- -Body: POST/PUT isteklerinde gönderilecek veri. String, hashtable veya byte array olabilir.
- -ContentType: İstek gövdesinin content type’ı. Örneğin “application/json”.
- -Credential: Kimlik doğrulama için PSCredential nesnesi.
- -UseBasicParsing: HTML parsing engine’ini devre dışı bırakır, Internet Explorer’a bağımlılığı kaldırır.
- -TimeoutSec: İstek zaman aşımı süresi (saniye).
- -SkipCertificateCheck: SSL sertifika doğrulamasını atlar. Sadece test ortamlarında kullanın.
- -OutFile: Yanıtı dosyaya kaydetmek için hedef dosya yolu.
- -SessionVariable: Cookie session yönetimi için oturum değişkeni oluşturur.
- -WebSession: Mevcut bir web session’ını kullanmak için.
GET İstekleri ile Başlayalım
En basit kullanım senaryosuyla başlayalım. Bir sunucunun ayakta olup olmadığını kontrol eden basit bir health check script’i:
function Test-WebEndpoint {
param(
[string]$Url,
[int]$TimeoutSec = 10
)
try {
$response = Invoke-WebRequest -Uri $Url -TimeoutSec $TimeoutSec -UseBasicParsing
if ($response.StatusCode -eq 200) {
Write-Host "[OK] $Url - HTTP $($response.StatusCode)" -ForegroundColor Green
return $true
} else {
Write-Host "[WARN] $Url - HTTP $($response.StatusCode)" -ForegroundColor Yellow
return $false
}
}
catch {
Write-Host "[FAIL] $Url - $($_.Exception.Message)" -ForegroundColor Red
return $false
}
}
# Birden fazla endpoint kontrolü
$endpoints = @(
"https://www.google.com",
"https://api.github.com",
"https://yourinternalapp.company.com"
)
foreach ($endpoint in $endpoints) {
Test-WebEndpoint -Url $endpoint
}
Bu script’i görev zamanlayıcıya ekleyin, log’a yazdırın ve monitoring sisteminize bağlayın. Basit ama etkili.
REST API ile Gerçek Dünya Senaryosu: GitHub API
Diyelim ki bir organizasyondaki tüm repository’leri listelemek istiyorsunuz. GitHub API’sı bunun için mükemmel bir örnek:
# GitHub Personal Access Token ile kimlik doğrulama
$token = "ghp_xxxxxxxxxxxxxxxxxxxxxx"
$org = "mycompany"
$headers = @{
"Authorization" = "Bearer $token"
"Accept" = "application/vnd.github.v3+json"
"X-GitHub-Api-Version" = "2022-11-28"
}
# Tüm repo'ları sayfalayarak çek
$allRepos = @()
$page = 1
do {
$repos = Invoke-RestMethod -Uri "https://api.github.com/orgs/$org/repos?per_page=100&page=$page" `
-Headers $headers `
-Method GET
$allRepos += $repos
$page++
} while ($repos.Count -eq 100)
# Sonuçları işle
Write-Host "Toplam repository sayisi: $($allRepos.Count)"
$allRepos | Select-Object name, visibility, updated_at | Sort-Object updated_at -Descending | Format-Table
Sayfalama mantığına dikkat edin. API’lar genellikle tek seferde sınırlı kayıt döner, bunu handle etmek production-ready script’lerin olmazsa olmazıdır.
POST İsteği: JSON Body Göndermek
Bir Jira ticket’ı oluşturmak ya da Slack’e mesaj atmak gibi senaryolarda POST isteklerine ihtiyaç duyarsınız. İşte pratik bir Slack webhook örneği:
function Send-SlackNotification {
param(
[string]$WebhookUrl,
[string]$Message,
[string]$Channel = "#ops-alerts",
[string]$Color = "good" # good, warning, danger
)
$payload = @{
channel = $Channel
attachments = @(
@{
color = $Color
text = $Message
footer = "PowerShell Alert | $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
}
)
} | ConvertTo-Json -Depth 5
try {
$response = Invoke-RestMethod -Uri $WebhookUrl `
-Method POST `
-Body $payload `
-ContentType "application/json"
Write-Host "Slack bildirimi gonderildi: $response"
}
catch {
Write-Error "Slack bildirimi gonderilemedi: $($_.Exception.Message)"
}
}
# Kullanim
$webhookUrl = "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"
Send-SlackNotification -WebhookUrl $webhookUrl `
-Message "Backup islemi tamamlandi. 42 dosya yedeklendi." `
-Color "good"
# Kritik alert
Send-SlackNotification -WebhookUrl $webhookUrl `
-Message "KRITIK: Web01 sunucusunda disk dolulugu %95'e ulasti!" `
-Color "danger"
ConvertTo-Json -Depth 5 parametresine dikkat edin. Nested objeleriniz varsa derinlik belirtmezseniz bazı alanlar serialize edilemez.
Kimlik Doğrulama Yöntemleri
Basic Authentication
# Yontem 1: Header ile
$username = "admin"
$password = "P@ssw0rd"
$encodedCreds = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$username`:$password"))
$headers = @{
"Authorization" = "Basic $encodedCreds"
}
$result = Invoke-RestMethod -Uri "https://internal-api.company.com/api/v1/servers" `
-Headers $headers
# Yontem 2: Credential nesnesiyle
$cred = Get-Credential # Interaktif pencere acar
# Veya script icinde:
$securePass = ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential("admin", $securePass)
$result = Invoke-RestMethod -Uri "https://internal-api.company.com/api/v1/servers" `
-Credential $cred `
-Authentication Basic
Bearer Token ile OAuth2
Kurumsal ortamlarda sıkça karşılaşılan Azure AD / OAuth2 token akışı:
# Once token al
$tokenEndpoint = "https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token"
$tokenBody = @{
client_id = "your-client-id"
client_secret = "your-client-secret"
scope = "https://graph.microsoft.com/.default"
grant_type = "client_credentials"
}
$tokenResponse = Invoke-RestMethod -Uri $tokenEndpoint `
-Method POST `
-Body $tokenBody `
-ContentType "application/x-www-form-urlencoded"
$accessToken = $tokenResponse.access_token
# Token ile API'yi kullan
$headers = @{
"Authorization" = "Bearer $accessToken"
"Content-Type" = "application/json"
}
# Microsoft Graph API - tum kullanicilari listele
$users = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/users" `
-Headers $headers
Write-Host "Aktif kullanici sayisi: $($users.value.Count)"
Dosya İndirme: Büyük Dosyaları Verimli Yönetmek
function Download-FileWithProgress {
param(
[string]$Url,
[string]$OutputPath,
[hashtable]$Headers = @{}
)
Write-Host "Indiriliyor: $Url"
Write-Host "Hedef: $OutputPath"
try {
# Buyuk dosyalar icin OutFile parametresi bellek dostu
$startTime = Get-Date
Invoke-WebRequest -Uri $Url `
-OutFile $OutputPath `
-Headers $Headers `
-UseBasicParsing `
-TimeoutSec 300
$endTime = Get-Date
$duration = ($endTime - $startTime).TotalSeconds
$fileSize = (Get-Item $OutputPath).Length / 1MB
Write-Host "[TAMAM] Dosya indirildi: $([math]::Round($fileSize, 2)) MB, Sure: $([math]::Round($duration, 1)) saniye" -ForegroundColor Green
}
catch {
Write-Error "Indirme basarisiz: $($_.Exception.Message)"
if (Test-Path $OutputPath) { Remove-Item $OutputPath }
}
}
# Kullanim ornekleri
Download-FileWithProgress -Url "https://releases.hashicorp.com/terraform/1.6.0/terraform_1.6.0_windows_amd64.zip" `
-OutputPath "C:toolsterraform.zip"
# Private repo'dan download - token ile
$authHeaders = @{ "Authorization" = "Bearer $token" }
Download-FileWithProgress -Url "https://artifactory.company.com/repo/myapp-v2.1.msi" `
-OutputPath "D:deploymentsmyapp-v2.1.msi" `
-Headers $authHeaders
Hata Yönetimi ve Retry Mekanizması
Production ortamında en önemli konu budur. Ağ geçici olarak kopabilir, API rate limit’e takılabilirsiniz. Akıllı bir retry mekanizması şart:
function Invoke-WebRequestWithRetry {
param(
[string]$Uri,
[string]$Method = "GET",
[hashtable]$Headers = @{},
[string]$Body = $null,
[string]$ContentType = "application/json",
[int]$MaxRetries = 3,
[int]$RetryDelaySeconds = 5
)
$attempt = 0
$lastError = $null
while ($attempt -lt $MaxRetries) {
$attempt++
try {
$params = @{
Uri = $Uri
Method = $Method
Headers = $Headers
TimeoutSec = 30
UseBasicParsing = $true
}
if ($Body) {
$params["Body"] = $Body
$params["ContentType"] = $ContentType
}
$response = Invoke-RestMethod @params
if ($attempt -gt 1) {
Write-Host "Istek $attempt. denemede basarili oldu." -ForegroundColor Yellow
}
return $response
}
catch {
$lastError = $_
$statusCode = $_.Exception.Response.StatusCode.value__
# 4xx hatalarda retry yapmaya gerek yok (client hatasi)
if ($statusCode -ge 400 -and $statusCode -lt 500) {
Write-Error "Client hatasi (HTTP $statusCode), retry yapilmiyor: $Uri"
throw
}
Write-Warning "Deneme $attempt/$MaxRetries basarisiz. HTTP $statusCode. $RetryDelaySeconds saniye bekleniyor..."
if ($attempt -lt $MaxRetries) {
Start-Sleep -Seconds ($RetryDelaySeconds * $attempt) # Exponential backoff
}
}
}
Write-Error "Tum denemeler basarisiz oldu: $Uri"
throw $lastError
}
# Kullanim
$result = Invoke-WebRequestWithRetry -Uri "https://unstable-api.company.com/data" `
-Method "GET" `
-MaxRetries 3 `
-RetryDelaySeconds 10
Exponential backoff burada kritik. Her başarısız denemede bekleme süresini artırarak sunucuya daha az baskı yapıyorsunuz.
Gerçek Dünya Senaryosu: Otomatik SSL Sertifika Kontrol Scripti
Sertifikaların ne zaman dolacağını takip etmek sysadmin’lerin baş ağrısıdır. Bu script tüm domain’lerinizi kontrol eder ve yaklaşan expiryları bildirir:
function Check-SSLCertificates {
param(
[string[]]$Domains,
[int]$WarningDays = 30,
[string]$SlackWebhook = ""
)
$results = @()
foreach ($domain in $Domains) {
try {
$uri = "https://$domain"
$request = [Net.HttpWebRequest]::Create($uri)
$request.Timeout = 10000
$request.AllowAutoRedirect = $false
try { $request.GetResponse() | Out-Null } catch {}
$cert = $request.ServicePoint.Certificate
if ($cert) {
$expiry = [DateTime]::Parse($cert.GetExpirationDateString())
$daysLeft = ($expiry - (Get-Date)).Days
$thumbprint = $cert.GetCertHashString()
$status = if ($daysLeft -le 0) { "EXPIRED" }
elseif ($daysLeft -le $WarningDays) { "WARNING" }
else { "OK" }
$results += [PSCustomObject]@{
Domain = $domain
ExpiryDate = $expiry.ToString("yyyy-MM-dd")
DaysLeft = $daysLeft
Status = $status
Thumbprint = $thumbprint.Substring(0, 8) + "..."
}
$color = switch ($status) {
"OK" { "Green" }
"WARNING" { "Yellow" }
"EXPIRED" { "Red" }
}
Write-Host "[$status] $domain - $daysLeft gun kaldi ($($expiry.ToString('yyyy-MM-dd')))" -ForegroundColor $color
}
}
catch {
Write-Warning "$domain kontrol edilemedi: $($_.Exception.Message)"
$results += [PSCustomObject]@{
Domain = $domain
ExpiryDate = "N/A"
DaysLeft = -999
Status = "ERROR"
Thumbprint = "N/A"
}
}
}
# Kritik durumlar icin Slack bildirimi
$criticals = $results | Where-Object { $_.Status -in @("WARNING", "EXPIRED") }
if ($criticals -and $SlackWebhook) {
$message = "SSL Sertifika Uyarisi:`n"
foreach ($c in $criticals) {
$message += "- $($c.Domain): $($c.DaysLeft) gun kaldi ($($c.Status))`n"
}
$payload = @{ text = $message } | ConvertTo-Json
Invoke-RestMethod -Uri $SlackWebhook -Method POST -Body $payload -ContentType "application/json"
}
return $results
}
# Kullanim
$domains = @(
"www.sirketim.com",
"api.sirketim.com",
"mail.sirketim.com",
"vpn.sirketim.com"
)
$sonuclar = Check-SSLCertificates -Domains $domains `
-WarningDays 45 `
-SlackWebhook "https://hooks.slack.com/services/xxx/yyy/zzz"
# Raporu CSV'ye kaydet
$sonuclar | Export-Csv -Path "C:reportsssl-check-$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation
Proxy Ayarları ve Kurumsal Ortamlar
Şirket ortamlarında çoğu zaman proxy üzerinden çıkmanız gerekir:
# Sistem proxy'sini kullan
$response = Invoke-WebRequest -Uri "https://external-api.com/data" `
-UseDefaultCredentials `
-Proxy "http://proxy.company.com:8080" `
-ProxyUseDefaultCredentials
# Veya environment variable ile
$env:HTTP_PROXY = "http://proxy.company.com:8080"
$env:HTTPS_PROXY = "http://proxy.company.com:8080"
$env:NO_PROXY = "localhost,127.0.0.1,.company.com"
# Self-signed sertifika olan test ortamlari icin (SADECE LAB'DA!)
# PowerShell 7+
$response = Invoke-RestMethod -Uri "https://dev-api.internal.com/health" `
-SkipCertificateCheck
# PowerShell 5.1 icin
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
# !!! Dikkat: Bu global bir degisiklik, isleminiz bittikten sonra geri alin
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null
Response Header’larını Analiz Etmek
Bazen body’den çok header’lar önemlidir. Rate limiting bilgisi, redirect takibi, cache kontrolü gibi:
$response = Invoke-WebRequest -Uri "https://api.github.com/rate_limit" `
-Headers @{ "Authorization" = "Bearer $token" } `
-UseBasicParsing
# Rate limit bilgilerini oku
$rateLimit = $response.Headers["X-RateLimit-Limit"]
$remaining = $response.Headers["X-RateLimit-Remaining"]
$resetTime = $response.Headers["X-RateLimit-Reset"]
$resetDate = [DateTimeOffset]::FromUnixTimeSeconds([int]$resetTime).LocalDateTime
Write-Host "Rate Limit: $remaining / $rateLimit kalan"
Write-Host "Sifirlama zamani: $($resetDate.ToString('HH:mm:ss'))"
# Eger limit azsa bekle
if ([int]$remaining -lt 10) {
$waitSeconds = ($resetDate - (Get-Date)).TotalSeconds + 5
Write-Warning "Rate limit esigine yaklasiliyor. $waitSeconds saniye bekleniyor..."
Start-Sleep -Seconds $waitSeconds
}
PowerShell 7 ile Gelen Yenilikler
PowerShell 7 kullanıyorsanız bazı ekstra özellikler sizi bekliyor:
- -SkipCertificateCheck: Artık doğrudan parametre olarak mevcut, workaround gerekmez.
- -SkipHttpErrorCheck: HTTP hata kodlarında exception fırlatmak yerine yanıtı döner. Özellikle status code’u kendiniz handle etmek istediğinizde kullanışlı.
- -RetryIntervalSec ve -MaximumRetryCount: Native retry desteği geldi, elle yazmanıza gerek kalmadı.
- -HttpVersion: HTTP/2 ve HTTP/3 desteği.
# PowerShell 7 native retry
$result = Invoke-RestMethod -Uri "https://flaky-api.com/data" `
-MaximumRetryCount 3 `
-RetryIntervalSec 5 `
-SkipHttpErrorCheck
Sonuç
Invoke-WebRequest ve Invoke-RestMethod, modern sysadmin’in araç kutusunun vazgeçilmez parçaları. Monitoring script’lerinden deployment otomasyonuna, sertifika kontrolünden API entegrasyonlarına kadar bu iki cmdlet her yerde karşınıza çıkacak.
Pratikte dikkat etmeniz gereken birkaç nokta var: Üretim script’lerinizde her zaman hata yönetimi ekleyin, hassas bilgileri asla script içine hardcode etmeyin (bunun yerine Get-Secret veya environment variable kullanın), proxy ayarlarını göz önünde bulundurun ve rate limiting’e karşı bir mekanizma kurun.
PowerShell 7’ye geçiş yapabilirseniz yapın. Native retry, SkipCertificateCheck ve HTTP/2 desteği gibi özellikler gerçekten işinizi kolaylaştırıyor. Windows Server 2019 ve üzeri zaten destekliyor, eski sunucularınıza da kolayca kurabilirsiniz.
Bu konuyu sindirmenin en iyi yolu pratik yapmak. Kendi monitoring script’lerinizi yazın, kullandığınız ürünlerin API dokümantasyonlarını inceleyin ve küçük otomasyonlarla başlayın. Bir süre sonra web servisleriyle konuşmak size ikinci doğa haline gelecek.