Can't Deploy Image to Azure App Service via GitHub Actions - azure-web-app-service

I've tried so many different things here, so i'm super excited to see what the issue is :) (but, im assuming it's something silly on my end)
Here's the pertinent bit of my GitHub actions YML:
deploy:
name: Update Azure App Service
needs: [tests]
runs-on: ubuntu-latest
steps:
- name: Deploy to Azure
uses: azure/webapps-deploy#v2
with:
app-name: $APP_SERVICE_NAME
images: $IMAGE_NAME
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
GitHub actions logs (debug mode):
##[debug]Evaluating condition for step: 'Deploy to Azure'
##[debug]Evaluating: success()
##[debug]Evaluating success:
##[debug]=> true
##[debug]Result: true
##[debug]Starting: Deploy to Azure
##[debug]Loading inputs
##[debug]Evaluating: secrets.AZURE_WEBAPP_PUBLISH_PROFILE
##[debug]Evaluating Index:
##[debug]..Evaluating secrets:
##[debug]..=> Object
##[debug]..Evaluating String:
##[debug]..=> 'AZURE_WEBAPP_PUBLISH_PROFILE'
##[debug]=> '***'
##[debug]Result: '***'
##[debug]Loading env
Run azure/webapps-deploy#v2
::add-mask::***
::add-mask::***
##[debug][GET] https://SNIP.scm.azurewebsites.net:443/diagnostics/runtime
##[debug]loaded affinity cookie ["ARRAffinity=SNIP;Path=/;HttpOnly;Secure;Domain=SNIP-qa-ause.scm.azurewebsites.net","ARRAffinitySameSite=SNIP;Path=/;HttpOnly;SameSite=None;Secure;Domain=SNIP.scm.azurewebsites.net"]
##[debug]getAppRuntime. Data: {"statusCode":200,"statusMessage":"OK","headers":{"connection":"close","content-type":"application/json; charset=utf-8","date":"Fri, 22 Jul 2022 06:27:05 GMT","server":"Kestrel","set-cookie":["ARRAffinity=SNIP;Path=/;HttpOnly;Secure;Domain=app-be-SNIP.scm.azurewebsites.net","ARRAffinitySameSite=SNIP;Path=/;HttpOnly;SameSite=None;Secure;Domain=app-be-SNIP.azurewebsites.net"],"transfer-encoding":"chunked"},"body":{"nodejs":[],"system":{"os_name":"Unix 4.15.0.169","os_build_lab_ex":"","cores":2}}}
##[debug]App Runtime OS: Unix 4.15.0.169
Error: Deployment Failed with Error: Error: Publish profile is invalid for app-name and slot-name provided. Provide correct publish profile credentials for app.
##[debug]Deployment failed
##[debug]Node Action run completed with exit code 1
##[debug]AZURE_HTTP_USER_AGENT='GITHUBACTIONS_DeployWebAppToAzure_0d7e9cbfcd2b52a8a34111a0798ab22d77203fc1f56b2732d2431bfaeb336577'
##[debug]AZURE_HTTP_USER_AGENT=''
##[debug]Finishing: Deploy to Azure
The obvious solution here is what the error says: secret is wrong. It's not. I've added secret to GitHub repository, with the value of the Azure App Service. Done this twice, to make sure i've downloaded it correct, and copied as is.
Other things i've tried:
Tried setting WEBSITE_WEBDEPLOY_USE_SCM to true in the App Service configuration.
Tried setting slot-name to Production in the GitHu action step (even though, there is no slots here)
Other things, that might help solve my problem:
I'm building a .NET image as a Docker image, and pushing this to Azure Containr Registry. I'm then trying to 'update' this, in Azure App Service
The Azure App Service is running Linux, not Windows
"Deployment Center" in App Service still has "Source: Container Registry", pinned to a static image. This is because this was required when i setup App Service. But i assume that my GitHub actions builds will override this?
Any help would be great. :) Thanks in advance!
EDIT
I know i can:
Push to a constant tag, e.g latest
Set the image name to latest in Azure App Service
Set continuous deployment to "on" in Azure App Service
And this will automatically update App Service, because of the webhook.
But this will not help production scenarios, as i need to create different image tags, for different versions i can rollback. Since the version is different, i can't "pin" to a constant tag. Therefore, i need some kind of task to update Azure App Service with a specific tag. I'd prefer to have the same CI for dev/prod.

Related

Deploy .NET Core app to Azure Web App with ARM template and GitHub Actions

