Skip to content

Commit cb133db

Browse files
authored
Merge pull request #4 from PerfectThymeTech/marvinbuss/function_workflows
Add Function Workflows
2 parents 53e203c + 274491e commit cb133db

File tree

11 files changed

+240
-54
lines changed

11 files changed

+240
-54
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
name: Function App Deploy Template
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
environment:
7+
required: true
8+
type: string
9+
default: "dev"
10+
description: "Specifies the environment of the deployment."
11+
python_version:
12+
required: true
13+
type: string
14+
default: "3.10"
15+
description: "Specifies the python version."
16+
function_directory:
17+
required: true
18+
type: string
19+
description: "Specifies the directory of the Azure Function."
20+
function_name:
21+
required: true
22+
type: string
23+
description: "Specifies the name of the Azure Function."
24+
secrets:
25+
TENANT_ID:
26+
required: true
27+
description: "Specifies the tenant id of the deployment."
28+
CLIENT_ID:
29+
required: true
30+
description: "Specifies the client id."
31+
CLIENT_SECRET:
32+
required: true
33+
description: "Specifies the client secret."
34+
SUBSCRIPTION_ID:
35+
required: true
36+
description: "Specifies the client id."
37+
38+
jobs:
39+
deployment:
40+
name: Function App Deploy
41+
runs-on: self-hosted
42+
continue-on-error: false
43+
environment: ${{ inputs.environment }}
44+
45+
steps:
46+
# Setup Python 3.10
47+
- name: Setup Python 3.10
48+
id: python_setup
49+
uses: actions/setup-python@v4
50+
with:
51+
python-version: ${{ inputs.python_version }}
52+
53+
# Check Out Repository
54+
- name: Check Out Repository
55+
id: checkout_repository
56+
uses: actions/checkout@v3
57+
58+
# Install Function Dependencies
59+
- name: Resolve Function Dependencies
60+
id: function_dependencies
61+
shell: bash
62+
run: |
63+
pushd '${{ inputs.function_directory }}'
64+
python -m pip install --upgrade pip
65+
pip install -r requirements.txt --target=".python_packages/lib/site-packages"
66+
popd
67+
68+
# Login to Azure
69+
- name: Azure Login
70+
id: azure_login
71+
uses: azure/login@v1
72+
with:
73+
creds: '{"clientId":"${{ secrets.CLIENT_ID }}","clientSecret":"${{ secrets.CLIENT_SECRET }}","subscriptionId":"${{ secrets.SUBSCRIPTION_ID }}","tenantId":"${{ secrets.TENANT_ID }}"}'
74+
75+
# Deploy Function
76+
- name: Deploy Function
77+
id: function_deploy
78+
uses: Azure/functions-action@v1
79+
with:
80+
app-name: ${{ inputs.function_name }}
81+
package: ${{ inputs.function_directory }}
82+
scm-do-build-during-deployment: true
83+
enable-oryx-build: true
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Function App Test Template
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
python_version:
7+
required: true
8+
type: string
9+
default: "3.10"
10+
description: "Specifies the python version."
11+
function_directory:
12+
required: true
13+
type: string
14+
description: "Specifies the directory of the Azure Function."
15+
16+
jobs:
17+
deployment:
18+
name: Function App Test
19+
runs-on: ubuntu-latest
20+
continue-on-error: false
21+
22+
steps:
23+
# Setup Python 3.10
24+
- name: Setup Python 3.10
25+
id: python_setup
26+
uses: actions/setup-python@v4
27+
with:
28+
python-version: ${{ inputs.python_version }}
29+
30+
# Check Out Repository
31+
- name: Check Out Repository
32+
id: checkout_repository
33+
uses: actions/checkout@v3
34+
35+
# Run Python Tests
36+
- name: Run Python Tests
37+
id: python_test
38+
run: |
39+
pip install -r ${{ inputs.function_directory }}/requirements.txt -q
40+
pip install -r requirements.txt -q
41+
pytest

