Search code examples
azurepowershellgithub-actionsazure-managed-app

How to deploy Azure Managed App code Artifacts?


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.


Solution

  • 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.