I'm pretty familiar with Azure DevOps, pipelines all that stuff, and now I'm trying to dig into GitHub Actions. The question is basically pretty simple, I want to deploy my .NET Core 5 App to Azure. The only problem is, that all examples include this publish profile.
Since I provision the infrastructure with an ARM template, the publish profile is simply not there yet. I could find some examples that deploy the ARM template and a couple of examples that deploy the Web App, but no example combining both. Maybe I'm a little bit polluted by the way Azure DevOps works and the (wonderful) idea of service connections.
So my question is, how do I publish a web app to Azure when I don't have the ability to download a publish profile and store that in my GitHub secrets, using GitHub Actions?
OK, I found this one out myself. Apparently there's an action you can use that will download the publish profile for you. This means that you don't have to have the publish profile up front. The step looks like this:
- name: Get WebApp/FunctionApp publish profile
id: webapp-dev
uses: aliencube/publish-profile-actions#v1
env:
AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }}
with:
resourceGroupName: 'your-resource-group-name'
appName: 'your-app-name'
This leaves you with an output variable called `profile' which can be used in following steps like so:
- name: 'Run Azure webapp deploy action using publish profile credentials'
uses: azure/webapps-deploy#v1
with:
app-name: 'your-app-name'
publish-profile: ${{ steps.webapp-dev.outputs.profile }}
package: './'
This means you can now provision resources using ARM templates, get the publish profile (just-in-time) and use that to deploy your system... Everybody happy...

Security hole in Azure Pipelines?

I've been researching Azure DevOps and I've come across what looks like a pretty obvious security hole in Azure pipelines.
So, I'm creating my pipeline as YAML and defining 2 stages: a build stage, and a deployment stage. The deployment stage looks like this:
- stage: deployApiProdStage
displayName: 'Deploy API to PROD'
dependsOn: buildTestApiStage
jobs:
- deployment: deployApiProdJob
displayName: 'Deploy API to PROD'
timeoutInMinutes: 10
condition: and(succeeded(), eq(variables.isRelease, true))
environment: PROD
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp#1
displayName: 'Deploy Azure web app'
inputs:
azureSubscription: '(service connection to production web app)'
appType: 'webAppLinux'
appName: 'my-web-app'
package: '$(Pipeline.Workspace)/$(artifactName)/**/*.zip'
runtimeStack: 'DOTNETCORE|3.1'
startUpCommand: 'dotnet My.Api.dll'
The Microsoft documentation talks about securing this by adding approvals and checks to an environment; in the above case, the PROD environment. This would be fine if the protected resource here that allows publishing to my PROD web app - the service connection in azureSubscription - were pulled from the PROD environment. Unfortunately, as far as I can tell, it's not. It's associated instead with the pipeline itself.
This means that when the pipeline is first run, the Azure DevOps UI prompts me to permit the pipeline access to the service connection, which is needed for any deployment to happen. Once access is permitted, that pipeline has access to that service connection for evermore. This means that from then on, that service connection can be used no matter which environment is specified for the job. Worse still, any environment name specified that is not recognized does not cause an error, but causes a blank environment to be created by default!
So even if I setup a manual approval for the PROD environment, if someone in the organization manages to slip a change through our code review (which is possible, with regular large code reviews) that changes the environment name to 'NewPROD' in the azure-pipelines.yml file, the CI/CD will create that new environment, and go ahead and deploy immediately to PROD because the new environment has no checks or approvals!
Surely it would make sense for the service connection to be associated with the environment instead. It would also make sense to have an option to ban the auto-creation of new environments - I don't really see how that's particularly useful anyway. Right now, as far as I can tell, this is a huge security hole that could allow deployments to critical environments by anyone who has commit access to the repo or manages to slip a change to the azure-pipelines.yml file through the approval process, introducing a major single point of failure/weakness. What happened to the much-acclaimed incremental approach to securing your pipelines? Am I missing something here, or is this security hole as bad as I think it is?
In your example, it seemed you created/used an empty environment, there is no deployment target. Currently, only the Kubernetes resource and virtual machine resource types are supported in an environment.
https://learn.microsoft.com/en-us/azure/devops/pipelines/process/environments?view=azure-devops
The resource in your example is a service connection, so you need to go the service connection and define checks for this service connection.
https://learn.microsoft.com/en-us/azure/devops/pipelines/process/approvals?view=azure-devops&tabs=check-pass

Deploy ARM Template and Docker image

Well, everyone keeps saying to try to learn something new during this pandemic, so that's what I'm trying to do. I'm brand new to YAML and Docker, and fairly new to ARM templates.
I've been able to use YAML to build and publish .NET Core apps using the dotnet YAML commands. I've been able to use YAML to build a Docker image of a .NET Core 3.1 Web API app.
I'm publishing the docker image to an Azure Resource Container.
Where I'm stuck:
Ideally, I want to use ARM templates in the .NET Core project to be able to deploy my Azure Portal resources via code vs creating manually in the portal. This should include the Azure Resource Container.
It seems like what I need to do to accomplish this is build the docker image during the Build phase and then publish that to the $(System.ArtifactsDirectory) so that I can hold it there until the Azure Resources have been deployed via ARM templates.
I can't find anything about publishing the docker image as a pipeline artifact though. Only things about pushing to DockerHub, GitHub, and Azure Resource Containers, which isn't what I want to do right now.
Anyone have any ideas how I can pull this off?
I actually found a way to pull this off. The article I read about this is over on dev.to
There are a few things that I needed to do to pull this off.
First, don't set the containerRegistry on build.
- task: Docker#2
displayName: Build Docker Image
inputs:
repository: $(imageName)
command: build
dockerfile: '**/Dockerfile'
buildContext: 'HopefulMommaDesignsAPI'
tags: $(Build.BuildId)
The reason for this is because if you set the container registry then, with ACR at least, it prefixes the ACR URL to the docker image name. However, that URL is encrypted when doing this in Azure DevOps. There's nothing wrong with that, but it causes confusion when you list images because it will show up as ***/ImageName.
Second, save the image as a TAR file in the artifact staging directory.
- task: Docker#2
displayName: 'Save image to TAR'
inputs:
repository: '$(imageName)'
command: save
arguments: '--output $(build.artifactstagingdirectory)/$(imageName).image.tar $(imageName):$(Build.BuildId)'
addPipelineData: false
That will include the .tar file when you publish your build.
The next steps are during the deploy jobs.
After you have downloaded the build artifact and deployed the ARM templates, you'll need to load the docker image from the .tar file.
- task: Docker#2
displayName: 'Load Image from Tar'
inputs:
command: load
arguments: '--input $(build.artifactstagingdirectory)/$(buildArtifactName)/$(imageName).image.tar'
Now that the docker image has been loaded you'll want to ReTag the image and publish it.
- task: Docker#2
displayName: 'ReTag Image with ACR URL - BuildId'
inputs:
containerRegistry: 'Hopeful Momma Designs ACR'
repository: $(imageName)
command: tag
arguments: '$(imageName):$(Build.BuildId) $(containerRegistryUrl)/$(imageName):$(Build.BuildId)'
- task: Docker#2
displayName: 'ReTag Image with ACR URL - latest'
inputs:
containerRegistry: 'Hopeful Momma Designs ACR'
repository: $(imageName)
command: tag
arguments: '$(imageName):$(Build.BuildId) $(containerRegistryUrl)/$(imageName):latest'
- task: Docker#2
displayName: push
inputs:
containerRegistry: 'Hopeful Momma Designs ACR'
repository: $(imageName)
command: push
dockerfile: '**/Dockerfile'
buildContext: 'HopefulMommaDesignsAPI'
tags: |
$(Build.BuildId)
latest
NOTE: If your Azure Container Registry is part of your ARM template, the Docker commands where you include the containerRegistry will fail because you have to manually set up the service connection from Azure DevOps to the Azure Container Registry before you can reference it in your YAML files as the containerRegistry.
This isn't a huge deal for me because I will normally work on pipelines in sections anyway. I'll make sure the build works, then I'll make sure the ARM templates work, then I'll make sure the docker images pushes to the ACR. If you're rebuilding an environment from scratch, just comment out the deployment steps after the ARM template step to deploy the infrastructure and then uncomment the remaining steps. It isn't the cleanest solution, but I like that better than having a separate pipeline just to deploy infrastructure.
You publish your container to an Azure Container Registry. Containers aren't build artifacts in the traditional sense.
I suspect that you have a chicken/egg problem. You want to create the registry during your deployment process, but you don't have a registry during the build process. So how can you deploy your container to a registry that won't exist until the next phase, right? In fact, you'll discover that you need to create a service connection for the ACR before you can publish resources to it. The ARM template isn't going to be able to do that for you.
This is a case where it's common to separate the application's requirements from larger infrastructural requirements. The ACR is a supporting piece of infrastructure. It's common in cases like this to have a separate pipeline that creates required supporting infrastructure to solve the chicken-and-egg problem.
Also, please be aware of correct terminology. There's nothing called an "Azure Resource Container". There is something called an "Azure Container Registry", which is what I assume you're talking about.

Is it possible to do continuous deployment CI/CD of an Azure Function through a Linux Environment via Azure DevOps?

When creating a function in Azure through a Linux environment it seems CI/CD is completely missing from it's capabilities as I can't see any actual files. My VS code tells me this
Error: This plan does not support viewing files.
and when I try to deploy my files to the server through the Azure pipeline everything works except for the
Azure App Service Deploy
Which tells me this.
2020-04-21T19:48:37.6676043Z ##[error]Failed to deploy web package to App Service.
2020-04-21T19:48:37.6689536Z ##[error]Error: Error: Failed to deploy web package to App Service. Conflict (CODE: 409)
I did get it working directly through VS Code with a windows environment and didn't notice any of those issues.
Can you confirm this is not possible through Linux or perhaps there is a solution for what I am looking for.
is it possible to do continuous deployment CI/CD of an Azure Function through a Linux Environment via Azure DevOps?
The answer is Yes.
To deploy a Azure Function, you should use Azure Function App task instead of Azure App Service Deploy task. For below example.
steps:
- task: AzureFunctionApp#1
inputs:
azureSubscription: '<Azure service connection>'
appType: functionAppLinux
appName: '<Name of function app>'
#Uncomment the next lines to deploy to a deployment slot
#Note that deployment slots is not supported for Linux Dynamic SKU
#deployToSlotOrASE: true
#resourceGroupName: '<Resource Group Name>'
#slotName: '<Slot name>'
Please check out this document Continuous delivery by using Azure DevOps for detailed examples.

How do I use the Serverless Framework in an Azure DevOps Build Pipeline without Browser Authentication?

I'm trying to deploy the simple NodeJS hello-world functions the Serverless Framework provides to my Azure free-tier account from an Azure DevOps Build Pipeline using the Service Principal credentials I created when making the deployment from my desktop originally. I've used several of the Build Agents and Tasks combinations, including Windows and Ubuntu Agents as well as Bash, Command Line, Azure Powershell, and Azure CLI tasks with the DevOps provided link to the Service Principal credentials. I've made sure to add them as Pipeline variables so that they are included in the tasks' environmental variables and I've confirmed that they are there when the tasks run. I also make sure that the Azure CLI is installed and logged into with the subscription set. No matter what settings/permissions I tweak or new configurations I try, when the task runs successfully to the point where the serverless framework attempts the deployment it always tries to get me to use a browser to authenticate my account. This obviously defeats the purpose of a CI/CD pipeline and even if I do use a browser to authenticate, the process just hangs there.
The sample code and deployment works on my desktop, so I know the credentials work. I believe I've emulated each step I take on my desktop in the Build Pipeline, yet while my desktop deploys without browser authentication the build always requests it. Does anyone have experience in this manner and know what step/configuration I'm missing?
To look at the sample code and process look here or run these steps:
serverless create -t azure-nodejs -p testApp
cd .\testApp\
Change Node Runtime and Region in serverless.yml (nodejs12.x not supported & no free tier in West US)
serverless deploy
Here's the link I used to get this working on my desktop: link
Edit: Here is the default serverless.yml created by the steps above:
service: azure-serverless-helloworld
provider:
name: azure
region: East US
runtime: nodejs8.10
environment:
VARIABLE_FOO: 'foo'
plugins:
- serverless-azure-functions
package:
exclude:
- local.settings.json
- .vscode/**
- index.html
functions:
hello:
handler: src/handlers/hello.sayHello
events:
- http: true
x-azure-settings:
methods:
- GET
authLevel: anonymous
goodbye:
handler: src/handlers/goodbye.sayGoodbye
events:
- http: true
x-azure-settings:
methods:
- GET
authLevel: anonymous
You can try below steps to run sls package in command line task to create a deployment package, and then use Azure Function App task to deploy to azure.
1,install specific version nodejs using Node.js tool installer task
_
2, install serverless using npm task to run custom command
3, use npm task to run install command to install dependencies
_
4, Use command line task to run sls package to create the deployment package
_
5, use azure function app deploy task to deploy the deployment package
Right now the Serverless Framework thinks you're trying to deploy your application using the Serverless Dashboard (which does not yet support Azure).
I'm not sure, because it haven't posted your serverless.yml file, but I think you'll need to remove the app and org attributes from your serverless.yml configuration file. Then it will stop asking you to log in.
Using the serverless framework to deploy a function through DevOps gave me the same issue.
The problem is that the sls deplopy command will build, package and deploy the code but will ask you for credentials each time you run the pipeline.
I solved this using the command serverless package in the build task, after that I deployed the zip that was generated for the command with a normal web app deploy task.

Resources