Skip to main content

Serverless automation using PowerShell Core in Azure Functions – Bölüm 4

· 6 min read
Hasan Gural

Bir önceki yazımızda Visual Studio Code üzerinde ilk fonksiyonumuzu oluşturduk. Hatırlarsanız fonksiyonumuzun adı getResourceStatus olarak belirlemiştik. Fonksiyon bize Azure sanal sunucuların hakkında anlık raporlar (html output olarak) üretmesini sağlamak temel hedefi idi. Bu fonksiyon RESTful isteklerin kabul ediyor olacak ve istediğimiz zaman abonelik içerisinde bulunan sanal sunucuların yapılandırma bilgilerini anlık ve her yerden bir web request ile raporlayabileceğiz.

Fonksiyonumuzu deploy etmeden önce bildiğiniz gibi Azure Subscription içerisine Azure Function App deploy etmemiz gerekiyor. Bunu dilerseniz Azure Portal, ARM Template, VSCode, Powershell yapabilirsiniz. Bu yazı içerisinde size aşağıdaki Powershell Script'ini takip ederek Azure Function App oluşturmanızı yardımcı olacak olan kod bloğunu paylaşıyorum.

#region Variables

$SubscriptionId = 'SubscriptionId'
$functionAppName = 'xmshowtoFunctionApp-dev-01'
$appServicePlanName = 'functionappPlan'
$resourceGroupName = 'xmshowtoFunc-dev-01'
$location = 'West Europe'

#endregion Variables

#region Login with Azure Account and select your subscription

Login-AzAccount

Select-AzSubscription -Subscription $SubscriptionId

#endregion Login with Azure Account and select your subscription

### Creating a resource group for Azure Function App

New-AzResourceGroup -Name $resourceGroupName -Location $location -Force -Confirm:$false

### Creating a Azure Function App in the Resource Group.

$functionAppSettings = @{
'FUNCTIONS\_EXTENSION\_VERSION' = '~3';
'FUNCTIONS\_WORKER\_RUNTIME' = 'powershell';
'alwaysOn' = 'true'
}

