Wednesday, 9 February 2022

Azure File Share Usage Report for all Subscription in your Tenant (Email and Web hooks notification)

 I was setting up monitoring for my Azure environment and I had an requirement to email the Fileshare usage of all fileshares in a tenant to Support team on daily Basis and also sent notification in Slack. I used PowerShell to consolidate the data and Logic apps for the integration for Slack notification.

To breakdown the script for better understanding, I have used a credential from credential manager which had RBAC permissions to all subscriptions in the tenant and I used the function in  https://gallery.technet.microsoft.com/scriptcenter/Accessing-Windows-7210ae91 for my Azure and SFTP authentication using the credentials already in credential manager. I am using Azure Management API's to get the information of the File storage as I found PowerShell Modules had restriction to Storage account opened only for particular subnet outside of the server I am running the script. I have used the method in  https://www.powershellgallery.com/packages/LSECosmos/0.2.1-alpha/Content/Get-AzAccessToken.ps1 for API Authentication.


The script will consolidate result in HTML format and Json format. Json format will be sent as Payload to Webhooks to Logic Apps were you can integrate with Slack or Teams alerts. HTML format will be styled with yellow rows for 80 to 90% and Red Rows for usage above 90% as below.

File Share Usage

StorageAccountName

SubscriptionName

ResourceGroupName

ShareName

TotalCapacity(GB)

UsedSpace(GB)

PercentageUsed

StgAcctName1

Sub-1

RG-1

FileShare-1

1

0

0

StgAcctName2

Sub-1

RG-2

FileShare-2

10

0

2

StgAcctName3

Sub-2

RG-3

FileShare-3

100

0

0

StgAcctName4

Sub-3

RG-4

FileShare-4

1024

926

90

StgAcctName5

Sub-4

RG-5

FileShare-5

250

91

37

StgAcctName6

Sub-4

RG-5

FileShare-6

1024

845

83

 

