PowerShell ile HTTP İstekleri: Invoke-WebRequest ve Invoke-RestMethod Kullanımı

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.

Yorum yapın