Skip to content

Commit

Permalink
Merge pull request #44 from AlexanderSehr/users/alsehr/oidc
Browse files Browse the repository at this point in the history
feat: Replacing usage of Service Principal service connection with OIDC-based solution
  • Loading branch information
AlexanderSehr authored Nov 25, 2024
2 parents ed93813 + 4c918ad commit c536ca7
Show file tree
Hide file tree
Showing 22 changed files with 120 additions and 42 deletions.
42 changes: 20 additions & 22 deletions .azuredevops/azureImageBuilder/.templates/pipeline.jobs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ jobs:
# Set agent up
Set-EnvironmentOnAgent -PSModules $Modules
# [Sanitization] task(s)
#-----------------------
- task: AzureCLI@2
name: Remove_ImageTemplates_Task
displayName: 'Remove previous Image Template resources'
Expand All @@ -55,11 +53,11 @@ jobs:
scriptType: pscore
scriptLocation: inlineScript
inlineScript: |
# Log into Az-PowerShell context
. $profile # Load PS-Profile configuration
$SecuredPassword = ConvertTo-SecureString -AsPlainText -String $env:servicePrincipalKey
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $env:servicePrincipalId, $SecuredPassword
$null = Connect-AzAccount -ServicePrincipal -TenantId $env:tenantId -Credential $Credential
# Load PS-Profile configuration
. $profile
# Login to Azure-Powershell context
$null = Connect-AzAccount -ApplicationId $env:servicePrincipalId -Tenant $env:tenantId -FederatedToken $env:idToken
Write-Verbose "Load function" -Verbose
. (Join-Path '$(System.DefaultWorkingDirectory)' '$(orchestrationFunctionsPath)' 'image' 'Remove-ImageTemplate.ps1')
Expand All @@ -81,11 +79,11 @@ jobs:
scriptType: pscore
scriptLocation: inlineScript
inlineScript: |
# Log into Az-PowerShell context
. $profile # Load PS-Profile configuration
$SecuredPassword = ConvertTo-SecureString -AsPlainText -String $env:servicePrincipalKey
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $env:servicePrincipalId, $SecuredPassword
$null = Connect-AzAccount -ServicePrincipal -TenantId $env:tenantId -Credential $Credential
# Load PS-Profile configuration
. $profile
# Login to Azure-Powershell context
$null = Connect-AzAccount -ApplicationId $env:servicePrincipalId -Tenant $env:tenantId -FederatedToken $env:idToken
Write-Verbose "Load function" -Verbose
. (Join-Path '$(System.DefaultWorkingDirectory)' '$(orchestrationFunctionsPath)' 'image' 'Remove-DeploymentScript.ps1')
Expand All @@ -107,11 +105,11 @@ jobs:
scriptType: pscore
scriptLocation: inlineScript
inlineScript: |
# Log into Az-PowerShell context
. $profile # Load PS-Profile configuration
$SecuredPassword = ConvertTo-SecureString -AsPlainText -String $env:servicePrincipalKey
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $env:servicePrincipalId, $SecuredPassword
$null = Connect-AzAccount -ServicePrincipal -TenantId $env:tenantId -Credential $Credential
# Load PS-Profile configuration
. $profile
# Login to Azure-Powershell context
$null = Connect-AzAccount -ApplicationId $env:servicePrincipalId -Tenant $env:tenantId -FederatedToken $env:idToken
Write-Verbose "Load function" -Verbose
. (Join-Path '$(System.DefaultWorkingDirectory)' '$(orchestrationFunctionsPath)' 'image' 'Remove-ResourcesInStagingRg.ps1')
Expand Down Expand Up @@ -174,11 +172,11 @@ jobs:
scriptType: pscore
scriptLocation: inlineScript
inlineScript: |
# Log into Az-PowerShell context
. $profile # Load PS-Profile configuration
$SecuredPassword = ConvertTo-SecureString -AsPlainText -String $env:servicePrincipalKey
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $env:servicePrincipalId, $SecuredPassword
$null = Connect-AzAccount -ServicePrincipal -TenantId $env:tenantId -Credential $Credential
# Load PS-Profile configuration
. $profile
# Login to Azure-Powershell context
$null = Connect-AzAccount -ApplicationId $env:servicePrincipalId -Tenant $env:tenantId -FederatedToken $env:idToken
# Load used functions
. (Join-Path '$(System.DefaultWorkingDirectory)' '$(sharedOrchestrationFunctionsPath)' 'deployment' 'New-TemplateDeployment.ps1')
Expand Down
6 changes: 3 additions & 3 deletions .azuredevops/azureImageBuilder/variables.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ variables:
poolName_dev: '' # Use this for self-hosted agents
poolName_prd: '' # Use this for self-hosted agents

