Skip to content

Commit f4170d9

Browse files
Copilotweinong
andauthored
Support Azure Pipelines Environment Variables for AzurePipelinesCredential (#713)
* Initial plan * Add Azure Pipelines environment variable support Co-authored-by: weinong <4204090+weinong@users.noreply.github.com> * Update documentation for Azure Pipelines environment variables Co-authored-by: weinong <4204090+weinong@users.noreply.github.com> * Apply suggestion from @weinong * Apply suggestion from @weinong * Remove references to addSpnToEnvironment from documentation The Azure Pipelines environment variables are injected automatically by Azure Pipelines tasks when using Azure Resource Manager service connections, regardless of the addSpnToEnvironment setting. Updated both azurepipelines.md and get-token.md to reflect this. Co-authored-by: weinong <4204090+weinong@users.noreply.github.com> * Fix Azure Pipelines validation and update documentation - Remove strict validation for tenant-id and service-connection-id in azurepipelines login method as these can come from environment variables - Remove validation for SYSTEM_ACCESSTOKEN and SYSTEM_OIDCREQUESTURI in Validate() since they're only needed when getting tokens, not when converting kubeconfig - Update all documentation references from "Azure Pipelines tasks" to "AzureCLI@2 task" to be more specific - Add az aks get-credentials command to example so server-id is not needed - Remove Terraform integration example as requested - Update Usage section in get-token.md with correct environment variable information - Update tests to reflect the more lenient validation Co-authored-by: weinong <4204090+weinong@users.noreply.github.com> * Remove irrelevant Azure Pipelines validation comments Removed lines 171-173 which were just explanatory comments about validation that was removed. Since there's no actual validation code for Azure Pipelines login method anymore, these comments are no longer relevant in the context. Co-authored-by: weinong <4204090+weinong@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: weinong <4204090+weinong@users.noreply.github.com> Co-authored-by: Weinong Wang <weinong@outlook.com>
1 parent 660ef82 commit f4170d9

File tree

5 files changed

+266
-92
lines changed

5 files changed

+266
-92
lines changed

docs/book/src/cli/get-token.md

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ Usage:
1313

1414
Flags:
1515
--authority-host string Workload Identity authority host. It may be specified in AZURE_AUTHORITY_HOST environment variable
16-
--azure-pipelines-service-connection-id string Service connection (resource) ID used by azurepipelines login method
16+
--azure-pipelines-service-connection-id string Service connection (resource) ID used by azurepipelines login method. It may be specified in AZURESUBSCRIPTION_SERVICE_CONNECTION_ID environment variable
1717
--cache-dir string directory to cache authentication record (default "/home/weinongw/.kube/cache/kubelogin/")
1818
--client-certificate string AAD client cert in pfx. Used in spn login. It may be specified in AAD_SERVICE_PRINCIPAL_CLIENT_CERTIFICATE or AZURE_CLIENT_CERTIFICATE_PATH environment variable
1919
--client-certificate-password string Password for AAD client cert. Used in spn login. It may be specified in AAD_SERVICE_PRINCIPAL_CLIENT_CERTIFICATE_PASSWORD or AZURE_CLIENT_CERTIFICATE_PASSWORD environment variable
20-
--client-id string AAD client application ID. It may be specified in AAD_SERVICE_PRINCIPAL_CLIENT_ID or AZURE_CLIENT_ID environment variable
20+
--client-id string AAD client application ID. It may be specified in AAD_SERVICE_PRINCIPAL_CLIENT_ID or AZURE_CLIENT_ID environment variable. For Azure Pipelines login, it may be specified in AZURESUBSCRIPTION_CLIENT_ID environment variable
2121
--client-secret string AAD client application secret. Used in spn login. It may be specified in AAD_SERVICE_PRINCIPAL_CLIENT_SECRET or AZURE_CLIENT_SECRET environment variable
2222
--disable-environment-override Enable or disable the use of env-variables. Default false
2323
--disable-instance-discovery set to true to disable instance discovery in environments with their own simple Identity Provider (not AAD) that do not have instance metadata discovery endpoint. Default false
@@ -33,7 +33,7 @@ Flags:
3333
--pop-enabled set to true to use a PoP token for authentication or false to use a regular bearer token
3434
--redirect-url string The URL Microsoft Entra ID will redirect to with the access token. This is only used for interactive login. This is an optional parameter.
3535
--server-id string AAD server application ID
36-
-t, --tenant-id string AAD tenant ID. It may be specified in AZURE_TENANT_ID environment variable
36+
-t, --tenant-id string AAD tenant ID. It may be specified in AZURE_TENANT_ID environment variable. For Azure Pipelines login, it may be specified in AZURESUBSCRIPTION_TENANT_ID environment variable
3737
--timeout duration Timeout duration for Azure CLI token requests. It may be specified in AZURE_CLI_TIMEOUT environment variable (default 30s)
3838
--use-azurerm-env-vars Use environment variable names of Terraform Azure Provider (ARM_CLIENT_ID, ARM_CLIENT_SECRET, ARM_CLIENT_CERTIFICATE_PATH, ARM_CLIENT_CERTIFICATE_PASSWORD, ARM_TENANT_ID)
3939
--username string user name for ropc login flow. It may be specified in AAD_USER_PRINCIPAL_NAME or AZURE_USERNAME environment variable
@@ -232,6 +232,28 @@ users:
232232
233233
### Azure Pipelines
234234
235+
When using `AzureCLI@2` task with Azure Resource Manager service connections, environment variables are automatically set. You only need to provide the `--server-id`:
236+
237+
```yaml
238+
kind: Config
239+
preferences: {}
240+
users:
241+
- name: demouser
242+
user:
243+
exec:
244+
apiVersion: client.authentication.k8s.io/v1beta1
245+
args:
246+
- get-token
247+
- --server-id
248+
- <AAD server app ID>
249+
- --login
250+
- azurepipelines
251+
command: kubelogin
252+
env: null
253+
```
254+
255+
If environment variables are not available, provide all parameters explicitly:
256+
235257
```yaml
236258
kind: Config
237259
preferences: {}
@@ -255,3 +277,8 @@ users:
255277
command: kubelogin
256278
env: null
257279
```
280+
281+
> **Note**: When using `AzureCLI@2` task with Azure Resource Manager service connections, the following environment variables are automatically set and used:
282+
> - `AZURESUBSCRIPTION_TENANT_ID` for `--tenant-id`
283+
> - `AZURESUBSCRIPTION_CLIENT_ID` for `--client-id`
284+
> - `AZURESUBSCRIPTION_SERVICE_CONNECTION_ID` for `--azure-pipelines-service-connection-id`

docs/book/src/concepts/login-modes/azurepipelines.md

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ The authentication leverages Azure Pipelines' managed identity integration throu
2323
- `--server-id`: Application ID of the server/resource (typically the AKS cluster's server ID)
2424
- `--azure-pipelines-service-connection-id`: The resource ID of the Azure Resource Manager service connection
2525

26+
> **Note**: When using `AzureCLI@2` task with Azure Resource Manager service connections, Azure Pipelines automatically sets the following environment variables which kubelogin will use if the corresponding flags are not provided:
27+
> - `AZURESUBSCRIPTION_TENANT_ID` - Automatically used for `--tenant-id`
28+
> - `AZURESUBSCRIPTION_CLIENT_ID` - Automatically used for `--client-id`
29+
> - `AZURESUBSCRIPTION_SERVICE_CONNECTION_ID` - Automatically used for `--azure-pipelines-service-connection-id`
30+
>
31+
> This means you only need to provide the `--server-id` parameter when these environment variables are available.
32+
2633
## Usage Examples
2734

2835
### Basic Usage in Pipeline
@@ -37,7 +44,32 @@ steps:
3744
scriptType: 'bash'
3845
scriptLocation: 'inlineScript'
3946
inlineScript: |
47+
# Download kubeconfig from AKS
48+
az aks get-credentials -g ${RESOURCE_GROUP} -n ${AKS_NAME}
49+
4050
# Configure kubeconfig to use azurepipelines login
51+
# tenant-id, client-id, and service-connection-id are automatically detected from environment variables
52+
kubelogin convert-kubeconfig --login azurepipelines
53+
54+
# Now kubectl commands will authenticate using Azure Pipelines credentials
55+
kubectl get nodes
56+
```
57+
58+
### Basic Usage with Explicit Parameters
59+
60+
If you prefer to explicitly provide all parameters:
61+
62+
```yaml
63+
# azure-pipelines.yml
64+
steps:
65+
- task: AzureCLI@2
66+
displayName: 'Deploy to AKS'
67+
inputs:
68+
azureSubscription: 'my-service-connection'
69+
scriptType: 'bash'
70+
scriptLocation: 'inlineScript'
71+
inlineScript: |
72+
# Configure kubeconfig to use azurepipelines login with explicit parameters
4173
kubelogin convert-kubeconfig \
4274
--login azurepipelines \
4375
--tenant-id $(tenant-id) \
@@ -47,13 +79,18 @@ steps:
4779
4880
# Now kubectl commands will authenticate using Azure Pipelines credentials
4981
kubectl get nodes
50-
addSpnToEnvironment: true # This enables SYSTEM_ACCESSTOKEN
5182
```
5283
5384
### Direct Token Retrieval
5485
5586
```bash
5687
# In Azure DevOps pipeline (with "Allow scripts to access the OAuth token" enabled)
88+
# Simplified version - uses environment variables automatically set by Azure Pipelines
89+
kubelogin get-token \
90+
--login azurepipelines \
91+
--server-id <cluster-server-id>
92+
93+
# Or with explicit parameters
5794
kubelogin get-token \
5895
--login azurepipelines \
5996
--tenant-id <tenant-id> \
@@ -62,42 +99,27 @@ kubelogin get-token \
6299
--azure-pipelines-service-connection-id <service-connection-resource-id>
63100
```
64101

65-
### Terraform Integration
102+
## Environment Variable Support
66103

67-
```yaml
68-
# azure-pipelines.yml for Terraform deployments
69-
steps:
70-
- task: TerraformTaskV3@3
71-
displayName: 'Terraform Apply'
72-
inputs:
73-
provider: 'azurerm'
74-
command: 'apply'
75-
workingDirectory: '$(System.DefaultWorkingDirectory)/terraform'
76-
environmentServiceNameAzureRM: 'my-service-connection'
77-
commandOptions: |
78-
-auto-approve
79-
env:
80-
# Configure kubeconfig for kubectl provider in Terraform
81-
KUBECONFIG: $(Agent.TempDirectory)/kubeconfig
82-
- script: |
83-
# Convert kubeconfig to use azurepipelines authentication
84-
kubelogin convert-kubeconfig \
85-
--login azurepipelines \
86-
--tenant-id $(tenant-id) \
87-
--client-id $(client-id) \
88-
--server-id $(server-id) \
89-
--azure-pipelines-service-connection-id $(service-connection-resource-id) \
90-
--kubeconfig $(Agent.TempDirectory)/kubeconfig
91-
displayName: 'Configure kubectl authentication'
92-
condition: always()
93-
```
104+
When using `AzureCLI@2` task with Azure Resource Manager service connections, Azure Pipelines automatically sets environment variables for the service connection. Kubelogin automatically detects and uses these variables:
105+
106+
| Environment Variable | Used For | Command-line Flag Equivalent |
107+
|---------------------|----------|------------------------------|
108+
| `AZURESUBSCRIPTION_TENANT_ID` | Tenant ID | `--tenant-id` |
109+
| `AZURESUBSCRIPTION_CLIENT_ID` | Client ID | `--client-id` |
110+
| `AZURESUBSCRIPTION_SERVICE_CONNECTION_ID` | Service Connection ID | `--azure-pipelines-service-connection-id` |
111+
112+
**Precedence**: Command-line flags always take precedence over environment variables. This allows you to override specific values when needed.
113+
114+
**Disabling Environment Variables**: You can use the `--disable-environment-override` flag to ignore all environment variables and require explicit parameters.
94115

95116
## How It Works
96117

97118
1. **Service Connection**: Azure DevOps service connections provide managed identity or service principal authentication to Azure resources
98119
2. **System Access Token**: When "Allow scripts to access the OAuth token" is enabled, Azure Pipelines provides a `SYSTEM_ACCESSTOKEN` environment variable
99-
3. **OIDC Integration**: The `azurepipelines` login method uses Azure SDK's `AzurePipelinesCredential` to exchange the system access token for an Azure AD token
100-
4. **Token Caching**: Authentication tokens are cached to improve performance across multiple kubectl operations
120+
3. **Environment Variables**: When using `AzureCLI@2` task with Azure Resource Manager service connections, Azure Pipelines automatically sets subscription-specific environment variables
121+
4. **OIDC Integration**: The `azurepipelines` login method uses Azure SDK's `AzurePipelinesCredential` to exchange the system access token for an Azure AD token
122+
5. **Token Caching**: Authentication tokens are cached to improve performance across multiple kubectl operations
101123

102124
## Troubleshooting
103125

pkg/internal/env/variables.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,9 @@ const (
3131
// env vars used by Azure Pipelines
3232
SystemAccessToken = "SYSTEM_ACCESSTOKEN"
3333
SystemOIDCRequestURI = "SYSTEM_OIDCREQUESTURI"
34+
35+
// env vars used by Azure Pipelines service connections
36+
AzureSubscriptionTenantID = "AZURESUBSCRIPTION_TENANT_ID"
37+
AzureSubscriptionServiceConnectionID = "AZURESUBSCRIPTION_SERVICE_CONNECTION_ID"
38+
AzureSubscriptionClientID = "AZURESUBSCRIPTION_CLIENT_ID"
3439
)

pkg/internal/token/options.go

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) {
9393
fs.StringVarP(&o.LoginMethod, "login", "l", o.LoginMethod,
9494
fmt.Sprintf("Login method. Supported methods: %s. It may be specified in %s environment variable", GetSupportedLogins(), env.LoginMethod))
9595
fs.StringVar(&o.ClientID, "client-id", o.ClientID,
96-
fmt.Sprintf("AAD client application ID. It may be specified in %s or %s environment variable", env.KubeloginClientID, env.AzureClientID))
96+
fmt.Sprintf("AAD client application ID. It may be specified in %s or %s environment variable. For Azure Pipelines login, it may be specified in %s environment variable", env.KubeloginClientID, env.AzureClientID, env.AzureSubscriptionClientID))
9797
fs.StringVar(&o.ClientSecret, "client-secret", o.ClientSecret,
9898
fmt.Sprintf("AAD client application secret. Used in spn login. It may be specified in %s or %s environment variable", env.KubeloginClientSecret, env.AzureClientSecret))
9999
fs.StringVar(&o.ClientCert, "client-certificate", o.ClientCert,
@@ -111,11 +111,11 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) {
111111
fs.StringVar(&o.AuthorityHost, "authority-host", o.AuthorityHost,
112112
fmt.Sprintf("Workload Identity authority host. It may be specified in %s environment variable", env.AzureAuthorityHost))
113113
fs.StringVar(&o.AzurePipelinesServiceConnectionID, "azure-pipelines-service-connection-id", o.AzurePipelinesServiceConnectionID,
114-
"Service connection (resource) ID used by azurepipelines login method")
114+
fmt.Sprintf("Service connection (resource) ID used by azurepipelines login method. It may be specified in %s environment variable", env.AzureSubscriptionServiceConnectionID))
115115
fs.StringVar(&o.AuthRecordCacheDir, "token-cache-dir", o.AuthRecordCacheDir, "directory to cache authentication record")
116116
_ = fs.MarkDeprecated("token-cache-dir", "use --cache-dir instead")
117117
fs.StringVar(&o.AuthRecordCacheDir, "cache-dir", o.AuthRecordCacheDir, "directory to cache authentication record")
118-
fs.StringVarP(&o.TenantID, "tenant-id", "t", o.TenantID, fmt.Sprintf("AAD tenant ID. It may be specified in %s environment variable", env.AzureTenantID))
118+
fs.StringVarP(&o.TenantID, "tenant-id", "t", o.TenantID, fmt.Sprintf("AAD tenant ID. It may be specified in %s environment variable. For Azure Pipelines login, it may be specified in %s environment variable", env.AzureTenantID, env.AzureSubscriptionTenantID))
119119
fs.StringVarP(&o.Environment, "environment", "e", o.Environment, "Azure environment name")
120120
fs.BoolVar(&o.IsLegacy, "legacy", o.IsLegacy, "set to true to get token with 'spn:' prefix in audience claim")
121121
fs.BoolVar(&o.UseAzureRMTerraformEnv, "use-azurerm-env-vars", o.UseAzureRMTerraformEnv,
@@ -168,22 +168,6 @@ func (o *Options) Validate() error {
168168
return fmt.Errorf("timeout must be greater than 0")
169169
}
170170

171-
// Azure Pipelines login method validation
172-
if o.LoginMethod == AzurePipelinesLogin {
173-
if o.TenantID == "" {
174-
return fmt.Errorf("tenant ID is required for azurepipelines login method")
175-
}
176-
if o.AzurePipelinesServiceConnectionID == "" {
177-
return fmt.Errorf("--azure-pipelines-service-connection-id is required for --login azurepipelines")
178-
}
179-
if os.Getenv(env.SystemAccessToken) == "" {
180-
return fmt.Errorf("environment variable %s not set; enable \"Allow scripts to access the OAuth token\" in the pipeline", env.SystemAccessToken)
181-
}
182-
if os.Getenv(env.SystemOIDCRequestURI) == "" {
183-
return fmt.Errorf("environment variable %s not set; this should be automatically set by Azure Pipelines", env.SystemOIDCRequestURI)
184-
}
185-
}
186-
187171
return nil
188172
}
189173

@@ -267,6 +251,25 @@ func (o *Options) UpdateFromEnv() {
267251
o.AuthorityHost = v
268252
}
269253
}
254+
255+
if o.LoginMethod == AzurePipelinesLogin {
256+
if o.ClientID == "" {
257+
if v, ok := os.LookupEnv(env.AzureSubscriptionClientID); ok {
258+
o.ClientID = v
259+
}
260+
}
261+
if o.TenantID == "" {
262+
if v, ok := os.LookupEnv(env.AzureSubscriptionTenantID); ok {
263+
o.TenantID = v
264+
}
265+
}
266+
if o.AzurePipelinesServiceConnectionID == "" {
267+
if v, ok := os.LookupEnv(env.AzureSubscriptionServiceConnectionID); ok {
268+
o.AzurePipelinesServiceConnectionID = v
269+
}
270+
}
271+
}
272+
270273
if v, ok := os.LookupEnv("AZURE_CLI_TIMEOUT"); ok {
271274
if timeout, err := time.ParseDuration(v); err == nil {
272275
o.Timeout = timeout

0 commit comments

Comments
 (0)