Skip to content

Commit

Permalink
Added iac and ci files
Browse files Browse the repository at this point in the history
  • Loading branch information
Skleni committed Apr 18, 2023
1 parent 28471e7 commit 1bbe1cc
Show file tree
Hide file tree
Showing 8 changed files with 345 additions and 4 deletions.
71 changes: 71 additions & 0 deletions .github/workflows/azure-static-web-apps-red-water-002751303.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Azure Static Web Apps CI/CD

on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- main
env:
DOTNET_CORE_VERSION: 7.0.x
FUNCTIONS_DIRECTORY: src/TicTacToe/TicTacToe.Functions
FUNCTIONS_APP_NAME: serverless-tictactoe-functions

jobs:
build_and_deploy_swa:
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
name: Build and Deploy SWA
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Build And Deploy
id: builddeploy
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_RED_WATER_002751303 }}
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
action: "upload"
###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
app_location: "/src/TicTacToe/TicTacToe.Blazor" # App source code path
api_location: "" # Api source code path - optional
output_location: "wwwroot" # Built app content directory - optional
###### End of Repository/Build Configurations ######

build_and_deploy_functions:
name: Build and Deploy functions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ env.DOTNET_CORE_VERSION }}
- name: Restore
run: dotnet restore "${{ env.FUNCTIONS_DIRECTORY }}"
- name: Build
run: dotnet build "${{ env.FUNCTIONS_DIRECTORY }}" --configuration Release --no-restore
- name: Publish
run: dotnet publish "${{ env.FUNCTIONS_DIRECTORY }}" --configuration Release --no-build --output "${{ env.FUNCTIONS_DIRECTORY }}/Publish"
- name: 'Deploy to Azure Function App'
uses: Azure/functions-action@v1
with:
app-name: ${{ env.FUNCTIONS_APP_NAME }}
package: ${{ env.FUNCTIONS_DIRECTORY }}/Publish
publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }}

close_pull_request_job:
if: github.event_name == 'pull_request' && github.event.action == 'closed'
runs-on: ubuntu-latest
name: Close Pull Request Job
steps:
- name: Close Pull Request
id: closepullrequest
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_RED_WATER_002751303 }}
action: "close"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,4 @@ MigrationBackup/

# Ionide (cross platform F# VS Code tools) working folder
.ionide/
/iac/params.json
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
# samples-serverless-tictactoe

## SignalR emulator for local testing
https://github.com/Azure/azure-signalr/blob/dev/docs/emulator.md
https://github.com/Azure/azure-signalr/blob/dev/docs/emulator.md

```cmd
asrs-emulator start
```

## Bicep deployment

```cmd
az deployment group create --resource-group samples-serverless-tictactoe --template-file iac/main.bicep --name samples-serverless-tictactoe
```
165 changes: 165 additions & 0 deletions iac/main.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
param location string = resourceGroup().location

@description('The globally unique name of the SignalR resource to create.')
param signalRName string = '${resourceGroup().name}-signalR-${uniqueString(resourceGroup().id)}'

@description('The globally unique name of the storage account to create.')
param storageAccountName string = '${resourceGroup().name}storage${uniqueString(resourceGroup().id)}'

@description('The name of the function app')
param functionAppName string = '${resourceGroup().name}-functions'

@description('The name of the static web app')
param swaName string = '${resourceGroup().name}-swa'

@description('The GitHub token for the repository with the following permissions: "Read access to metadata" and "Read and Write access to actions, actions variables, code, secrets, and workflows" (used for creating the static web app). See https://github.blog/2022-10-18-introducing-fine-grained-personal-access-tokens-for-github/.')
@secure()
param gitHubToken string

resource staticWebApp 'Microsoft.Web/staticSites@2021-01-15' = {
name: swaName
location: location
sku: {
name: 'Standard'
size: 'Standard'
}
properties: {
branch: 'main'
repositoryToken: gitHubToken
repositoryUrl: 'https://github.com/softawaregmbh/samples-serverless-tictactoe'
buildProperties: {
appLocation: '/src/TicTacToe/TicTacToe.Blazor'
outputLocation: 'wwwroot'
}
}
tags: null
}

resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = {
name: storageAccountName
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'Storage'
}

resource consumptionPlan 'Microsoft.Web/serverfarms@2021-03-01' = {
name: '${functionAppName}-plan'
location: location
sku: {
name: 'Y1'
tier: 'Dynamic'
}
}

resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
name: '${functionAppName}-ai'
location: location
kind: 'web'
properties: {
Application_Type: 'web'
Request_Source: 'rest'
}
}

resource functionApp 'Microsoft.Web/sites@2020-12-01' = {
name: functionAppName
location: location
kind: 'functionapp'
identity: {
type: 'SystemAssigned'
}
properties: {
httpsOnly: true
serverFarmId: consumptionPlan.id
siteConfig: {
minTlsVersion: '1.2'
ftpsState: 'Disabled'
cors: {
allowedOrigins: ['https://${staticWebApp.properties.defaultHostname}']
}
appSettings: [
{
name: 'AzureWebJobsStorage__accountName'
value: storageAccountName
}
{
name: 'AzureSignalRConnectionString__serviceUri'
value: 'https://${signalRName}.service.signalr.net'
}
{
name: 'FUNCTIONS_EXTENSION_VERSION'
value: '~4'
}
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: appInsights.properties.InstrumentationKey
}
{
name: 'FUNCTIONS_WORKER_RUNTIME'
value: 'dotnet-isolated'
}
]
}
}
}

