github.com/yoctocloud/packer@v0.6.2-0.20160520224004-e11a0a18423f/builder/azure/arm/builder.go (about) 1 // Copyright (c) Microsoft Corporation. All rights reserved. 2 // Licensed under the MIT License. See the LICENSE file in builder/azure for license information. 3 4 package arm 5 6 import ( 7 "errors" 8 "fmt" 9 "log" 10 "time" 11 12 packerAzureCommon "github.com/mitchellh/packer/builder/azure/common" 13 14 "github.com/Azure/go-autorest/autorest/azure" 15 16 "github.com/mitchellh/packer/builder/azure/common/constants" 17 "github.com/mitchellh/packer/builder/azure/common/lin" 18 19 "github.com/mitchellh/multistep" 20 packerCommon "github.com/mitchellh/packer/common" 21 "github.com/mitchellh/packer/helper/communicator" 22 "github.com/mitchellh/packer/packer" 23 ) 24 25 type Builder struct { 26 config *Config 27 stateBag multistep.StateBag 28 runner multistep.Runner 29 } 30 31 const ( 32 DefaultPublicIPAddressName = "packerPublicIP" 33 DefaultSasBlobContainer = "system/Microsoft.Compute" 34 DefaultSasBlobPermission = "r" 35 DefaultSecretName = "packerKeyVaultSecret" 36 ) 37 38 func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { 39 c, warnings, errs := newConfig(raws...) 40 if errs != nil { 41 return warnings, errs 42 } 43 44 b.config = c 45 46 b.stateBag = new(multistep.BasicStateBag) 47 b.configureStateBag(b.stateBag) 48 b.setTemplateParameters(b.stateBag) 49 50 return warnings, errs 51 } 52 53 func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { 54 ui.Say("Preparing builder ...") 55 56 log.Print(":: Configuration") 57 packerAzureCommon.DumpConfig(b.config, func(s string) { log.Print(s) }) 58 59 b.stateBag.Put("hook", hook) 60 b.stateBag.Put(constants.Ui, ui) 61 62 spnCloud, spnKeyVault, err := b.getServicePrincipalTokens(ui.Say) 63 if err != nil { 64 return nil, err 65 } 66 67 ui.Message("Creating Azure Resource Manager (ARM) client ...") 68 azureClient, err := NewAzureClient( 69 b.config.SubscriptionID, 70 b.config.ResourceGroupName, 71 b.config.StorageAccount, 72 spnCloud, 73 spnKeyVault) 74 75 if err != nil { 76 return nil, err 77 } 78 79 b.config.storageAccountBlobEndpoint, err = b.getBlobEndpoint(azureClient, b.config.ResourceGroupName, b.config.StorageAccount) 80 if err != nil { 81 return nil, err 82 } 83 84 b.setTemplateParameters(b.stateBag) 85 86 var steps []multistep.Step 87 88 if b.config.OSType == constants.Target_Linux { 89 steps = []multistep.Step{ 90 NewStepCreateResourceGroup(azureClient, ui), 91 NewStepValidateTemplate(azureClient, ui, Linux), 92 NewStepDeployTemplate(azureClient, ui, Linux), 93 NewStepGetIPAddress(azureClient, ui), 94 &communicator.StepConnectSSH{ 95 Config: &b.config.Comm, 96 Host: lin.SSHHost, 97 SSHConfig: lin.SSHConfig(b.config.UserName), 98 }, 99 &packerCommon.StepProvision{}, 100 NewStepGetOSDisk(azureClient, ui), 101 NewStepPowerOffCompute(azureClient, ui), 102 NewStepCaptureImage(azureClient, ui), 103 NewStepDeleteResourceGroup(azureClient, ui), 104 NewStepDeleteOSDisk(azureClient, ui), 105 } 106 } else if b.config.OSType == constants.Target_Windows { 107 steps = []multistep.Step{ 108 NewStepCreateResourceGroup(azureClient, ui), 109 NewStepValidateTemplate(azureClient, ui, KeyVault), 110 NewStepDeployTemplate(azureClient, ui, KeyVault), 111 NewStepGetCertificate(azureClient, ui), 112 NewStepSetCertificate(b.config, ui), 113 NewStepValidateTemplate(azureClient, ui, Windows), 114 NewStepDeployTemplate(azureClient, ui, Windows), 115 NewStepGetIPAddress(azureClient, ui), 116 &communicator.StepConnectWinRM{ 117 Config: &b.config.Comm, 118 Host: func(stateBag multistep.StateBag) (string, error) { 119 return stateBag.Get(constants.SSHHost).(string), nil 120 }, 121 WinRMConfig: func(multistep.StateBag) (*communicator.WinRMConfig, error) { 122 return &communicator.WinRMConfig{ 123 Username: b.config.UserName, 124 Password: b.config.tmpAdminPassword, 125 }, nil 126 }, 127 }, 128 &packerCommon.StepProvision{}, 129 NewStepGetOSDisk(azureClient, ui), 130 NewStepPowerOffCompute(azureClient, ui), 131 NewStepCaptureImage(azureClient, ui), 132 NewStepDeleteResourceGroup(azureClient, ui), 133 NewStepDeleteOSDisk(azureClient, ui), 134 } 135 } else { 136 return nil, fmt.Errorf("Builder does not support the os_type '%s'", b.config.OSType) 137 } 138 139 if b.config.PackerDebug { 140 ui.Message(fmt.Sprintf("temp admin user: '%s'", b.config.UserName)) 141 ui.Message(fmt.Sprintf("temp admin password: '%s'", b.config.Password)) 142 } 143 144 b.runner = b.createRunner(&steps, ui) 145 b.runner.Run(b.stateBag) 146 147 // Report any errors. 148 if rawErr, ok := b.stateBag.GetOk(constants.Error); ok { 149 return nil, rawErr.(error) 150 } 151 152 // If we were interrupted or cancelled, then just exit. 153 if _, ok := b.stateBag.GetOk(multistep.StateCancelled); ok { 154 return nil, errors.New("Build was cancelled.") 155 } 156 157 if _, ok := b.stateBag.GetOk(multistep.StateHalted); ok { 158 return nil, errors.New("Build was halted.") 159 } 160 161 if template, ok := b.stateBag.GetOk(constants.ArmCaptureTemplate); ok { 162 return NewArtifact( 163 template.(*CaptureTemplate), 164 func(name string) string { 165 month := time.Now().AddDate(0, 1, 0).UTC() 166 sasUrl, _ := azureClient.BlobStorageClient.GetBlobSASURI(DefaultSasBlobContainer, name, month, DefaultSasBlobPermission) 167 return sasUrl 168 }) 169 } 170 171 return &Artifact{}, nil 172 } 173 174 func (b *Builder) Cancel() { 175 if b.runner != nil { 176 log.Println("Cancelling the step runner...") 177 b.runner.Cancel() 178 } 179 } 180 181 func (b *Builder) createRunner(steps *[]multistep.Step, ui packer.Ui) multistep.Runner { 182 if b.config.PackerDebug { 183 return &multistep.DebugRunner{ 184 Steps: *steps, 185 PauseFn: packerCommon.MultistepDebugFn(ui), 186 } 187 } 188 189 return &multistep.BasicRunner{ 190 Steps: *steps, 191 } 192 } 193 194 func (b *Builder) getBlobEndpoint(client *AzureClient, resourceGroupName string, storageAccountName string) (string, error) { 195 account, err := client.AccountsClient.GetProperties(resourceGroupName, storageAccountName) 196 if err != nil { 197 return "", err 198 } 199 200 return *account.Properties.PrimaryEndpoints.Blob, nil 201 } 202 203 func (b *Builder) configureStateBag(stateBag multistep.StateBag) { 204 stateBag.Put(constants.AuthorizedKey, b.config.sshAuthorizedKey) 205 stateBag.Put(constants.PrivateKey, b.config.sshPrivateKey) 206 207 stateBag.Put(constants.ArmComputeName, b.config.tmpComputeName) 208 stateBag.Put(constants.ArmDeploymentName, b.config.tmpDeploymentName) 209 stateBag.Put(constants.ArmKeyVaultName, b.config.tmpKeyVaultName) 210 stateBag.Put(constants.ArmLocation, b.config.Location) 211 stateBag.Put(constants.ArmPublicIPAddressName, DefaultPublicIPAddressName) 212 stateBag.Put(constants.ArmResourceGroupName, b.config.tmpResourceGroupName) 213 stateBag.Put(constants.ArmStorageAccountName, b.config.StorageAccount) 214 } 215 216 func (b *Builder) setTemplateParameters(stateBag multistep.StateBag) { 217 stateBag.Put(constants.ArmTemplateParameters, b.config.toTemplateParameters()) 218 stateBag.Put(constants.ArmVirtualMachineCaptureParameters, b.config.toVirtualMachineCaptureParameters()) 219 } 220 221 func (b *Builder) getServicePrincipalTokens(say func(string)) (*azure.ServicePrincipalToken, *azure.ServicePrincipalToken, error) { 222 var servicePrincipalToken *azure.ServicePrincipalToken 223 var servicePrincipalTokenVault *azure.ServicePrincipalToken 224 225 var err error 226 227 if b.config.useDeviceLogin { 228 servicePrincipalToken, err = packerAzureCommon.Authenticate(*b.config.cloudEnvironment, b.config.SubscriptionID, say) 229 if err != nil { 230 return nil, nil, err 231 } 232 } else { 233 auth := NewAuthenticate(*b.config.cloudEnvironment, b.config.ClientID, b.config.ClientSecret, b.config.TenantID) 234 235 servicePrincipalToken, err = auth.getServicePrincipalToken() 236 if err != nil { 237 return nil, nil, err 238 } 239 240 servicePrincipalTokenVault, err = auth.getServicePrincipalTokenWithResource(packerAzureCommon.AzureVaultScope) 241 if err != nil { 242 return nil, nil, err 243 } 244 } 245 246 return servicePrincipalToken, servicePrincipalTokenVault, nil 247 }