I am new to managed application. I have created managed application definition which includes Azure Function, App Service, Azure SQL, KeyVault and Storage. All resources have managed identities enabled. I have deployed app to azure service catalog and created resources from there. I am now trying to push artifacts for Azure Function, Azure web app and create database structure.
I have tried using Github Actions CI/CD with Azure credentials with contributor role. I am getting System deny assignments.
I have also tried using App Registration with user_impersonation permission but I am not clear how to run CLI scripts after resources are created.
Script for bicep.
@description('Location for all resources.')
param location string = resourceGroup().location
@description('Environment.')
param environment string = 'dev-k'
@description('The name of the SQL logical server.')
param serverName string = uniqueString('sql', resourceGroup().id)
@description('The administrator username of the SQL logical server.')
param administratorLogin string = 'DbUser'
@description('The administrator password of the SQL logical server.')
@secure()
param administratorLoginPassword string = '@Abcd9090!'
@description('The name of the SQL Database.')
var uniqueStringVal = '${environment}-${uniqueString(resourceGroup().id)}'
var sqlDBName = 'zeis-${uniqueStringVal}'
var keyVaultName = 'kv-${uniqueStringVal}'
var functionAppName = 'fn-${uniqueStringVal}-ZesEmployeeManagementEmployeeSync'
var appServicePlanName = 'appService-${uniqueStringVal}'
var storageAccountName = replace('storage-${uniqueStringVal}', '-', '')
resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
name: storageAccountName
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
identity: {
type: 'SystemAssigned'
}
properties: {
minimumTlsVersion: 'TLS1_2'
}
}
resource appServicePlan 'Microsoft.Web/serverfarms@2023-12-01' = {
name: appServicePlanName
location: location
sku: {
name: 'Y1'
capacity: 0
size: 'Y'
family: 'Y'
tier: 'Dynamic'
}
}
resource functionApp 'Microsoft.Web/sites@2023-12-01' = {
name: functionAppName
location: location
kind: 'functionapp'
identity: {
type: 'SystemAssigned'
}
properties: {
serverFarmId: appServicePlan.id
siteConfig: {
appSettings: [
{
name: 'FUNCTIONS_WORKER_RUNTIME'
value: 'dotnet-isolated'
}
{
name: 'AzureWebJobsStorage'
value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value};EndpointSuffix=core.windows.net'
}
]
}
}
}
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
name: keyVaultName
location: location
properties: {
tenantId: subscription().tenantId
sku: {
family: 'A'
name: 'standard'
}
accessPolicies: []
}
}
resource keyVaultRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
scope: keyVault
name: guid(resourceGroup().id, '${functionAppName}', 'Contributor')
properties: {
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') // Contributor Role
principalId: functionApp.identity.principalId
}
}
resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = {
name: serverName
location: location
identity: {
type: 'SystemAssigned'
}
properties: {
administratorLogin: administratorLogin
administratorLoginPassword: administratorLoginPassword
}
}
resource sqlDB 'Microsoft.Sql/servers/databases@2023-08-01-preview' = {
parent: sqlServer
name: sqlDBName
location: location
sku: {
name: 'GP_S_Gen5'
tier: 'GeneralPurpose'
family: 'Gen5'
capacity: 1
}
identity: {
type: 'None'
}
properties: {
collation: 'SQL_Latin1_General_CP1_CI_AS'
maxSizeBytes: 34359738368
autoPauseDelay: 60
catalogCollation: 'SQL_Latin1_General_CP1_CI_AS'
availabilityZone: 'NoPreference'
readScale: 'Disabled'
}
}
// resource symbolicname 'Microsoft.Graph/[email protected]' = {
// api: {
// acceptMappedClaims: true
// preAuthorizedApplications: [
// {
// appId: 'string'
// delegatedPermissionIds: [
// 'string'
// ]
// }
// ]
// }
// appRoles: [
// {
// allowedMemberTypes: [
// 'string'
// ]
// description: 'string'
// displayName: 'string'
// id: 'string'
// isEnabled: bool
// value: 'string'
// }
// ]
// defaultRedirectUri: 'string'
// description: 'string'
// disabledByMicrosoftStatus: 'string'
// displayName: 'string'
// groupMembershipClaims: 'string'
// identifierUris: [
// 'string'
// ]
// info: {
// marketingUrl: 'string'
// privacyStatementUrl: 'string'
// supportUrl: 'string'
// termsOfServiceUrl: 'string'
// }
// requestSignatureVerification: {
// allowedWeakAlgorithms: 'string'
// isSignedRequestRequired: bool
// }
// requiredResourceAccess: [
// {
// resourceAccess: [
// {
// id: 'string'
// type: 'string'
// }
// ]
// resourceAppId: 'string'
// }
// ]
// samlMetadataUrl: 'string'
// serviceManagementReference: 'string'
// servicePrincipalLockConfiguration: {
// allProperties: bool
// credentialsWithUsageSign: bool
// credentialsWithUsageVerify: bool
// isEnabled: bool
// tokenEncryptionKeyId: bool
// }
// signInAudience: 'string'
// spa: {
// redirectUris: [
// 'string'
// ]
// }
// tags: [
// 'string'
// ]
// tokenEncryptionKeyId: 'string'
// uniqueName: 'string'
// verifiedPublisher: {
// displayName: 'devkapp'
// verifiedPublisherId: 'string'
// }
// }
Script generate azure credentials.
az ad sp create-for-rbac --name "" --role contributor --scopes /subscriptions/$subscriptionId --json-auth
name: Deploy DotNet project to Azure Function App
on:
push:
branches: ["main"]
env:
AZURE_FUNCTIONAPP_NAME: 'fn-dev-k-jziuqfqqrrrci-ZesEmployeeManagementEmployeeSync' # set this to your function app name on Azure
AZURE_FUNCTIONAPP_PACKAGE_PATH: './ZesEmployeeManagement' # set this to the path to your function app project, defaults to the repository root
DOTNET_VERSION: '8.0.x' # set this to the dotnet version to use (e.g. '2.1.x', '3.1.x', '5.0.x')
AZURE_CREDENTIALS: '{"clientId":"","clientSecret":"","tenantId":"", "subscriptionId": ""}'
jobs:
build-and-deploy:
runs-on: windows-latest # For Linux, use ubuntu-latest
environment: dev
steps:
- name: 'Checkout GitHub Action'
uses: actions/checkout@v4
- name: Setup DotNet ${{ env.DOTNET_VERSION }} Environment
uses: actions/setup-dotnet@v3
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: 'Resolve Project Dependencies Using Dotnet'
shell: powershell # For Linux, use bash
run: |
pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}'
dotnet build --configuration Release --output ./output
popd
- name: Log in to Azure
uses: azure/login@v2
with:
creds: ${{ env.AZURE_CREDENTIALS }}
- name: Upload to Blob Storage
uses: azure/CLI@v1
with:
inlineScript: |
az storage blob upload \
--account-name {storage} \
--container-name {container} \
--file functionapp.zip \
--name functionapp.zip \
--auth-mode login
# Configure Function App to use the package from Blob Storage
- name: Set WEBSITE_RUN_FROM_PACKAGE
run: |
az webapp config appsettings set \
--name {functionName} \
--resource-group {resourceGroup} \
--settings WEBSITE_RUN_FROM_PACKAGE=https://{storage}.blob.core.windows.net/{container}/functionapp.zip
Microsoft documentation about is not clear, it only focuses on deployment of definitions. ChatGPT straight up invents new things. I experienced the worst case of AI hallucinations.
The trick to make it work is by authorizations
parameter in az managedapp definition create
. I need to use a user
or service principle
with the same role and group defined in authorizations
parameter. I used service princpiple
to login in my CI/CD workflow.