// see https://stackoverflow.com/questions/72368709/azure-function-apps-signalr-extension-is-not-populated-to-use-in-signalr-upstre
var signalRKeyName = 'signalr_extension'
module signalRKey 'signalRKey.bicep' = {
name: '${functionAppName}-systemkey-${signalRKeyName}'
params: {
functionAppName: functionAppName
keyName: signalRKeyName
}
dependsOn:[
functionApp
]
}

module signalR 'signalR.bicep' = {
name: signalRName
params: {
location: location
functionAppName: functionAppName
signalRName: signalRName
swaName: swaName
}
dependsOn:[
signalRKey
]
}

var storageBlobDataOwnerRoleId = 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b'
resource storageAccountRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(storageAccountName, functionAppName, storageBlobDataOwnerRoleId)
scope: storageAccount
properties: {
principalId: functionApp.identity.principalId
principalType: 'ServicePrincipal'
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', storageBlobDataOwnerRoleId)
}
}

var storageQueueDataContributorRoleId = '974c5e8b-45b9-4653-ba55-5f855dd0fb88'
resource storageQueueDataContributorRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(storageAccountName, functionAppName, storageQueueDataContributorRoleId)
scope: storageAccount
properties: {
principalId: functionApp.identity.principalId
principalType: 'ServicePrincipal'
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', storageQueueDataContributorRoleId)
}
}

var storageTableDataContributorRoleId = '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3'
resource storageTableDataContributorRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(storageAccountName, functionAppName, storageTableDataContributorRoleId)
scope: storageAccount
properties: {
principalId: functionApp.identity.principalId
principalType: 'ServicePrincipal'
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', storageTableDataContributorRoleId)
}
}
85 changes: 85 additions & 0 deletions iac/signalR.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
param location string = resourceGroup().location

param signalRName string
param functionAppName string
param swaName string

resource functionApp 'Microsoft.Web/sites@2020-12-01' existing = {
name: functionAppName
}

resource staticWebApp 'Microsoft.Web/staticSites@2021-01-15' existing = {
name: swaName
}

resource signalR 'Microsoft.SignalRService/signalR@2022-02-01' = {
name: signalRName
location: location
sku: {
capacity: 1
name: 'Free_F1'
tier: 'Free'
}
kind: 'SignalR'
properties: {
tls: {
clientCertEnabled: false
}
features: [
{
flag: 'ServiceMode'
value: 'Serverless'
}
{
flag: 'EnableConnectivityLogs'
value: 'true'
}
{
flag: 'EnableMessagingLogs'
value: 'true'
}
{
flag: 'EnableLiveTrace'
value: 'true'
}
]
cors: {
allowedOrigins: [
'https://${staticWebApp.properties.defaultHostname}'
]
}
publicNetworkAccess: 'Enabled'
networkACLs: {
defaultAction: 'Deny'
publicNetwork: {
allow: [
'ServerConnection'
'ClientConnection'
'RESTAPI'
'Trace'
]
}
}
upstream: {
templates: [
{
categoryPattern: '*'
eventPattern: '*'
hubPattern: '*'
urlTemplate: 'https://${functionApp.properties.defaultHostName}/runtime/webhooks/signalr?code=${listKeys(resourceId('Microsoft.Web/sites/host', functionAppName, 'default'), '2022-03-01').systemkeys.signalr_extension}'
}
]
}
}
}

var signalRServiceOwnerRoleId = '7e4f1700-ea5a-4f59-8f37-079cfe29dce3'
resource signalRServiceOwnerRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(signalRName, functionAppName, signalRServiceOwnerRoleId)
scope: signalR
properties: {
principalId: functionApp.identity.principalId
principalType: 'ServicePrincipal'
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', signalRServiceOwnerRoleId)
}
}
9 changes: 9 additions & 0 deletions iac/signalRKey.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
param functionAppName string
param keyName string

resource signalRKey 'Microsoft.Web/sites/host/systemkeys@2021-03-01' = {
name: '${functionAppName}/default/${keyName}'
properties: {
name: keyName
}
}
2 changes: 1 addition & 1 deletion src/TicTacToe/TicTacToe.Blazor/Pages/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ else
hubConnection = new HubConnectionBuilder()
.WithUrl(Configuration["ApiUrl"]!)
.Build();
Console.WriteLine(Configuration["ApiUrl"]);

hubConnection.On<string, Player>("GameJoined", (gameId, player) =>
{
this.gameId = gameId;
Expand Down
4 changes: 2 additions & 2 deletions src/TicTacToe/TicTacToe.Blazor/wwwroot/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"ApiUrl": "/api"
}
"ApiUrl": "https://serverless-tictactoe-functions.azurewebsites.net/api"
}

0 comments on commit 1bbe1cc

Please sign in to comment.