Skip to content

Commit

Permalink
msi for native support (#6655)
Browse files Browse the repository at this point in the history
* msi for native support

* native powershell changes

* Not printing access token in log

* rename

* Error messgae changed

* error message changed

* unused code

* error message changed

* build failure

* url changed

* native powershell

* MSI

* response modifieed
  • Loading branch information
RoshanKumarMicrosoft authored Apr 15, 2018
1 parent c09381d commit 4f134b9
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 5 deletions.
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()
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}"
}
}

0 comments on commit 4f134b9

Please sign in to comment.