Drücke "Enter", um den Text zu überspringen.

Schlagwort: Azure

Azure & PowerShell – Das UsageDetails CSV nach Tags auswerten

Hinweis: Der Großteil des Artikels ist auch in meinem YouTube-Video zu diesem Thema enthalten:

Auf Grund vieler Nachfragen aus Community und Kunden habe ich mich dazu entschieden, dieses Thema etwas genauer zu beleuchten… Wenn man Azure in größerem Umfang nutzt, dann kommt schnell die Frage nach interner Kostenverrechnung („Innerbetriebliche Leistungsverrechnung“ / „Charge back“) oder zumindest der Möglichkeit, die Kosten den jeweiligen Verursachern zuzuordnen und transparent darzustellen. Ein wesentliches Hilfsmittel dafür ist das Tagging von Ressourcen. Klar, man könnte die Ressourcen auch nach Kostenstellen getrennt in unterschiedlichen Subscriptions unterbringen, das stößt in der Praxis aber schnell an Grenzen und auf Probleme.

Hat man nun seine Ressourcen mit den entsprechenden Tags versehen, z.B. nach Besitzer („Owner“), Kostenstelle („Cost center“) oder Abteilung („Department“), dann tauchen diese Tags leider noch nicht in der regulären Rechnung auf.

Nun kann man sich aus dem Azure Portal (oder auch aus dem EA / Enterprise Portal oder via REST API) eine Detail-Aufstellung der Kosten herunterladen. Dazu muss man zu „Cost Management + Billing“ / Billing-Scope auswählen / „Usage + Charges“ / Monat wählen / Download-Button klicken / Kurz warten / “ Usage Details Version 2″ auswählen. Diese „Usage Details“ enthalten dann auch die Tags. Allerdings: Selbst wenn man das CSV-File so umformatiert, dass Excel damit umgehen kann, ergibt sich noch ein Problem:

Die einzelnen Tags werden alle zusammen geworfen und in einer „Spalte“ vermischt. Damit ist das Suchen oder Filtern nach einzelnen Tags schon recht schwer – eine bestimmte Kombination aus Tags ist damit unmöglich zu filtern. Dazu müsste man die Tags jeweils in einzelnen Spalten ablegen. Und da kommt PowerShell und dessen Möglichkeit, mit dem JSON-Format umzugehen, ins Spiel! Ich habe eine Script geschrieben, was die Spalte in die einzelnen Tags aufteilt. Dabei werden auch gleich Zahlen und Daten in das lokale Format überführt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# Script to expand tags from the usage details CSV provided by Azure / Microsoft
# to filter usage by tags; also converts some numbers to local format
# Download CSV file by hand first!
 
 
# This is needed for the FileOpen Dialog	
Add-Type -AssemblyName System.Windows.Forms
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{ 
    InitialDirectory = [Environment]::GetFolderPath('Desktop') 
    Filter = 'CSV-Files (*.csv)|*.csv'
}
$null = $FileBrowser.ShowDialog() # Just to open the dialog
 
If($FileBrowser.FileName -eq "")
{
    Write-Verbose "No file selected - aborting."
    Break    
}
 
$CSV = Import-Csv $FileBrowser.FileName -Delimiter ","
 