#Alert Slack and Email Azure File Share metrics for all Stg Account Fileshare in the tenant
#CreatedBy - Vinoth Manoharan
#Version 1.1
#DateCreated - 07/02/2022
Disconnect-AzAccount
#Connect-AzAccount
#Import-Module Az.Storage
# simple password vault access class
# https://gallery.technet.microsoft.com/scriptcenter/Accessing-Windows-7210ae91
class StoredCredential{
[System.Management.Automation.PSCredential] $PSCredential
[string] $account;
[string] $password;
# loads credential from vault
StoredCredential( [string] $name ){
[void][Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime]
$vault = New-Object Windows.Security.Credentials.PasswordVault
$cred = $vault.FindAllByResource($name) | select -First 1
$cred.retrievePassword()
$this.account = $cred.userName
$this.password = $cred.password
$pwd_ss = ConvertTo-SecureString $cred.password -AsPlainText -Force
$this.PSCredential = New-Object System.Management.Automation.PSCredential ($this.account, $pwd_ss )
}
static [bool] Exists( [string] $name ){
[void][Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime]
$vault = New-Object Windows.Security.Credentials.PasswordVault
try{
$vault.FindAllByResource($name)
}
catch{
if ( $_.Exception.message -match "element not found" ){
return $false
}
throw $_.exception
}
return $true
}
static [StoredCredential] Store( [string] $name, [string] $login, [string] $pwd ){
[void][Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime]
$vault=New-Object Windows.Security.Credentials.PasswordVault
$cred=New-Object Windows.Security.Credentials.PasswordCredential($name, $login, $pwd)
$vault.Add($cred)
return [StoredCredential]::new($name)
}
static [StoredCredential] Store( [string] $name, [PSCredential] $pscred ){
return [StoredCredential]::Store( $name, $pscred.UserName, ($pscred.GetNetworkCredential()).Password )
}
} # class StoredCredential
#################################################################################################################################
$CredentialName = "Credential"
#Get Credential from CredentialManager
if ( [StoredCredential]::Exists( $CredentialName ) ){
$credential = [StoredCredential]::New( $CredentialName )
Write-Host "$CredentialName is found in the CREDENTIAL MANAGER!!!!"
}
else{
Write-Host -ForegroundColor Red "$CredentialName is not found in the CREDENTIAL MANAGER!!!!"
EXIT;
}
#$credential.PSCredential
Connect-AzAccount -Credential $credential.PSCredential
$subscr = Get-AzSubscription -TenantId 'xxxxxxxxxxxxxxxx' #Include your Tenant ID Here
$finalresult = @()
ForEach($sub in $subscr)
{
Select-AzSubscription $sub.SubscriptionId
#https://www.powershellgallery.com/packages/LSECosmos/0.2.1-alpha/Content/Get-AzAccessToken.ps1
$azContext = Get-AzContext
$azProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
$profileClient = New-Object -TypeName Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient -ArgumentList ($azProfile)
$token = $profileClient.AcquireAccessToken($azContext.Subscription.TenantId)
$authHeader = @{
'Content-Type'='application/json'
'Authorization'='Bearer ' + $token.AccessToken
}
$stgaccounts = @()
$stgaccounts = Get-AzStorageAccount
forEach($stgacct in $stgaccounts)
{
$share = @()
$stgctx=@()
$stgctx = Get-AzStorageAccount -ResourceGroupName $stgacct.ResourceGroupName -Name $stgacct.StorageAccountName
$stgctx.StorageAccountName
#$shares = Get-AzStorageShare -Context $stgctx.context
$url1 = 'https://management.azure.com'+$stgctx.Id+'/fileServices/default/shares/'+'?api-version=2021-04-01'
$sharesarray =Invoke-RestMethod -Method Get -Uri $url1 -Headers $authHeader
$shares = $sharesarray.value
if ($shares -ne $null -or $shares -ne @())
{
ForEach($share in $shares)
{
$url = 'https://management.azure.com'+$share.id+'?api-version=2021-04-01&$expand=stats'
$response = Invoke-RestMethod -Method Get -Uri $url -Headers $authHeader
if($response -ne $null)
{
$totalcapacity = $response.properties.shareQuota
$usedSpace = $response.properties.shareUsageBytes
#Write-Host $stgctx.StorageAccountName '--' $stgctx.ResourceGroupName '--' $share.Name '--' $totalcapacity '--' $usedSpace
$finalresult += Select-Object @{n='StorageAccountName';e={$stgctx.StorageAccountName}},@{n='SubscriptionName';e={$sub.Name}},@{n='ResourceGroupName';e={$stgctx.ResourceGroupName}},@{n='ShareName';e={$share.Name}},@{n='TotalCapacity(GB)';e={[int]$totalcapacity}},@{n='UsedSpace(GB)';e={[int]($usedSpace/1GB)}},@{n='PercentageUsed';e={[int](($usedSpace/1GB)/($totalcapacity)*100)}} -InputObject ''
}
}
}
}
}
$finalresult|ft
$jsonbase = @{}
$jsonbase.Add("filesharesizeinfo",$finalresult)
$jsonfinal = $jsonBase | ConvertTo-Json
$jsonfinal
Invoke-WebRequest -Uri "<WebhookURI>" -Method Post -Body $jsonfinal -ContentType "application/json" -UseBasicParsing
<#
$head=@"
<style>
@charset "UTF-8";
table
{
font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;
border-collapse:collapse;
}
td
{
font-size:1em;
border:1px solid #98bf21;
padding:5px 5px 5px 5px;
}
th
{
font-size:1.1em;
text-align:center;
padding-top:5px;
padding-bottom:5px;
padding-right:7px;
padding-left:7px;
background-color:#A7C942;
color:#ffffff;
}
name tr
{
color:#F00000;
background-color:#EAF2D3;
}
</style>
"@
#>
#background-color: white
# HTML formatting
$head = @"
<style>
BODY{background-color:white;}
TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}
TH{border-width: 1px;padding: 5px;border-style: solid;border-color: black;foreground-color: black;background-color: LightBlue}
TD{border-width: 1px;padding: 5px;border-style: solid;border-color: black;foreground-color: black}
.red{background-color: red;}
.yellow{background-color: yellow;}
</style>
"@
$SmtpServer = 'smtp.office365.com'
$smtpport = 587
$emailbody = @()
[xml]$html = $finalresult|Sort-Object -Property SubscriptionName,ResourceGroupName,StorageAccountName,ShareName|Select *|ConvertTo-Html -Fragment
for ($i=1;$i -le $html.table.tr.count-1;$i++)
{
$class = $html.CreateAttribute("class")
[int]$checkpct = $html.table.tr[$i].td[6]
if ($checkpct -ge 90) {
#$html.table.tr[$i].SetAttribute('class','red')| out-null
$class.Value = "red"
$html.table.tr[$i].attributes.append($class) | out-null
}elseif($checkpct -ge 80 -and $checkpct -lt 90) {
#$html.table.tr[$i].SetAttribute('class','red')| out-null
$class.Value = "yellow"
$html.table.tr[$i].attributes.append($class) | out-null
}
}
$body = @"
<H1>File Share Usage</H1>
$($html.innerxml)
"@
$emailbody = ConvertTo-Html -Head $head -Body $body| out-string
$CredentialName = "SMTPCredential"
#Get Credential from CredentialManager
if ( [StoredCredential]::Exists( $CredentialName ) ){
$credential = [StoredCredential]::New( $CredentialName )
Write-Host "$CredentialName is found in the CREDENTIAL MANAGER!!!!"
}
else{
Write-Host -ForegroundColor Red "$CredentialName is not found in the CREDENTIAL MANAGER!!!!"
EXIT;
}
Send-MailMessage -From <fromaddr> -To <toemailaddr> -Subject " FileShare Usage" -Body $emailbody -BodyAsHtml -SmtpServer $SmtpServer -Port $smtpport -Credential $credential.PSCredential -UseSsl
Copyright © 2022 Vinoth N Manoharan.The information provided in this post is provided "as is" with no implied warranties or guarantees.

20 comments: