PoshServerLess.psm1
import-module -name AZ -force -ErrorAction SilentlyContinue class AzFunctionsApp { [string] $FunctionAppName [string] $FunctionAppPath [string] $RessourceGroup [string] $FunctionHostName [string] $FunctionAppStorageName [string] $FunctionAppStorageShareName [string] $FunctionAppLocation [string] $FunctionTimeZone [string] $FunctionRuntime = "PowerShell" hidden [Boolean] $FunctionAppExistLocaly = $false [hashtable] $functionAppExtension = @{} [hashtable] $FunctionAppSettings = @{} [AzFunction[]] $Azfunctions = @() AzFunctionsApp ([string] $FunctionAppName, [string] $functionAppPath) { $this.init($FunctionAppName, $functionAppPath) } AzFunctionsApp ([string] $FunctionAppName, [string] $functionAppPath, [string] $RessourceGroup) { $this.init($FunctionAppName, $functionAppPath, $RessourceGroup) } AzFunctionsApp ([string] $FunctionAppName, [string] $functionAppPath, [string] $RessourceGroup, [string] $AzureLocation) { $this.init($FunctionAppName, $functionAppPath, $RessourceGroup, $AzureLocation) } hidden init([string] $FunctionAppName, [string] $functionAppPath) { $this.FunctionAppName = $FunctionAppName $this.FunctionAppPath = $functionAppPath $this.ListFunction() } hidden init([string] $FunctionAppName, [string] $functionAppPath, [string] $FunctionResourceGroup, [string] $FunctionAppLocation) { $this.FunctionAppName = $FunctionAppName $this.FunctionAppPath = join-path -path $functionAppPath -ChildPath $this.FunctionAppName $this.FunctionAppLocation = $FunctionAppLocation $this.RessourceGroup = $FunctionResourceGroup if (!(test-path -Path $this.FunctionAppPath -ErrorAction SilentlyContinue)) { try { new-item -Path $this.FunctionAppPath -ItemType Directory $Functionrequirements = "@{ 'Az' = '1.*'`" }" $FunctionHostConfig = @{ "version" = "2.0" "functionTimeout" = "00:05:00" "extensionBundle" = @{ "id" = "Microsoft.Azure.Functions.ExtensionBundle" "version"= "[1.*, 2.0.0)" } "logging"= @{ "logLevel"= @{ "default"= "Information" } "fileLoggingMode"= "always" } "managedDependency"= @{ "enabled"= $true } } $profileConfig = "if (`$env:MSI_SECRET -and (Get-Module -ListAvailable Az.Accounts)) { Connect-AzAccount -Identity }" new-item -Path (join-path $this.FunctionAppPath -ChildPath "profile.ps1") -ItemType File | Set-Content -PassThru -Encoding utf8 -Value $profileConfig new-item -Path (join-path $this.FunctionAppPath -ChildPath "requirements.psd1") -ItemType File | Set-Content -PassThru -Encoding utf8 -Value $Functionrequirements.ToString() new-item -Path (join-path $this.FunctionAppPath -ChildPath "host.json") -ItemType File | Set-Content -PassThru -Encoding utf8 -Value ($FunctionHostConfig | convertTo-json -Depth 4) } catch { Write-Error -Message " Exception Type: $($_.Exception.GetType().FullName) $($_.Exception.Message)" } } else { throw "Can not create a function app in $($this.FunctionAppPath)" } } hidden init([string] $FunctionAppName, [string] $functionAppPath, [string] $FunctionResourceGroup) { if (TestAzConnection) { try { $FunctionAppConfig = Get-AzWebApp -ResourceGroupName $FunctionResourceGroup -Name $FunctionAppName $this.FunctionAppName = $FunctionAppName $this.FunctionAppPath = join-path -Path $functionAppPath -ChildPath $FunctionAppName $this.RessourceGroup = $FunctionResourceGroup $this.FunctionAppLocation = $FunctionAppConfig.Location $this.FunctionHostName = $FunctionAppConfig.HostNames[0] $This.FunctionRuntime = ($FunctionAppConfig.SiteConfig.AppSettings | where-object name -eq "FUNCTIONS_WORKER_RUNTIME").Value $FunctionExtVerion = ($FunctionAppConfig.SiteConfig.AppSettings | where-object name -eq "FUNCTIONS_EXTENSION_VERSION").Value $this.FunctionTimeZone = ($FunctionAppConfig.SiteConfig.AppSettings | where-object name -eq "WEBSITE_TIME_ZONE").Value $this.FunctionAppStorageShareName = ($FunctionAppConfig.SiteConfig.AppSettings | where-object name -eq "WEBSITE_CONTENTSHARE").Value $FunctionStorageConfigString = ($FunctionAppConfig.SiteConfig.AppSettings | where-object name -eq "AzureWebJobsStorage").Value $FunctionStorageConfigHash = ConvertFrom-StringData -StringData $FunctionStorageConfigString.Replace(";","`r`n") $this.FunctionAppStorageName = $FunctionStorageConfigHash.AccountName $this.FunctionAppSettings = $FunctionStorageConfigHash #$this.GetAppSettings($FunctionAppConfig.SiteConfig.AppSettings) if ($FunctionExtVerion -ne "~2") { throw "Error this module only support Azure functions v2 with PowerShell" } $storageAccountObject = Get-AzStorageAccount -ResourceGroupName $FunctionResourceGroup -Name $FunctionStorageConfigHash.AccountName $StorageFileObject = Get-AzStorageFile -ShareName $this.FunctionAppStorageShareName -Context $storageAccountObject.Context -Path "/site/wwwroot" | Get-AzStorageFile GetFile -CloudFilesObject $StorageFileObject -context $storageAccountObject.Context -AzurePath "/site/wwwroot" -LocalPath $this.FunctionAppPath -AzureStorageShareName $this.FunctionAppStorageShareName $this.ListFunction() } catch { Write-Error -Message " Exception Type: $($_.Exception.GetType().FullName) $($_.Exception.Message)" } } else { throw "Not connected to Azure, use Login-AzAccount first" } } [Boolean] TestFunctionAppExistInAzure () { try { $DnsResolve = Resolve-DnsName -Name "$($this.FunctionAppName).azurewebsites.net" -ErrorAction SilentlyContinue if ($null -eq $DnsResolve) { return $false } else { return $true } } catch { Write-Error -Message " Exception Type: $($_.Exception.GetType().FullName) $($_.Exception.Message)" exit } } hidden [void] ListFunction () { try { if (test-path -Path $this.FunctionAppPath -ErrorAction SilentlyContinue) { $this.FunctionAppExistLocaly = $true $FunctionList = get-childitem -Path $this.FunctionAppPath -Exclude @("microsoft","bin","obj", "modules") -Directory foreach ($function in $FunctionList) { $functionPath = join-path -Path $this.FunctionAppPath -ChildPath $function.name $this.Azfunctions += [AzFunction]::new( $FunctionPath, $true) } if (test-path -Path (join-path -Path $this.FunctionAppPath -ChildPath "extensions.csproj") -ErrorAction SilentlyContinue) { [xml] $CsPRojetExtenstionItem = Get-Content -Path (join-path -Path $this.FunctionAppPath -ChildPath "extensions.csproj") foreach ($extension in $CsPRojetExtenstionItem.Project.ItemGroup.PackageReference) { $this.functionAppExtension.Add($extension.Include, $extension.Version) } } } } catch { Write-Error -Message " Exception Type: $($_.Exception.GetType().FullName) $($_.Exception.Message)" } } [void] GetAppSettings ([Collections.Generic.List[Microsoft.Azure.Management.WebSites.Models.NameValuePair]] $AppSettingList) { foreach ($appSetting in $AppSettingList) { write-verbose "Adding Key $($appSetting.name) / Value $($appSetting.value)" $this.FunctionAppSettings.Add($appSetting.name, $appSetting.value) } } [void] LoadFunctionFromAzure ([string] $RessourceGroup) { $this.RessourceGroup = $RessourceGroup $FunctionAppConfig = Get-AzWebApp -ResourceGroupName $this.RessourceGroup -Name $this.FunctionAppName $this.FunctionAppLocation = $FunctionAppConfig.Location $this.FunctionHostName = $FunctionAppConfig.HostNames[0] $This.FunctionRuntime = ($FunctionAppConfig.SiteConfig.AppSettings | where-object name -eq "FUNCTIONS_WORKER_RUNTIME").Value $FunctionExtVerion = ($FunctionAppConfig.SiteConfig.AppSettings | where-object name -eq "FUNCTIONS_EXTENSION_VERSION").Value $this.FunctionTimeZone = ($FunctionAppConfig.SiteConfig.AppSettings | where-object name -eq "WEBSITE_TIME_ZONE").Value $this.FunctionAppStorageShareName = ($FunctionAppConfig.SiteConfig.AppSettings | where-object name -eq "WEBSITE_CONTENTSHARE").Value $FunctionStorageConfigString = ($FunctionAppConfig.SiteConfig.AppSettings | where-object name -eq "AzureWebJobsStorage").Value $FunctionStorageConfigHash = ConvertFrom-StringData -StringData $FunctionStorageConfigString.Replace(";","`r`n") $this.FunctionAppStorageName = $FunctionStorageConfigHash.AccountName $this.FunctionAppSettings = $FunctionStorageConfigHash #$this.GetAppSettings($FunctionAppConfig.SiteConfig.AppSettings) if ($FunctionExtVerion -ne "~2") { throw "Error this module only support Azure functions v2 with PowerShell" } } [void] UpdateAppSetting ([string] $Name, [string] $Value) { if ($null -eq $this.FunctionAppSettings[$name] ) { $this.FunctionAppSettings.add($name,$value) }elseif ($name -in @("FUNCTIONS_WORKER_RUNTIME","AzureWebJobsStorage","FUNCTIONS_EXTENSION_VERSION","WEBSITE_CONTENTAZUREFILECONNECTIONSTRING","WEBSITE_CONTENTSHARE")) { throw "You can not change this function App Setting, FUNCTIONS_WORKER_RUNTIME,AzureWebJobsStorage,FUNCTIONS_EXTENSION_VERSION,WEBSITE_CONTENTAZUREFILECONNECTIONSTRING,WEBSITE_CONTENTSHARE" } else { $this.FunctionAppSettings[$name] = $value } try { if ($null -ne $this.RessourceGroup) { Set-AzWebApp -AppSettings $this.FunctionAppSettings -Name $this.FunctionAppName -ResourceGroupName $this.RessourceGroup } else { throw "You can't update App Settings as the object do not have any resource group, add a resource group" } } catch { Write-Error -Message " Exception Type: $($_.Exception.GetType().FullName) $($_.Exception.Message)" } } [void] RemoveFunction ([string] $Functionname) { $NewFunctiongArray = @() $functionPath = $null foreach ($Azfunction in $this.Azfunctions){ if ($Azfunction.FunctionName -ne $Functionname) { $NewFunctiongArray += $Azfunction } else { $functionPath = $Azfunction.FunctionPath } } $this.Azfunctions = $NewFunctiongArray if ($null -ne $functionPath) { try { remove-item -path $functionPath -Force -Recurse } catch { Write-Error -Message " Exception Type: $($_.Exception.GetType().FullName) $($_.Exception.Message)" } } } [void] AddFunction ([AzFunction] $FunctionObject) { if ( (Split-Path -Path $FunctionObject.FunctionPath -Parent) -eq $this.FunctionAppPath) { if (!(test-path -Path $FunctionObject.FunctionPath -ErrorAction SilentlyContinue)) { $FunctionObject.WriteFunction() } $this.Azfunctions += $FunctionObject } else { throw "The Path of The function $($FunctionObject.FunctionName) should be the same as the the Function App" } } [String] deployFunctionApp ([bool] $ManagedIdentity=$true) { try { if ( -not $this.TestFunctionAppExistInAzure() ) { $ModulePath = $PSScriptRoot $jsonArmTemplatePath = Join-Path -Path $ModulePath -ChildPath "function.json" $jsonArmTemplateObject = (Get-Content -Path $jsonArmTemplatePath -Raw | ConvertFrom-Json -AsHashtable) $DeploiementName = CreateUniqueString -BufferSize 15 $DeployParam = @{ "mode" = "Incremental" "ResourceGroupName" = $this.RessourceGroup "TemplateObject" = $jsonArmTemplateObject "functionAppName" = $this.FunctionAppName } if ($null -ne $this.FunctionAppLocation) { $DeployParam.add("location", $this.FunctionAppLocation) } if (! $ManagedIdentity) { $DeployParam.add("ManagedIdentity", "no") } else { $DeployParam.add("ManagedIdentity", "yes") } # implemente test $DeployParam.add("name", $DeploiementName) New-AzResourceGroupDeployment @DeployParam #New-AzResourceGroupDeployment -Name $DeploiementName -mode Incremental -ResourceGroupName $this.RessourceGroup -TemplateObject $jsonArmTemplateObject -functionAppName $this.FunctionAppName return $DeploiementName } else { throw "The Azure Functions App $($this.FunctionAppName) all ready exist in Azure" return $null } } catch { Write-Error -Message " Exception Type: $($_.Exception.GetType().FullName) $($_.Exception.Message)" exit } } [void] getFunctiondeploymentStatus ([String] $DeployementUserName, [String] $DeployementPassword) { } [void] PublishFunctionApp () { $FunctionZippedFolderPath = $this.CompressFunction() if ($null -ne $FunctionZippedFolderPath) { try { Publish-AzWebapp -ResourceGroupName $this.RessourceGroup -Name $this.FunctionAppName -ArchivePath $FunctionZippedFolderPath -force } catch { Write-Error -Message " Exception Type: $($_.Exception.GetType().FullName) $($_.Exception.Message)" } } else { throw "Zip file $($FunctionZippedFolderPath) not found" } } [string] CompressFunction () { try { $TmpFuncZipDeployFileName = [System.IO.Path]::GetRandomFileName() $TmpFuncZipDeployFileName = $TmpFuncZipDeployFileName.remove($TmpFuncZipDeployFileName.Length - 4) + ".zip" $TmpFuncZipDeployPath = join-path -Path $ENV:tmp -ChildPath $TmpFuncZipDeployFileName $excludeFilesAndFolders = @(".git",".vscode","bin","Microsoft",".funcignore",".gitignore") $FileToSendArray = @() foreach ($file in get-childitem -Path $this.FunctionAppPath) { if ($file.name -notin $excludeFilesAndFolders) { $FileToSendArray += $file.fullname } } compress-archive -Path $FileToSendArray -DestinationPath $TmpFuncZipDeployPath return $TmpFuncZipDeployPath } catch { Write-Error -Message " Exception Type: $($_.Exception.GetType().FullName) $($_.Exception.Message)" return $null } } } class AzFunction { [string] $FunctionName [string] $FunctionPath [AzFunctionsTrigger] $TriggerBinding [AzFunctionsBinding[]] $Binding = @() [Boolean] $overwrite [Boolean] $FunctionExist = $false [string] $JsonFunctionBindings hidden Init([string] $FunctionName, [string] $FunctionPath, [Boolean] $overwrite= $false) { $this.overwrite = $overwrite $this.FunctionName = $FunctionName $this.FunctionPath = $FunctionPath $this.TestFunctionPath(); if ($this.FunctionExist) { $this.LoadFunction() } } hidden [void] TestFunctionPath() { $this.FunctionExist= test-path -Path $this.FunctionPath -ErrorAction SilentlyContinue } hidden [Boolean] TestHttpOutBinding() { $HttpControl = $false if ($this.TriggerBinding.TriggerType -eq "http") { foreach ($binding in $this.Binding) { if (($binding.BindingDirection -eq "out") -and ($binding.BindingType -eq "http")) { $HttpControl = $true } } } else { $HttpControl = $true } return $HttpControl } [void] BuildJsonFunction() { $FunctionBinding = New-Object System.Collections.ArrayList $FunctionBinding.add($this.TriggerBinding) foreach ($binding in $this.Binding) { $FunctionBinding.add($binding) } $this.JsonFunctionBindings = @{"disabled"=$false; "bindings"=$FunctionBinding} | ConvertTo-Json -Depth 5 } AzFunction ([string] $FunctionName, [string] $FunctionPath) { $this.init($FunctionName, $FunctionPath, $false) } AzFunction ([string] $FunctionPath, [Boolean] $overwrite) { $this.init((split-path -Path $FunctionPath -Leaf), $FunctionPath, $overwrite) } AzFunction ([string] $FunctionName, [string] $FunctionPath, [Boolean] $overwrite) { $this.init( $FunctionName, $FunctionPath, $overwrite) } [void] AddBinding ([AzFunctionsBinding]$BindingObject) { $this.Binding += $BindingObject } [void] AddBindings ([AzFunctionsBinding[]]$BindingObjects) { foreach ($bindingObject in $BindingObjects) { $this.AddBinding($bindingObject) } } [void] AddTriger ([AzFunctionsTrigger]$Triger) { $this.TriggerBinding = $Triger } [Boolean] testAzFunction () { if (($this.Binding.count -ge 1) -and ($this.TriggerBinding.TriggerType -eq "http") ) { return $true } elseif (($this.Binding.count -ge 0) -and ($this.TriggerBinding.TriggerType -ne "http") -and ($null -ne $this.TriggerBinding) ) { return $true } else { return $false } } [boolean] TestAzFuncBinding ([string] $BindingName) { $SearchResult = $false foreach ($binding in $this.Binding){ if ($binding.BindingName -eq $BindingName) { $SearchResult = $true } } return $SearchResult } [void] RemoveAzFuncBinding ([string] $BindingName) { $NewBindingArray = @() foreach ($binding in $this.Binding){ if ($binding.BindingName -ne $BindingName) { $NewBindingArray += $binding } } $this.Binding = $NewBindingArray } [void] WriteFunction () { $FunctionConfigFile = join-path -Path $this.FunctionPath -ChildPath "function.json" if ($this.testAzFunction() -and $this.TestHttpOutBinding()) { if ((test-path -Path $FunctionConfigFile -ErrorAction SilentlyContinue)) { try { remove-item -Path $FunctionConfigFile -Force } catch { Write-Error -Message " Exception Type: $($_.Exception.GetType().FullName) $($_.Exception.Message)" } } if (!(test-path -Path $this.FunctionPath -ErrorAction SilentlyContinue)) { try { new-item -Path $this.FunctionPath -ItemType Directory } catch { Write-Error -Message " Exception Type: $($_.Exception.GetType().FullName) $($_.Exception.Message)" } } try { new-item -ItemType File -Path $FunctionConfigFile $this.BuildJsonFunction() Set-Content -Value $this.JsonFunctionBindings -Path $FunctionConfigFile -Encoding utf8 } catch { Write-Error -Message " Exception Type: $($_.Exception.GetType().FullName) $($_.Exception.Message)" } } else { throw "You can not have a function without trigger or a http trigger without a http out binding" } } [void] LoadFunction () { $FunctionConfigFile = join-path -Path $this.FunctionPath -ChildPath "function.json" if ((test-path -Path $FunctionConfigFile -ErrorAction SilentlyContinue)) { try { $FunctionJsonConfig = Get-Content $FunctionConfigFile -Raw | ConvertFrom-Json ForEach ($Binding in $FunctionJsonConfig.bindings) { if ($Binding.Type -like "*Trigger") { switch ($Binding.Type) { "timerTrigger" { $this.AddTriger([timerTrigger]::new($Binding.name, $Binding.Schedule)) } "queueTrigger" { $this.AddTriger([queueTrigger]::new($Binding.name, $Binding.queueName, $Binding.connection)) } "serviceBusTrigger" { $this.AddTriger([serviceBusTrigger]::new($Binding.name, $Binding.queueName, $Binding.connection)) } "blobTrigger" { $this.AddTriger( [blobTrigger]::new($Binding.name, $Binding.path, $Binding.connection)) } "httpTrigger" { $this.AddTriger( [httpTrigger]::new($Binding.name, $Binding.authLevel, $Binding.methods)) } } } else { switch ($Binding.Type) { "http" { $this.AddBinding([http]::new($Binding.name)) } "table" { if ($Binding.Direction -eq "in") { [tableIn]$TableInBinding = [tableIn]::new($Binding.name, $Binding.tableName, $Binding.connection) if ($null -ne $Binding.partitionKey) { $TableInBinding.partitionKey = $Binding.partitionKey } if ($null -ne $Binding.rowKey) { $TableInBinding.rowKey = $Binding.rowKey } if ($null -ne $Binding.take) { $TableInBinding.take = $Binding.take } if ($null -ne $Binding.filter) { $TableInBinding.filter = $Binding.filter } $this.AddBinding($TableInBinding) } else { $this.AddBinding( [table]::new($Binding.name, $Binding.tableName, $Binding.connection)) } } "blob" { $this.AddBinding([blob]::new($Binding.direction, $Binding.name, $Binding.path, $binding.connection)) } "queue" { $this.AddBinding([queue]::new($Binding.name, $Binding.queueName , $Binding.connection)) } } } } } catch [System.ArgumentException] { Write-Error -Message "Error while reading the json file" } catch { Write-Error -Message " Exception Type: $($_.Exception.GetType().FullName) $($_.Exception.Message)" } } } } class AzFunctionsTrigger { [string] $TriggerType [string] $TriggerName [string] hidden $direction = "in" } class queueTrigger : AzFunctionsTrigger { [string] $queueName [string] $connection queueTrigger ([string] $triggerName, [String] $queueName, [string] $connection) { $this.TriggerType = "queueTrigger" $this.TriggerName = $triggerName $this.connection = $connection $this.queueName = $queueName } } class timerTrigger : AzFunctionsTrigger { [string] $Schedule timerTrigger ([string] $triggerName, [string] $Schedule) { $this.TriggerType = "timerTrigger" $this.TriggerName = $triggerName $this.Schedule = $Schedule } } class serviceBusTrigger : AzFunctionsTrigger { [string] $queueName [string] $connection serviceBusTrigger ([string] $triggerName, [string] $queueName, [string] $Connection) { $this.TriggerType = "serviceBusTrigger" $this.TriggerName = $triggerName $this.queueName = $queueName $this.connection = $Connection } } class httpTrigger : AzFunctionsTrigger { [string] $authLevel [string[]] $methods = @() httpTrigger ([string] $triggerName, [string] $authLevel, [string[]] $methods) { $this.TriggerType = "httpTrigger" $this.TriggerName = $triggerName $this.methods += $methods $this.authLevel = $authLevel } } class blobTrigger : AzFunctionsTrigger { [string] $path [string] $connection blobTrigger ([string] $triggerName, [string] $path, [string] $connection) { $this.TriggerType = "blobTrigger" $this.TriggerName = $triggerName $this.connection = $connection $this.path = $path } } class AzFunctionsBinding { [string] $BindingName [string] $BindingDirection [string] $BindingType } class blob : AzFunctionsBinding { [String] $path [string] $connection blob([string]$direction, [string]$name, [String] $Path, [string] $connection){ $this.BindingDirection = $direction $this.BindingName = $name $this.path = $Path $this.connection = $connection $this.BindingType = "blob" } } class http : AzFunctionsBinding { http ([string] $Name) { $this.BindingName = $Name $this.BindingDirection = "out" $this.BindingType = "http" } } class queue : AzFunctionsBinding { [string] $queueName [string] $connection queue ([string] $Name, [string] $QueuName, [string] $connection) { $this.BindingName = $Name $this.connection = $Connection $this.queueName = $QueuName $this.BindingDirection = "out" $this.BindingType = "queue" } } class table : AzFunctionsBinding { [string] $connection [string] $tableName table([String]$Name, [string] $tableName, [string] $Connection) { $this.BindingName = $Name $this.connection = $Connection $this.tableName = $tableName $this.BindingDirection = "out" $this.BindingType = "table" } } class tableIn : AzFunctionsBinding { [string] $connection [string] $tableName [string] $partitionKey [string] $rowkey [int] $take [string] $filter tableIn([String]$Name, [string] $tableName, [string] $Connection) { $this.BindingName = $Name $this.connection = $Connection $this.tableName = $tableName $this.BindingDirection = "in" $this.BindingType = "table" } } function GetFile ( [object[]] $CloudFilesObject, [Microsoft.WindowsAzure.Commands.Common.Storage.LazyAzureStorageContext] $context, [string] $AzurePath= "/site/wwwroot", [string] $LocalPath, [String] $AzureStorageShareName ) { foreach ($CloudFile in $CloudFilesObject) { $fileobject = Get-AzStorageFile -ShareName $AzureStorageShareName -Context $context -Path $AzurePath | Get-AzStorageFile | where-object name -eq $CloudFile.name if ($fileobject.GetType().ToString() -eq "Microsoft.Azure.Storage.File.CloudFile") { $relative = $AzurePath.replace("/site/wwwroot","") $relative = $relative.replace("/","\") $relativeLocalPath = Join-Path -Path $LocalPath -ChildPath $relative $RelativeFilePath = Join-Path -Path $relativeLocalPath -ChildPath $fileobject.Name write-verbose "Copy $($fileobject.Name) File to path $($RelativeFilePath) Path $($LocalPath )" $fileobject | Get-AzStorageFileContent -Destination $RelativeFilePath } elseif (($fileobject.name -ne "Microsoft") -and ($fileobject.name -ne "bin") ) { $azPath = $AzurePath + "/" +$fileobject.Name $FolderPath = join-path -Path $localPath -ChildPath ($azPath.replace("/site/wwwroot/","")).replace("/","\") new-item -Path $FolderPath -ItemType Directory | Out-Null $fileobject = Get-AzStorageFile -ShareName $AzureStorageShareName -Context $context -Path $azPath | Get-AzStorageFile GetFile -CloudFilesObject $fileobject -context $context -AzurePath $azPath -LocalPath $localPath -AzureStorageShareName $AzureStorageShareName } } } function getMsTimeZone () { $ModulePath = $PSScriptRoot try { $jsonArmTemplatePath = Join-Path -Path $ModulePath -ChildPath "function.json" $ObjectData = (Get-Content -Path $jsonArmTemplatePath -Raw | ConvertFrom-Json) return $ObjectData.parameters.timezone.allowedValues } catch { Write-Error -Message " Exception Type: $($_.Exception.GetType().FullName) $($_.Exception.Message)" } } function TestModulePresent ([string] $moduleName="Az") { return ! $null -eq (get-module -ListAvailable | where-object name -eq $moduleName) } function CreateUniqueString ([int] $BufferSize= 10) { $randomArray = ((0x30..0x39) + ( 0x41..0x5A) + ( 0x61..0x7A) | Get-Random -Count $BufferSize | ForEach-Object {[char]$_}) return -join $randomArray } Function TestAzConnection () { try { $AzContext = get-azContext if ($null -eq $AzContext) { return $false } else { return $true } } catch [System.Management.Automation.CommandNotFoundException] { write-error "No AZURE PowerShell module" return $false } catch { Write-Error -Message " Exception Type: $($_.Exception.GetType().FullName) $($_.Exception.Message)" return $false } } function add-PoshServerlessFunctionBinding { <# .SYNOPSIS Add a Binding object to an existing azFunction Object .DESCRIPTION Add a Binding object to an existing azFunction Object .PARAMETER FunctionObject Specifies the function Object .PARAMETER BindingObject Specifie the Binding Object .EXAMPLE $MyFunction = new-PoshServerlessFunction -FunctionAppPath "c:\work\functionAppFolder\" -FunctionName "TimerFunction" $Biding = new-PoshServerlessFunctionBinding -Direction out -BindingName MyBinding -BindingType queue -connection MyStorage add-PoshServerlessFunctionBinding -FunctionObject $MyFunction -BindingObject $Biding #> [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [AzFunctionsBinding] $BindingObject, [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [AzFunction] $FunctionObject ) $FunctionObject.AddBinding($BindingObject) } function add-PoshServerLessFunctionToApp { <# .SYNOPSIS Add a function object to an existing Function App Object .DESCRIPTION Add a function object to an existing Function App Object .PARAMETER FunctionObject Specifies the function Object .PARAMETER FunctionAppObject Specifie the function App Object .EXAMPLE add-PoshServerLessFunctionToApp -FunctionObject $MyNewFunction -FunctionAppObject $MyApp #> [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [AzFunctionsApp] $FunctionAppObject, [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [AzFunction] $FunctionObject ) $FunctionAppObject.AddFunction($FunctionObject) } function get-PoshServerlessFunction { <# .SYNOPSIS Read a specific Azure Function from a path .DESCRIPTION Read a specific Azure Function from a path .PARAMETER Path Specifies the function path .PARAMETER OverWrite switch Specifies if the AZFunc Module should recreate the folder Default $false, in this case the module only rewrite the function.json .OUTPUTS AzFunction object .EXAMPLE get-PoshServerlessFunction -FunctionPath "c:\work\functionAppFolder\TimerFunction" Load the function TimerFunction from the FunctionAppFolder .EXAMPLE get-PoshServerlessFunction -FunctionPath "c:\work\functionAppFolder\TimerFunction" -OverWrite Load the function TimerFunction from the FunctionAppFolder and tell the module to overwrite the function folder #> [OutputType([AzFunction])] [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)] [ValidateScript({Test-Path $_\function.json})] [string] $FunctionPath, [switch] $OverWrite ) return [AzFunction]::new($FunctionPath, $OverWrite) } function get-PoshServerlessFunctionApp { <# .SYNOPSIS Create an Azure Function App object by reading a folder .DESCRIPTION Create an Azure Function App object by reading a folder If you have download a functions App or create it you can use this function to create an AzFunctionsApp object from the folder It will read the host.json and function folder .PARAMETER FunctionAppPath [String] the function App Path if the path doesn't exist the cmdlet will fail .PARAMETER FunctionAppName [String] the function App Name #> [OutputType([AzFunctionsApp])] [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)] [ValidateScript({Test-Path $_\host.json})] [string] $FunctionAppPath, [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $FunctionAppName ) return [AzFunctionsApp]::new($FunctionAppName, $FunctionAppPath) } function get-PoshServerlessFunctionBinding { <# .SYNOPSIS retreive Binding from a AzFunc Object .DESCRIPTION retreive Binding from a AzFunc Object .PARAMETER FunctionObject Specifies the function Object .EXAMPLE $FunctionObjectVar | get-PoshServerlessFunctionBinding #> [OutputType([AzFunctionsBinding[]])] [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)] [AzFunction] $FunctionObject ) return $FunctionObject.Binding } function get-PoshServerlessFunctionTrigger { <# .SYNOPSIS retreive trigger object from a AzFunc Object .DESCRIPTION retreive trigger object from a AzFunc Object .PARAMETER FunctionObject Specifies the function Object .EXAMPLE $FunctionObjectVar | get-PoshServerlessFunctionBinding #> [OutputType([AzFunctionsTrigger])] [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)] [AzFunction] $FunctionObject ) return $FunctionObject.TriggerBinding } function Initialize-PoshServerLessFunctionApp { <# .SYNOPSIS Create an Azure Function App in Azure And deploy the current function App object in .DESCRIPTION Create an Azure Function App in Azure And deploy the current function App object in This cmdlet can be use for creating a function localy and then create the function App in Azure and then deploy the functions or to copy a functions app .PARAMETER FunctionAppObject In or Out Queue binding accept only Out direction .PARAMETER RessourceGroup The ressource group .PARAMETER ManagedIdentity Indicate if the Function App need Managed Identity .EXAMPLE Initialize-PoshServerLessFunctionApp -FunctionAppObject $MyFunctionApp -RessourceGroup MyRg to copy a function app $functionsAppToCopy = sync-PoshServerlessFunctionApp -FunctionName MyFunctionApp01 -ResourceGroupName MyRessourceGroup -LocalFunctionPath 'c:\work\Myfunction' $NewFunctionAppFromCopy = get-PoshServerlessFunctionApp -FunctionAppPath "c:\work\Myfunction\MyFunctionApp01" -FunctionAppName FunctionAppUniqueName Initialize-PoshServerLessFunctionApp -FunctionAppObject $NewFunctionAppFromCopy -RessourceGroup MyRg This will copy the content of MyFunctionApp01 into FunctionAppUniqueName #> [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)] [AzFunctionsApp] $FunctionAppObject, [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)] [string] $RessourceGroup, [switch] $ManagedIdentity ) $FunctionAppObject.RessourceGroup = $RessourceGroup if ( TestAzConnection ) { if (-not $functionAppObject.TestFunctionAppExistInAzure()) { if ($ManagedIdentity) { $FunctionAppObject.deployFunctionApp( $true ) write-verbose "Deploy Function With Managed Identity" } else { $FunctionAppObject.deployFunctionApp( $false) write-verbose "Deploy Function Without Managed Identity" } $FunctionAppObject.LoadFunctionFromAzure($FunctionAppObject.RessourceGroup) } else { throw "The Azure Functions App $($this.FunctionAppName) all ready exist in Azure" } }else { throw "You are not connected to Azure" } } function new-PoshServerlessFunction { <# .SYNOPSIS Create a new azure function object .DESCRIPTION Create a new azure function object The FunctionAppPath and the name parameter will build the function path .PARAMETER FunctionAppPath Specifies the function App path .PARAMETER FunctionName Specifie the name of the function. .PARAMETER OverWrite switch Specifies if the AZFunc Module should recreate the function folder if exist Default $false, in this case the module only rewrite the function.json .OUTPUTS AzFunction object .EXAMPLE new-PoshServerlessFunction -FunctionAppPath "c:\work\functionAppFolder\" -FunctionName "TimerFunction" create a new azFunction Object #> [OutputType([AzFunction])] [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [ValidateScript({Test-Path $_})] [string] $FunctionAppPath, [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $FunctionName, [switch] $OverWrite ) $functionPath = join-path -Path $FunctionAppPath -ChildPath $FunctionName return [AzFunction]::new($FunctionName,$FunctionPath, $OverWrite) } function new-PoshServerlessFunctionApp { <# .SYNOPSIS Create a new azure function App object and the function app file .DESCRIPTION Create a new azure function App object and the function app file It doesn't create the function in Azure .PARAMETER FunctionAppPath Specifies the function App local path, this path must not exist .PARAMETER FunctionAppName The host name of the function App. This Name must be globally unique .PARAMETER FunctionAppLocation The Function App desired location .PARAMETER FunctionAppResourceGroup The Function App desired Resource Group .OUTPUTS AzFunctionsApp object .EXAMPLE new-PoshServerlessFunctionApp -FunctionAppPath "c:\work\functionAppFolder\" -FunctionAppName "MyFunction01" -FunctionAppLocation "WestEurope" -FunctionAppResourceGroup "MyRg" create a new azFunction Object #> [OutputType([AzFunctionsApp])] [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $FunctionAppPath, [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $FunctionAppName, [string] [ValidateSet("UKSouth", "UKWest", "NorthEurope","WestEurope", "FranceCentral","SouthAfricaNorth","CentralIndia", "SouthIndia","WestIndia","JapanEast","JapanWest","KoreaCentral", "EastAsia","SoutheastAsia","AustraliaCentral", "AustraliaCentral2", "AustraliaEast", "AustraliaSoutheast", "BrazilSouth", "CanadaCentral", "CanadaEast", "ChinaEast","ChinaEast2", "ChinaNorth", "GermanyCentral", "GermanyNortheast","WestUS", "WestUS2", "CentralUS", "EastUS","EastUS2","NorthCentralUS","SouthCentralUS","WestCentralUS" )] $FunctionAppLocation, [string] $FunctionAppResourceGroup ) return [AzFunctionsApp]::new($FunctionAppName,$FunctionAppPath, $FunctionAppResourceGroup, $FunctionAppLocation) } function new-PoshServerlessFunctionBinding { <# .SYNOPSIS Create an AzfunctionBinding object .DESCRIPTION Create an AzfunctionBinding object There are two types of Direction In and Out and "blob","http","queue", "table" .PARAMETER Direction In or Out Queue binding accept only Out direction .PARAMETER BindingName In or Out Queue binding accept only Out direction .EXAMPLE $Biding = new-PoshServerlessFunctionBinding -Direction out -BindingName MyBinding -BindingType queue -connection MyStorage #> [OutputType([AzFunctionsBinding])] [CmdletBinding()] param( [parameter(Mandatory = $true, ParameterSetName = "blob")] [parameter(Mandatory = $true, ParameterSetName = "queue")] [parameter(Mandatory = $true, ParameterSetName = "table")] [parameter(Mandatory = $true, ParameterSetName = "http")] [ValidateSet("in","Out")] [string] $Direction, [parameter(Mandatory = $true, ParameterSetName = "blob")] [parameter(Mandatory = $true, ParameterSetName = "queue")] [parameter(Mandatory = $true, ParameterSetName = "table")] [parameter(Mandatory = $true, ParameterSetName = "http")] [string] $BindingName, [parameter(Mandatory = $true, ParameterSetName = "blob")] [parameter(Mandatory = $true, ParameterSetName = "queue")] [parameter(Mandatory = $true, ParameterSetName = "table")] [parameter(Mandatory = $true, ParameterSetName = "http")] [ValidateSet("blob","http","queue", "table")] [string] $BindingType, [parameter(Mandatory = $true, ParameterSetName = "blob")] [string] $Path, [parameter(Mandatory = $true, ParameterSetName = "blob")] [parameter(Mandatory = $true, ParameterSetName = "queue")] [parameter(Mandatory = $true, ParameterSetName = "table")] [string] $connection, [parameter(Mandatory = $true, ParameterSetName = "queue")] [string] $queueName, [parameter(Mandatory = $true, ParameterSetName = "table")] [string] $tableName, [parameter(Mandatory = $false, ParameterSetName = "table")] [string] $partitionKey, [parameter(Mandatory = $false,ParameterSetName = "table")] [string] $rowkey, [parameter(Mandatory = $false,ParameterSetName = "table")] [int] $take, [parameter(Mandatory = $false,ParameterSetName = "table")] [string] $filter ) switch ($PSCmdlet.ParameterSetName) { "blob" { return [blob]::new($direction, $BindingName, $Path, $connection) } "table" { if ($direction -eq "in") { $tableinObject = [table]::new($BindingName, $tableName, $Connection) if ($PSBoundParameters.ContainsKey('filter')) { $tableinObject.filter = $filter } if ($PSBoundParameters.ContainsKey('take')) { $tableinObject.take = $take } if ($PSBoundParameters.ContainsKey('rowkey')) { $tableinObject.rowkey = $rowkey } if ($PSBoundParameters.ContainsKey('partitionKey')) { $tableinObject.partitionKey = $partitionKey } return $tableinObject } else { return [table]::new($BindingName, $tableName, $Connection) } } "queue" { return [queue]::new($BindingName, $QueuName, $connection) } "http" { return [http]::new($BindingName) } } } function new-PoshServerlessFunctionTrigger { <# .SYNOPSIS Create an AzFunctionTrigger .DESCRIPTION Create an AzFunctionTrigger There are 5 types queueTrigger, timerTrigger, serviceBusTrigger, httpTrigger, blobTrigger .PARAMETER TriggerType Kind of trigger "queueTrigger","timerTrigger", "httpTrigger","serviceBusTrigger","blobTrigger" .PARAMETER TriggerName Name of the trigger. The name will be use in the run.ps1 as a parameter .PARAMETER connection The name of AppSetting for the storage configuration .PARAMETER queueName For queue trigger only, Name of the queue in the storage .PARAMETER ServiceBusqueueName For Service Bus trigger only, Name of the queue in the storage .PARAMETER Schedule For timer trigger only, the schedule in a cron format, 0 * 8 * * * .PARAMETER methods For web trigger only, Allowed HTTP verbs @("POST", "GET") .PARAMETER authLevel For web trigger only, authorisation level anonymous no API key needed function the function App key is needed admin the function master key is needed (this key can be also use in scm) .PARAMETER authLevel For blob trigger only, path in the storage account the function will monitor container/{name} path .EXAMPLE $TriggerObject = new-PoshServerlessFunctionTrigger -TriggerName QueueTrigger -TriggerType queueTrigger -queueName myQueue -connection MyAzFuncStorage #> [OutputType([AzFunctionsTrigger])] [CmdletBinding()] param( [parameter(Mandatory = $true, ParameterSetName = "serviceBusTrigger")] [parameter(Mandatory = $true, ParameterSetName = "queueTrigger")] [parameter(Mandatory = $true, ParameterSetName = "blobTrigger")] [parameter(Mandatory = $true, ParameterSetName = "timerTrigger")] [parameter(Mandatory = $true, ParameterSetName = "httpTrigger")] [ValidateSet("queueTrigger","timerTrigger", "httpTrigger","serviceBusTrigger","blobTrigger")] [string] $TriggerType, [parameter(Mandatory = $true, ParameterSetName = "serviceBusTrigger")] [parameter(Mandatory = $true, ParameterSetName = "queueTrigger")] [parameter(Mandatory = $true, ParameterSetName = "blobTrigger")] [parameter(Mandatory = $true, ParameterSetName = "timerTrigger")] [parameter(Mandatory = $true, ParameterSetName = "httpTrigger")] [string] $TriggerName, [parameter(Mandatory = $true, ParameterSetName = "serviceBusTrigger")] [parameter(Mandatory = $true, ParameterSetName = "queueTrigger")] [parameter(Mandatory = $true, ParameterSetName = "blobTrigger")] [string] $connection, [parameter(Mandatory = $true, ParameterSetName = "queueTrigger")] [string] $queueName, [parameter(Mandatory = $true, ParameterSetName = "serviceBusTrigger")] [string] $ServiceBusqueueName, [parameter(Mandatory = $true, ParameterSetName = "timerTrigger")] [string] $Schedule, [parameter(Mandatory = $true, ParameterSetName = "httpTrigger")] [string[]] $methods, [parameter( ParameterSetName = "httpTrigger")] [ValidateSet("function","admin")] [string] $authLevel = "function", [parameter(Mandatory = $true, ParameterSetName = "blobTrigger")] [string] $path ) switch ($PSCmdlet.ParameterSetName) { "queueTrigger" { return [queueTrigger]::new($triggerName, $queueName, $connection) } "blobTrigger" { return [blobTrigger]::new($triggerName, $path, $connection) } "httpTrigger" { return [httpTrigger]::new($triggerName, $authLevel, $methods) } "timerTrigger" { return [timerTrigger]::new($triggerName, $Schedule) } "serviceBusTrigger" { return [serviceBusTrigger]::new($triggerName, $ServiceBusqueueName, $connection) } } } function publish-PoshServerLessFunctionApp { <# .SYNOPSIS Publish the function to Azure .DESCRIPTION Publish the function app to Azure This action will replace all the functions inside Azure by those in the function app Object You need to have a valid ResourceGroup in the object (for example by using sync-PoshServerlessFunctionApp) .PARAMETER FunctionAppObject Specifies the function Object .EXAMPLE $myFunctionApp = sync-PoshServerlessFunctionApp -FunctionName MyFunctionApp01 -ResourceGroupName MyRessourceGroup -LocalFunctionPath 'c:\work\Myfunction' $myFunction = new-PoshServerlessFunction -FunctionAppPath "c:\work\Myfunction\timerfunc" -FunctionName "TimerFunction" $TriggerObject = new-PoshServerlessFunctionTrigger -TriggerName QueueTrigger -TriggerType queueTrigger -queueName myQueue -connection MyAzFuncStorage update-PoshServerlessFunctionTrigger -FunctionObject myFunction -TriggerObject $TriggerObject add-PoshServerLessFunctionToApp -FunctionObject $myFunction -FunctionAppObject $myFunctionApp publish-PoshServerLessFunctionApp -FunctionAppObject $myFunctionApp #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Medium')] param( [parameter(Mandatory = $true, ValueFromPipeline=$true)] [ValidateNotNull()] [AzFunctionsApp] $FunctionAppObject ) if ($PSCmdlet.ShouldProcess($FunctionAppObject.FunctionAppName,"Publish this Function to Azure, it will rewrite the entire App in Azure")) { $FunctionAppObject.PublishFunctionApp() } } function remove-PoshServerlessFunctionBinding { [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)] [AzFunction] $FunctionObject, [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $BindingName ) if (test-PoshServerlessFunctionBinding -FunctionObject $FunctionObject -BindingName $BindingName) { $FunctionObject.RemoveAzFuncBinding($BindingName) } else { throw "Error: No Binding found" } } function remove-PoshServerLessFunctionToApp { <# .SYNOPSIS Remove a function object to an existing Function App Object .DESCRIPTION Remove a function object to an existing Function App Object It also remove the function from the disk .PARAMETER FunctionName Specifies the function Name .PARAMETER FunctionAppObject Specifie the Binding Object .EXAMPLE #> [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [AzFunctionsApp] $FunctionAppObject, [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $FunctionName ) $FunctionAppObject.RemoveFunction($FunctionName) } function Resolve-PoshServerlessFunctionApp { <# .SYNOPSIS This function resolve Azure Azure Parameters like AppSetting for a function App that exit localy and on Azure but are not sync .DESCRIPTION This function resolve Azure Azure Parameters like AppSetting for a function App that exit localy and on Azure but are not sync .PARAMETER FunctionAppObject A [AzFunctionsApp] object .PARAMETER ResourceGroupName The name of the ressource Group .EXAMPLE $FunctionApp = get-PoshServerlessFunctionApp -FunctionAppPath Path -FunctionAppName MyFuntion Resolve-PoshServerlessFunctionApp -FunctionAppObject $FunctionApp -ResourceGroupName MyRessourceGroup #> [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)] [AzFunctionsApp] $FunctionAppObject, [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $ResourceGroupName ) $FunctionAppObject.LoadFunctionFromAzure($ResourceGroupName) } function set-PoshServerlessFunctionAppSetting { <# .SYNOPSIS Add or Update an App Setting .DESCRIPTION Add or Update an App Setting .PARAMETER FunctionsAppObject The Function App object .PARAMETER AppSettingName string Representing the timezone see the value can't be FUNCTIONS_WORKER_RUNTIME,AzureWebJobsStorage,FUNCTIONS_EXTENSION_VERSION,WEBSITE_CONTENTAZUREFILECONNECTIONSTRING,WEBSITE_CONTENTSHARE .PARAMETER AppSettingValue string Representing the timezone see .EXAMPLE #> [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [AzFunctionsApp] $FunctionAppObject, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [ValidateScript({$_ -notin @("FUNCTIONS_WORKER_RUNTIME","AzureWebJobsStorage","FUNCTIONS_EXTENSION_VERSION","WEBSITE_CONTENTAZUREFILECONNECTIONSTRING","WEBSITE_CONTENTSHARE")})] [string] $AppSettingName, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $AppSettingValue ) write-verbose "Setting value for $($AppSettingName)" $PatternWhiteSpace ="[\s-[\r\n]]" if (! $AppSettingName -match $PatternWhiteSpace) { $FunctionAppObject.UpdateAppSetting($AppSettingName, $AppSettingValue) } else { throw "White Space are not allowed in App Settings Name" } } function Set-PoshServerlessFunctionAppTimezone { <# .SYNOPSIS Set the time zone setting for the function app object .DESCRIPTION Set the time zone setting for the function app object To take effect the function need be initialised either by sync-PoshServerlessFunctionApp or new-PoshServerLessFunctionApp .PARAMETER FunctionApp The Function App object .PARAMETER TimeZone string Representing the timezone see .EXAMPLE #> [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [AzFunctionsApp] $FunctionAppObject, [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] [ValidateSet("Dateline Standard Time", "UTC-11", "Aleutian Standard Time", "Hawaiian Standard Time", "Marquesas Standard Time", "Alaskan Standard Time", "UTC-09", "Pacific Standard Time (Mexico)", "UTC-08", "Pacific Standard Time", "US Mountain Standard Time", "Mountain Standard Time (Mexico)", "Mountain Standard Time", "Central America Standard Time", "Central Standard Time", "Easter Island Standard Time", "Central Standard Time (Mexico)", "Canada Central Standard Time", "SA Pacific Standard Time", "Eastern Standard Time (Mexico)", "Eastern Standard Time", "Haiti Standard Time", "Cuba Standard Time", "US Eastern Standard Time", "Turks And Caicos Standard Time", "Paraguay Standard Time", "Atlantic Standard Time", "Venezuela Standard Time", "Central Brazilian Standard Time", "SA Western Standard Time", "Pacific SA Standard Time", "Newfoundland Standard Time", "Tocantins Standard Time", "E. South America Standard Time", "SA Eastern Standard Time", "Argentina Standard Time", "Greenland Standard Time", "Montevideo Standard Time", "Magallanes Standard Time", "Saint Pierre Standard Time", "Bahia Standard Time", "UTC-02", "Mid-Atlantic Standard Time", "Azores Standard Time", "Cape Verde Standard Time", "UTC", "Morocco Standard Time", "GMT Standard Time", "Greenwich Standard Time", "W. Europe Standard Time", "Central Europe Standard Time", "Romance Standard Time", "Sao Tome Standard Time", "Central European Standard Time", "W. Central Africa Standard Time", "Jordan Standard Time", "GTB Standard Time", "Middle East Standard Time", "Egypt Standard Time", "E. Europe Standard Time", "Syria Standard Time", "West Bank Standard Time", "South Africa Standard Time", "FLE Standard Time", "Israel Standard Time", "Kaliningrad Standard Time", "Sudan Standard Time", "Libya Standard Time", "Namibia Standard Time", "Arabic Standard Time", "Turkey Standard Time", "Arab Standard Time", "Belarus Standard Time", "Russian Standard Time", "E. Africa Standard Time", "Iran Standard Time", "Arabian Standard Time", "Astrakhan Standard Time", "Azerbaijan Standard Time", "Russia Time Zone 3", "Mauritius Standard Time", "Saratov Standard Time", "Georgian Standard Time", "Caucasus Standard Time", "Afghanistan Standard Time", "West Asia Standard Time", "Ekaterinburg Standard Time", "Pakistan Standard Time", "India Standard Time", "Sri Lanka Standard Time", "Nepal Standard Time", "Central Asia Standard Time", "Bangladesh Standard Time", "Omsk Standard Time", "Myanmar Standard Time", "SE Asia Standard Time", "Altai Standard Time", "W. Mongolia Standard Time", "North Asia Standard Time", "N. Central Asia Standard Time", "Tomsk Standard Time", "China Standard Time", "North Asia East Standard Time", "Singapore Standard Time", "W. Australia Standard Time", "Taipei Standard Time", "Ulaanbaatar Standard Time", "Aus Central W. Standard Time", "Transbaikal Standard Time", "Tokyo Standard Time", "North Korea Standard Time", "Korea Standard Time", "Yakutsk Standard Time", "Cen. Australia Standard Time", "AUS Central Standard Time", "E. Australia Standard Time", "AUS Eastern Standard Time", "West Pacific Standard Time", "Tasmania Standard Time", "Vladivostok Standard Time", "Lord Howe Standard Time", "Bougainville Standard Time", "Russia Time Zone 10", "Magadan Standard Time", "Norfolk Standard Time", "Sakhalin Standard Time", "Central Pacific Standard Time", "Russia Time Zone 11", "New Zealand Standard Time", "UTC+12", "Fiji Standard Time", "Kamchatka Standard Time", "Chatham Islands Standard Time", "UTC+13", "Tonga Standard Time", "Samoa Standard Time", "Line Islands Standard Time")] $TimeZone ) $FunctionAppObject.UpdateAppSetting("WEBSITE_TIME_ZONE", $TimeZone) } function sync-PoshServerlessFunctionApp { <# .SYNOPSIS Download a function App content to the local workstation .DESCRIPTION Download a function App content to the local workstation this cmdlet use Azure PowerShell, you need to install it install-module -name AZ -scope CurrentUser You need a valid connexion to azure before, run login-azaccount before runing this cmdlet .PARAMETER FunctionName The function name in Azure ex: MyPowerShellAzFunction .PARAMETER ResourceGroupName The name of the ressouce group in Azure where the function app is .PARAMETER LocalFunctionPath The local path to download Azure functions files and folder The path should be empty .EXAMPLE sync-PoshServerlessFunctionApp -FunctionName MyFunctionApp01 -ResourceGroupName MyRessourceGroup -LocalFunctionPath 'c:\work\Myfunction' #> [OutputType([AzFunctionsApp])] [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)] [string] $FunctionAppName, [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $ResourceGroupName, [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $LocalFunctionPath ) $FunctionPath = join-path -Path $LocalFunctionPath -ChildPath $FunctionAppName if (test-path -Path $LocalFunctionPath -ErrorAction SilentlyContinue) { if (test-path -Path $FunctionPath -ErrorAction SilentlyContinue) { throw "The Path of The function $($LocalFunctionPath) is not empty" }else { new-item -Path $FunctionPath -ItemType Directory | out-null } } else { try { new-item -Path $FunctionPath -ItemType Directory | out-null } catch { Write-Error -Message " Exception Type: $($_.Exception.GetType().FullName) $($_.Exception.Message)" } } return [AzFunctionsApp]::new($FunctionAppName, $LocalFunctionPath, $ResourceGroupName) } function test-PoshServerlessFunctionBinding { <# .SYNOPSIS Test if a binding exist in a AzFunc Object .DESCRIPTION Test if a binding exist in a AzFunc Object, by BindingName Return a boolean True if the Binding exist False if the bindind do not exist .PARAMETER FunctionObject The AzFunction Object to test .PARAMETER BindingName The Binding name to test (string) .OUTPUTS Boolean .EXAMPLE test-PoshServerlessFunctionBinding -FunctionObject $FunctionObject -BindingName BindingNameToTest #> [OutputType([boolean])] [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)] [AzFunction] $FunctionObject, [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $BindingName ) return $FunctionObject.TestAzFuncBinding($BindingName) } function update-PoshServerlessFunctionTrigger { <# .SYNOPSIS Update an AzFunction Object with a trigger Object .DESCRIPTION Update an AzFunction Object with a trigger Object .PARAMETER FunctionObject Specifies the function Object .PARAMETER TriggerObject Specifie the Trigger binding Object .EXAMPLE $myFunction = new-PoshServerlessFunction -FunctionAppPath "c:\work\Myfunction\timerfunc" -FunctionName "TimerFunction" $TriggerObject = new-PoshServerlessFunctionTrigger -TriggerName QueueTrigger -TriggerType queueTrigger -queueName myQueue -connection MyAzFuncStorage update-PoshServerlessFunctionTrigger -FunctionObject myFunction -TriggerObject $TriggerObject #> [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [AzFunctionsTrigger] $triggerObject, [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [AzFunction] $FunctionObject ) $FunctionObject.AddTriger($triggerObject) } function write-PoshServerlessFunction { <# .SYNOPSIS Update the function folder with the azFunctionObject object .DESCRIPTION Update the function folder with the azFunctionObject object .PARAMETER FunctionObject Specifies the function Object .EXAMPLE $AzFunctionObject | write-PoshServerlessFunction #> [CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)] [AzFunction] $FunctionObject ) $FunctionObject.WriteFunction() } |