2016-03-04 05:14:55 -05:00
package arm
import (
2018-04-06 04:12:58 -04:00
"context"
2016-03-04 05:14:55 -05:00
"errors"
"fmt"
2018-05-18 03:32:01 -04:00
packerAzureCommon "github.com/hashicorp/packer/builder/azure/common"
2016-03-04 05:14:55 -05:00
"log"
2017-06-27 16:19:21 -04:00
"os"
"runtime"
2016-06-30 19:51:52 -04:00
"strings"
2016-04-21 19:50:03 -04:00
"time"
2016-03-04 05:14:55 -05:00
2018-04-06 04:12:58 -04:00
armstorage "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2017-10-01/storage"
"github.com/Azure/azure-sdk-for-go/storage"
"github.com/Azure/go-autorest/autorest/adal"
2018-05-18 03:32:01 -04:00
"github.com/dgrijalva/jwt-go"
2017-04-04 16:39:01 -04:00
"github.com/hashicorp/packer/builder/azure/common/constants"
"github.com/hashicorp/packer/builder/azure/common/lin"
packerCommon "github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator"
2018-01-19 19:18:44 -05:00
"github.com/hashicorp/packer/helper/multistep"
2017-04-04 16:39:01 -04:00
"github.com/hashicorp/packer/packer"
2016-03-04 05:14:55 -05:00
)
type Builder struct {
2018-05-01 19:40:40 -04:00
config * Config
stateBag multistep . StateBag
runner multistep . Runner
ctxCancel context . CancelFunc
2016-03-04 05:14:55 -05:00
}
const (
2018-04-06 04:12:58 -04:00
DefaultSasBlobContainer = "system/Microsoft.Compute"
DefaultSecretName = "packerKeyVaultSecret"
2016-03-04 05:14:55 -05:00
)
func ( b * Builder ) Prepare ( raws ... interface { } ) ( [ ] string , error ) {
c , warnings , errs := newConfig ( raws ... )
if errs != nil {
return warnings , errs
}
b . config = c
b . stateBag = new ( multistep . BasicStateBag )
2016-04-21 19:50:03 -04:00
b . configureStateBag ( b . stateBag )
b . setTemplateParameters ( b . stateBag )
2017-05-29 00:06:09 -04:00
b . setImageParameters ( b . stateBag )
2016-03-04 05:14:55 -05:00
return warnings , errs
}
func ( b * Builder ) Run ( ui packer . Ui , hook packer . Hook , cache packer . Cache ) ( packer . Artifact , error ) {
2018-05-18 03:32:01 -04:00
claims := jwt . MapClaims { }
var p jwt . Parser
2016-06-22 19:04:13 -04:00
ui . Say ( "Running builder ..." )
2018-05-01 19:40:40 -04:00
ctx , cancel := context . WithCancel ( context . Background ( ) )
b . ctxCancel = cancel
defer cancel ( )
2016-06-22 19:04:13 -04:00
if err := newConfigRetriever ( ) . FillParameters ( b . config ) ; err != nil {
return nil , err
}
2016-03-04 05:14:55 -05:00
2016-04-29 18:02:28 -04:00
log . Print ( ":: Configuration" )
packerAzureCommon . DumpConfig ( b . config , func ( s string ) { log . Print ( s ) } )
2016-03-04 05:14:55 -05:00
b . stateBag . Put ( "hook" , hook )
b . stateBag . Put ( constants . Ui , ui )
2016-04-21 19:50:03 -04:00
spnCloud , spnKeyVault , err := b . getServicePrincipalTokens ( ui . Say )
2016-03-04 05:14:55 -05:00
if err != nil {
return nil , err
}
ui . Message ( "Creating Azure Resource Manager (ARM) client ..." )
2016-04-21 19:50:03 -04:00
azureClient , err := NewAzureClient (
b . config . SubscriptionID ,
b . config . ResourceGroupName ,
b . config . StorageAccount ,
2016-07-07 20:28:47 -04:00
b . config . cloudEnvironment ,
2016-04-21 19:50:03 -04:00
spnCloud ,
spnKeyVault )
2016-03-04 05:14:55 -05:00
if err != nil {
return nil , err
}
2016-06-30 19:51:52 -04:00
resolver := newResourceResolver ( azureClient )
if err := resolver . Resolve ( b . config ) ; err != nil {
return nil , err
}
2018-05-18 03:32:01 -04:00
_ , _ , err = p . ParseUnverified ( spnCloud . OAuthToken ( ) , claims )
if err != nil {
return nil , err
}
b . config . ObjectID = claims [ "oid" ] . ( string )
if b . config . ObjectID == "" && b . config . OSType != constants . Target_Linux {
ui . Error ( "\n Got empty Object ID in the OAuth token , we need this for Key vault Access, bailing" )
return nil , err
}
2017-05-29 00:06:09 -04:00
if b . config . isManagedImage ( ) {
2018-05-01 19:40:40 -04:00
group , err := azureClient . GroupsClient . Get ( ctx , b . config . ManagedImageResourceGroupName )
2017-05-29 00:06:09 -04:00
if err != nil {
2017-05-30 14:25:46 -04:00
return nil , fmt . Errorf ( "Cannot locate the managed image resource group %s." , b . config . ManagedImageResourceGroupName )
2017-05-29 00:06:09 -04:00
}
2017-05-30 14:25:46 -04:00
b . config . manageImageLocation = * group . Location
2017-05-29 00:06:09 -04:00
// If a managed image already exists it cannot be overwritten.
2018-05-01 19:40:40 -04:00
_ , err = azureClient . ImagesClient . Get ( ctx , b . config . ManagedImageResourceGroupName , b . config . ManagedImageName , "" )
2017-05-29 00:06:09 -04:00
if err == nil {
2018-03-10 13:17:38 -05:00
if b . config . PackerForce {
ui . Say ( fmt . Sprintf ( "the managed image named %s already exists, but deleting it due to -force flag" , b . config . ManagedImageName ) )
2018-05-01 19:40:40 -04:00
f , err := azureClient . ImagesClient . Delete ( ctx , b . config . ManagedImageResourceGroupName , b . config . ManagedImageName )
2018-04-06 04:12:58 -04:00
if err == nil {
2018-05-01 19:40:40 -04:00
err = f . WaitForCompletion ( ctx , azureClient . ImagesClient . Client )
2018-04-06 04:12:58 -04:00
}
2018-03-10 13:17:38 -05:00
if err != nil {
return nil , fmt . Errorf ( "failed to delete the managed image named %s : %s" , b . config . ManagedImageName , azureClient . LastError . Error ( ) )
}
} else {
return nil , fmt . Errorf ( "the managed image named %s already exists in the resource group %s, use the -force option to automatically delete it." , b . config . ManagedImageName , b . config . ManagedImageResourceGroupName )
}
2017-05-29 00:06:09 -04:00
}
2018-05-10 16:15:35 -04:00
} else {
// User is not using Managed Images to build, warning message here that this path is being deprecated
ui . Error ( "Warning: You are using Azure Packer Builder to create VHDs which is being deprecated, consider using Managed Images. Learn more http://aka.ms/packermanagedimage" )
2017-05-29 00:06:09 -04:00
}
2017-11-30 19:43:35 -05:00
if b . config . BuildResourceGroupName != "" {
2018-05-01 19:40:40 -04:00
group , err := azureClient . GroupsClient . Get ( ctx , b . config . BuildResourceGroupName )
2017-11-30 19:43:35 -05:00
if err != nil {
return nil , fmt . Errorf ( "Cannot locate the existing build resource resource group %s." , b . config . BuildResourceGroupName )
}
b . config . Location = * group . Location
}
2017-06-07 18:01:10 -04:00
if b . config . StorageAccount != "" {
2018-05-01 19:40:40 -04:00
account , err := b . getBlobAccount ( ctx , azureClient , b . config . ResourceGroupName , b . config . StorageAccount )
2017-06-07 18:01:10 -04:00
if err != nil {
return nil , err
}
b . config . storageAccountBlobEndpoint = * account . AccountProperties . PrimaryEndpoints . Blob
2017-05-29 00:06:09 -04:00
2017-06-07 18:01:10 -04:00
if ! equalLocation ( * account . Location , b . config . Location ) {
return nil , fmt . Errorf ( "The storage account is located in %s, but the build will take place in %s. The locations must be identical" , * account . Location , b . config . Location )
}
2017-05-29 00:06:09 -04:00
}
2016-04-21 19:50:03 -04:00
2016-06-30 19:51:52 -04:00
endpointConnectType := PublicEndpoint
2017-08-06 18:32:44 -04:00
if b . isPublicPrivateNetworkCommunication ( ) && b . isPrivateNetworkCommunication ( ) {
endpointConnectType = PublicEndpointInPrivateNetwork
} else if b . isPrivateNetworkCommunication ( ) {
2016-06-30 19:51:52 -04:00
endpointConnectType = PrivateEndpoint
}
2017-05-29 00:06:09 -04:00
b . setRuntimeParameters ( b . stateBag )
2016-04-21 19:50:03 -04:00
b . setTemplateParameters ( b . stateBag )
2017-05-29 00:06:09 -04:00
b . setImageParameters ( b . stateBag )
2016-04-21 19:50:03 -04:00
var steps [ ] multistep . Step
2017-11-30 03:11:17 -05:00
deploymentName := b . stateBag . Get ( constants . ArmDeploymentName ) . ( string )
2016-10-12 20:54:59 -04:00
if b . config . OSType == constants . Target_Linux {
2016-04-21 19:50:03 -04:00
steps = [ ] multistep . Step {
NewStepCreateResourceGroup ( azureClient , ui ) ,
2016-05-21 02:01:16 -04:00
NewStepValidateTemplate ( azureClient , ui , b . config , GetVirtualMachineDeployment ) ,
2017-11-30 03:11:17 -05:00
NewStepDeployTemplate ( azureClient , ui , b . config , deploymentName , GetVirtualMachineDeployment ) ,
2016-06-30 19:51:52 -04:00
NewStepGetIPAddress ( azureClient , ui , endpointConnectType ) ,
2016-04-21 19:50:03 -04:00
& communicator . StepConnectSSH {
Config : & b . config . Comm ,
Host : lin . SSHHost ,
SSHConfig : lin . SSHConfig ( b . config . UserName ) ,
} ,
2016-04-29 18:02:28 -04:00
& packerCommon . StepProvision { } ,
2016-04-21 19:50:03 -04:00
NewStepGetOSDisk ( azureClient , ui ) ,
2018-02-26 20:33:40 -05:00
NewStepGetAdditionalDisks ( azureClient , ui ) ,
2016-04-21 19:50:03 -04:00
NewStepPowerOffCompute ( azureClient , ui ) ,
NewStepCaptureImage ( azureClient , ui ) ,
NewStepDeleteResourceGroup ( azureClient , ui ) ,
NewStepDeleteOSDisk ( azureClient , ui ) ,
2018-02-26 20:33:40 -05:00
NewStepDeleteAdditionalDisks ( azureClient , ui ) ,
2016-04-21 19:50:03 -04:00
}
2016-10-12 20:54:59 -04:00
} else if b . config . OSType == constants . Target_Windows {
2017-11-30 03:11:17 -05:00
keyVaultDeploymentName := b . stateBag . Get ( constants . ArmKeyVaultDeploymentName ) . ( string )
2016-04-21 19:50:03 -04:00
steps = [ ] multistep . Step {
NewStepCreateResourceGroup ( azureClient , ui ) ,
2016-05-21 02:01:16 -04:00
NewStepValidateTemplate ( azureClient , ui , b . config , GetKeyVaultDeployment ) ,
2017-11-30 03:11:17 -05:00
NewStepDeployTemplate ( azureClient , ui , b . config , keyVaultDeploymentName , GetKeyVaultDeployment ) ,
2016-04-21 19:50:03 -04:00
NewStepGetCertificate ( azureClient , ui ) ,
NewStepSetCertificate ( b . config , ui ) ,
2016-05-21 02:01:16 -04:00
NewStepValidateTemplate ( azureClient , ui , b . config , GetVirtualMachineDeployment ) ,
2017-11-30 03:11:17 -05:00
NewStepDeployTemplate ( azureClient , ui , b . config , deploymentName , GetVirtualMachineDeployment ) ,
2016-06-30 19:51:52 -04:00
NewStepGetIPAddress ( azureClient , ui , endpointConnectType ) ,
2018-04-05 18:24:54 -04:00
& StepSaveWinRMPassword {
2018-04-16 14:51:04 -04:00
Password : b . config . tmpAdminPassword ,
BuildName : b . config . PackerBuildName ,
2018-04-05 18:24:54 -04:00
} ,
2016-04-21 19:50:03 -04:00
& communicator . StepConnectWinRM {
Config : & b . config . Comm ,
Host : func ( stateBag multistep . StateBag ) ( string , error ) {
return stateBag . Get ( constants . SSHHost ) . ( string ) , nil
} ,
WinRMConfig : func ( multistep . StateBag ) ( * communicator . WinRMConfig , error ) {
return & communicator . WinRMConfig {
Username : b . config . UserName ,
Password : b . config . tmpAdminPassword ,
} , nil
} ,
} ,
2016-04-29 18:02:28 -04:00
& packerCommon . StepProvision { } ,
2016-04-21 19:50:03 -04:00
NewStepGetOSDisk ( azureClient , ui ) ,
2018-02-23 18:34:13 -05:00
NewStepGetAdditionalDisks ( azureClient , ui ) ,
2016-04-21 19:50:03 -04:00
NewStepPowerOffCompute ( azureClient , ui ) ,
NewStepCaptureImage ( azureClient , ui ) ,
NewStepDeleteResourceGroup ( azureClient , ui ) ,
NewStepDeleteOSDisk ( azureClient , ui ) ,
2018-02-23 18:34:13 -05:00
NewStepDeleteAdditionalDisks ( azureClient , ui ) ,
2016-04-21 19:50:03 -04:00
}
} else {
return nil , fmt . Errorf ( "Builder does not support the os_type '%s'" , b . config . OSType )
2016-03-04 05:14:55 -05:00
}
if b . config . PackerDebug {
ui . Message ( fmt . Sprintf ( "temp admin user: '%s'" , b . config . UserName ) )
ui . Message ( fmt . Sprintf ( "temp admin password: '%s'" , b . config . Password ) )
2017-06-26 16:44:34 -04:00
if b . config . sshPrivateKey != "" {
2017-06-27 16:19:21 -04:00
debugKeyPath := fmt . Sprintf ( "%s-%s.pem" , b . config . PackerBuildName , b . config . tmpComputeName )
ui . Message ( fmt . Sprintf ( "temp ssh key: %s" , debugKeyPath ) )
b . writeSSHPrivateKey ( ui , debugKeyPath )
2017-06-26 16:44:34 -04:00
}
2016-03-04 05:14:55 -05:00
}
2016-09-13 20:04:18 -04:00
b . runner = packerCommon . NewRunner ( steps , b . config . PackerConfig , ui )
2016-03-04 05:14:55 -05:00
b . runner . Run ( b . stateBag )
// Report any errors.
if rawErr , ok := b . stateBag . GetOk ( constants . Error ) ; ok {
return nil , rawErr . ( error )
}
// If we were interrupted or cancelled, then just exit.
if _ , ok := b . stateBag . GetOk ( multistep . StateCancelled ) ; ok {
return nil , errors . New ( "Build was cancelled." )
}
if _ , ok := b . stateBag . GetOk ( multistep . StateHalted ) ; ok {
return nil , errors . New ( "Build was halted." )
}
2017-06-07 18:01:10 -04:00
if b . config . isManagedImage ( ) {
return NewManagedImageArtifact ( b . config . ManagedImageResourceGroupName , b . config . ManagedImageName , b . config . manageImageLocation )
} else if template , ok := b . stateBag . GetOk ( constants . ArmCaptureTemplate ) ; ok {
2016-04-21 19:50:03 -04:00
return NewArtifact (
template . ( * CaptureTemplate ) ,
func ( name string ) string {
2017-05-28 03:38:45 -04:00
blob := azureClient . BlobStorageClient . GetContainerReference ( DefaultSasBlobContainer ) . GetBlobReference ( name )
2018-04-06 04:12:58 -04:00
options := storage . BlobSASOptions { }
options . BlobServiceSASPermissions . Read = true
options . Expiry = time . Now ( ) . AddDate ( 0 , 1 , 0 ) . UTC ( ) // one month
sasUrl , _ := blob . GetSASURI ( options )
2016-04-21 19:50:03 -04:00
return sasUrl
} )
}
return & Artifact { } , nil
2016-03-04 05:14:55 -05:00
}
2017-06-27 16:19:21 -04:00
func ( b * Builder ) writeSSHPrivateKey ( ui packer . Ui , debugKeyPath string ) {
f , err := os . Create ( debugKeyPath )
if err != nil {
ui . Say ( fmt . Sprintf ( "Error saving debug key: %s" , err ) )
}
defer f . Close ( )
// Write the key out
if _ , err := f . Write ( [ ] byte ( b . config . sshPrivateKey ) ) ; err != nil {
ui . Say ( fmt . Sprintf ( "Error saving debug key: %s" , err ) )
return
}
// Chmod it so that it is SSH ready
if runtime . GOOS != "windows" {
if err := f . Chmod ( 0600 ) ; err != nil {
ui . Say ( fmt . Sprintf ( "Error setting permissions of debug key: %s" , err ) )
}
}
}
2017-08-06 18:32:44 -04:00
func ( b * Builder ) isPublicPrivateNetworkCommunication ( ) bool {
return DefaultPrivateVirtualNetworkWithPublicIp != b . config . PrivateVirtualNetworkWithPublicIp
}
2016-06-30 19:51:52 -04:00
func ( b * Builder ) isPrivateNetworkCommunication ( ) bool {
return b . config . VirtualNetworkName != ""
}
2016-03-04 05:14:55 -05:00
func ( b * Builder ) Cancel ( ) {
2018-05-01 19:40:40 -04:00
if b . ctxCancel != nil {
log . Printf ( "Cancelling Azure builder..." )
b . ctxCancel ( )
}
2016-03-04 05:14:55 -05:00
if b . runner != nil {
log . Println ( "Cancelling the step runner..." )
b . runner . Cancel ( )
}
}
2017-05-29 00:06:09 -04:00
func equalLocation ( location1 , location2 string ) bool {
return strings . EqualFold ( canonicalizeLocation ( location1 ) , canonicalizeLocation ( location2 ) )
}
func canonicalizeLocation ( location string ) string {
return strings . Replace ( location , " " , "" , - 1 )
}
2018-05-01 19:40:40 -04:00
func ( b * Builder ) getBlobAccount ( ctx context . Context , client * AzureClient , resourceGroupName string , storageAccountName string ) ( * armstorage . Account , error ) {
account , err := client . AccountsClient . GetProperties ( ctx , resourceGroupName , storageAccountName )
2016-04-21 19:50:03 -04:00
if err != nil {
2017-05-29 00:06:09 -04:00
return nil , err
2016-04-21 19:50:03 -04:00
}
2017-05-29 00:06:09 -04:00
return & account , err
2016-04-21 19:50:03 -04:00
}
func ( b * Builder ) configureStateBag ( stateBag multistep . StateBag ) {
2016-03-04 05:14:55 -05:00
stateBag . Put ( constants . AuthorizedKey , b . config . sshAuthorizedKey )
stateBag . Put ( constants . PrivateKey , b . config . sshPrivateKey )
2018-04-11 11:25:33 -04:00
stateBag . Put ( constants . ArmTags , b . config . AzureTags )
2016-03-04 05:14:55 -05:00
stateBag . Put ( constants . ArmComputeName , b . config . tmpComputeName )
stateBag . Put ( constants . ArmDeploymentName , b . config . tmpDeploymentName )
2017-11-30 03:11:17 -05:00
if b . config . OSType == constants . Target_Windows {
stateBag . Put ( constants . ArmKeyVaultDeploymentName , fmt . Sprintf ( "kv%s" , b . config . tmpDeploymentName ) )
}
2016-04-21 19:50:03 -04:00
stateBag . Put ( constants . ArmKeyVaultName , b . config . tmpKeyVaultName )
2018-03-10 14:17:43 -05:00
stateBag . Put ( constants . ArmNicName , b . config . tmpNicName )
stateBag . Put ( constants . ArmPublicIPAddressName , b . config . tmpPublicIPAddressName )
2017-11-09 06:20:09 -05:00
if b . config . TempResourceGroupName != "" && b . config . BuildResourceGroupName != "" {
stateBag . Put ( constants . ArmDoubleResourceGroupNameSet , true )
}
if b . config . tmpResourceGroupName != "" {
stateBag . Put ( constants . ArmResourceGroupName , b . config . tmpResourceGroupName )
stateBag . Put ( constants . ArmIsExistingResourceGroup , false )
} else {
stateBag . Put ( constants . ArmResourceGroupName , b . config . BuildResourceGroupName )
stateBag . Put ( constants . ArmIsExistingResourceGroup , true )
}
2016-04-21 19:50:03 -04:00
stateBag . Put ( constants . ArmStorageAccountName , b . config . StorageAccount )
2017-05-29 00:06:09 -04:00
stateBag . Put ( constants . ArmIsManagedImage , b . config . isManagedImage ( ) )
2017-05-30 14:25:46 -04:00
stateBag . Put ( constants . ArmManagedImageResourceGroupName , b . config . ManagedImageResourceGroupName )
stateBag . Put ( constants . ArmManagedImageName , b . config . ManagedImageName )
2018-05-15 14:41:26 -04:00
stateBag . Put ( constants . ArmAsyncResourceGroupDelete , b . config . AsyncResourceGroupDelete )
2017-05-29 00:06:09 -04:00
}
// Parameters that are only known at runtime after querying Azure.
func ( b * Builder ) setRuntimeParameters ( stateBag multistep . StateBag ) {
2017-11-30 19:43:35 -05:00
stateBag . Put ( constants . ArmLocation , b . config . Location )
2017-05-30 14:25:46 -04:00
stateBag . Put ( constants . ArmManagedImageLocation , b . config . manageImageLocation )
2016-04-21 19:50:03 -04:00
}
func ( b * Builder ) setTemplateParameters ( stateBag multistep . StateBag ) {
2016-03-04 05:14:55 -05:00
stateBag . Put ( constants . ArmVirtualMachineCaptureParameters , b . config . toVirtualMachineCaptureParameters ( ) )
2016-04-21 19:50:03 -04:00
}
2016-03-04 05:14:55 -05:00
2017-05-29 00:06:09 -04:00
func ( b * Builder ) setImageParameters ( stateBag multistep . StateBag ) {
stateBag . Put ( constants . ArmImageParameters , b . config . toImageParameters ( ) )
}
2017-05-28 03:38:45 -04:00
func ( b * Builder ) getServicePrincipalTokens ( say func ( string ) ) ( * adal . ServicePrincipalToken , * adal . ServicePrincipalToken , error ) {
var servicePrincipalToken * adal . ServicePrincipalToken
var servicePrincipalTokenVault * adal . ServicePrincipalToken
2016-03-04 05:14:55 -05:00
2016-04-21 19:50:03 -04:00
var err error
2016-03-04 05:14:55 -05:00
2016-04-21 19:50:03 -04:00
if b . config . useDeviceLogin {
2018-05-18 03:32:01 -04:00
servicePrincipalToken , err = packerAzureCommon . Authenticate ( * b . config . cloudEnvironment , b . config . TenantID , say , b . config . cloudEnvironment . ServiceManagementEndpoint )
2016-04-21 19:50:03 -04:00
if err != nil {
return nil , nil , err
}
2018-05-18 03:32:01 -04:00
servicePrincipalTokenVault , err = packerAzureCommon . Authenticate ( * b . config . cloudEnvironment , b . config . TenantID , say , b . config . cloudEnvironment . KeyVaultEndpoint )
if err != nil {
return nil , nil , err
}
2016-04-21 19:50:03 -04:00
} else {
auth := NewAuthenticate ( * b . config . cloudEnvironment , b . config . ClientID , b . config . ClientSecret , b . config . TenantID )
2016-03-04 05:14:55 -05:00
2016-04-21 19:50:03 -04:00
servicePrincipalToken , err = auth . getServicePrincipalToken ( )
if err != nil {
return nil , nil , err
}
2018-05-18 03:32:01 -04:00
servicePrincipalToken . EnsureFresh ( )
2016-04-21 19:50:03 -04:00
2016-07-07 20:28:47 -04:00
servicePrincipalTokenVault , err = auth . getServicePrincipalTokenWithResource (
strings . TrimRight ( b . config . cloudEnvironment . KeyVaultEndpoint , "/" ) )
2016-04-21 19:50:03 -04:00
if err != nil {
return nil , nil , err
}
2018-05-18 03:32:01 -04:00
servicePrincipalTokenVault . EnsureFresh ( )
2016-04-21 19:50:03 -04:00
}
2016-03-04 05:14:55 -05:00
2016-04-21 19:50:03 -04:00
return servicePrincipalToken , servicePrincipalTokenVault , nil
2016-03-04 05:14:55 -05:00
}