|
| 1 | +name: contoso-traders-app-deployment |
| 2 | + |
| 3 | +on: |
| 4 | + workflow_dispatch: |
| 5 | + push: |
| 6 | + branches: ["main"] |
| 7 | + paths: |
| 8 | + [ |
| 9 | + "src/ContosoTraders.Api.Carts/**", |
| 10 | + "src/ContosoTraders.Api.Core/**", |
| 11 | + "src/ContosoTraders.Api.Images/**", |
| 12 | + "src/ContosoTraders.Api.Products/**", |
| 13 | + "src/ContosoTraders.Ui.Website/**", |
| 14 | + ] |
| 15 | + |
| 16 | +env: |
| 17 | + ACR_NAME: contosotradersacr |
| 18 | + AKS_CLUSTER_NAME: contoso-traders-aks |
| 19 | + AKS_DNS_LABEL: contoso-traders-products |
| 20 | + AKS_NODES_RESOURCE_GROUP_NAME: contoso-traders-aks-nodes-rg |
| 21 | + AKS_SECRET_NAME_ACR_PASSWORD: contoso-traders-acr-password |
| 22 | + AKS_SECRET_NAME_KV_ENDPOINT: contoso-traders-kv-endpoint |
| 23 | + AKS_SECRET_NAME_MI_CLIENTID: contoso-traders-mi-clientid |
| 24 | + CARTS_ACA_NAME: contoso-traders-carts |
| 25 | + CARTS_ACR_REPOSITORY_NAME: contosotradersapicarts |
| 26 | + CDN_PROFILE_NAME: contoso-traders-cdn |
| 27 | + KV_NAME: contosotraderskv |
| 28 | + PRODUCTS_ACR_REPOSITORY_NAME: contosotradersapiproducts |
| 29 | + RESOURCE_GROUP_NAME: contoso-traders-rg |
| 30 | + UI_CDN_ENDPOINT_NAME: contoso-traders-ui2 |
| 31 | + UI_STORAGE_ACCOUNT_NAME: contosotradersui2 |
| 32 | + USER_ASSIGNED_MANAGED_IDENTITY_NAME: contoso-traders-mi-kv-access |
| 33 | + |
| 34 | +jobs: |
| 35 | + deploy-carts-api: |
| 36 | + runs-on: ubuntu-latest |
| 37 | + steps: |
| 38 | + - name: checkout code |
| 39 | + uses: actions/checkout@v3 |
| 40 | + - name: azure login |
| 41 | + uses: azure/login@v1 |
| 42 | + with: |
| 43 | + creds: ${{ secrets.SERVICEPRINCIPAL }} |
| 44 | + - name: extract acr password |
| 45 | + uses: azure/CLI@v1 |
| 46 | + id: extract-acr-password |
| 47 | + with: |
| 48 | + inlineScript: echo "acrPassword"="$(az acr credential show -n ${{ env.ACR_NAME }}${{ secrets.ENVIRONMENT }} -g ${{ env.RESOURCE_GROUP_NAME }} --query "passwords[0].value" --output tsv)" >> $GITHUB_OUTPUT |
| 49 | + - name: azure container registry login |
| 50 | + uses: azure/docker-login@v1 |
| 51 | + with: |
| 52 | + login-server: ${{ env.ACR_NAME }}${{ secrets.ENVIRONMENT }}.azurecr.io |
| 53 | + username: ${{ env.ACR_NAME }}${{ secrets.ENVIRONMENT }} |
| 54 | + password: ${{ steps.extract-acr-password.outputs.acrPassword }} |
| 55 | + - name: docker build |
| 56 | + run: docker build src -f ./src/ContosoTraders.Api.Carts/Dockerfile -t ${{ env.ACR_NAME }}${{ secrets.ENVIRONMENT }}.azurecr.io/${{ env.CARTS_ACR_REPOSITORY_NAME }}:latest -t ${{ env.ACR_NAME }}${{ secrets.ENVIRONMENT }}.azurecr.io/${{ env.CARTS_ACR_REPOSITORY_NAME }}:${{ github.sha }} |
| 57 | + - name: docker push (to acr) |
| 58 | + run: docker push --all-tags ${{ env.ACR_NAME }}${{ secrets.ENVIRONMENT }}.azurecr.io/${{ env.CARTS_ACR_REPOSITORY_NAME }} |
| 59 | + - name: deploy to aca |
| 60 | + uses: azure/CLI@v1 |
| 61 | + with: |
| 62 | + inlineScript: | |
| 63 | + az config set extension.use_dynamic_install=yes_without_prompt |
| 64 | + az containerapp update -n ${{ env.CARTS_ACA_NAME }}${{ secrets.ENVIRONMENT }} -g ${{ env.RESOURCE_GROUP_NAME }} --image ${{ env.ACR_NAME }}${{ secrets.ENVIRONMENT }}.azurecr.io/${{ env.CARTS_ACR_REPOSITORY_NAME }}:${{ github.sha }} |
| 65 | +
|
| 66 | + deploy-products-api: |
| 67 | + runs-on: ubuntu-latest |
| 68 | + steps: |
| 69 | + - name: checkout code |
| 70 | + uses: actions/checkout@v3 |
| 71 | + - name: azure login |
| 72 | + uses: azure/login@v1 |
| 73 | + with: |
| 74 | + creds: ${{ secrets.SERVICEPRINCIPAL }} |
| 75 | + - name: install helm |
| 76 | + uses: Azure/setup-helm@v3 |
| 77 | + id: install-helm |
| 78 | + - name: extract acr password |
| 79 | + uses: azure/CLI@v1 |
| 80 | + id: extract-acr-password |
| 81 | + with: |
| 82 | + inlineScript: echo "acrPassword"="$(az acr credential show -n ${{ env.ACR_NAME }}${{ secrets.ENVIRONMENT }} -g ${{ env.RESOURCE_GROUP_NAME }} --query "passwords[0].value" --output tsv)" >> $GITHUB_OUTPUT |
| 83 | + - name: azure container registry login |
| 84 | + uses: azure/docker-login@v1 |
| 85 | + with: |
| 86 | + login-server: ${{ env.ACR_NAME }}${{ secrets.ENVIRONMENT }}.azurecr.io |
| 87 | + username: ${{ env.ACR_NAME }}${{ secrets.ENVIRONMENT }} |
| 88 | + password: ${{ steps.extract-acr-password.outputs.acrPassword }} |
| 89 | + - name: docker build |
| 90 | + run: docker build src -f ./src/ContosoTraders.Api.Products/Dockerfile -t ${{ env.ACR_NAME }}${{ secrets.ENVIRONMENT }}.azurecr.io/${{ env.PRODUCTS_ACR_REPOSITORY_NAME }}:latest -t ${{ env.ACR_NAME }}${{ secrets.ENVIRONMENT }}.azurecr.io/${{ env.PRODUCTS_ACR_REPOSITORY_NAME }}:${{ github.sha }} |
| 91 | + - name: docker push (to acr) |
| 92 | + run: docker push --all-tags ${{ env.ACR_NAME }}${{ secrets.ENVIRONMENT }}.azurecr.io/${{ env.PRODUCTS_ACR_REPOSITORY_NAME }} |
| 93 | + - name: set aks context |
| 94 | + uses: azure/aks-set-context@v3 |
| 95 | + with: |
| 96 | + resource-group: ${{ env.RESOURCE_GROUP_NAME }} |
| 97 | + cluster-name: ${{ env.AKS_CLUSTER_NAME }}${{ secrets.ENVIRONMENT }} |
| 98 | + - name: setup kubectl |
| 99 | + uses: azure/setup-kubectl@v3 |
| 100 | + - name: create kubernetes secret (acr password) |
| 101 | + |
| 102 | + with: |
| 103 | + secret-name: ${{ env.AKS_SECRET_NAME_ACR_PASSWORD }} |
| 104 | + container-registry-url: ${{ env.ACR_NAME }}${{ secrets.ENVIRONMENT }}.azurecr.io |
| 105 | + container-registry-username: ${{ env.ACR_NAME }}${{ secrets.ENVIRONMENT }} |
| 106 | + container-registry-password: ${{ steps.extract-acr-password.outputs.acrPassword }} |
| 107 | + - name: get managedIdentityClientId |
| 108 | + uses: azure/CLI@v1 |
| 109 | + id: get-managedIdentityClientId |
| 110 | + with: |
| 111 | + inlineScript: echo "managedIdentityClientId"="$(az identity show -g ${{ env.RESOURCE_GROUP_NAME }} --name ${{ env.USER_ASSIGNED_MANAGED_IDENTITY_NAME }}${{ secrets.ENVIRONMENT }} --query "clientId" -o tsv)" >> $GITHUB_OUTPUT |
| 112 | + - name: create kubernetes secret (kv endpoint) |
| 113 | + |
| 114 | + with: |
| 115 | + secret-type: "generic" |
| 116 | + secret-name: ${{ env.AKS_SECRET_NAME_KV_ENDPOINT }} |
| 117 | + string-data: '{ "${{ env.AKS_SECRET_NAME_KV_ENDPOINT }}" : "https://${{ env.KV_NAME }}${{ secrets.ENVIRONMENT }}.vault.azure.net/" }' |
| 118 | + - name: create kubernetes secret (managed identity client id) |
| 119 | + |
| 120 | + with: |
| 121 | + secret-type: "generic" |
| 122 | + secret-name: ${{ env.AKS_SECRET_NAME_MI_CLIENTID }} |
| 123 | + string-data: '{ "${{ env.AKS_SECRET_NAME_MI_CLIENTID }}" : "${{ steps.get-managedIdentityClientId.outputs.managedIdentityClientId }}" }' |
| 124 | + - name: substitute tokens in deployment manifest |
| 125 | + uses: cschleiden/[email protected] |
| 126 | + with: |
| 127 | + tokenPrefix: "{" |
| 128 | + tokenSuffix: "}" |
| 129 | + files: ./src/ContosoTraders.Api.Products/Manifests/Deployment.yaml |
| 130 | + env: |
| 131 | + ENVIRONMENT: ${{ secrets.ENVIRONMENT }} |
| 132 | + - name: lint deployment manifest |
| 133 | + uses: azure/k8s-lint@v1 |
| 134 | + with: |
| 135 | + manifests: ./src/ContosoTraders.Api.Products/Manifests/Deployment.yaml |
| 136 | + - name: apply deployment manifest |
| 137 | + uses: Azure/k8s-deploy@v4 |
| 138 | + with: |
| 139 | + manifests: ./src/ContosoTraders.Api.Products/Manifests/Deployment.yaml |
| 140 | + images: ${{ env.ACR_NAME }}${{ secrets.ENVIRONMENT }}.azurecr.io/${{ env.PRODUCTS_ACR_REPOSITORY_NAME }}:${{ github.sha }} |
| 141 | + imagepullsecrets: ${{ env.AKS_SECRET_NAME_ACR_PASSWORD }} |
| 142 | + force: true |
| 143 | + - name: apply service manifest |
| 144 | + uses: Azure/k8s-deploy@v4 |
| 145 | + with: |
| 146 | + manifests: ./src/ContosoTraders.Api.Products/Manifests/Service.yaml |
| 147 | + force: true |
| 148 | + # create the ingress controller |
| 149 | + - name: create ingress controller |
| 150 | + run: | |
| 151 | + az aks get-credentials --resource-group ${{ env.RESOURCE_GROUP_NAME }} --name ${{ env.AKS_CLUSTER_NAME }}${{ secrets.ENVIRONMENT }} |
| 152 | + ${{ steps.install-helm.outputs.helm-path }} repo add ingress-nginx https://kubernetes.github.io/ingress-nginx |
| 153 | + ${{ steps.install-helm.outputs.helm-path }} repo update |
| 154 | + ${{ steps.install-helm.outputs.helm-path }} upgrade --install nginx-ingress ingress-nginx/ingress-nginx \ |
| 155 | + --set controller.replicaCount=1 \ |
| 156 | + --set controller.nodeSelector."beta\.kubernetes\.io/os"=linux \ |
| 157 | + --set defaultBackend.nodeSelector."beta\.kubernetes\.io/os"=linux \ |
| 158 | + --set controller.admissionWebhooks.patch.nodeSelector."beta\.kubernetes\.io/os"=linux \ |
| 159 | + --set controller.service.externalTrafficPolicy=Local |
| 160 | + - name: set dns label on public ip |
| 161 | + uses: azure/CLI@v1 |
| 162 | + with: |
| 163 | + inlineScript: az network public-ip update --dns-name ${{ env.AKS_DNS_LABEL }}${{ secrets.ENVIRONMENT }} -g ${{ env.AKS_NODES_RESOURCE_GROUP_NAME }} -n $(az network public-ip list --query "[?starts_with(name,'kubernetes-') ].name" -o tsv -g ${{ env.AKS_NODES_RESOURCE_GROUP_NAME }}) |
| 164 | + # hack: extract the full fqdn / dns label of the aks app's public IP address |
| 165 | + - name: get aks-fqdn |
| 166 | + uses: azure/CLI@v1 |
| 167 | + id: get-aks-fqdn |
| 168 | + with: |
| 169 | + # note: There should be a whitespace between ')' and ']'. More details: https://stackoverflow.com/a/59154958 |
| 170 | + inlineScript: echo "aksFqdn"="$(az network public-ip list --query "[?starts_with(name,'kubernetes-') ].dnsSettings.fqdn" -o tsv -g ${{ env.AKS_NODES_RESOURCE_GROUP_NAME }})" >> $GITHUB_OUTPUT |
| 171 | + # install cert-manager |
| 172 | + - name: apply namespace manifest |
| 173 | + uses: Azure/k8s-deploy@v4 |
| 174 | + with: |
| 175 | + manifests: ./src/ContosoTraders.Api.Products/Manifests/Namespace.yaml |
| 176 | + force: true |
| 177 | + - name: install cert-manager |
| 178 | + run: | |
| 179 | + az aks get-credentials --resource-group ${{ env.RESOURCE_GROUP_NAME }} --name ${{ env.AKS_CLUSTER_NAME }}${{ secrets.ENVIRONMENT }} |
| 180 | + kubectl apply --validate=false -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.yaml |
| 181 | + - name: apply clusterIssuer manifest |
| 182 | + uses: Azure/k8s-deploy@v4 |
| 183 | + with: |
| 184 | + manifests: ./src/ContosoTraders.Api.Products/Manifests/ClusterIssuer.yaml |
| 185 | + force: true |
| 186 | + - name: substitute tokens in certificate manifest |
| 187 | + uses: cschleiden/[email protected] |
| 188 | + with: |
| 189 | + tokenPrefix: "{" |
| 190 | + tokenSuffix: "}" |
| 191 | + files: ./src/ContosoTraders.Api.Products/Manifests/Certificate.yaml |
| 192 | + env: |
| 193 | + AKS_FQDN: ${{ steps.get-aks-fqdn.outputs.aksFqdn }} |
| 194 | + - name: apply certificate manifest |
| 195 | + uses: Azure/k8s-deploy@v4 |
| 196 | + with: |
| 197 | + manifests: ./src/ContosoTraders.Api.Products/Manifests/Certificate.yaml |
| 198 | + force: true |
| 199 | + - name: substitute tokens in ingress manifest |
| 200 | + uses: cschleiden/[email protected] |
| 201 | + with: |
| 202 | + tokenPrefix: "{" |
| 203 | + tokenSuffix: "}" |
| 204 | + files: ./src/ContosoTraders.Api.Products/Manifests/Ingress.yaml |
| 205 | + env: |
| 206 | + AKS_FQDN: ${{ steps.get-aks-fqdn.outputs.aksFqdn }} |
| 207 | + - name: apply ingress manifest |
| 208 | + uses: Azure/k8s-deploy@v4 |
| 209 | + with: |
| 210 | + manifests: ./src/ContosoTraders.Api.Products/Manifests/Ingress.yaml |
| 211 | + force: true |
| 212 | + - name: apply clusterRole manifest |
| 213 | + uses: Azure/k8s-deploy@v4 |
| 214 | + with: |
| 215 | + manifests: ./src/ContosoTraders.Api.Products/Manifests/ClusterRole.yaml |
| 216 | + force: true |
| 217 | + - name: set productsApiEndpoint in kv |
| 218 | + uses: azure/CLI@v1 |
| 219 | + with: |
| 220 | + inlineScript: az keyvault secret set --vault-name ${{ env.KV_NAME }}${{ secrets.ENVIRONMENT }} --name productsApiEndpoint --value ${{ steps.get-aks-fqdn.outputs.aksFqdn }} --description "endpoint url (fqdn) of the products api" |
| 221 | + |
| 222 | + deploy-ui: |
| 223 | + runs-on: ubuntu-latest |
| 224 | + needs: [deploy-carts-api, deploy-products-api] |
| 225 | + steps: |
| 226 | + - name: checkout code |
| 227 | + uses: actions/checkout@v3 |
| 228 | + - name: azure login |
| 229 | + uses: azure/login@v1 |
| 230 | + with: |
| 231 | + creds: ${{ secrets.SERVICEPRINCIPAL }} |
| 232 | + - name: get carts api endpoint |
| 233 | + uses: azure/CLI@v1 |
| 234 | + id: get-cartsApiEndpoint |
| 235 | + with: |
| 236 | + inlineScript: echo "cartsApiEndpoint"="$(az keyvault secret show --vault-name ${{ env.KV_NAME }}${{ secrets.ENVIRONMENT }} --name cartsApiEndpoint --query value -o tsv)" >> $GITHUB_OUTPUT |
| 237 | + - name: get products api endpoint |
| 238 | + uses: azure/CLI@v1 |
| 239 | + id: get-productsApiEndpoint |
| 240 | + with: |
| 241 | + inlineScript: echo "productsApiEndpoint"="$(az keyvault secret show --vault-name ${{ env.KV_NAME }}${{ secrets.ENVIRONMENT }} --name productsApiEndpoint --query value -o tsv)" >> $GITHUB_OUTPUT |
| 242 | + - name: substitute tokens in ui configuration |
| 243 | + uses: cschleiden/[email protected] |
| 244 | + with: |
| 245 | + tokenPrefix: "{{" |
| 246 | + tokenSuffix: "}}" |
| 247 | + files: ./src/ContosoTraders.Ui.Website/src/services/configService.js |
| 248 | + env: |
| 249 | + CARTS_API_ENDPOINT: ${{ steps.get-cartsApiEndpoint.outputs.cartsApiEndpoint }} |
| 250 | + PRODUCTS_API_ENDPOINT: ${{ steps.get-productsApiEndpoint.outputs.productsApiEndpoint }} |
| 251 | + - name: npm install |
| 252 | + run: npm install |
| 253 | + working-directory: src/ContosoTraders.Ui.Website |
| 254 | + - name: npm run build |
| 255 | + run: npm run build |
| 256 | + working-directory: src/ContosoTraders.Ui.Website |
| 257 | + - name: deploy to storage |
| 258 | + uses: azure/CLI@v1 |
| 259 | + with: |
| 260 | + inlineScript: az storage blob sync --account-name '${{ env.UI_STORAGE_ACCOUNT_NAME }}${{ secrets.ENVIRONMENT }}' -c '$web' -s 'src/ContosoTraders.Ui.Website/build' |
| 261 | + - name: purge CDN endpoint |
| 262 | + uses: azure/CLI@v1 |
| 263 | + with: |
| 264 | + inlineScript: az cdn endpoint purge --no-wait --content-paths '/*' -n '${{ env.UI_CDN_ENDPOINT_NAME }}${{ secrets.ENVIRONMENT }}' -g '${{ env.RESOURCE_GROUP_NAME }}' --profile-name '${{ env.CDN_PROFILE_NAME }}${{ secrets.ENVIRONMENT }}' |
0 commit comments