for ($i=0; $i -lt $CSV.length; $i++) 
{
    # Showing progress
    Write-Progress -Activity "Expanding in Progress..." -Status "$([math]::truncate($i / $($CSV.length) * 100))% complete..." -PercentComplete $($i / $($CSV.length) * 100)
 
    # Converting dates and numbers to local format
    $CSV[$i].Date = [datetime]::ParseExact( $CSV[$i].Date, 'MM/dd/yyyy', $null).ToString("d")
    $CSV[$i].BillingPeriodStartDate = [datetime]::ParseExact( $CSV[$i].BillingPeriodStartDate, 'MM/dd/yyyy', $null).ToString("d")
    $CSV[$i].BillingPeriodEndDate = [datetime]::ParseExact( $CSV[$i].BillingPeriodEndDate, 'MM/dd/yyyy', $null).ToString("d")
    $CSV[$i].Quantity = [float]$CSV[$i].Quantity
    $CSV[$i].EffectivePrice = [float]$CSV[$i].EffectivePrice
    $CSV[$i].Cost = [float]$CSV[$i].Cost
    $CSV[$i].UnitPrice = [float]$CSV[$i].UnitPrice
 
 
    # Expand Tags
    $Tags = "{ $($CSV[$i].Tags) }" | ConvertFrom-Json # We need to add some brackets here...
    if ($Tags -ne $null) {
         $Tags.PSObject.Properties | ForEach { 
            $TagName = "Tag-$($_.Name)" 
            Add-Member -InputObject $CSV[$i] $TagName $_.Value 
            # Adding the heading - what a rhyme (; ...
            if ($CSV[0].PSObject.Properties[$TagName] -eq $null) {
                Add-Member -InputObject $CSV[0] $TagName $null -Force
            }
        }
    }
 
}
 
# Saving as Excel-readable CSV
$CSV | Export-Csv "$([System.IO.Path]::GetDirectoryName($FileBrowser.FileName))\$([io.path]::GetFileNameWithoutExtension($FileBrowser.FileName))_expanded.csv" -NoTypeInformation -Delimiter ";"

Das Script findet ihr natürlich auch auf meinem GitHub Repo:

https://github.com/HaikoHertes/scripts/blob/master/Azure/Management/Expand-AzureTagsFromUsageDetails.ps1 

Das Resultat der Umwandlung wird dann in eine neue Excel-Datei im selben Folder wie die Originaldatei abgespeichert und kann dann problemlos in Excel geöffnet werden:

Wie man hier sieht sind die Tags nun in separaten Spalten untergebracht, so dass man sehr gut danach sortieren, darauf filtern oder mehr tun kann.

Viel Spaß beim Ausprobieren bzw. Benutzen!

6 Comments

PowerShell – Nicht verwendete Azure Ressourcen finden

Ein regelmäßiges Problem im Azure-Alltag ist das Aufräumen nicht mehr benötigter Azure-Ressourcen. Diese kosten in der Regel unnötig Geld und stellen zum Teil auch ein (Sicherheits-)Risiko dar (wenn zum Beispiel eine nicht mehr verwendete VM aus dem Fokus gerät und über Jahre nicht gepatcht wird).

Um dieser Herausforderung zu begegnen gilt es zunächst, die Ressourcen, die „weg“ können, zu identifizieren. Dazu habe ich eine erste Version eines PowerShell Skriptes erstellt, welches:

  • Nicht verwendete Public IPs
  • Nicht verwendete NICs
  • Nicht verwendete NSGs
  • Nicht verwendete Managed Disks

findet und aufführt.

Über die Zeit möchte ich das Skript weiter ausbauen.

Ihr findet es in meinem GitHub Repository:

https://github.com/HaikoHertes/scripts/blob/master/Azure/CostOptimization/GetOrphanedAzureRessources.ps1

Und ja, natürlich bietet Azure mit seinen Recommendations hier auch schon einiges, allerdings eben nicht „live“…

Viel Spaß beim Ausprobieren – über Kommentare freue ich mich!

Schreibe einen Kommentar...

Azure jetzt (wieder) in Deutschland verfügbar!

Zumindest für einige wenige…

Microsoft hat heute (still und heimlich und ohne großes Event) die neuen Deutschen Rechenzentren für Azure freigeschalten. Man kann sie auch im Portal schon sehen/auswählen:

Allerdings ist diese Region bisher nur für ausgewählte Kunden nutzbar. Beim Versuch, eine Ressource dorthin bereit zu stellen, kommt derzeit noch ein Fehler:

Einem Beitrag von Microsoft selbst sind auch weitere Details zu entnehmen, auch, wann und wie es weitergeht:

https://news.microsoft.com/de-de/microsoft-eroeffnet-neue-cloud-rechenzentrumsregionen-in-deutschland/
Schreibe einen Kommentar...

Azure – VMs nach zeit gesteuert hoch- und runterfahren