New-AzResource -ResourceType 'Microsoft.Web/Sites' \`
-ResourceName $functionAppName -kind 'functionapp' \`
-Location $location \`
-ResourceGroupName $resourceGroupName \`
-Properties @{}

### Configuring Functions App Settings and Assign Managed Identiy.
Set-AzWebApp -Name $functionAppName \`
-AppSettings $functionAppSettings \`
-ResourceGroupName $resourceGroupName -AssignIdentity $true

Yukarıdaki kod bloğunda görülebileceği gibi aslında çok kompleks satırlar yok. Sadece amacımız, Azure Function Apps servisini Powershell ile deploy etmek ve bir takım configuration bilgileri göndermek. Azure Function App ilk defa deneyimliyorsanız, Azure Portal üzerinden oluşturmanızı şiddetle tavsiye ederim. Temel sebebi ise, size soracağı parametrik değerlerin ne olduğunu anlayabilmeniz. Resim içerisinde kullandığımız parametre olan 'AssignIdentity' kısmında Azure Function App'im için otomatik olarak Managed Identity oluşturup atayacak. Eğer Managed Service Identity nedir diye merak ediyorsanız, msHowto veya Microsoft Docs bakabilirsiniz. Bu Managed Service Identiy ( MSI) abonelik üzerinde en az reader yada contributor hakkı vermelisiniz ki, function için gelen endpoint istekler doğrultusunda abonelik içerisinde sorgular yapabilsin. Özetle Fonksiyonunuz MSI kullanıyor olacak, Azure hizmetine authenticate olurken, Managed Service Identity adı, Azure Function App adı ile aynı olacak. Powershell Script çalıştırdık, Azure Function App oluştu ve URL olarak şu şekilde görmekteyim. Aşağıda ekran görüntüsü bulunmaktadır.

https://mshowtofunc-dev-01.azurewebsites.net/

Not: Managed Service Identity, abonelik seviyesinde veya rapor almak istediğiniz seviyede izin vermelisiniz.

Şimdi artık asıl işi yapacak fonksiyonumuz için geliştirdiğimiz Powershell Script inceleyelim. Aşağıdaki ekran görüntüsünü ve Powershell Kod bloğunun tümünü bulabilirsiniz.

using namespace System.Net

### Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

### Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."

function Get-AZVMReportTable {
$head = @'
<Title> Azure Resource Report v.0.1 </Title>
<style>
\* {
margin: 20px;
padding: 0; }
body {
font: 14px/1.4 Segoe UI, Serif; }
#page-wrap {
margin: 50px;}
p {
margin: 20px 0; }
/\*
Generic Styling, for Desktops/Laptops
\*/
table {
width: 80%;}
.subnet {background-color: Aqua}
.danger {background-color: red}
.warn {back ground-color: yellow}
/\* Zebra striping \*/
/\*
tr:nth-of-type(odd) {
background: #eee; }
\*/
th {
background: #333;
color: white;
font-weight: bold; }
td, th {
padding: 6px;
border: 1px solid #ccc;
text-align: center; }
</style>
<br>
<H1><center> Azure Resource Report v.0.1 </center></H1>
<br>
<br>
'@

# Defining Variables

$footer = (Get-Date)
$resourceTable = @()
$getVMs = Search-AzGraph -Query "Resources | where type == 'microsoft.compute/virtualmachines' | order by name asc"

# Defining Variables
if($null -ne $getVMs){
Write-Information "VMs are found. $($getVms.Count)"
foreach ($vm in $getVMs) {

$vmObject = \[PSCustomObject\]@{

'ResourceGroupName' = $vm.ResourceGroup
'VMName' = $vm.Name
"VMSize" = $vm.properties.hardwareProfile.vmSize;
'SubscriptionId' = $vm.subscriptionId

}

$resourceTable += $vmObject
}
}

if ($resourceTable) {

$getHTMLCode = $resourceTable | ConvertTo-HTML -Fragment
$htmlCode = ConvertTo-Html -Head $head -PreContent ($getHTMLCode | Out-String) -PostContent "<br><I>Generated : $footer</I>"

if ($htmlCode) {

Add-Type -AssemblyName System.Web
$HtmlDecode = \[System.Web.HttpUtility\]::HtmlDecode($htmlCode)
return $HtmlDecode

}
}
}

$getHTML = Get-AZVMReportTable

if ($getHTML) {

$status = \[HttpStatusCode\]::OK
$body = $getHTML
}
else {

$status = \[HttpStatusCode\]::BadRequest
$body = "Sorry we are having an issue."
}

### Associate values to output bindings by calling 'Push-OutputBinding'

Push-OutputBinding -Name Response -Value (\[HttpResponseContext\]@{
StatusCode = $status
headers = @{'content-type' = 'text/html' }
Body = $body
})

#>

Yaptıklarımızı sırasıyla açıklayalım. Azure Graph Resource Modülünü kullanarak, abonelik içerisinde bulunan tüm sanal sunucuların detaylarını toplayıp, bunları bir HTML content haline getirdikten sonra, Azure Function tarafından binding kullanarak html sayfa olarak istek yapılan endpoint noktasından geri dönüş sağlayacağız. Şimdi tekrar pekiştirilmesi için, fonksiyonumuzu zaten oluştururken 'HTTP Trigger' olarak belirtmiştik, yani aslında bizim bir public endpoint'imiz var buna bir istek geldiği zaman, Get, Post tipinde fonksiyon tetiklenecek ve işini gerçekleştirecek. Daha sonra işi bittikten sonra bunu Out Binding sayesinde bir çıkış gerçekleştirip bunu bize html content olarak geri dönecek ve browser'dan anlamlı şeyler göreceğiz. Bindings detayları için, fonksiyon içerisindeki function.json dosyasına göz gezdirebilirsiniz. Bu dosyanın içerisinde authLevel değerine dikkat ederseniz endpoint istek yapmaya çalışırken çalıştığını anlayabilirsiniz.

Sıra geldi, geliştirdiğimiz fonksiyonu, Azure Functions App içerisine deploy etme aşamasına. Eğer isterseniz, fonksiyonunuzu 'func host start' komutuyla yada Visual Studio Code üzerinde 'F5' basarak kendi makinanız üzerinde çalıştırabilirsiniz. Local çalıştırmanın faydası, local debuging yapabilmek ve daha fazlası tabiki, eğer bu örnek için local çalıştıracaksanız Azure tarafına authenticate olma işini çözmeyi unutmayın. En kolay bir şekilde fonskiyonu Azure Functions Apps içerisine göndermek için, Visual Studio Code üzerinde, Azure Functions ve Azure Account extensionlarına sahip iseniz, 'F1' tuşuna basmanız ve yukarıda daha önce oluşturduğumuz Azure Function App'in adını seçmeniz. İşte bu kadar! Geliştirdiğim fonksiyonu, Azure Functions App içerisine Visual Studio üzerinden kolay bir şekilde deploy edebiliyoruz. Tabi isterseniz, Azure DevOps, GitHub Action, Git kullanarak CI/CD süreçler ile daha komplex ortamlarınızı için kullanabilirsiniz.

Yukarıdaki işlemleri gerçekleştirdikten sonra, Visual Studio Code tarafından başarılı bir şekilde deployment gerçekleştiğine emin olduktan sonra, Azure Functions App üzerinde artık fonskiyonunuzun olduğunu göreceksiniz. Azure Portal üzerinden Functions App gidelim ve beraber doğrulamasını gerçekleştirelim.

Resim üzerinde biraz açıklamaya çalıştım ama tekrar özet geçmekte fayda var. Bildiğiniz gibi fonksiyonumuzun adı, 'getResourceStatus' şeklinde idi. Bu şekilde bir folder Azure Function App Projesi içerisinde bulunmaktadır. Bunun bir fonksiyon olarak oluşturulduğunu, içerisinde ise 'Run.ps1' isimli script'in içeriğini görmekteyim. Şimdi event-based automation bir adım daha yaklaştık ve artık bizim için Powershell Script bir API gibi davranacak ve yukarıdaki ekran aldığımız URL bir istek göndermemiz yeterli olacak. Biliyorsunuz şuan için Browser tabanlı olarak bu endpoint istek atabiliriz, dilerseniz Postman vs diğer third party uygulamalar ile istek yapabilirsiniz. Genelde API testlerimi gerçekleştirirken, PostMan kullanıyorum.

Son ekran görüntümüzde gördüğünüz gibi sağ taraftaki ekrandaki URL dikkat ederseniz, Function için atanan URL browser üzerinden istek gönderdim ve bana istediğim fonksiyonum işlevini bitince sonuçları döndürdü. Çok kolay bir şekilde HTML bazlı bir rapor hazırladım ve bunu sadece bu endpoint istek attığım zamanlar anlık sonuçlar şeklinde elde edebileceğim.Bu yazı içerisinde vermek istediğimiz en temel mesaj event-based automation ile yapmak istedikleriniz tamamen sizin hayal gücünüze bağlı olduğunu unutmayın. Şuan browser tabanlı bir istek yaptık ama mesela aynı isteği Microsoft'un diğer uygulamaları ile yapabilir mesela ( PowerApps ) sonuçları oradan görebilir veya farklı servisler ile entegre edebilirsiniz.