AzFnUtil.psm1

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
<#
 # Module that includes utility functions that make use of
 #
 # AzureRM and relevant SDKs in order to
 #
 # help with azure functions management.
 #>


<#
 .DESCRIPTION
 Used for validating whether an azure web app is a function.
 
 .PARAMETER appSvc
 The web app used to perform the relevant checks by.
 #>

Function Test-AzFn {
param(
[Parameter(Mandatory=$true,Position=0)]
[PSCustomObject]$appSvc
)
return $appSvc.kind -ilike "*function*";
}

<#
 .DESCRIPTION
 Used to check the running status of a specific azure function from an azure web app list.
 
 .PARAMETER fnName
 The azure function name to check.
  
 .PARAMETER webapp
 The list of azure functions app services to perform the necessary checks based on the function name.
 #>

Function Test-FnStopped {
param(
[Parameter(Mandatory=$true,Position=0)]
[string]$fnName,
[Parameter(Mandatory=$true,Position=1)]
[PSCustomObject[]]$webapp
)
$reswebApp = $webApp | ?{Test-AzFn -appSvc $_}
return ($reswebapp | ?{$_.Name -eq $fnName}).state -eq "Stopped";
}

<#
 .SYNOPSIS
 Retrieve a nested listing of all azure function apps and relevant functions -with details-
 
 .DESCRIPTION
 Get all azure function apps from list of web apps and all inner function data that are a
 part of a function project. This includes basic top level information and the
 extended functions data of the web application such as: invokable url, inner function name, language.
 #>

Function Get-AzureFn {

if(((Get-Module -ListAvailable Azure) -ne $null) -and ($env:PSModulePath -ilike "*\Microsoft SDKs\Azure\*")) {
Write-Host "Azure SDK detected OK.." -ForegroundColor Cyan -BackgroundColor DarkBlue
    $is32bit = (Test-Path "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell");
    $is64bit = (Test-Path "$env:ProgramFiles\Microsoft SDKs\Azure\PowerShell");
    $sdkval ="";
if ($is32bit) {
 $sdkval = "32-bit";
 }
 if ($is64bit) {
 $sdkval ="64-bit";
 }
 Write-Host "Running script with $($sdkval) SDK.." -ForegroundColor Cyan -BackgroundColor DarkBlue
}

Try {
 if (((Get-Module AzureRM.Profile) -eq $null) -or ((Get-Module AzureRM.Websites) -eq $null)) {
  Write-Warning "`r`nSetting Azure SDK Modules..";
    Import-Module AzureRM.Profile,AzureRM.Websites -ErrorVariable "testmodule" -Force
 }
}
Catch {
    Write-Error "Cannot import modules properly, error $testmodule - Please make sure that the Azure SDK is installed properly.";
    return;
}

if ((Get-AzureRmContext -OutVariable 'azureRmCtx') -eq $null) {
Login-AzureRmAccount; return Write-Host "`r`n -- Login complete, re-run script. -- `r`n" -ErrorAction Stop;
}

$rgfnInfo = (Get-AzureRmResource | ?{$_.Kind -eq "web"});
if ($rgfnInfo -eq $null) {
Trap [System.Exception] {
return Write-Host "Could not retrieve azure resources, exiting.." -BackgroundColor Red -ForegroundColor DarkGray -ErrorAction Stop;
 }
}
$rgData = @{"rgName"=($rgfnInfo | Select -First 1 -ExpandProperty ResourceGroupName);
            "Plan"=$(($rgfnInfo | Select -First 1 -ExpandProperty Location)+"Plan")
            };

Try {
$appsvc = Get-AzureRmAppServicePlan -ResourceGroupName $rgData['rgName'] -Name $rgData['Plan']
$webAppsvcs = Get-AzureRmWebApp -AppServicePlan $appsvc
$subscr = (Get-AzureSubscription).SubscriptionId

# Azure profile modules v5.*
 $rmProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile;
 $rmClient = [Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient]::new($rmProfile);
 $bearerAccess = $rmClient.AcquireAccessToken($azureRmCtx.Tenant.TenantId);

 $fnlisting = Invoke-WebRequest -Method Get -Uri "https://management.azure.com/subscriptions/$subscr/resourceGroups/$($rgData['rgName'])/providers/Microsoft.Web/sites/?api-version=2016-08-01" -UseDefaultCredentials -UseBasicParsing -Headers @{Authorization=("Bearer {0}" -f $bearerAccess.AccessToken)}
 $fns = ($fnlisting.Content | ConvertFrom-Json).Value.properties;
 $stoppedfns = $fns | %{ Test-FnStopped -fnName $_.name -webapp $_ } -ErrorAction SilentlyContinue

 $fnOutput = @();

 $stoppedfns | %{
 $fnObjOut = New-Object -TypeName PSCustomObject;
 $fnObjOut | Add-Member -MemberType NoteProperty -Name "Name" -Value $_.Name
 $fnObjOut | Add-Member -MemberType NoteProperty -Name "State" -Value $_.State;
 $fnObjOut | Add-Member -MemberType NoteProperty -Name "Enabled" -Value $_.Enabled;
 $fnObjOut | Add-Member -MemberType NoteProperty -Name "HttpsOnly" -Value $_.HttpsOnly;
 $fnObjOut | Add-Member -MemberType NoteProperty -Name "HostName" -Value $_.DefaultHostName;
 $fnObjOut | Add-Member -MemberType NoteProperty -Name "FunctionsDetail" -Value "";
 $fnOutput += $fnObjOut;
 }

 $datafnOut = @();
 $stoppedfns | %{
 $data = Invoke-WebRequest -Method Get -Uri "https://management.azure.com/subscriptions/$subscr/resourceGroups/$($rgData['rgName'])/providers/Microsoft.Web/sites/$($_.name)/functions?api-version=2016-08-01" -UseDefaultCredentials -UseBasicParsing -Headers @{Authorization=("Bearer {0}" -f $bearerAccess.AccessToken)};
 $datafnOut+=($data.Content | ConvertFrom-Json).value.properties;
    }

 [PSCustomObject[]]$fnInfo=@(); $r=0;
  $datafnOut | %{
  $fnInfo += @{"name"=$_.name;"href"=$_.href;"invoke_url"=$_.invoke_url;"language"=$_.language;"isDisabled"=$_.isDisabled};
  
  $fnOutput[$r].FunctionsDetail = $fnInfo;
  $r++;
  }
 }
Catch {
  Logout-AzureRmAccount;
  Clear-AzureRmContext -Force;
  Clear-AzureRmDefault -Force;
 return Write-Error "Exception Occured - $_" -ErrorAction Stop;
 }
 
# Terminate azure session and cmdlet process.
  Logout-AzureRmAccount;
  Clear-AzureRmContext -Force;
  Clear-AzureRmDefault -Force;
  return ($fnOutput | Format-Table)
}

