Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

msi for native support #6655

Merged
merged 14 commits into from
Apr 15, 2018
2 changes: 1 addition & 1 deletion Tasks/AzurePowerShell/Utility.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function Update-PSModulePathForHostedAgent {
$hostedAgentAzureModulePath = Get-LatestModule -patternToMatch "^azure_[0-9]+\.[0-9]+\.[0-9]+$" -patternToExtract "[0-9]+\.[0-9]+\.[0-9]+$" -Classic:$true
}

if($authScheme -eq 'ServicePrincipal' -or $authScheme -eq '')
if($authScheme -eq 'ServicePrincipal' -or $authScheme -eq 'ManagedServiceIdentity' -or $authScheme -eq '')
{
$env:PSModulePath = $hostedAgentAzureModulePath + ";" + $env:PSModulePath
$env:PSModulePath = $env:PSModulePath.TrimStart(';')
Expand Down
99 changes: 98 additions & 1 deletion Tasks/Common/VstsAzureHelpers_/InitializeFunctions.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,108 @@ function Initialize-AzureSubscription {

Set-CurrentAzureRMSubscription -SubscriptionId $Endpoint.Data.SubscriptionId -TenantId $Endpoint.Auth.Parameters.TenantId
}
} else {
} elseif ($Endpoint.Auth.Scheme -eq 'ManagedServiceIdentity') {
$accountId = $env:BUILD_BUILDID
if($env:RELEASE_RELEASEID){
$accountId = $env:RELEASE_RELEASEID
}
$date = Get-Date -Format o
$accountId = -join($accountId, "-", $date)
$access_token = Get-MsiAccessToken $Endpoint 0 0
try {
Write-Host "##[command]Add-AzureRmAccount -AccessToken ****** -AccountId $accountId "
$null = Add-AzureRmAccount -AccessToken $access_token -AccountId $accountId
} catch {
# Provide an additional, custom, credentials-related error message.
Write-VstsTaskError -Message $_.Exception.Message
throw (New-Object System.Exception((Get-VstsLocString -Key AZ_MsiFailure), $_.Exception))
}

Set-CurrentAzureRMSubscription -SubscriptionId $Endpoint.Data.SubscriptionId -TenantId $Endpoint.Auth.Parameters.TenantId
}else {
throw (Get-VstsLocString -Key AZ_UnsupportedAuthScheme0 -ArgumentList $Endpoint.Auth.Scheme)
}
}


# Get the Bearer Access Token from the Endpoint
function Get-MsiAccessToken {
[CmdletBinding()]
param([Parameter(Mandatory=$true)] $endpoint,
[Parameter(Mandatory=$true)] $retryCount,
[Parameter(Mandatory=$true)] $timeToWait)

$msiClientId = "";
if($endpoint.Data.msiClientId){
$msiClientId = "&client_id=" + $endpoint.Data.msiClientId;
}
$tenantId = $endpoint.Auth.Parameters.TenantId

# Prepare contents for GET
$method = "GET"
$apiVersion = "2018-02-01";
$authUri = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=" + $apiVersion + "&resource=" + $endpoint.Url + $msiClientId;

# Call Rest API to fetch AccessToken
Write-Verbose "Fetching Access Token For MSI"

try
{
$retryLimit = 5;
$proxyUri = Get-ProxyUri $authUri
if ($proxyUri -eq $null)
{
Write-Verbose "No proxy settings"
$response = Invoke-WebRequest -Uri $authUri -Method $method -Headers @{Metadata="true"} -UseBasicParsing
}
else
{
Write-Verbose "Using Proxy settings"
$response = Invoke-WebRequest -Uri $authUri -Method $method -Headers @{Metadata="true"} -UseDefaultCredentials -Proxy $proxyUri -ProxyUseDefaultCredentials -UseBasicParsing
}

# Action on the based of response
if(($response.StatusCode -eq 429) -or ($response.StatusCode -eq 500))
{
if($retryCount -lt $retryLimit)
{
$retryCount += 1
$waitedTime = 2000 + $timeToWait * 2
Start-Sleep -m $waitedTime
Get-MsiAccessToken $endpoint $retryCount $waitedTime
}
else
{
throw (Get-VstsLocString -Key AZ_MsiAccessTokenFetchFailure -ArgumentList $response.StatusCode, $response.StatusDescription)
}
}
elseif ($response.StatusCode -eq 200)
{
$accessToken = $response.Content | ConvertFrom-Json
return $accessToken.access_token
}
else
{
throw (Get-VstsLocString -Key AZ_MsiAccessNotConfiguredProperlyFailure -ArgumentList $response.StatusCode, $response.StatusDescription)
}

}
catch
{
$exceptionMessage = $_.Exception.Message.ToString()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ajay-MS - Can you ensure post Roshan's change, your changes to log the error code is factored in.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will take into separate PR.

Write-Verbose "ExceptionMessage: $exceptionMessage (in function: Get-MsiAccessToken)"
if($exceptionMessage -match "400")
{
throw (Get-VstsLocString -Key AZ_MsiAccessNotConfiguredProperlyFailure -ArgumentList $response.StatusCode, $response.StatusDescription)
}
else
{
throw $_.Exception
}
}
}


