Wednesday, January 27, 2021

Azure Pipelines: Create/Update Azure API Management APIs

In this post, let's see how we can create/update Azure API Management APIs from both classic release pipelines and YAML based pipelines.

Here I am assuming, We have already created Azure API Management Service in Azure and we have set up Products.

The process is actually quite easy, thanks to this great free extension API Management Suite by Stephane Eyskens

First, let's have a look at how we can use this in classic release pipelines.

Once this extension is installed, you should be able to see a bunch of tasks starting with API Management - *. From there what we need is API Management - Create or Update API task. We just need to add this task after the deployment of the API.

API Management - Create or Update API

Then it's just a matter of configuring the task.
API Management - Create or Update API Settings
Most of the things here are pretty obvious, so I am not going to explain each one of them. The important ones are,
  • Products
    • Since I have already created a product in the APIM, I can just enter the name of the Product.
    • If you are creating the Product as part of the pipeline (using API Management - Create or update product task), then you need to select Product created by previous task.
  • OpenAPI Specification
    • The APIM needs to have access to OpenAPI specification in order to create the API. You have several options here. You need to select the version, it's format (json or yaml), any authorization needed to access the specification and the location (URL, Code or Build Artifact)
  • Configuration of the API
    • Here, the nice thing is you can specify the Policy. I prefer maintaining the policy in the code, so here I have just selected my policy by providing it's path.
So that's basically it. Once the API is deployed, this task will run, it will pull down the OpenAPI specification, create/update APIM API, apply the policy and it's all good to go.

If you prefer having deployment steps as part of YAML pipeline, we can easily set up this task there as well. Basically something like this.
task: stephane-eyskens.apim.apim.apim@5
    displayName: 'API Management - Create/Update API '
    inputs:
      ConnectedServiceNameARM: ''
      ResourceGroupName: ''
      ApiPortalName: ''
      UseProductCreatedByPreviousTask: false
      product1: ''
      OpenAPISpec: 'v3'
      swaggerlocation: ''
      targetapi: ''
      DisplayName: ''
      pathapi: ''
      subscriptionRequired: false
      TemplateSelector: Artifact
      policyArtifact: '$(Pipeline.Workspace)\scripts\apim\apim-policy.xml'
      MicrosoftApiManagementAPIVersion: '2018-01-01'
Don't worry, you have the UI to select the options just like classic releases.
API Management - Create or Update API Settings
So hope this helps. 

Huge thanks to Stephane Eyskens for coming up with this nice set of tasks.

Happy Coding.

Regards,
Jaliya

Thursday, January 14, 2021

Azure Pipelines: Passing Variables Between Jobs and Stages

In this post, let's see how we can pass variables between Stages and Jobs in Azure Pipelines..

Consider the following simple pipeline.

stages:
stage: Build
  jobs:
  - job: FirstJob
    pool:
      vmImage: 'windows-latest'
    steps:
     - bash: echo "##vso[task.setvariable variable=myVariable;isOutput=true]Hello World"
       name: stepSetVariable
       
  - job: SecondJob
    dependsOn: FirstJob
    pool:
      vmImage: 'ubuntu-latest'
    variables:
      # Map variable
      # syntax: $[ dependencies.{JobName}.outputs['{stepName}.{variableName}'] ]
      myJobVariable: $[ dependencies.FirstJob.outputs['stepSetVariable.myVariable'] ]  
    steps:
    # Echos Hello World
    - script: echo $(myJobVariable)
      name: stepEchoVariable

stage: Deployment
  dependsOn: Build
  pool:
    vmImage: 'macOS-latest'
  variables:
    # Map variable
    # syntax: $[ stageDependencies.{BuildName}.{JobName}.outputs['{stepName}.{variableName}'] ]
    myStageVariable: $[ stageDependencies.Build.FirstJob.outputs['stepSetVariable.myVariable'] ]
  jobs:
  - job: DeploymentJob
    steps:
    # Echos Hello World
    - script: echo $(myStageVariable)
      name: stepEchoVariable

Here,

  • I have 2 Stages, Build and Deployment
  • Build stage has 2 jobs, 
    • In the FirstJob, I am creating a variable called myVariable. I am making it an output variable and setting its value to Hello World.
    • In the SecondJob
      • The SecondJob is depending on FirstJob because the variable needs to be created first. 
      • Then I am declaring a variable called myJobVariable and mapping it with myVariable created in FirstJob. Since it's within the same Stage, I can map the variable using this syntax. 
$[ dependencies.{JobName}.outputs['{stepName}.{variableName}'] ]
      • Then I am just printing the value of myJobVariable
  • In Deployment stage,
    • This stage is depending on Build stage, again because we are trying to access an output variable from Build stage.
    • Then I am declaring a variable called myStageVariable and mapping it with myVariable created in Build stages' FirstJob. Since it's in a different Stage, I can reference the variable using stageDependencies
$[ stageDependencies.{BuildName}.{JobName}.outputs['{stepName}.{variableName}'] ]

    • Then I have just a single job to print the value of myStageVariable

Note: I have used different images for two jobs in Build stage and for the Deployment stage, so the concept is more clear.

Hope this helps.

Happy Coding.

Regards,
Jaliya

Thursday, January 7, 2021

EF Core 5.0: Excluding Tables from Migrations

In this post, let's see how we can exclude Tables from Migrations in EF Core 5.0. It's actually pretty easy.

Consider the below use case.

public class IdentityDbContext : DbContext
{
    public DbSet<ApplicationUser> ApplicationUsers { getset; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        // Configuring
    }
}

public class OrderDbContext : DbContext
{
    public DbSet<ApplicationUser> ApplicationUsers { getset; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        // Configuring
    }
}

public class ApplicationUser
{
    public int Id { getset; }

    public string Name { getset; }
}

Here the ApplicationUser is shared between two DbContexts (IdentityDbContext and OrderDbContext). And now let's say between these two DbContexts, we are only going to maintain full ApplicationUser information in only IdentityDbContext. In that case, we don't want further changes on ApplicationUser to be applied on OrderDbContext.

To achieve that, we can exclude the ApplicationUser in migrations for OrderDbContext as below.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<ApplicationUser>().ToTable("ApplicationUsers", t => t.ExcludeFromMigrations());
}

Unfortunately, we need to call ToTable and from there customize the TableBuilder to ExcludeFromMigrations.

Hope this helps.

Happy Coding.

Regards,
Jaliya