Microsoft Workloads on AWS

How to federate into AWS from Azure DevOps using OpenID Connect

In this blog post, I will demonstrate how to use the OpenID Connect (OIDC) options in AWS Toolkit for Azure DevOps version 1.15.0+ to federate into AWS accounts and obtain temporary credentials without managing static AWS Identity and Access Management (IAM) credentials.

Introduction

Azure DevOps Pipelines enable continuous build, test, and deployment across platforms and clouds. The AWS Toolkit for Azure DevOps, a free extension for hosted and on-premises environments, simplifies AWS resource management. The toolkit integrates with key AWS services like Amazon Simple Storage Service (Amazon S3), AWS CodeDeploy, AWS Lambda, AWS CloudFormation, and Amazon SQS.

Azure DevOps now supports workload identity federation. Combined with the AWS Toolkit’s OIDC capabilities, teams can leverage native identity management to deploy and access AWS resources using standard IAM controls. The process uses an Azure DevOps-provided token to call AWS Security Token Service (STS), which generates temporary, and limited-privilege security credentials, aligning with best practice security principles. Figure 1 illustrates the flow of getting and using credentials from the AWS STS service via federation.

How to federate into AWS from Azure DevOps using OpenID Connect

Figure 1 – Azure DevOps credentials through AWS STS flow

High-level overview

Note: Since this post uses IAM as part of the solution, it will require permission to CreateRole, ListOpenIDConnectProviders, and CreateOpenIDConnectProvider as a minimum. In most cases, you would need to attach permissions to the role, but not needed in our example.

  1. Create a YAML pipeline in Azure DevOps and gather the organization GUID.
  2. Configure an identity provider in AWS for OIDC federation.
  3. Create an IAM role in AWS that can be assumed from the identity provider.
  4. Run the Azure DevOps pipeline to confirm successful federation.

Prerequisites

  • An AWS account with sufficient permissions to create IAM Providers and IAM role and policies
  • An Azure DevOps project with access to configure service connections, those are authenticated connections between Azure Pipelines and external or remote services.
  • The AWS Toolkit for Azure DevOps version 1.15+ installed for that project, see AWS Toolkit for Azure DevOps in the Visual Studio Marketplace for installation instructions.

Create a YAML pipeline in Azure DevOps and gather the organization GUID

For the configuration of the identity provider in AWS, we will need the Organization GUID from Azure DevOps. First, we need to create a service connection that will reference an IAM role named azdo-federation that we will create later.

From your Azure DevOps project settings:

  1. Under Pipelines, select Service Connections.
  2. Select New service connection.
  3. Choose AWS, select Next.
  4. In Role to assume, use the role ARN, for our example we will use a role named azdo-federation, to get the ARN replace your AWS account ID in the following: arn:aws:iam::123456789012:role/azdo-federation.
  5. (Optional) The Role Session Name field if left empty will default to aws-vsts-tools, you can input another value here.
  6. Check the use OIDC option.
  7. For Service Connection Name use aws-oidc-federation.
  8. Select Save.

Obtain the organization GUID from Azure DevOps by running the pipeline

Note: Since the setup is incomplete, our pipeline will fail but the information needed to configure an identity provider will be available from the logs.

From your Azure DevOps project pipelines:

  1. Select New pipeline.
  2. Choose Azure Repos Git.
  3. Select Starter pipeline.
  4. Copy and paste the following YAML, adjusting awsCredentials for the service connection name, and the regionName if needed.
    # Sample pipeline to test connectivity to AWS (adjust service connection name (awsCredentials), and regionName as needed)
    
    trigger:
        - main
    
    pool:
        vmImage: ubuntu-latest
    
    variables:
      aws.rolecredential.maxduration: "3600" # Can be set from 900 - 43200s
      
    steps:
        - task: AWSCLI@1
          displayName: "Running aws-cli get-caller-identity"
          # continueOnError: true # If you need the pipeline to succeed
          inputs:
            awsCredentials: "aws-oidc-federation"
            regionName: 'aws-region-name'
            awsCommand: 'sts'
            awsSubCommand: 'get-caller-identity'
    
  5. Select Save and run.

After a few seconds, your pipeline will prompt for permission to use the service connection (Figure 2).

Figure 2 – Azure DevOps pipeline requires permission to use the service connection

Select view and review the information to grant permission using the Permit button.

After the pipeline runs, check the logs of the task named, Running aws-cli get-caller-identity, for a line that start with OIDC Token generated. From this, you will have the issuer, audience and subject line needed for the rest of the setup (Figure 3).

Figure 3 – Logs of the tasks Running aws-cli get-caller-identity

With this information, we can create the identity provider in our AWS account.

Configure an identity provider in AWS for OIDC federation

In this step, we will use the issuer obtained from the logs.

From the AWS IAM console follow these steps.

  1. Under Access management, select Identity providers on the left menu.
  2. Select Add Provider.
  3. Choose OpenID Connect as the Provider type.
  4. In the Provider URL use the issuer URL obtained from the previous section. Each tenant of Azure DevOps will have a unique OrganizationGUID, the expected format is https://vstoken.dev.azure.com/{OrganizationGUID}.
  5. In the Audience field, use api://AzureADTokenExchange. This is a fixed value for Azure DevOps. It was also found in the logs from the pipeline run.
  6. Select Add Provider.
  7. Take note of the ARN of the newly created provider, it will be needed in the next step.

Create an IAM role in AWS that can be assumed from the identity provider