serviceConnection_sbx: '<PrivateConnection>'
serviceConnection_dev: '<PrivateConnection>'
serviceConnection_prd: '<PrivateConnection>'
serviceConnection_sbx: '<ServiceConnectionName>'
serviceConnection_dev: '<ServiceConnectionName>'
serviceConnection_prd: '<ServiceConnectionName>'
#endregion

#region specific
Expand Down
10 changes: 5 additions & 5 deletions .azuredevops/managedDevOpsPool/.templates/pipeline.jobs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ jobs:
scriptType: pscore
scriptLocation: inlineScript
inlineScript: |
# Log into Az-PowerShell context
. $profile # Load PS-Profile configuration
$SecuredPassword = ConvertTo-SecureString -AsPlainText -String $env:servicePrincipalKey
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $env:servicePrincipalId, $SecuredPassword
$null = Connect-AzAccount -ServicePrincipal -TenantId $env:tenantId -Credential $Credential
# Load PS-Profile configuration
. $profile
# Login to Azure-Powershell context
$null = Connect-AzAccount -ApplicationId $env:servicePrincipalId -Tenant $env:tenantId -FederatedToken $env:idToken
# Load used functions
. (Join-Path '$(System.DefaultWorkingDirectory)' '$(sharedOrchestrationFunctionsPath)' 'deployment' 'New-TemplateDeployment.ps1')
Expand Down
6 changes: 3 additions & 3 deletions .azuredevops/managedDevOpsPool/variables.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ variables:
poolName_dev: '' # Use this for self-hosted agents
poolName_prd: '' # Use this for self-hosted agents

serviceConnection_sbx: '<PrivateConnection>'
serviceConnection_dev: '<PrivateConnection>'
serviceConnection_prd: '<PrivateConnection>'
serviceConnection_sbx: '<ServiceConnectionName>'
serviceConnection_dev: '<ServiceConnectionName>'
serviceConnection_prd: '<ServiceConnectionName>'
#endregion

#region specific
Expand Down
11 changes: 4 additions & 7 deletions constructs/managedDevOpsPool/templates/pool.deploy.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,11 @@ param poolMaximumConcurrency int = 1
param poolSize string = 'Standard_B1ms'

@description('Optional. The managed identity definition for the Managed DevOps Pool.')
import { managedIdentitiesType } from 'br/public:avm/res/dev-ops-infrastructure/pool:0.1.1'
param poolManagedIdentities managedIdentitiesType

// import { managedIdentityOnlyUserAssignedType } from 'br/public:avm/utl/types/avm-common-types:0.1.0'
// param poolManagedIdentities managedIdentityOnlyUserAssignedType?
import { managedIdentityOnlyUserAssignedType } from 'br/public:avm/utl/types/avm-common-types:0.3.0'
param poolManagedIdentities managedIdentityOnlyUserAssignedType?