<#
 .DESCRIPTION
 Calling an azure function directly from its url and perform
 error checks. An alternative response can be set via a blob container
 if there is an error on the function.
 
 .PARAMETER InvokeUrlPath
 The function's endpoint url used to make the http call and error checks.
 #>

Function Invoke-AzureFunctionEndPoint {
param(
[Parameter(Mandatory=$true,Position=0)]
[string]$InvokeUrlPath
)
Try {
$urlpath = "$InvokeUrlPath" -replace "\?","" -replace "<","" -replace ">","" -replace "@","" -replace "%", "" -replace "\\", "" -replace "&","" -replace "\*", "" -replace "-","" -replace "|","" -replace "`r`n","" -replace "\^","" -replace "\(", "" -replace "\)","" -replace ":{","" -replace ":\'","" -replace "\'}","" -replace ":\[","" -replace "\[\{","" -replace "\}\]" -replace "}","" -replace "\]","" -replace "\'","";
$response = Invoke-RestMethod -Uri $urlpath -ErrorVariable '$error' -UseDefaultCredentials -UseBasicParsing -ErrorAction SilentlyContinue
}
Catch {
Write-Host "Call to azure function failed, getting default response.." | Out-Null; $error | Out-File ".\error-azurefn.log"
$altern = ((Invoke-WebRequest -Uri "https://xxxxxx.blob.core.windows.net/xxxxxx/data.json").Content | ConvertFrom-Json);
return $altern; # Catch exception, retrieve alternative response from an azure blob container
}
return ($response.httpResponse | ConvertFrom-Json);
}