An IAM role is an entity that allow you to assign specific permissions. To control who can use that role and under which conditions, we use a trust policy. To follow the least-privileged principle, we will add a condition in the trust policy so that only one specific service connection from Azure DevOps will be able to use the IAM role that we are creating. Azure DevOps passes the service connection in the subject field as follows: “sc://{OrganizationName}/{ProjectName}/{ServiceConnectionName}“.

Continue from the AWS IAM console.

In this step, we will use the subject that we got from the logs in our first run. The expected format is sc://{OrganizationName}/{ProjectName}/{ServiceConnectionName}.

  1. Under Access management, select Roles.
  2. Select Create role.
  3. For Trusted entity type, select Web Identity.
  4. Select the right identity provider from the drop-down, it starts with vstoken.dev.azure.com/{OrganizationGUID}.
  5. In the Audience drop-down select api://AzureADTokenExchange.
  6. To limit this role to only one service connection, we will add a condition. Under Condition, select Add condition, in the Key, select vstoken.dev.azure.com/{OrganizationGUID}:sub. In the Condition, select StringEquals. For the Value, use the subject obtained from the logs, it should have this format: sc://{OrganizationName}/{ProjectName}/{ServiceConnectionName}.
  7. Select Next, you can leave the permission empty as our pipeline just validates our identity, but in a real pipeline, this is where you would attach the needed policy.
  8. Select Next, input azdo-federation as the Role name, review the details.Here is the complete trust policy. Replace the bolded italicized text with the correct ids.
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Federated": "arn:aws:iam::123456789012:oidc-provider/vstoken.dev.azure.com/{OrganizationGUID}"
          },
          "Action": "sts:AssumeRoleWithWebIdentity",
          "Condition": {
            "StringEquals": {
              "vstoken.dev.azure.com/00000000-0000-0000-0000-000000000000:aud": "api://AzureADTokenExchange",
              "vstoken.dev.azure.com/00000000-0000-0000-0000-000000000000:sub": "sc://{OrganizationName}/{ProjectName}/{ServiceConnectionName}"
            }
          }
        }
      ]
    }
  9. Select Create role.

Run the Azure DevOps pipeline to confirm successful federation

At this point, if we rerun the pipeline we have created, the federation should now succeed and provide us with similar result from the task log (Figure 4).

Figure 4 – Output from the aws-cli get-caller-identity task

Changes needed to current existing pipelines/tasks

You have just learned how to use OIDC in your pipelines. The option does not require changes to your current task. To update your pipelines, you need to reconfigure the service connection to enable the use OIDC option, the role to assume, and remove the static access/secret keys (if defined). Check this GitHub comment for a way to accomplish this via the CLI or API. The task will try the assume role method first ensuring minimal change to existing pipeline.

Using federation with other tools

While the AWS Toolkit for Azure DevOps provides tasks for many AWS services, it does not cover all use cases. This federation technique can be used with any of the 3rd party tool as it provides temporary credentials like the basic standardized provider of the AWS SDKs.

Here is an example of using Terraform. Using the AWSShellScript task, we will configure the AWS credentials so it can be used by the tools in the pipeline. Also, if you have long running processes, you can adjust the aws.rolecredential.maxduration variable inside your pipeline to ensure your credentials are valid for the duration of the task (from 900 to 43200 seconds).

Here’s a sample pipeline that achieve this with Terraform:

# Sample pipeline that pass AWS credential to another task

trigger:
- main

variables:
  aws.rolecredential.maxduration: "3600" # Duration in seconds of the temporary credentials, can be set from 900 - 43200

pool:
  vmImage: ubuntu-latest

steps:
- task: AWSShellScript@1
  continueOnError: true
  displayName: "Running aws-cli get-caller-identity"
  inputs:
    awsCredentials: "aws-oidc-federation"
    regionName: "aws-region-name"
    scriptType: inline
    inlineScript: |
      terraform init
      terraform apply -auto-approve

And the corresponding output calling the data provider aws_caller_identity data source (Figure 5).

Figure 5 – Output from the terraform task using the aws_caller_identity data source

If you need assistance in configuring the terraform state to use an Amazon S3 bucket, you can refer to the Terraform documentation for S3 backend storage.

Cleanup

In your AWS account, delete the IAM role and the identity provider that was created to remove any access right from Azure DevOps. In Azure DevOps, remove the service connection, the created pipeline and files that were committed to the repository.

Conclusion

With this new feature part of the AWS Toolkit for Azure DevOps, you can now rely on best practices of using temporary credentials and not have to provision and rotate static keys or IAM users. This will allow you to use an IAM Role with temporary credentials minimizing the need for credential rotation reducing operational needs. You can control the session duration for the credentials allowing you maximum flexibility in securing your environment.

Links:
AWS Toolkit for Azure DevOps Marketplace


AWS has significantly more services, and more features within those services, than any other cloud provider, making it faster, easier, and more cost effective to move your existing applications to the cloud and build nearly anything you can imagine. Give your Microsoft applications the infrastructure they need to drive the business outcomes you want. Visit our .NET on AWS and AWS Database blogs for additional guidance and options for your Microsoft workloads. Contact us to start your migration and modernization journey today.

Mathieu Bruneau

Mathieu Bruneau

Mathieu Bruneau is a Containers Specialist Solutions Architect at Amazon Web Services Canada. He’s been bridging discussions between Operations and Developers teams’ way before the term DevOps became popular. Math is located in Montreal, Canada and enjoys spending time with wife and his 3 boys, either playing video games or throwing some frisbees around.