function Set-CurrentAzureSubscription {
[CmdletBinding()]
param(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@
"loc.messages.AZ_ServicePrincipalAuthNotSupportedAzureVersion0": "Service principal authentication is not supported in version '{0}' of the Azure module.",
"loc.messages.AZ_UnsupportedAuthScheme0": "Unsupported authentication scheme '{0}' for Azure endpoint.",
"loc.messages.AZ_AvailableModules": "The list of available {0} modules:",
"loc.messages.AZ_InvalidARMEndpoint": "Specified AzureRM endpoint is invalid."
"loc.messages.AZ_InvalidARMEndpoint": "Specified AzureRM endpoint is invalid.",
"loc.messages.AZ_MsiAccessNotConfiguredProperlyFailure": "Could not fetch access token for Managed Service Principal. Please configure Managed Service Identity (MSI) for virtual machine 'https://aka.ms/azure-msi-docs'. Status code: '{0}', status message: {1}",
"loc.messages.AZ_MsiAccessTokenFetchFailure": "Could not fetch access token for Managed Service Principal. Status code: '{0}', status message: {1}",
"loc.messages.AZ_MsiFailure": "Could not fetch access token for Managed Service Principal. {0}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
[CmdletBinding()]
param()

# Arrange.
. $PSScriptRoot\..\..\..\..\Tests\lib\Initialize-Test.ps1
Microsoft.PowerShell.Core\Import-Module Microsoft.PowerShell.Security
Unregister-Mock Import-Module
Register-Mock Write-VstsTaskError
$module = Microsoft.PowerShell.Core\Import-Module $PSScriptRoot\.. -PassThru

$endpoint = @{
Auth = @{
Parameters = @{
ServicePrincipalId = 'Some service principal ID'
ServicePrincipalKey = 'Some service principal key'
TenantId = 'Some tenant ID'
}
Scheme = 'ManagedServiceIdentity'
}
Data = @{
SubscriptionId = 'Some subscription ID'
SubscriptionName = 'Some subscription name'
}
}

$content = @"
{"access_token" : "Dummy Token" }
"@

$response = @{
Content = $content
StatusCode = 200
StatusDescription = 'OK'
};

$variableSets = @(
@{ StorageAccount = 'Some storage account' }
)
foreach ($variableSet in $variableSets) {
Write-Verbose ('-' * 80)
Unregister-Mock Add-AzureRMAccount
Unregister-Mock Set-CurrentAzureRMSubscription
Unregister-Mock Invoke-WebRequest
Unregister-Mock Set-UserAgent
Register-Mock Add-AzureRMAccount { 'some output' }
Register-Mock Set-CurrentAzureRMSubscription
Register-Mock Set-UserAgent
Register-Mock Invoke-WebRequest { $response }

# Act.
$result = & $module Initialize-AzureSubscription -Endpoint $endpoint -StorageAccount $variableSet.StorageAccount

Assert-AreEqual $null $result
Assert-WasCalled Set-CurrentAzureRMSubscription -- -SubscriptionId $endpoint.Data.SubscriptionId -TenantId $endpoint.Auth.Parameters.TenantId
}
3 changes: 3 additions & 0 deletions Tasks/Common/VstsAzureHelpers_/Tests/L0.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ describe('Common-VstsAzureHelpers_ Suite', function () {
it('(Initialize-Azure) throws when service name is null', (done) => {
psr.run(path.join(__dirname, 'Initialize-Azure.ThrowsWhenServiceNameIsNull.ps1'), done);
})
it('(Initialize-AzureSubscription) manged service identity should pass ', (done) => {
psr.run(path.join(__dirname, 'Initialize-AzureSubscription.ManagedServiceIdentity.ps1'), done);
})
it('(Initialize-AzureSubscription) passes values when cert auth', (done) => {
psr.run(path.join(__dirname, 'Initialize-AzureSubscription.PassesValuesWhenCertAuth.ps1'), done);
})
Expand Down
2 changes: 1 addition & 1 deletion Tasks/Common/VstsAzureHelpers_/VstsAzureHelpers_.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function Initialize-Azure {

# Determine which modules are preferred.
$preferredModules = @( )
if ($endpoint.Auth.Scheme -eq 'ServicePrincipal') {
if (($endpoint.Auth.Scheme -eq 'ServicePrincipal') -or ($endpoint.Auth.Scheme -eq 'ManagedServiceIdentity')) {
$preferredModules += 'AzureRM'
} elseif ($endpoint.Auth.Scheme -eq 'UserNamePassword' -and $strict -eq $false) {
$preferredModules += 'Azure'
Expand Down
5 changes: 4 additions & 1 deletion Tasks/Common/VstsAzureHelpers_/module.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
"AZ_ServicePrincipalAuthNotSupportedAzureVersion0": "Service principal authentication is not supported in version '{0}' of the Azure module.",
"AZ_UnsupportedAuthScheme0": "Unsupported authentication scheme '{0}' for Azure endpoint.",
"AZ_AvailableModules": "The list of available {0} modules:",
"AZ_InvalidARMEndpoint": "Specified AzureRM endpoint is invalid."
"AZ_InvalidARMEndpoint": "Specified AzureRM endpoint is invalid.",
"AZ_MsiAccessNotConfiguredProperlyFailure": "Could not fetch access token for Managed Service Principal. Please configure Managed Service Identity (MSI) for virtual machine 'https://aka.ms/azure-msi-docs'. Status code: '{0}', status message: {1}",
"AZ_MsiAccessTokenFetchFailure": "Could not fetch access token for Managed Service Principal. Status code: '{0}', status message: {1}" ,
"AZ_MsiFailure": "Could not fetch access token for Managed Service Principal. {0}"
}
}