In meinen Workshops und anderen Kundenterminen kommt immer wieder die Frage, wie man Virtuelle Maschinen in Azure nach Zeit gesteuert hoch- und wieder runterfahren kann. Das ist eigentlich ganz einfach – man benötigt dazu nur folgendes:

  • Einen Azure Automation Account
  • Ein PowerShell Runbook mit entsprechendem Skript
  • VMs mit den entsprechenden Tags

Ich habe mich dazu zu folgenden Tags entschieden:

  • AutoShutdown – Entscheidet, ob die VM automatisch heruntergefahren werden soll; kennt die Werte “Yes” and “No”
  • AutoShutdownTime – enthält die entsprechende Zeit für den Shutdown im Format HH:mm:ss nach UTC
  • AutoStartup – Entscheidet, ob die VM automatisch gestartet werden soll; kennt die Werte “Yes” and “No”
  • AutoStartupTime – enthält die entsprechende Zeit für den Start im Format HH:mm:ss nach UTC

image

Das PowerShell-Skript findet ihr in meinem GitHub Repo:

https://github.com/HaikoHertes/scripts/blob/master/Azure/Automation/Snoozing/StartAndStopVMsWithAzureAutomation.ps1

Das Skript ist etwas komplexer als die beiden anderen im Repo, berücksichtigt dabei aber auch, dass eine VM ggf. morgens heruntergefahren und abends gestartet werden soll.

image

Auf Youtube habe ich ein Video veröffentlicht, dass das Vorgehen mit anderen Skripten zeigt:

YoutubeStartfolieAzure

Probiert es einfach mal aus – viel Spaß!

Schreibe einen Kommentar...

Am Samstag beim Global Azure Bootcamp

Wer kommenden Samstag den 27. April 2019 noch nichts vor hat, dem sei das Global Azure Bootcamp, welches weltweit gleichzeitig an wahnsinnig vielen Orten stattfindet, nahegelegt.

Ich selbst werde in Jena sein, um beim dortigen Bootcamp über Opensource DevOps zu sprechen.

Die folgende Agenda mit spannenden Vorträgen erwartet euch dort:

10:00-10:15 Uhr – Begrüßung
10:15-11:15 Uhr – „Azure Governance – Das Regelwerk für Azure“ – Eric Berg
11:15-11:30 Uhr – Kaffeepause
11:30-12:30 Uhr – „Azure Kubernetes Services (AKS) und Azure DevOps“ – Mark Blume
12:30-13:00 Uhr – Mittag
13:00-14:00 Uhr – “Azure DevOps – aber in OpenSource” – Haiko Hertes
14:00-14:15 Uhr – Kaffeepause
14:15-15:15 Uhr – „Machine Learning in der Cloud mit Azure Machine Learning Service“ – Stefan Hellfritzsch

Wer mehr über das Global Azure Bootcamp und die anderen Locations erfahren möchte, der kann dies hier tun:
https://global.azurebootcamp.net/

Ich würde mich freuen, das eine oder andere bekannte Gesicht in Jena zu sehen.

Schreibe einen Kommentar...

Azure / PowerShell – Azure SQL Performance Empfehlungen per PowerShell abholen und verteilen

Azure und insbesondere Azure SQL ist klasse – es nimmt einem viele Dinge der täglichen Verwaltung ab, einiges davon sogar automatisch. Klar, das hat seinen Preis, immerhin ist Azure SQL nicht ganz billig, aber wenn man es schon bezahlt, dann kann man auch seine Fähigkeiten nutzen. Eine davon ist, automatisch anhand der Nutzung einer Datenbank Empfehlungen für die Leistungsoptimierung zu geben. Diese kann man sich im UI bzw. dem Azure Portal anschauen. Dazu öffnet man entweder links im Blade den Punkt „Recommondations“ unterhalb von „Intelligent Performance“ oder den Punkt „Performance“ auf der Main-Page bei den Notifications:

Dort sieht man dann einige Empfehlungen aufgeführt (vorausgesetzt, Azure hat etwas gefunden, was wiederum eine regelmäßige Nutzung der Datenbank voraussetzt):