@description('Optional. Defines how the machine will be handled once it executed a job.')
import { agentProfileType } from 'br/public:avm/res/dev-ops-infrastructure/pool:0.1.1'
import { agentProfileType } from 'br/public:avm/res/dev-ops-infrastructure/pool:0.2.0'
param poolAgentProfile agentProfileType = {
kind: 'Stateless'
}
Expand Down Expand Up @@ -169,7 +166,7 @@ module vnetPermission 'br/public:avm/ptn/authorization/resource-role-assignment:
}
}

module pool 'br/public:avm/res/dev-ops-infrastructure/pool:0.1.1' = {
module pool 'br/public:avm/res/dev-ops-infrastructure/pool:0.3.0' = {
name: '${uniqueString(deployment().name, resourceLocation)}-pool'
scope: rg
params: {
Expand Down
4 changes: 2 additions & 2 deletions docs/wiki/Creating images with the Azure Image Builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ This sections gives you an overview on how to use the Azure Image Builder (AIB)
## Prerequisites

The deployments described in the following sections assume certain prerequisites to be in place prior to deployment.

- The deployment principal (e.g., the Service Principal tied to the deploying Service Connection) must have at least `Contributor` & `User Access Administrator` permissions on the target subscription to be able to deploy both resources and assign permissions to created user-assigned identities
- The deployment principal (i.e., the principal tied to the deploying Service Connection) **must** be setup for OIDC. This is required so that all AzureCLI tasks that also run _pwsh_ commands can log into the Azure PowerShell context too. For instructions on this matter, please refer to [this guide](./OIDC).
- The deployment principal must have at least `Contributor` & `Role Based Access Control Administrator` permissions on the target subscription to be able to deploy both resources and assign permissions to created user-assigned identities
- IF you have a policy in place that prevents Storage Accounts from being deployed without a Firewall, you have to create an exemption for the Image Template / Staging Resource Group you can configure for the Image Template Resource (parameter `imageTemplateResourceGroupName`). The rationale is that the Azure-Image-Builder service uses this resource group to deploy both temporal resources used during the image build (e.g., a Virtual Machine), as well as a Storage Account to store temporal files & a 'packerlogs/customization.log' file in (which contains the logs of the image build). This Storage Account has no firewall configured, has a random name, and cannot be configured at deploy time.

## Elements
Expand Down
77 changes: 77 additions & 0 deletions docs/wiki/OIDC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
This section gives you an overview of how to set up a managed identity and use it for an OIDC-based service connection in Azure DevOPs

### _Navigation_
- [Create Service Connection (Part 1)](#create-service-connection-part-1)
- [Create managed identity](#create-managed-identity)
- [Create federated credential](#create-federated-credential)
- [Create Service Connection (Part 2)](#create-service-connection-part-2)

# Create Service Connection (Part 1)

1. To start the setup, navigate to the 'Service connections' of your 'Project Settings' and select 'New service connection'

<img src="./media/oidc/serviceConnection-init.png" alt="Init service connection" height="300">

1. Next, select 'Azure Resource Manager' as the service connection type and click on 'Next'

<img src="./media/oidc/serviceConnection-type.png" alt="Select service connection type" height="300">

1. When asked which type of authentication you'd like to use, select 'Workload Identity Federation (manual)'

<img src="./media/oidc/serviceConnection-authentication.png" alt="Select service connection authentication" height="350">

1. Finally, give the to-be service connection a name, and select 'Next' one last time (for now)

<img src="./media/oidc/serviceConnection-name.png" alt="Select service connection name" height="450">

1. In this following view, take not of the 'Issuer' & 'Subject identifier' values. You'll need those to create the federated credential

<img src="./media/oidc/serviceConnection-issuer.png" alt="Select service connection issuer info" height="350">

# Create managed identity

With the service connection prepared, we can now move on the the Managed Identity in Azure

1. To start, navigate to the Managed Identity overview and select '+ Create'

<img src="./media/oidc/managedIdentityCreate.png" alt="Init MSI creation" height="200">

1. Next, provide the required information like the resource group, name & location and initialize the creation by selecting the 'Review + create', followed by another click on the 'Create' button

<img src="./media/oidc/managedIdentityDetails.png" alt="MSI details" height="450">

1. Once the resource is created, navigate to it so that we can start creating the federated credential. Also, take note of the 'Client ID' in the resource's overview which we will need again to finish up the Service Connection creation later.

<img src="./media/oidc/managedIdentityId.png" alt="MSI details" height="300">

# Create federated credential

1. On the managed identity resource, select 'Federated credentials' in the resource blade, followed by '+ Add Credential'

<img src="./media/oidc/credentialInit.png" alt="Init credential" height="350">

1. Now, for the
- 'Federated credential scenario', select '**Other** Configure an identity managed by an external OpenID Connect Provider to access Azure resources as this application'
- 'Issuer Url' provide the 'Issuer' value from the last step in the preceding Service Connection creation
- 'Subject identifier' provide the 'Subject identifier' value from the last step in the preceding Service Connection creation
- Name, a name of your choice

<img src="./media/oidc/credentialDetails.png" alt="Credential details" height="500">

1. Finally, finish the creation of the Federated Credential by selecting 'Add'

# Create Service Connection (Part 2)

Now, with the federated credential ready, we can return to the creation of the Service Connection to wrap it up. To do so,

1. Provide all required details in the Service connection's form, including
- Subscription ID: Must be the Subscription ID of the Subscription containing the created Managed Identity
- Subscription Name: The name of the Subscription
- Service Principal Id: The client ID of the created Managed Identity, taken from its overview page. **Note:** This refers to the Client ID, not the Object / Principal ID
- Tenant ID: The ID of the Tenant containing the created Managed Identity

<img src="./media/oidc/serviceConnection-finish.png" alt="Service Connection finish" height="700">

1. Finally, select 'Verify and save' and you should be good to go to use the Service Connection in your pipelines.

> **NOTE:** Don't forget to grant your Managed Identity the permissions it needs to perform its tasks (e.g., create resources)
6 changes: 6 additions & 0 deletions docs/wiki/Self-hosted Managed DevOps Pool.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This sections gives you an overview on how to use the Managed DevOps Pool pipeli

- [Overview](#overview)
- [Advantages](#Advantages)
- [Prerequisites](#prerequisites)
- [Elements](#elements)
- [File structure & flow](#file-structure--flow)
- [Process](#process)
Expand All @@ -27,6 +28,11 @@ This sections gives you an overview on how to use the Managed DevOps Pool pipeli
- As the Managed DevOps Pool can be configured to e.g. scale in to 0 and spin up a new VM only if a job is scheduled
- As a single agent is installed on a new instance. Hence virtual machines can be deployed using a SKU with less compute power

## Prerequisites

The deployments described in the following sections assume certain prerequisites to be in place prior to deployment.
- The deployment principal (i.e., the principal tied to the deploying Service Connection) **must** be setup for OIDC. This is required so that all AzureCLI tasks that also run _pwsh_ commands can log into the Azure PowerShell context too. For instructions on this matter, please refer to [this guide](./OIDC).

## Elements

<img src="./media/pool/managedPoolSetup.png" alt="Managed DevOps Pool infrastructure" height="200">
Expand Down
Binary file added docs/wiki/media/oidc/credentialDetails.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/wiki/media/oidc/credentialInit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/wiki/media/oidc/managedIdentityCreate.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/wiki/media/oidc/managedIdentityDetails.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/wiki/media/oidc/managedIdentityId.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/wiki/media/oidc/serviceConnection-init.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/wiki/media/oidc/serviceConnection-issuer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/wiki/media/oidc/serviceConnection-name.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/wiki/media/pool/addUser.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/wiki/media/pool/projectCollection.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/wiki/media/pool/setPoolPermissionsAdmin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit c536ca7

Please sign in to comment.