.github/workflows/functionApp.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Function App Deployment
2+
on:
3+
push:
4+
branches:
5+
- main
6+
paths:
7+
- "**.py"
8+
9+
pull_request:
10+
branches:
11+
- main
12+
paths:
13+
- "**.py"
14+
15+
jobs:
16+
function_test:
17+
uses: ./.github/workflows/_functionAppTestTemplate.yml
18+
name: "Function App Test"
19+
with:
20+
python_version: "3.10"
21+
function_directory: "./code/function"
22+
23+
function_deploy:
24+
uses: ./.github/workflows/_functionAppDeployTemplate.yml
25+
name: "Function App Deploy"
26+
needs: [function_test]
27+
if: github.event_name == 'push' || github.event_name == 'release'
28+
with:
29+
environment: "dev"
30+
python_version: "3.10"
31+
function_directory: "./code/function"
32+
function_name: "myfunc-dev-fctn001"
33+
secrets:
34+
TENANT_ID: ${{ secrets.TENANT_ID }}
35+
CLIENT_ID: ${{ secrets.CLIENT_ID }}
36+
CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }}
37+
SUBSCRIPTION_ID: ${{ secrets.SUBSCRIPTION_ID }}

.github/workflows/python.yml

Lines changed: 0 additions & 44 deletions
This file was deleted.

code/function/api/v1/api_v1.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
from function.api.v1.endpoints import heartbeat, sample
33

44
api_v1_router = APIRouter()
5-
api_v1_router.include_router(sample.router, prefix="/landingZone", tags=["sample"])
5+
api_v1_router.include_router(sample.router, prefix="/sample", tags=["sample"])
66
api_v1_router.include_router(heartbeat.router, prefix="/health", tags=["health"])

code/function/api/v1/endpoints/sample.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
router = APIRouter()
1010

1111

12-
@router.post("/create", response_model=SampleResponse, name="create")
12+
@router.post("/sample", response_model=SampleResponse, name="sample")
1313
async def post_predict(
1414
data: SampleRequest,
1515
) -> SampleResponse:
1616
logger.info(f"Received request: {data}")
17-
return SampleResponse(output=f"Hello ${data.input}")
17+
return SampleResponse(output=f"Hello {data.input}")