Diese Daten kann man sich auch automatisch abrufen und auf Wunsch dann z.B. an die Entwickler verteilen. Dazu bediene ich mich einfach der PowerShell:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
$ErrorActionPreference = "Stop"
$SubscriptionId = "YOUR_SUBSCRIPTION_ID"
 
function Get-SQLServerRecommendations()
{
    # Define the resource types
    $resourceTypes = ("Microsoft.Sql/servers/databases")
    $advisors = ("CreateIndex", "DropIndex","DbParameterization","SchemaIssue");
    $results = @()
 
    # Loop through all subscriptions
 
    $rgs = Get-AzureRmResourceGroup
 
    # Loop through all resource groups
    foreach($rg in $rgs) {
        $rgname = $rg.ResourceGroupName;
 
        # Loop through all resource types
        foreach($resourceType in $resourceTypes) {
            $resources = Get-AzureRmResource -ResourceGroupName $rgname -ResourceType $resourceType
 
            # Loop through all databases
            # Extract resource groups, servers and databases
            foreach ($resource in $resources) {
                $resourceId = $resource.ResourceId
                if ($resourceId -match ".*RESOURCEGROUPS/(?<content>.*)/PROVIDERS.*") {
                    $ResourceGroupName = $matches['content']
                } else {
                    continue
                }
                if ($resourceId -match ".*SERVERS/(?<content>.*)/DATABASES.*") {
                    $ServerName = $matches['content']
                } else {
                    continue
                }
                if ($resourceId -match ".*/DATABASES/(?<content>.*)") {
                    $DatabaseName = $matches['content']
                } else {
                    continue
                }
 
                # Skip if master
                if ($DatabaseName -eq "master") {
                    continue
                }
 
                # Loop through all Automatic tuning recommendation types
                foreach ($advisor in ($advisors -notmatch "SchemaIssue")) {
                    $recs = Get-AzureRmSqlDatabaseRecommendedAction -ResourceGroupName $ResourceGroupName -ServerName $ServerName  -DatabaseName $DatabaseName -AdvisorName $advisor
                    foreach ($r in $recs) {
                        if ($r.State.CurrentValue -eq "Active") {
                            $object = New-Object -TypeName PSObject
                            $object | Add-Member -Name 'SubscriptionId' -MemberType Noteproperty -Value $subscriptionId
                            $object | Add-Member -Name 'ResourceGroupName' -MemberType Noteproperty -Value $r.ResourceGroupName
                            $object | Add-Member -Name 'ServerName' -MemberType Noteproperty -Value $r.ServerName
                            $object | Add-Member -Name 'DatabaseName' -MemberType Noteproperty -Value $r.DatabaseName
                            $object | Add-Member -Name 'Advisor' -MemberType Noteproperty -Value $advisor
                            $object | Add-Member -Name 'Script' -MemberType Noteproperty -Value $r.ImplementationDetails.Script
                            $results += $object
                        }
                    }
                }
            }
        }
    }
    Return $results
}
 
$AzurePasswordSecure = ConvertTo-SecureString "$($YOUR_AZURE_PASSWORD)" -AsPlainText -Force
$AzureCredentials = New-Object System.Management.Automation.PSCredential ("$YOUR_AZURE_USER", $AzurePasswordSecure)
Connect-AzureRmAccount -Credential $AzureCredentials | Out-Null
Select-AzureRmSubscription -Subscription $SubscriptionId | Out-Null
 
$Recommendations = Get-SQLServerRecommendations
$table = $Recommendations | Sort-Object DatabaseName,Advisor | Format-Table Databasename,Advisor,Script -AutoSize -Wrap
Write-Output $table
 
$head = "<style>
td {background-color:lightgrey;}
table {width:100%;}
th {font-size:14pt;background-color:lightblue;}
</style>
<title>SQL Server performance recommendations</title>"
 
[string]$html = $Recommendations | ConvertTo-Html -Property Databasename,Advisor,Script -Body "<h1>Azure SQL Server automatic tuning recommendations for $stage</h1>Auto-generated by PUT_SOMETHING_HERE<br><br>" -Head $head
 
