I have been working off of this documentation: https://abp.io/docs/latest/solution-templates/layered-web-application/deployment/azure-deployment/step3-deployment-github-action?UI=MVC&DB=EF&Tiered=No, and it does work to deploy to Azure, but if I have the line there to generate openiddict.pfx, it always fails, and it doesn't throw any error message that helps me understand what is going on.
For reference, here is my workflow (I also have never been able to make the DbMigrator work, and I may have to open a new ticket for that too if I can't get it to work):
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions
name: Build and deploy ASP.Net Core app to Azure Web App - armada
on:
push:
branches:
- Armada-ABP-Pro
workflow_dispatch:
jobs:
build:
runs-on: windows-latest
permissions:
contents: read #This is required for actions/checkout
steps:
- uses: actions/checkout@v4
- name: Set up .NET Core
uses: actions/setup-dotnet@v4
with:
dotnet-version: 'v9.0'
- name: Install ABP CLI
run: |
dotnet tool install -g Volo.Abp.Cli
abp install-libs
shell: bash
- name: Build with dotnet
run: dotnet build --configuration Release
- name: dotnet publish
run: dotnet publish -c Release -r win-x64 --self-contained false -o "$env:DOTNET_ROOT\myapp"
shell: pwsh
working-directory: ./src/ArmadaIO.Web # Replace with your project name
env:
ASPNETCORE_ENVIRONMENT: Production
- name: Generate openiddict.pfx
run: dotnet dev-certs https -v -ep ${{env.DOTNET_ROOT}}\myapp\openiddict.pfx -p c41eb3e7-8a8e-429f-9052-0850406f2f11 # Replace with your password
- name: Upload artifact for deployment job
uses: actions/upload-artifact@v4
with:
name: .net-app
path: ${{env.DOTNET_ROOT}}/myapp
deploy:
runs-on: windows-latest
needs: build
environment:
name: 'Production'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
permissions:
id-token: write #This is required for requesting the JWT
contents: read #This is required for actions/checkout
steps:
- name: Download artifact from build job
uses: actions/download-artifact@v4
with:
name: .net-app
- name: Login to Azure
uses: azure/login@v2
with:
client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_D0E4A82237114C8FB52A40680A31F7B7 }}
tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_AF7C7E6475CD4DE9A6FC1907FEBD8DF6 }}
subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_9000905F725645769C73D60324653A76 }}
- name: Deploy to Azure Web App
id: deploy-to-webapp
uses: azure/webapps-deploy@v3
with:
app-name: 'armada'
slot-name: 'Production'
package: .
If I comment out the openiddict line, it runs and deploys (but ofc doesn't actually run and throws a startup error saying it can't find openiddict.pfx). If I uncomment the line, it just shows me this:
and if I pull up the raw logs for that line it shows me this:
2025-08-20T20:23:04.5490958Z ##[group]Run dotnet dev-certs https -v -ep C:\Program Files\dotnet\myapp\openiddict.pfx -p [mypassword]
2025-08-20T20:23:04.5491858Z [36;1mdotnet dev-certs https -v -ep C:\Program Files\dotnet\myapp\openiddict.pfx -p [mypassword][0m
2025-08-20T20:23:04.5526676Z shell: C:\Program Files\PowerShell\7\pwsh.EXE -command ". '{0}'"
2025-08-20T20:23:04.5526989Z env:
2025-08-20T20:23:04.5527153Z DOTNET_ROOT: C:\Program Files\dotnet
2025-08-20T20:23:04.5527379Z ##[endgroup]
2025-08-20T20:23:05.1704144Z Specify --help for a list of available options and commands.
2025-08-20T20:23:05.3106943Z ##[error]Process completed with exit code 1.
So I feel stuck here. I did find this blog post: https://codejack.com/2022/12/deploying-abp-io-to-an-azure-appservice/, and I used it for testing, but that didn't work for me either. Here is my WebModule with the default code, as well as the commented code that I was using to test based on the blog post above:
public override void PreConfigureServices(ServiceConfigurationContext context)
{
var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration();
context.Services.PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options =>
{
options.AddAssemblyResource(
typeof(ArmadaResource),
typeof(ArmadaIODomainModule).Assembly,
typeof(ArmadaIODomainSharedModule).Assembly,
typeof(ArmadaIOApplicationModule).Assembly,
typeof(ArmadaIOApplicationContractsModule).Assembly,
typeof(ArmadaIOWebModule).Assembly
);
});
PreConfigure<OpenIddictBuilder>(builder =>
{
builder.AddValidation(options =>
{
options.AddAudiences("ArmadaIO");
options.UseLocalServer();
options.UseAspNetCore();
});
});
if (!hostingEnvironment.IsDevelopment())
{
PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
{
options.AddDevelopmentEncryptionAndSigningCertificate = false;
});
PreConfigure<OpenIddictServerBuilder>(serverBuilder =>
{
serverBuilder.AddProductionEncryptionAndSigningCertificate("openiddict.pfx", configuration["AuthServer:CertificatePassPhrase"]!);
serverBuilder.SetIssuer(new Uri(configuration["AuthServer:Authority"]!));
//serverBuilder.AddEncryptionCertificate(GetEncryptionCertificate(hostingEnvironment, configuration));
//serverBuilder.AddSigningCertificate(GetSigningCertificate(hostingEnvironment, configuration));
});
}
}
//private X509Certificate2 GetSigningCertificate(IWebHostEnvironment hostingEnv,
// IConfiguration configuration)
//{
// var fileName = $"cert-signing.pfx";
// var passPhrase = configuration["ArmadaCertificate:X590:PassPhrase"];
// var file = Path.Combine(hostingEnv.ContentRootPath, fileName);
// if (File.Exists(file))
// {
// var created = File.GetCreationTime(file);
// var days = (DateTime.Now - created).TotalDays;
// if (days > 180)
// {
// File.Delete(file);
// }
// else
// {
// try
// {
// return new X509Certificate2(file, passPhrase, X509KeyStorageFlags.MachineKeySet);
// }
// catch (CryptographicException)
// {
// File.Delete(file);
// }
// }
// }
// // file doesn't exist, was deleted because it expired, or has an invalid passphrase
// using var algorithm = RSA.Create(keySizeInBits: 2048);
// var subject = new X500DistinguishedName("CN=Armada Signing Certificate");
// var request = new CertificateRequest(subject, algorithm,
// HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
// request.CertificateExtensions.Add(new X509KeyUsageExtension(
// X509KeyUsageFlags.DigitalSignature, critical: true));
// var certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow,
// DateTimeOffset.UtcNow.AddYears(2));
// File.WriteAllBytes(file, certificate.Export(X509ContentType.Pfx, passPhrase));
// return new X509Certificate2(file, passPhrase, X509KeyStorageFlags.MachineKeySet);
//}
//private X509Certificate2 GetEncryptionCertificate(IWebHostEnvironment hostingEnv,
// IConfiguration configuration)
//{
// var fileName = $"cert-encryption.pfx";
// var passPhrase = configuration["ArmadaCertificate:X590:PassPhrase"];
// var file = Path.Combine(hostingEnv.ContentRootPath, fileName);
// if (File.Exists(file))
// {
// var created = File.GetCreationTime(file);
// var days = (DateTime.Now - created).TotalDays;
// if (days > 180)
// {
// File.Delete(file);
// }
// else
// {
// try
// {
// return new X509Certificate2(file, passPhrase, X509KeyStorageFlags.MachineKeySet);
// }
// catch (CryptographicException)
// {
// File.Delete(file);
// }
// }
// }
// // file doesn't exist, was deleted because it expired, or has an invalid passphrase
// using var algorithm = RSA.Create(keySizeInBits: 2048);
// var subject = new X500DistinguishedName("CN=Armada Encryption Certificate");
// var request = new CertificateRequest(subject, algorithm,
// HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
// request.CertificateExtensions.Add(new X509KeyUsageExtension(
// X509KeyUsageFlags.KeyEncipherment, critical: true));
// var certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow,
// DateTimeOffset.UtcNow.AddYears(2));
// File.WriteAllBytes(file, certificate.Export(X509ContentType.Pfx, passPhrase));
// return new X509Certificate2(file, passPhrase, X509KeyStorageFlags.MachineKeySet);
//}
I was able to get this working one time in the past, but I can't seem to recreate it unfortunately. I did create an openiddict.pfx using the command in the terminal, then I uploaded it to my Azure certificates. That doesn't seem to work either. Perhaps I need to upload openiddict.pfx directly into the project root in Azure? I am not very knowledgeable about this, so I'm not sure what is safe or what could cause security issues.
Any advice here would be appreciated. Thanks.
8 Answer(s)
-
0
The error you are encountering when running dotnet dev-certs https -v -ep C:\Program Files\dotnet\myapp\openiddict.pfx ... is due to a permissions issue: the workflow is trying to write the openiddict.pfx file into C:\Program Files\dotnet\myapp, which is a protected system directory on Windows runners and does not allow write access for non-admin processes.
The ABP documentation recommends using ${{env.DOTNET_ROOT}}/myapp/openiddict.pfx, but on Windows runners, $DOTNET_ROOT is typically C:\Program Files\dotnet, which is not writable.
To fix this, change the output path for your publish and certificate generation steps to a directory that is guaranteed to be writable, such as $(Build.ArtifactStagingDirectory) or a relative path like ./myapp.
How to fix:
- Change your publish and certificate generation steps to use a relative path (e.g., ./myapp):
- name: dotnet publish run: dotnet publish -c Release -r win-x64 --self-contained false -o ./myapp shell: pwsh working-directory: ./src/ArmadaIO.Web env: ASPNETCORE_ENVIRONMENT: Production - name: Generate openiddict.pfx run: dotnet dev-certs https -v -ep ./myapp/openiddict.pfx -p c41eb3e7-8a8e-429f-9052-0850406f2f11 shell: pwsh
- Update the artifact upload path accordingly:
- name: Upload artifact for deployment job uses: actions/upload-artifact@v4 with: name: .net-app path: ./myapp
Summary:
- Do not write to C:\Program Files\dotnet\myapp (not writable).
- Use a relative path like ./myapp for both publish and certificate generation.
- This will resolve the exit code 1 and allow the workflow to generate and include openiddict.pfx.
Sources:
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
-
0
hi
Can you invite me to your GitHub repository? https://github.com/maliming Thanks.
-
0
I sent you an invite. I don't have it in there anymore, but I previously also tried to get the DbMigrator line working (something like this from the documentation):
- name: Run migrations run: dotnet run -- "${{ secrets.CONNECTION_STRING }}" # Set your connection string as a secret in your repository settings working-directory: ./src/yourapp.DbMigrator # Replace with your project name
and I wasn't able to get it to work. I just defaulted to deploying DbMigrator separately as a WebJob, and it works, but it would be much better to be able to migrate on deploy. Not sure if there is anything I need to look out for there. I can make a new ticket for that issue if necessary.
Thanks
-
0
Also, my repo is a bit of a mess right now, but Armada-ABP-Pro is my current working branch I am trying to deploy.
-
0
hi
Thanks. I will check your repository.
-
0
-
0
Beautiful! I tried so many things but I didn't try wrapping it in quotes! Glad to see it was an easy fix.
Thanks
-
0
Great