code/infra/function.tf

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ resource "azapi_resource" "function" {
4040
scmSiteAlsoStopped = false
4141
serverFarmId = azurerm_service_plan.service_plan.id
4242
storageAccountRequired = false
43+
vnetContentShareEnabled = true
4344
virtualNetworkSubnetId = azapi_resource.subnet_function.id
45+
vnetRouteAllEnabled = true
4446
siteConfig = {
4547
autoHealEnabled = false
4648
acrUseManagedIdentityCreds = false
@@ -66,6 +68,10 @@ resource "azapi_resource" "function" {
6668
name = "WEBSITE_CONTENTOVERVNET"
6769
value = "1"
6870
},
71+
{
72+
name = "WEBSITE_RUN_FROM_PACKAGE"
73+
value = "1"
74+
},
6975
{
7076
name = "AzureWebJobsStorage__accountName"
7177
value = azurerm_storage_account.storage.name
@@ -75,10 +81,10 @@ resource "azapi_resource" "function" {
7581
detailedErrorLoggingEnabled = true
7682
functionAppScaleLimit = 0
7783
functionsRuntimeScaleMonitoringEnabled = false
78-
ftpsState = "FtpsOnly"
84+
ftpsState = "Disabled"
7985
http20Enabled = false
8086
ipSecurityRestrictionsDefaultAction = "Deny"
81-
linuxFxVersion = "Python|3.10"
87+
linuxFxVersion = "Python|${var.python_version}"
8288
localMySqlEnabled = false
8389
loadBalancing = "LeastRequests"
8490
minTlsVersion = "1.2"
@@ -89,7 +95,6 @@ resource "azapi_resource" "function" {
8995
scmIpSecurityRestrictionsUseMain = false
9096
scmIpSecurityRestrictionsDefaultAction = "Deny"
9197
use32BitWorkerProcess = true
92-
vnetRouteAllEnabled = true
9398
vnetPrivatePortsCount = 0
9499
webSocketsEnabled = false
95100
}
@@ -131,3 +136,25 @@ resource "azurerm_monitor_diagnostic_setting" "diagnostic_setting_function" {
131136
}
132137
}
133138
}
139+
140+
resource "azurerm_private_endpoint" "function_private_endpoint" {
141+
name = "${azapi_resource.function.name}-pe"
142+
location = var.location
143+
resource_group_name = azurerm_resource_group.app_rg.name
144+
tags = var.tags
145+
146+
custom_network_interface_name = "${azapi_resource.function.name}-nic"
147+
private_service_connection {
148+
name = "${azapi_resource.function.name}-pe"
149+
is_manual_connection = false
150+
private_connection_resource_id = azapi_resource.function.id
151+
subresource_names = ["sites"]
152+
}
153+
subnet_id = azapi_resource.subnet_services.id
154+
private_dns_zone_group {
155+
name = "${azapi_resource.function.name}-arecord"
156+
private_dns_zone_ids = [
157+
var.private_dns_zone_id_sites
158+
]
159+
}
160+
}

code/infra/roleassignments.tf

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
resource "azurerm_role_assignment" "role_assignment_storage_function" {
1+
resource "azurerm_role_assignment" "function_role_assignment_storage" {
22
scope = azurerm_storage_account.storage.id
33
role_definition_name = "Storage Blob Data Owner"
44
principal_id = azapi_resource.function.identity[0].principal_id
55
}
6+
7+
resource "azurerm_role_assignment" "function_role_assignment_key_vault" {
8+
scope = azurerm_key_vault.key_vault.id
9+
role_definition_name = "Key Vault Secrets User"
10+
principal_id = azapi_resource.function.identity[0].principal_id
11+
}

code/infra/variables.tf

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ variable "tags" {
3333
}
3434

3535
variable "vnet_id" {
36-
description = "Specifies the resource ID of the Vnet used for the Data Landing Zone"
36+
description = "Specifies the resource ID of the Vnet used for the Azure Function."
3737
type = string
3838
sensitive = false
3939
validation {
@@ -43,7 +43,7 @@ variable "vnet_id" {
4343
}
4444

4545
variable "nsg_id" {
46-
description = "Specifies the resource ID of the default network security group for the Data Landing Zone"
46+
description = "Specifies the resource ID of the default network security group for the Azure Function."
4747
type = string
4848
sensitive = false
4949
validation {
@@ -53,7 +53,7 @@ variable "nsg_id" {
5353
}
5454

5555
variable "route_table_id" {
56-
description = "Specifies the resource ID of the default route table for the Data Landing Zone"
56+
description = "Specifies the resource ID of the default route table for the Azure Function."
5757
type = string
5858
sensitive = false
5959
validation {
@@ -62,6 +62,17 @@ variable "route_table_id" {
6262
}
6363
}
6464

65+
variable "python_version" {
66+
description = "Specifies the python version of the Azure Function."
67+
type = string
68+
sensitive = false
69+
default = "3.10"
70+
validation {
71+
condition = contains(["3.9", "3.10"], var.python_version)
72+
error_message = "Please specify a valid Python version."
73+
}
74+
}
75+
6576
variable "private_dns_zone_id_blob" {
6677
description = "Specifies the resource ID of the private DNS zone for Azure Storage blob endpoints. Not required if DNS A-records get created via Azue Policy."
6778
type = string
@@ -116,3 +127,14 @@ variable "private_dns_zone_id_key_vault" {
116127
error_message = "Please specify a valid resource ID for the private DNS Zone."
117128
}
118129
}
130+
131+
variable "private_dns_zone_id_sites" {
132+
description = "Specifies the resource ID of the private DNS zone for Azure Websites. Not required if DNS A-records get created via Azue Policy."
133+
type = string
134+
sensitive = false
135+
default = ""
136+
validation {
137+
condition = var.private_dns_zone_id_sites == "" || (length(split("/", var.private_dns_zone_id_sites)) == 9 && endswith(var.private_dns_zone_id_sites, "privatelink.azurewebsites.net"))
138+
error_message = "Please specify a valid resource ID for the private DNS Zone."
139+
}
140+
}

code/infra/vars.dev.tfvars

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ private_dns_zone_id_queue = "/subscriptions/8f171ff9-2b5b-4f0f-aed5-7fa360a1
1010
private_dns_zone_id_table = "/subscriptions/8f171ff9-2b5b-4f0f-aed5-7fa360a1d094/resourceGroups/mycrp-prd-global-dns/providers/Microsoft.Network/privateDnsZones/privatelink.table.core.windows.net"
1111
private_dns_zone_id_file = "/subscriptions/8f171ff9-2b5b-4f0f-aed5-7fa360a1d094/resourceGroups/mycrp-prd-global-dns/providers/Microsoft.Network/privateDnsZones/privatelink.file.core.windows.net"
1212
private_dns_zone_id_key_vault = "/subscriptions/8f171ff9-2b5b-4f0f-aed5-7fa360a1d094/resourceGroups/mycrp-prd-global-dns/providers/Microsoft.Network/privateDnsZones/privatelink.vaultcore.azure.net"
13+
private_dns_zone_id_sites = "/subscriptions/8f171ff9-2b5b-4f0f-aed5-7fa360a1d094/resourceGroups/mycrp-prd-global-dns/providers/Microsoft.Network/privateDnsZones/privatelink.azurewebsites.net"

0 commit comments

Comments
 (0)