Send-MailMessage -Body $html -SmtpServer YOUR.SMTPSERVER.COM -From sender@domain.com -To recipient@domain.com -Subject "MS SQL Recommendations - $(Get-Date -Format "yyyy-dd-MM HH:mm:ss")" -BodyAsHtml

Dieses Script wiederum kann man dann z.B. per Jenkins regelmäßig auslösen. Oder alternativ ein Azure Automation Runbook dafür anlegen… Viel Spaß beim Ausprobieren!

Die Mails sehen dann in etwa so aus:

Schreibe einen Kommentar...

YouTube Videos zu Microsoft Azure

Wie ihr ggf. dem ein- oder anderen Blog-Artikel hier entnehmen könnt, beschäftige ich mich seit einigen Monaten sowohl berufsbedingt als auch aus eigenem Interesse mit Microsoft Azure. Als logische Konsequenz daraus habe ich jetzt bereits einige erste YouTube Videos aus diesem Umfeld aufgezeichnet und veröffentlicht. Ihr findet diese hier:

azure

Konkret geht es in den ersten Videos um ARM Templates und Azure CDN – weitere werden folgen! Schaut mal rein – ich freue mich auf Kommentare, Fragen und Likes! Smiley

 

https://www.youtube.com/playlist?list=PLPK8RW8p4Ok_g5ojGI6Lq80POQRRscRpz

Schreibe einen Kommentar...

Azure / PowerShell – Die App Settings mehrerer / aller Web Apps exportieren

Jüngst wollte ich mir den Überblick über alle App Settings der vielen Web Apps, die wir einsetzen, und deren Werten verschaffen und dabei auch schauen, ob die Settings in allen Entwicklungs-Stages gleich bzw. analog passend sind. Dazu habe ich ein PowerShell Skript geschrieben, was alle Web Apps in allen aufgeführten Ressource Groups prüft und deren App Settings in ein gemeinsames CSV File exportiert:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# Adjust theese as needed
 
# Change this to the subscription you want to query
$SubscriptionId = "1234567-890123-132312312"
 
# Name the RG's here, that you want to check
[string[]]$ResourceGroups = "RG-A","RG-B","RG-C"
 
### no changes needed below ###
 
Connect-AzureRmAccount -Subscription $SubscriptionId
Set-AzureRmContext -SubscriptionId $SubscriptionId
 
$AllSettings = @()
[string[]]$AllURLs = $null
 
# Iterate over all RGs
Foreach($RG in $ResourceGroups)
{
    Write-Host "$RG..."
    # Get all WebApps in this RG
    $AllWebAppsInRG = Get-AzureRmWebApp -ResourceGroupName $RG
    Foreach($WebApp in ($AllWebAppsInRG))
    {
        Write-Host "$($WebApp.SiteName)..."
        $webAppObject = Get-AzureRmWebApp -ResourceGroupName $RG -Name $($WebApp.SiteName)
        $AppSettings = $webAppObject.SiteConfig.AppSettings
        $AllURLs += $WebApp.DefaultHostName
 
        ForEach($Setting in $AppSettings)
        {
            Write-Host "$($Setting.Name)"
            $SettingObject = New-Object PSCustomObject
            $SettingObject | Add-Member -Type NoteProperty -Name "Ressource Group" -Value $RG
            $SettingObject | Add-Member -Type NoteProperty -Name "WebApp Name" -Value $($WebApp.SiteName)
            $SettingObject | Add-Member -Type NoteProperty -Name "WebApp URL" -Value $($WebApp.DefaultHostName)
            $SettingObject | Add-Member -Type NoteProperty -Name "Setting Name" -Value $($Setting.Name)
            $SettingObject | Add-Member -Type NoteProperty -Name "Setting Value" -Value $($Setting.Value)
            $AllSettings += $SettingObject
        }
 
    }
 
}
 
$AllSettings | ConvertTo-Csv -Delimiter ";" -NoTypeInformation | Out-File "AllAppSettings.csv"

 

Danach sind die Settings alle in der Datei AllAppSettings im aktuellen Verzeichnis, und zwar so, dass man die Datei direkt in Excel öffnen kann. Viel Spaß beim Ausprobieren!

Schreibe einen Kommentar...